use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
use ratatui::style::{Color, Modifier, Style};
use ratatui::widgets::Widget;
use crate::editor::Editor;
pub struct ReferencesPopup<'a> {
editor: &'a Editor,
}
impl<'a> ReferencesPopup<'a> {
pub fn new(editor: &'a Editor) -> Self {
Self { editor }
}
}
impl Widget for ReferencesPopup<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
if !self.editor.showing_references || self.editor.references.is_empty() {
return;
}
let refs = &self.editor.references;
let selected = self.editor.reference_index;
let items: Vec<String> = refs
.iter()
.map(|r| {
let name = r
.uri
.rsplit('/')
.next()
.unwrap_or(&r.uri);
format!("{}:{}:{}", name, r.start_line + 1, r.start_col + 1)
})
.collect();
let max_items = 15.min(items.len());
let popup_height = (max_items as u16 + 2).min(area.height.saturating_sub(2));
let max_label_len = items.iter().take(max_items).map(|s| s.len()).max().unwrap_or(20);
let popup_width = (max_label_len as u16 + 4)
.max(20)
.min(area.width.saturating_sub(4));
let popup_x = area.x + (area.width.saturating_sub(popup_width)) / 2;
let popup_y = area.y + (area.height.saturating_sub(popup_height)) / 2;
let border_style = Style::default().fg(Color::Rgb(80, 90, 110)).bg(Color::Rgb(30, 33, 40));
let normal_style = Style::default().fg(Color::White).bg(Color::Rgb(30, 33, 40));
let selected_style = Style::default()
.fg(Color::White)
.bg(Color::Rgb(80, 90, 110))
.add_modifier(Modifier::BOLD);
for dy in 0..popup_height {
let y = popup_y + dy;
if y >= area.bottom() {
break;
}
for dx in 0..popup_width {
let x = popup_x + dx;
if x >= area.right() {
break;
}
let is_border = dy == 0 || dy == popup_height - 1 || dx == 0 || dx == popup_width - 1;
let ch = if is_border {
if dy == 0 && dx == 0 {
'┌'
} else if dy == 0 && dx == popup_width - 1 {
'┐'
} else if dy == popup_height - 1 && dx == 0 {
'└'
} else if dy == popup_height - 1 && dx == popup_width - 1 {
'┘'
} else if dy == 0 || dy == popup_height - 1 {
'─'
} else {
'│'
}
} else {
' '
};
buf[(x, y)].set_char(ch).set_style(if is_border { border_style } else { normal_style });
}
}
let title = " References ";
let title_x = popup_x + (popup_width.saturating_sub(title.len() as u16)) / 2;
for (i, ch) in title.chars().enumerate() {
let x = title_x + i as u16;
if x < area.right() && x > popup_x && x < popup_x + popup_width - 1 {
buf[(x, popup_y)].set_char(ch).set_style(
Style::default()
.fg(Color::Yellow)
.bg(Color::Rgb(30, 33, 40))
.add_modifier(Modifier::BOLD),
);
}
}
let content_height = (popup_height - 2) as usize;
let scroll = if selected >= content_height {
selected - content_height + 1
} else {
0
};
for i in 0..content_height {
let item_idx = scroll + i;
if item_idx >= items.len() {
break;
}
let y = popup_y + 1 + i as u16;
if y >= popup_y + popup_height - 1 {
break;
}
let style = if item_idx == selected {
selected_style
} else {
normal_style
};
for dx in 1..popup_width - 1 {
let x = popup_x + dx;
if x < area.right() {
buf[(x, y)].set_char(' ').set_style(style);
}
}
let label = &items[item_idx];
for (j, ch) in label.chars().enumerate() {
let x = popup_x + 2 + j as u16;
if x >= popup_x + popup_width - 1 || x >= area.right() {
break;
}
buf[(x, y)].set_char(ch).set_style(style);
}
}
}
}