aboutsummaryrefslogtreecommitdiff
path: root/helix-term/src
diff options
context:
space:
mode:
Diffstat (limited to 'helix-term/src')
-rw-r--r--helix-term/src/ui/picker.rs330
1 files changed, 167 insertions, 163 deletions
diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs
index 627f9472..e11dd1b7 100644
--- a/helix-term/src/ui/picker.rs
+++ b/helix-term/src/ui/picker.rs
@@ -490,6 +490,172 @@ impl<T: Item + 'static> FilePicker<T> {
}
fn render_picker(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) {
+ let text_style = cx.editor.theme.get("ui.text");
+ let selected = cx.editor.theme.get("ui.text.focus");
+ let highlight_style = cx.editor.theme.get("special").add_modifier(Modifier::BOLD);
+
+ // -- Render the frame:
+ // clear area
+ let background = cx.editor.theme.get("ui.background");
+ surface.clear_with(area, background);
+
+ // don't like this but the lifetime sucks
+ let block = Block::default().borders(Borders::ALL);
+
+ // calculate the inner area inside the box
+ let inner = block.inner(area);
+
+ block.render(area, surface);
+
+ // -- Render the input bar:
+
+ let area = inner.clip_left(1).with_height(1);
+
+ let count = format!("{}/{}", self.matches.len(), self.options.len());
+ surface.set_stringn(
+ (area.x + area.width).saturating_sub(count.len() as u16 + 1),
+ area.y,
+ &count,
+ (count.len()).min(area.width as usize),
+ text_style,
+ );
+
+ self.prompt.render(area, surface, cx);
+
+ // -- Separator
+ let sep_style = cx.editor.theme.get("ui.background.separator");
+ let borders = BorderType::line_symbols(BorderType::Plain);
+ for x in inner.left()..inner.right() {
+ if let Some(cell) = surface.get_mut(x, inner.y + 1) {
+ cell.set_symbol(borders.horizontal).set_style(sep_style);
+ }
+ }
+
+ // -- Render the contents:
+ // subtract area of prompt from top
+ let inner = inner.clip_top(2);
+
+ let rows = inner.height;
+ let offset = self.cursor - (self.cursor % std::cmp::max(1, rows as usize));
+ let cursor = self.cursor.saturating_sub(offset);
+
+ let options = self
+ .matches
+ .iter()
+ .skip(offset)
+ .take(rows as usize)
+ .map(|pmatch| &self.options[pmatch.index])
+ .map(|option| option.format(&self.editor_data))
+ .map(|mut row| {
+ const TEMP_CELL_SEP: &str = " ";
+
+ let line = row.cell_text().fold(String::new(), |mut s, frag| {
+ s.push_str(&frag);
+ s.push_str(TEMP_CELL_SEP);
+ s
+ });
+
+ // Items are filtered by using the text returned by menu::Item::filter_text
+ // but we do highlighting here using the text in Row and therefore there
+ // might be inconsistencies. This is the best we can do since only the
+ // text in Row is displayed to the end user.
+ let (_score, highlights) = FuzzyQuery::new(self.prompt.line())
+ .fuzzy_indices(&line, &self.matcher)
+ .unwrap_or_default();
+
+ let highlight_byte_ranges: Vec<_> = line
+ .char_indices()
+ .enumerate()
+ .filter_map(|(char_idx, (byte_offset, ch))| {
+ highlights
+ .contains(&char_idx)
+ .then(|| byte_offset..byte_offset + ch.len_utf8())
+ })
+ .collect();
+
+ // The starting byte index of the current (iterating) cell
+ let mut cell_start_byte_offset = 0;
+ for cell in row.cells.iter_mut() {
+ let spans = match cell.content.lines.get(0) {
+ Some(s) => s,
+ None => {
+ cell_start_byte_offset += TEMP_CELL_SEP.len();
+ continue;
+ }
+ };
+
+ let mut cell_len = 0;
+
+ let graphemes_with_style: Vec<_> = spans
+ .0
+ .iter()
+ .flat_map(|span| {
+ span.content
+ .grapheme_indices(true)
+ .zip(std::iter::repeat(span.style))
+ })
+ .map(|((grapheme_byte_offset, grapheme), style)| {
+ cell_len += grapheme.len();
+ let start = cell_start_byte_offset;
+
+ let grapheme_byte_range =
+ grapheme_byte_offset..grapheme_byte_offset + grapheme.len();
+
+ if highlight_byte_ranges.iter().any(|hl_rng| {
+ hl_rng.start >= start + grapheme_byte_range.start
+ && hl_rng.end <= start + grapheme_byte_range.end
+ }) {
+ (grapheme, style.patch(highlight_style))
+ } else {
+ (grapheme, style)
+ }
+ })
+ .collect();
+
+ let mut span_list: Vec<(String, Style)> = Vec::new();
+ for (grapheme, style) in graphemes_with_style {
+ if span_list.last().map(|(_, sty)| sty) == Some(&style) {
+ let (string, _) = span_list.last_mut().unwrap();
+ string.push_str(grapheme);
+ } else {
+ span_list.push((String::from(grapheme), style))
+ }
+ }
+
+ let spans: Vec<Span> = span_list
+ .into_iter()
+ .map(|(string, style)| Span::styled(string, style))
+ .collect();
+ let spans: Spans = spans.into();
+ *cell = Cell::from(spans);
+
+ cell_start_byte_offset += cell_len + TEMP_CELL_SEP.len();
+ }
+
+ row
+ });
+
+ let table = Table::new(options)
+ .style(text_style)
+ .highlight_style(selected)
+ .highlight_symbol(" > ")
+ .column_spacing(1)
+ .widths(&self.widths);
+
+ use tui::widgets::TableState;
+
+ table.render_table(
+ inner,
+ surface,
+ &mut TableState {
+ offset: 0,
+ selected: Some(cursor),
+ },
+ self.truncate_start,
+ );
+ }
+
+ fn render_preview(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) {
// +---------+ +---------+
// |prompt | |preview |
// +---------+ | |
@@ -836,169 +1002,7 @@ impl<T: Item + 'static> Component for Picker<T> {
}
fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) {
- let text_style = cx.editor.theme.get("ui.text");
- let selected = cx.editor.theme.get("ui.text.focus");
- let highlight_style = cx.editor.theme.get("special").add_modifier(Modifier::BOLD);
-
- // -- Render the frame:
- // clear area
- let background = cx.editor.theme.get("ui.background");
- surface.clear_with(area, background);
-
- // don't like this but the lifetime sucks
- let block = Block::default().borders(Borders::ALL);
-
- // calculate the inner area inside the box
- let inner = block.inner(area);
-
- block.render(area, surface);
-
- // -- Render the input bar:
-
- let area = inner.clip_left(1).with_height(1);
-
- let count = format!("{}/{}", self.matches.len(), self.options.len());
- surface.set_stringn(
- (area.x + area.width).saturating_sub(count.len() as u16 + 1),
- area.y,
- &count,
- (count.len()).min(area.width as usize),
- text_style,
- );
-
- self.prompt.render(area, surface, cx);
-
- // -- Separator
- let sep_style = cx.editor.theme.get("ui.background.separator");
- let borders = BorderType::line_symbols(BorderType::Plain);
- for x in inner.left()..inner.right() {
- if let Some(cell) = surface.get_mut(x, inner.y + 1) {
- cell.set_symbol(borders.horizontal).set_style(sep_style);
- }
- }
-
- // -- Render the contents:
- // subtract area of prompt from top
- let inner = inner.clip_top(2);
-
- let rows = inner.height;
- let offset = self.cursor - (self.cursor % std::cmp::max(1, rows as usize));
- let cursor = self.cursor.saturating_sub(offset);
-
- let options = self
- .matches
- .iter()
- .skip(offset)
- .take(rows as usize)
- .map(|pmatch| &self.options[pmatch.index])
- .map(|option| option.format(&self.editor_data))
- .map(|mut row| {
- const TEMP_CELL_SEP: &str = " ";
-
- let line = row.cell_text().fold(String::new(), |mut s, frag| {
- s.push_str(&frag);
- s.push_str(TEMP_CELL_SEP);
- s
- });
-
- // Items are filtered by using the text returned by menu::Item::filter_text
- // but we do highlighting here using the text in Row and therefore there
- // might be inconsistencies. This is the best we can do since only the
- // text in Row is displayed to the end user.
- let (_score, highlights) = FuzzyQuery::new(self.prompt.line())
- .fuzzy_indices(&line, &self.matcher)
- .unwrap_or_default();
-
- let highlight_byte_ranges: Vec<_> = line
- .char_indices()
- .enumerate()
- .filter_map(|(char_idx, (byte_offset, ch))| {
- highlights
- .contains(&char_idx)
- .then(|| byte_offset..byte_offset + ch.len_utf8())
- })
- .collect();
-
- // The starting byte index of the current (iterating) cell
- let mut cell_start_byte_offset = 0;
- for cell in row.cells.iter_mut() {
- let spans = match cell.content.lines.get(0) {
- Some(s) => s,
- None => {
- cell_start_byte_offset += TEMP_CELL_SEP.len();
- continue;
- }
- };
-
- let mut cell_len = 0;
-
- let graphemes_with_style: Vec<_> = spans
- .0
- .iter()
- .flat_map(|span| {
- span.content
- .grapheme_indices(true)
- .zip(std::iter::repeat(span.style))
- })
- .map(|((grapheme_byte_offset, grapheme), style)| {
- cell_len += grapheme.len();
- let start = cell_start_byte_offset;
-
- let grapheme_byte_range =
- grapheme_byte_offset..grapheme_byte_offset + grapheme.len();
-
- if highlight_byte_ranges.iter().any(|hl_rng| {
- hl_rng.start >= start + grapheme_byte_range.start
- && hl_rng.end <= start + grapheme_byte_range.end
- }) {
- (grapheme, style.patch(highlight_style))
- } else {
- (grapheme, style)
- }
- })
- .collect();
-
- let mut span_list: Vec<(String, Style)> = Vec::new();
- for (grapheme, style) in graphemes_with_style {
- if span_list.last().map(|(_, sty)| sty) == Some(&style) {
- let (string, _) = span_list.last_mut().unwrap();
- string.push_str(grapheme);
- } else {
- span_list.push((String::from(grapheme), style))
- }
- }
-
- let spans: Vec<Span> = span_list
- .into_iter()
- .map(|(string, style)| Span::styled(string, style))
- .collect();
- let spans: Spans = spans.into();
- *cell = Cell::from(spans);
-
- cell_start_byte_offset += cell_len + TEMP_CELL_SEP.len();
- }
-
- row
- });
-
- let table = Table::new(options)
- .style(text_style)
- .highlight_style(selected)
- .highlight_symbol(" > ")
- .column_spacing(1)
- .widths(&self.widths);
-
- use tui::widgets::TableState;
-
- table.render_table(
- inner,
- surface,
- &mut TableState {
- offset: 0,
- selected: Some(cursor),
- },
- self.truncate_start,
- );
+ unimplemented!()
}
fn cursor(&self, area: Rect, editor: &Editor) -> (Option<Position>, CursorKind) {