Start rendering autocompletion menu

Antonio Scandurra created

Change summary

crates/editor2/src/editor.rs | 467 ++++++++++++++++++-------------------
1 file changed, 228 insertions(+), 239 deletions(-)

Detailed changes

crates/editor2/src/editor.rs 🔗

@@ -54,13 +54,13 @@ use itertools::Itertools;
 pub use language::{char_kind, CharKind};
 use language::{
     language_settings::{self, all_language_settings, InlayHintSettings},
-    point_from_lsp, AutoindentMode, BracketPair, Buffer, CodeAction, Completion, CursorShape,
-    Diagnostic, IndentKind, IndentSize, Language, LanguageRegistry, LanguageServerName,
-    OffsetRangeExt, Point, Selection, SelectionGoal, TransactionId,
+    point_from_lsp, AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion,
+    CursorShape, Diagnostic, Documentation, IndentKind, IndentSize, Language, LanguageRegistry,
+    LanguageServerName, OffsetRangeExt, Point, Selection, SelectionGoal, TransactionId,
 };
 use lazy_static::lazy_static;
 use link_go_to_definition::{GoToDefinitionLink, InlayHighlight, LinkGoToDefinitionState};
-use lsp::{DiagnosticSeverity, Documentation, LanguageServerId};
+use lsp::{DiagnosticSeverity, LanguageServerId};
 use movement::TextLayoutDetails;
 use multi_buffer::ToOffsetUtf16;
 pub use multi_buffer::{
@@ -97,7 +97,7 @@ use text::{OffsetUtf16, Rope};
 use theme::{
     ActiveTheme, DiagnosticStyle, PlayerColor, SyntaxTheme, Theme, ThemeColors, ThemeSettings,
 };
-use ui::{v_stack, HighlightedLabel, IconButton, StyledExt, Tooltip};
+use ui::{h_stack, v_stack, HighlightedLabel, IconButton, StyledExt, Tooltip};
 use util::{post_inc, RangeExt, ResultExt, TryFutureExt};
 use workspace::{
     item::{ItemEvent, ItemHandle},
@@ -1224,207 +1224,195 @@ impl CompletionsMenu {
         workspace: Option<WeakView<Workspace>>,
         cx: &mut ViewContext<Editor>,
     ) -> AnyElement<Editor> {
-        todo!("old implementation below")
-    }
+        //     enum CompletionTag {}
 
-    //     enum CompletionTag {}
+        let settings = EditorSettings::get_global(cx);
+        let show_completion_documentation = settings.show_completion_documentation;
 
-    //     let settings = EditorSettings>(cx);
-    //     let show_completion_documentation = settings.show_completion_documentation;
+        let widest_completion_ix = self
+            .matches
+            .iter()
+            .enumerate()
+            .max_by_key(|(_, mat)| {
+                let completions = self.completions.read();
+                let completion = &completions[mat.candidate_id];
+                let documentation = &completion.documentation;
+
+                let mut len = completion.label.text.chars().count();
+                if let Some(Documentation::SingleLine(text)) = documentation {
+                    if show_completion_documentation {
+                        len += text.chars().count();
+                    }
+                }
 
-    //     let widest_completion_ix = self
-    //         .matches
-    //         .iter()
-    //         .enumerate()
-    //         .max_by_key(|(_, mat)| {
-    //             let completions = self.completions.read();
-    //             let completion = &completions[mat.candidate_id];
-    //             let documentation = &completion.documentation;
+                len
+            })
+            .map(|(ix, _)| ix);
 
-    //             let mut len = completion.label.text.chars().count();
-    //             if let Some(Documentation::SingleLine(text)) = documentation {
-    //                 if show_completion_documentation {
-    //                     len += text.chars().count();
-    //                 }
-    //             }
+        let completions = self.completions.clone();
+        let matches = self.matches.clone();
+        let selected_item = self.selected_item;
 
-    //             len
-    //         })
-    //         .map(|(ix, _)| ix);
+        let list = uniform_list("completions", matches.len(), move |editor, range, cx| {
+            let start_ix = range.start;
+            let completions_guard = completions.read();
 
-    //     let completions = self.completions.clone();
-    //     let matches = self.matches.clone();
-    //     let selected_item = self.selected_item;
-
-    //     let list = UniformList::new(self.list.clone(), matches.len(), cx, {
-    //         let style = style.clone();
-    //         move |_, range, items, cx| {
-    //             let start_ix = range.start;
-    //             let completions_guard = completions.read();
-
-    //             for (ix, mat) in matches[range].iter().enumerate() {
-    //                 let item_ix = start_ix + ix;
-    //                 let candidate_id = mat.candidate_id;
-    //                 let completion = &completions_guard[candidate_id];
-
-    //                 let documentation = if show_completion_documentation {
-    //                     &completion.documentation
-    //                 } else {
-    //                     &None
-    //                 };
+            matches[range]
+                .iter()
+                .enumerate()
+                .map(|(ix, mat)| {
+                    let item_ix = start_ix + ix;
+                    let candidate_id = mat.candidate_id;
+                    let completion = &completions_guard[candidate_id];
 
-    //                 items.push(
-    //                     MouseEventHandler::new::<CompletionTag, _>(
-    //                         mat.candidate_id,
-    //                         cx,
-    //                         |state, _| {
-    //                             let item_style = if item_ix == selected_item {
-    //                                 style.autocomplete.selected_item
-    //                             } else if state.hovered() {
-    //                                 style.autocomplete.hovered_item
-    //                             } else {
-    //                                 style.autocomplete.item
-    //                             };
-
-    //                             let completion_label =
-    //                                 Text::new(completion.label.text.clone(), style.text.clone())
-    //                                     .with_soft_wrap(false)
-    //                                     .with_highlights(
-    //                                         combine_syntax_and_fuzzy_match_highlights(
-    //                                             &completion.label.text,
-    //                                             style.text.color.into(),
-    //                                             styled_runs_for_code_label(
-    //                                                 &completion.label,
-    //                                                 &style.syntax,
-    //                                             ),
-    //                                             &mat.positions,
-    //                                         ),
-    //                                     );
-
-    //                             if let Some(Documentation::SingleLine(text)) = documentation {
-    //                                 Flex::row()
-    //                                     .with_child(completion_label)
-    //                                     .with_children((|| {
-    //                                         let text_style = TextStyle {
-    //                                             color: style.autocomplete.inline_docs_color,
-    //                                             font_size: style.text.font_size
-    //                                                 * style.autocomplete.inline_docs_size_percent,
-    //                                             ..style.text.clone()
-    //                                         };
-
-    //                                         let label = Text::new(text.clone(), text_style)
-    //                                             .aligned()
-    //                                             .constrained()
-    //                                             .dynamically(move |constraint, _, _| {
-    //                                                 gpui::SizeConstraint {
-    //                                                     min: constraint.min,
-    //                                                     max: vec2f(
-    //                                                         constraint.max.x(),
-    //                                                         constraint.min.y(),
-    //                                                     ),
-    //                                                 }
-    //                                             });
-
-    //                                         if Some(item_ix) == widest_completion_ix {
-    //                                             Some(
-    //                                                 label
-    //                                                     .contained()
-    //                                                     .with_style(
-    //                                                         style
-    //                                                             .autocomplete
-    //                                                             .inline_docs_container,
-    //                                                     )
-    //                                                     .into_any(),
-    //                                             )
-    //                                         } else {
-    //                                             Some(label.flex_float().into_any())
-    //                                         }
-    //                                     })())
-    //                                     .into_any()
-    //                             } else {
-    //                                 completion_label.into_any()
-    //                             }
-    //                             .contained()
-    //                             .with_style(item_style)
-    //                             .constrained()
-    //                             .dynamically(
-    //                                 move |constraint, _, _| {
-    //                                     if Some(item_ix) == widest_completion_ix {
-    //                                         constraint
-    //                                     } else {
-    //                                         gpui::SizeConstraint {
-    //                                             min: constraint.min,
-    //                                             max: constraint.min,
-    //                                         }
-    //                                     }
-    //                                 },
-    //                             )
-    //                         },
-    //                     )
-    //                     .with_cursor_style(CursorStyle::PointingHand)
-    //                     .on_down(MouseButton::Left, move |_, this, cx| {
-    //                         this.confirm_completion(
-    //                             &ConfirmCompletion {
-    //                                 item_ix: Some(item_ix),
-    //                             },
-    //                             cx,
-    //                         )
-    //                         .map(|task| task.detach());
-    //                     })
-    //                     .constrained()
-    //                     .with_min_width(style.autocomplete.completion_min_width)
-    //                     .with_max_width(style.autocomplete.completion_max_width)
-    //                     .into_any(),
-    //                 );
-    //             }
-    //         }
-    //     })
-    //     .with_width_from_item(widest_completion_ix);
-
-    //     enum MultiLineDocumentation {}
-
-    //     Flex::row()
-    //         .with_child(list.flex(1., false))
-    //         .with_children({
-    //             let mat = &self.matches[selected_item];
-    //             let completions = self.completions.read();
-    //             let completion = &completions[mat.candidate_id];
-    //             let documentation = &completion.documentation;
-
-    //             match documentation {
-    //                 Some(Documentation::MultiLinePlainText(text)) => Some(
-    //                     Flex::column()
-    //                         .scrollable::<MultiLineDocumentation>(0, None, cx)
-    //                         .with_child(
-    //                             Text::new(text.clone(), style.text.clone()).with_soft_wrap(true),
-    //                         )
-    //                         .contained()
-    //                         .with_style(style.autocomplete.alongside_docs_container)
-    //                         .constrained()
-    //                         .with_max_width(style.autocomplete.alongside_docs_max_width)
-    //                         .flex(1., false),
-    //                 ),
-
-    //                 Some(Documentation::MultiLineMarkdown(parsed)) => Some(
-    //                     Flex::column()
-    //                         .scrollable::<MultiLineDocumentation>(0, None, cx)
-    //                         .with_child(render_parsed_markdown::<MultiLineDocumentation>(
-    //                             parsed, &style, workspace, cx,
-    //                         ))
-    //                         .contained()
-    //                         .with_style(style.autocomplete.alongside_docs_container)
-    //                         .constrained()
-    //                         .with_max_width(style.autocomplete.alongside_docs_max_width)
-    //                         .flex(1., false),
-    //                 ),
-
-    //                 _ => None,
-    //             }
-    //         })
-    //         .contained()
-    //         .with_style(style.autocomplete.container)
-    //         .into_any()
-    // }
+                    let documentation = if show_completion_documentation {
+                        &completion.documentation
+                    } else {
+                        &None
+                    };
+
+                    // todo!("highlights")
+                    // let highlights = combine_syntax_and_fuzzy_match_highlights(
+                    //     &completion.label.text,
+                    //     style.text.color.into(),
+                    //     styled_runs_for_code_label(&completion.label, &style.syntax),
+                    //     &mat.positions,
+                    // )
+
+                    // todo!("documentation")
+                    // MouseEventHandler::new::<CompletionTag, _>(mat.candidate_id, cx, |state, _| {
+                    //     let completion_label = HighlightedLabel::new(
+                    //         completion.label.text.clone(),
+                    //         combine_syntax_and_fuzzy_match_highlights(
+                    //             &completion.label.text,
+                    //             style.text.color.into(),
+                    //             styled_runs_for_code_label(&completion.label, &style.syntax),
+                    //             &mat.positions,
+                    //         ),
+                    //     );
+                    //     Text::new(completion.label.text.clone(), style.text.clone())
+                    //         .with_soft_wrap(false)
+                    //         .with_highlights();
+
+                    //     if let Some(Documentation::SingleLine(text)) = documentation {
+                    //         h_stack()
+                    //             .child(completion_label)
+                    //             .with_children((|| {
+                    //                 let text_style = TextStyle {
+                    //                     color: style.autocomplete.inline_docs_color,
+                    //                     font_size: style.text.font_size
+                    //                         * style.autocomplete.inline_docs_size_percent,
+                    //                     ..style.text.clone()
+                    //                 };
+
+                    //                 let label = Text::new(text.clone(), text_style)
+                    //                     .aligned()
+                    //                     .constrained()
+                    //                     .dynamically(move |constraint, _, _| gpui::SizeConstraint {
+                    //                         min: constraint.min,
+                    //                         max: vec2f(constraint.max.x(), constraint.min.y()),
+                    //                     });
+
+                    //                 if Some(item_ix) == widest_completion_ix {
+                    //                     Some(
+                    //                         label
+                    //                             .contained()
+                    //                             .with_style(style.autocomplete.inline_docs_container)
+                    //                             .into_any(),
+                    //                     )
+                    //                 } else {
+                    //                     Some(label.flex_float().into_any())
+                    //                 }
+                    //             })())
+                    //             .into_any()
+                    //     } else {
+                    //         completion_label.into_any()
+                    //     }
+                    //     .contained()
+                    //     .with_style(item_style)
+                    //     .constrained()
+                    //     .dynamically(move |constraint, _, _| {
+                    //         if Some(item_ix) == widest_completion_ix {
+                    //             constraint
+                    //         } else {
+                    //             gpui::SizeConstraint {
+                    //                 min: constraint.min,
+                    //                 max: constraint.min,
+                    //             }
+                    //         }
+                    //     })
+                    // })
+                    // .with_cursor_style(CursorStyle::PointingHand)
+                    // .on_down(MouseButton::Left, move |_, this, cx| {
+                    //     this.confirm_completion(
+                    //         &ConfirmCompletion {
+                    //             item_ix: Some(item_ix),
+                    //         },
+                    //         cx,
+                    //     )
+                    //     .map(|task| task.detach());
+                    // })
+                    // .constrained()
+                    //
+                    div()
+                        .id(mat.candidate_id)
+                        .bg(gpui::green())
+                        .hover(|style| style.bg(gpui::blue()))
+                        .when(item_ix == selected_item, |div| div.bg(gpui::blue()))
+                        .child(completion.label.text.clone())
+                        .min_w(px(300.))
+                        .max_w(px(700.))
+                })
+                .collect()
+        })
+        .with_width_from_item(widest_completion_ix);
+
+        list.render()
+        // todo!("multiline documentation")
+        //     enum MultiLineDocumentation {}
+
+        //     Flex::row()
+        //         .with_child(list.flex(1., false))
+        //         .with_children({
+        //             let mat = &self.matches[selected_item];
+        //             let completions = self.completions.read();
+        //             let completion = &completions[mat.candidate_id];
+        //             let documentation = &completion.documentation;
+
+        //             match documentation {
+        //                 Some(Documentation::MultiLinePlainText(text)) => Some(
+        //                     Flex::column()
+        //                         .scrollable::<MultiLineDocumentation>(0, None, cx)
+        //                         .with_child(
+        //                             Text::new(text.clone(), style.text.clone()).with_soft_wrap(true),
+        //                         )
+        //                         .contained()
+        //                         .with_style(style.autocomplete.alongside_docs_container)
+        //                         .constrained()
+        //                         .with_max_width(style.autocomplete.alongside_docs_max_width)
+        //                         .flex(1., false),
+        //                 ),
+
+        //                 Some(Documentation::MultiLineMarkdown(parsed)) => Some(
+        //                     Flex::column()
+        //                         .scrollable::<MultiLineDocumentation>(0, None, cx)
+        //                         .with_child(render_parsed_markdown::<MultiLineDocumentation>(
+        //                             parsed, &style, workspace, cx,
+        //                         ))
+        //                         .contained()
+        //                         .with_style(style.autocomplete.alongside_docs_container)
+        //                         .constrained()
+        //                         .with_max_width(style.autocomplete.alongside_docs_max_width)
+        //                         .flex(1., false),
+        //                 ),
+
+        //                 _ => None,
+        //             }
+        //         })
+        //         .contained()
+        //         .with_style(style.autocomplete.container)
+        //         .into_any()
+    }
 
     pub async fn filter(&mut self, query: Option<&str>, executor: BackgroundExecutor) {
         let mut matches = if let Some(query) = query {
@@ -10110,49 +10098,50 @@ pub fn combine_syntax_and_fuzzy_match_highlights(
     result
 }
 
-// pub fn styled_runs_for_code_label<'a>(
-//     label: &'a CodeLabel,
-//     syntax_theme: &'a theme::SyntaxTheme,
-// ) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
-//     let fade_out = HighlightStyle {
-//         fade_out: Some(0.35),
-//         ..Default::default()
-//     };
+pub fn styled_runs_for_code_label<'a>(
+    label: &'a CodeLabel,
+    syntax_theme: &'a theme::SyntaxTheme,
+) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
+    let fade_out = HighlightStyle {
+        fade_out: Some(0.35),
+        ..Default::default()
+    };
+
+    let mut prev_end = label.filter_range.end;
+    label
+        .runs
+        .iter()
+        .enumerate()
+        .flat_map(move |(ix, (range, highlight_id))| {
+            let style = if let Some(style) = highlight_id.style(syntax_theme) {
+                style
+            } else {
+                return Default::default();
+            };
+            let mut muted_style = style;
+            muted_style.highlight(fade_out);
 
-//     let mut prev_end = label.filter_range.end;
-//     label
-//         .runs
-//         .iter()
-//         .enumerate()
-//         .flat_map(move |(ix, (range, highlight_id))| {
-//             let style = if let Some(style) = highlight_id.style(syntax_theme) {
-//                 style
-//             } else {
-//                 return Default::default();
-//             };
-//             let mut muted_style = style;
-//             muted_style.highlight(fade_out);
-
-//             let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
-//             if range.start >= label.filter_range.end {
-//                 if range.start > prev_end {
-//                     runs.push((prev_end..range.start, fade_out));
-//                 }
-//                 runs.push((range.clone(), muted_style));
-//             } else if range.end <= label.filter_range.end {
-//                 runs.push((range.clone(), style));
-//             } else {
-//                 runs.push((range.start..label.filter_range.end, style));
-//                 runs.push((label.filter_range.end..range.end, muted_style));
-//             }
-//             prev_end = cmp::max(prev_end, range.end);
+            let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
+            if range.start >= label.filter_range.end {
+                if range.start > prev_end {
+                    runs.push((prev_end..range.start, fade_out));
+                }
+                runs.push((range.clone(), muted_style));
+            } else if range.end <= label.filter_range.end {
+                runs.push((range.clone(), style));
+            } else {
+                runs.push((range.start..label.filter_range.end, style));
+                runs.push((label.filter_range.end..range.end, muted_style));
+            }
+            prev_end = cmp::max(prev_end, range.end);
 
-//             if ix + 1 == label.runs.len() && label.text.len() > prev_end {
-//                 runs.push((prev_end..label.text.len(), fade_out));
-//             }
+            if ix + 1 == label.runs.len() && label.text.len() > prev_end {
+                runs.push((prev_end..label.text.len(), fade_out));
+            }
 
-//             runs
-//         })
+            runs
+        })
+}
 
 pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
     let mut index = 0;