@@ -490,6 +490,172 @@ impl<T: Item + 'static> FilePicker<T> {
490490 }
491491
492492 fn render_picker(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) {
493+ let text_style = cx.editor.theme.get("ui.text");
494+ let selected = cx.editor.theme.get("ui.text.focus");
495+ let highlight_style = cx.editor.theme.get("special").add_modifier(Modifier::BOLD);
496+
497+ // -- Render the frame:
498+ // clear area
499+ let background = cx.editor.theme.get("ui.background");
500+ surface.clear_with(area, background);
501+
502+ // don't like this but the lifetime sucks
503+ let block = Block::default().borders(Borders::ALL);
504+
505+ // calculate the inner area inside the box
506+ let inner = block.inner(area);
507+
508+ block.render(area, surface);
509+
510+ // -- Render the input bar:
511+
512+ let area = inner.clip_left(1).with_height(1);
513+
514+ let count = format!("{}/{}", self.matches.len(), self.options.len());
515+ surface.set_stringn(
516+ (area.x + area.width).saturating_sub(count.len() as u16 + 1),
517+ area.y,
518+ &count,
519+ (count.len()).min(area.width as usize),
520+ text_style,
521+ );
522+
523+ self.prompt.render(area, surface, cx);
524+
525+ // -- Separator
526+ let sep_style = cx.editor.theme.get("ui.background.separator");
527+ let borders = BorderType::line_symbols(BorderType::Plain);
528+ for x in inner.left()..inner.right() {
529+ if let Some(cell) = surface.get_mut(x, inner.y + 1) {
530+ cell.set_symbol(borders.horizontal).set_style(sep_style);
531+ }
532+ }
533+
534+ // -- Render the contents:
535+ // subtract area of prompt from top
536+ let inner = inner.clip_top(2);
537+
538+ let rows = inner.height;
539+ let offset = self.cursor - (self.cursor % std::cmp::max(1, rows as usize));
540+ let cursor = self.cursor.saturating_sub(offset);
541+
542+ let options = self
543+ .matches
544+ .iter()
545+ .skip(offset)
546+ .take(rows as usize)
547+ .map(|pmatch| &self.options[pmatch.index])
548+ .map(|option| option.format(&self.editor_data))
549+ .map(|mut row| {
550+ const TEMP_CELL_SEP: &str = " ";
551+
552+ let line = row.cell_text().fold(String::new(), |mut s, frag| {
553+ s.push_str(&frag);
554+ s.push_str(TEMP_CELL_SEP);
555+ s
556+ });
557+
558+ // Items are filtered by using the text returned by menu::Item::filter_text
559+ // but we do highlighting here using the text in Row and therefore there
560+ // might be inconsistencies. This is the best we can do since only the
561+ // text in Row is displayed to the end user.
562+ let (_score, highlights) = FuzzyQuery::new(self.prompt.line())
563+ .fuzzy_indices(&line, &self.matcher)
564+ .unwrap_or_default();
565+
566+ let highlight_byte_ranges: Vec<_> = line
567+ .char_indices()
568+ .enumerate()
569+ .filter_map(|(char_idx, (byte_offset, ch))| {
570+ highlights
571+ .contains(&char_idx)
572+ .then(|| byte_offset..byte_offset + ch.len_utf8())
573+ })
574+ .collect();
575+
576+ // The starting byte index of the current (iterating) cell
577+ let mut cell_start_byte_offset = 0;
578+ for cell in row.cells.iter_mut() {
579+ let spans = match cell.content.lines.get(0) {
580+ Some(s) => s,
581+ None => {
582+ cell_start_byte_offset += TEMP_CELL_SEP.len();
583+ continue;
584+ }
585+ };
586+
587+ let mut cell_len = 0;
588+
589+ let graphemes_with_style: Vec<_> = spans
590+ .0
591+ .iter()
592+ .flat_map(|span| {
593+ span.content
594+ .grapheme_indices(true)
595+ .zip(std::iter::repeat(span.style))
596+ })
597+ .map(|((grapheme_byte_offset, grapheme), style)| {
598+ cell_len += grapheme.len();
599+ let start = cell_start_byte_offset;
600+
601+ let grapheme_byte_range =
602+ grapheme_byte_offset..grapheme_byte_offset + grapheme.len();
603+
604+ if highlight_byte_ranges.iter().any(|hl_rng| {
605+ hl_rng.start >= start + grapheme_byte_range.start
606+ && hl_rng.end <= start + grapheme_byte_range.end
607+ }) {
608+ (grapheme, style.patch(highlight_style))
609+ } else {
610+ (grapheme, style)
611+ }
612+ })
613+ .collect();
614+
615+ let mut span_list: Vec<(String, Style)> = Vec::new();
616+ for (grapheme, style) in graphemes_with_style {
617+ if span_list.last().map(|(_, sty)| sty) == Some(&style) {
618+ let (string, _) = span_list.last_mut().unwrap();
619+ string.push_str(grapheme);
620+ } else {
621+ span_list.push((String::from(grapheme), style))
622+ }
623+ }
624+
625+ let spans: Vec<Span> = span_list
626+ .into_iter()
627+ .map(|(string, style)| Span::styled(string, style))
628+ .collect();
629+ let spans: Spans = spans.into();
630+ *cell = Cell::from(spans);
631+
632+ cell_start_byte_offset += cell_len + TEMP_CELL_SEP.len();
633+ }
634+
635+ row
636+ });
637+
638+ let table = Table::new(options)
639+ .style(text_style)
640+ .highlight_style(selected)
641+ .highlight_symbol(" > ")
642+ .column_spacing(1)
643+ .widths(&self.widths);
644+
645+ use tui::widgets::TableState;
646+
647+ table.render_table(
648+ inner,
649+ surface,
650+ &mut TableState {
651+ offset: 0,
652+ selected: Some(cursor),
653+ },
654+ self.truncate_start,
655+ );
656+ }
657+
658+ fn render_preview(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) {
493659 // +---------+ +---------+
494660 // |prompt | |preview |
495661 // +---------+ | |
@@ -836,169 +1002,7 @@ impl<T: Item + 'static> Component for Picker<T> {
8361002 }
8371003
8381004 fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) {
839− let text_style = cx.editor.theme.get("ui.text");
840− let selected = cx.editor.theme.get("ui.text.focus");
841− let highlight_style = cx.editor.theme.get("special").add_modifier(Modifier::BOLD);
842−
843− // -- Render the frame:
844− // clear area
845− let background = cx.editor.theme.get("ui.background");
846− surface.clear_with(area, background);
847−
848− // don't like this but the lifetime sucks
849− let block = Block::default().borders(Borders::ALL);
850−
851− // calculate the inner area inside the box
852− let inner = block.inner(area);
853−
854− block.render(area, surface);
855−
856− // -- Render the input bar:
857−
858− let area = inner.clip_left(1).with_height(1);
859−
860− let count = format!("{}/{}", self.matches.len(), self.options.len());
861− surface.set_stringn(
862− (area.x + area.width).saturating_sub(count.len() as u16 + 1),
863− area.y,
864− &count,
865− (count.len()).min(area.width as usize),
866− text_style,
867− );
868−
869− self.prompt.render(area, surface, cx);
870−
871− // -- Separator
872− let sep_style = cx.editor.theme.get("ui.background.separator");
873− let borders = BorderType::line_symbols(BorderType::Plain);
874− for x in inner.left()..inner.right() {
875− if let Some(cell) = surface.get_mut(x, inner.y + 1) {
876− cell.set_symbol(borders.horizontal).set_style(sep_style);
877− }
878− }
879−
880− // -- Render the contents:
881− // subtract area of prompt from top
882− let inner = inner.clip_top(2);
883−
884− let rows = inner.height;
885− let offset = self.cursor - (self.cursor % std::cmp::max(1, rows as usize));
886− let cursor = self.cursor.saturating_sub(offset);
887−
888− let options = self
889− .matches
890− .iter()
891− .skip(offset)
892− .take(rows as usize)
893− .map(|pmatch| &self.options[pmatch.index])
894− .map(|option| option.format(&self.editor_data))
895− .map(|mut row| {
896− const TEMP_CELL_SEP: &str = " ";
897−
898− let line = row.cell_text().fold(String::new(), |mut s, frag| {
899− s.push_str(&frag);
900− s.push_str(TEMP_CELL_SEP);
901− s
902− });
903−
904− // Items are filtered by using the text returned by menu::Item::filter_text
905− // but we do highlighting here using the text in Row and therefore there
906− // might be inconsistencies. This is the best we can do since only the
907− // text in Row is displayed to the end user.
908− let (_score, highlights) = FuzzyQuery::new(self.prompt.line())
909− .fuzzy_indices(&line, &self.matcher)
910− .unwrap_or_default();
911−
912− let highlight_byte_ranges: Vec<_> = line
913− .char_indices()
914− .enumerate()
915− .filter_map(|(char_idx, (byte_offset, ch))| {
916− highlights
917− .contains(&char_idx)
918− .then(|| byte_offset..byte_offset + ch.len_utf8())
919− })
920− .collect();
921−
922− // The starting byte index of the current (iterating) cell
923− let mut cell_start_byte_offset = 0;
924− for cell in row.cells.iter_mut() {
925− let spans = match cell.content.lines.get(0) {
926− Some(s) => s,
927− None => {
928− cell_start_byte_offset += TEMP_CELL_SEP.len();
929− continue;
930− }
931− };
932−
933− let mut cell_len = 0;
934−
935− let graphemes_with_style: Vec<_> = spans
936− .0
937− .iter()
938− .flat_map(|span| {
939− span.content
940− .grapheme_indices(true)
941− .zip(std::iter::repeat(span.style))
942− })
943− .map(|((grapheme_byte_offset, grapheme), style)| {
944− cell_len += grapheme.len();
945− let start = cell_start_byte_offset;
946−
947− let grapheme_byte_range =
948− grapheme_byte_offset..grapheme_byte_offset + grapheme.len();
949−
950− if highlight_byte_ranges.iter().any(|hl_rng| {
951− hl_rng.start >= start + grapheme_byte_range.start
952− && hl_rng.end <= start + grapheme_byte_range.end
953− }) {
954− (grapheme, style.patch(highlight_style))
955− } else {
956− (grapheme, style)
957− }
958− })
959− .collect();
960−
961− let mut span_list: Vec<(String, Style)> = Vec::new();
962− for (grapheme, style) in graphemes_with_style {
963− if span_list.last().map(|(_, sty)| sty) == Some(&style) {
964− let (string, _) = span_list.last_mut().unwrap();
965− string.push_str(grapheme);
966− } else {
967− span_list.push((String::from(grapheme), style))
968− }
969− }
970−
971− let spans: Vec<Span> = span_list
972− .into_iter()
973− .map(|(string, style)| Span::styled(string, style))
974− .collect();
975− let spans: Spans = spans.into();
976− *cell = Cell::from(spans);
977−
978− cell_start_byte_offset += cell_len + TEMP_CELL_SEP.len();
979− }
980−
981− row
982− });
983−
984− let table = Table::new(options)
985− .style(text_style)
986− .highlight_style(selected)
987− .highlight_symbol(" > ")
988− .column_spacing(1)
989− .widths(&self.widths);
990−
991− use tui::widgets::TableState;
992−
993− table.render_table(
994− inner,
995− surface,
996− &mut TableState {
997− offset: 0,
998− selected: Some(cursor),
999− },
1000− self.truncate_start,
1001− );
1005+ unimplemented!()
10021006 }
10031007
10041008 fn cursor(&self, area: Rect, editor: &Editor) -> (Option<Position>, CursorKind) {