@@ -15,7 +15,7 @@ use unicode_width::UnicodeWidthStr; |
| 15 | 15 | use crate::buffer::Buffer; |
| 16 | 16 | use crate::editor::{Cursors, Position}; |
| 17 | 17 | use crate::fuss::VisibleItem; |
| 18 | | -use crate::lsp::{CompletionItem, Diagnostic, DiagnosticSeverity, HoverInfo, ServerManagerPanel}; |
| 18 | +use crate::lsp::{CompletionItem, Diagnostic, DiagnosticSeverity, HoverInfo, Location, ServerManagerPanel}; |
| 19 | 19 | use crate::syntax::{Highlighter, HighlightState, Token}; |
| 20 | 20 | |
| 21 | 21 | // Editor color scheme (256-color palette) |
@@ -1947,6 +1947,181 @@ impl Screen { |
| 1947 | 1947 | Ok(()) |
| 1948 | 1948 | } |
| 1949 | 1949 | |
| 1950 | + /// Render the LSP references panel (sidebar style) |
| 1951 | + pub fn render_references_panel( |
| 1952 | + &mut self, |
| 1953 | + locations: &[Location], |
| 1954 | + selected_index: usize, |
| 1955 | + query: &str, |
| 1956 | + workspace_root: &std::path::Path, |
| 1957 | + ) -> Result<()> { |
| 1958 | + let (width, height) = (self.cols as usize, self.rows as usize); |
| 1959 | + |
| 1960 | + // Panel dimensions - sidebar style on the right |
| 1961 | + let panel_width = 50.min(width / 2); |
| 1962 | + let panel_height = height.saturating_sub(3); // Leave room for tab bar and status bar |
| 1963 | + let start_col = width.saturating_sub(panel_width); |
| 1964 | + let start_row = 1u16; // Below tab bar |
| 1965 | + |
| 1966 | + // Filter locations based on query |
| 1967 | + let filtered: Vec<(usize, &Location)> = if query.is_empty() { |
| 1968 | + locations.iter().enumerate().collect() |
| 1969 | + } else { |
| 1970 | + let q = query.to_lowercase(); |
| 1971 | + locations.iter().enumerate() |
| 1972 | + .filter(|(_, loc)| loc.uri.to_lowercase().contains(&q)) |
| 1973 | + .collect() |
| 1974 | + }; |
| 1975 | + |
| 1976 | + // Colors |
| 1977 | + let bg = Color::AnsiValue(235); |
| 1978 | + let border_color = Color::AnsiValue(244); |
| 1979 | + let header_color = Color::Cyan; |
| 1980 | + let file_color = Color::AnsiValue(252); |
| 1981 | + let line_num_color = Color::AnsiValue(243); |
| 1982 | + let selected_bg = Color::AnsiValue(240); |
| 1983 | + let input_bg = Color::AnsiValue(238); |
| 1984 | + |
| 1985 | + // Draw top border with title |
| 1986 | + let title = format!(" References ({}) ", filtered.len()); |
| 1987 | + execute!( |
| 1988 | + self.stdout, |
| 1989 | + MoveTo(start_col as u16, start_row), |
| 1990 | + SetBackgroundColor(bg), |
| 1991 | + SetForegroundColor(border_color), |
| 1992 | + Print("┌"), |
| 1993 | + SetForegroundColor(header_color), |
| 1994 | + Print(&title), |
| 1995 | + SetForegroundColor(border_color), |
| 1996 | + Print(format!("{:─<width$}┐", "", width = panel_width.saturating_sub(title.len() + 2))), |
| 1997 | + )?; |
| 1998 | + |
| 1999 | + // Draw filter input row |
| 2000 | + execute!( |
| 2001 | + self.stdout, |
| 2002 | + MoveTo(start_col as u16, start_row + 1), |
| 2003 | + SetBackgroundColor(bg), |
| 2004 | + SetForegroundColor(border_color), |
| 2005 | + Print("│ "), |
| 2006 | + SetForegroundColor(Color::AnsiValue(248)), |
| 2007 | + Print("Filter: "), |
| 2008 | + SetBackgroundColor(input_bg), |
| 2009 | + SetForegroundColor(Color::White), |
| 2010 | + Print(format!("{:<width$}", query, width = panel_width.saturating_sub(12))), |
| 2011 | + SetBackgroundColor(bg), |
| 2012 | + SetForegroundColor(border_color), |
| 2013 | + Print("│"), |
| 2014 | + )?; |
| 2015 | + |
| 2016 | + // Draw separator |
| 2017 | + execute!( |
| 2018 | + self.stdout, |
| 2019 | + MoveTo(start_col as u16, start_row + 2), |
| 2020 | + SetBackgroundColor(bg), |
| 2021 | + SetForegroundColor(border_color), |
| 2022 | + Print(format!("├{:─<width$}┤", "", width = panel_width.saturating_sub(2))), |
| 2023 | + )?; |
| 2024 | + |
| 2025 | + // Calculate visible range with scrolling |
| 2026 | + let visible_rows = panel_height.saturating_sub(5); // Account for borders, title, filter, help |
| 2027 | + let scroll_offset = if selected_index >= visible_rows { |
| 2028 | + selected_index - visible_rows + 1 |
| 2029 | + } else { |
| 2030 | + 0 |
| 2031 | + }; |
| 2032 | + |
| 2033 | + // Draw reference items |
| 2034 | + for (display_idx, (_orig_idx, loc)) in filtered.iter().enumerate().skip(scroll_offset).take(visible_rows) { |
| 2035 | + let row = start_row + 3 + (display_idx - scroll_offset) as u16; |
| 2036 | + let is_selected = display_idx == selected_index; |
| 2037 | + |
| 2038 | + // Extract relative path and line number |
| 2039 | + let path_str = if loc.uri.starts_with("file://") { |
| 2040 | + &loc.uri[7..] |
| 2041 | + } else { |
| 2042 | + &loc.uri |
| 2043 | + }; |
| 2044 | + |
| 2045 | + // Make path relative to workspace if possible |
| 2046 | + let display_path = if let Ok(rel_path) = std::path::Path::new(path_str).strip_prefix(workspace_root) { |
| 2047 | + rel_path.to_string_lossy().to_string() |
| 2048 | + } else { |
| 2049 | + // Just show filename if we can't make it relative |
| 2050 | + std::path::Path::new(path_str) |
| 2051 | + .file_name() |
| 2052 | + .map(|n| n.to_string_lossy().to_string()) |
| 2053 | + .unwrap_or_else(|| path_str.to_string()) |
| 2054 | + }; |
| 2055 | + |
| 2056 | + let line_info = format!(":{}", loc.range.start.line + 1); |
| 2057 | + let max_path_width = panel_width.saturating_sub(line_info.len() + 4); |
| 2058 | + let truncated_path = if display_path.len() > max_path_width { |
| 2059 | + format!("...{}", &display_path[display_path.len().saturating_sub(max_path_width - 3)..]) |
| 2060 | + } else { |
| 2061 | + display_path |
| 2062 | + }; |
| 2063 | + |
| 2064 | + let item_bg = if is_selected { selected_bg } else { bg }; |
| 2065 | + |
| 2066 | + execute!( |
| 2067 | + self.stdout, |
| 2068 | + MoveTo(start_col as u16, row), |
| 2069 | + SetBackgroundColor(item_bg), |
| 2070 | + SetForegroundColor(border_color), |
| 2071 | + Print("│ "), |
| 2072 | + SetForegroundColor(file_color), |
| 2073 | + Print(format!("{:<width$}", truncated_path, width = max_path_width)), |
| 2074 | + SetForegroundColor(line_num_color), |
| 2075 | + Print(&line_info), |
| 2076 | + SetForegroundColor(border_color), |
| 2077 | + Print(format!("{:>width$}│", "", width = panel_width.saturating_sub(truncated_path.len() + line_info.len() + 3))), |
| 2078 | + )?; |
| 2079 | + } |
| 2080 | + |
| 2081 | + // Fill remaining rows with empty space |
| 2082 | + let items_drawn = filtered.len().saturating_sub(scroll_offset).min(visible_rows); |
| 2083 | + for i in items_drawn..visible_rows { |
| 2084 | + let row = start_row + 3 + i as u16; |
| 2085 | + execute!( |
| 2086 | + self.stdout, |
| 2087 | + MoveTo(start_col as u16, row), |
| 2088 | + SetBackgroundColor(bg), |
| 2089 | + SetForegroundColor(border_color), |
| 2090 | + Print(format!("│{:width$}│", "", width = panel_width.saturating_sub(2))), |
| 2091 | + )?; |
| 2092 | + } |
| 2093 | + |
| 2094 | + // Draw help text row |
| 2095 | + let help_row = start_row + 3 + visible_rows as u16; |
| 2096 | + let help_text = "↑↓:nav Enter:go Esc:close"; |
| 2097 | + execute!( |
| 2098 | + self.stdout, |
| 2099 | + MoveTo(start_col as u16, help_row), |
| 2100 | + SetBackgroundColor(bg), |
| 2101 | + SetForegroundColor(border_color), |
| 2102 | + Print("├"), |
| 2103 | + SetForegroundColor(Color::AnsiValue(243)), |
| 2104 | + Print(format!(" {:<width$}", help_text, width = panel_width.saturating_sub(3))), |
| 2105 | + SetForegroundColor(border_color), |
| 2106 | + Print("┤"), |
| 2107 | + )?; |
| 2108 | + |
| 2109 | + // Draw bottom border |
| 2110 | + execute!( |
| 2111 | + self.stdout, |
| 2112 | + MoveTo(start_col as u16, help_row + 1), |
| 2113 | + SetBackgroundColor(bg), |
| 2114 | + SetForegroundColor(border_color), |
| 2115 | + Print(format!("└{:─<width$}┘", "", width = panel_width.saturating_sub(2))), |
| 2116 | + ResetColor, |
| 2117 | + )?; |
| 2118 | + |
| 2119 | + // Hide cursor when in references panel |
| 2120 | + execute!(self.stdout, Hide)?; |
| 2121 | + |
| 2122 | + Ok(()) |
| 2123 | + } |
| 2124 | + |
| 1950 | 2125 | /// Render the LSP server manager panel |
| 1951 | 2126 | pub fn render_server_manager_panel(&mut self, panel: &ServerManagerPanel) -> Result<()> { |
| 1952 | 2127 | if !panel.visible { |