@@ -1,4 +1,7 @@
-use crate::code_context_menus::{CompletionsMenu, SortableMatch};
+use crate::{
+ code_context_menus::{CompletionsMenu, SortableMatch},
+ editor_settings::SnippetSortOrder,
+};
use fuzzy::StringMatch;
use gpui::TestAppContext;
@@ -74,7 +77,7 @@ fn test_sort_matches_local_variable_over_global_variable(_cx: &mut TestAppContex
sort_key: (2, "floorf128"),
},
];
- CompletionsMenu::sort_matches(&mut matches, query);
+ CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::default());
assert_eq!(
matches[0].string_match.string.as_str(),
"foo_bar_qux",
@@ -122,7 +125,7 @@ fn test_sort_matches_local_variable_over_global_variable(_cx: &mut TestAppContex
sort_key: (1, "foo_bar_qux"),
},
];
- CompletionsMenu::sort_matches(&mut matches, query);
+ CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::default());
assert_eq!(
matches[0].string_match.string.as_str(),
"foo_bar_qux",
@@ -185,7 +188,7 @@ fn test_sort_matches_local_variable_over_global_enum(_cx: &mut TestAppContext) {
sort_key: (0, "while let"),
},
];
- CompletionsMenu::sort_matches(&mut matches, query);
+ CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::default());
assert_eq!(
matches[0].string_match.string.as_str(),
"element_type",
@@ -234,7 +237,7 @@ fn test_sort_matches_local_variable_over_global_enum(_cx: &mut TestAppContext) {
sort_key: (2, "REPLACEMENT_CHARACTER"),
},
];
- CompletionsMenu::sort_matches(&mut matches, query);
+ CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::default());
assert_eq!(
matches[0].string_match.string.as_str(),
"element_type",
@@ -272,7 +275,7 @@ fn test_sort_matches_local_variable_over_global_enum(_cx: &mut TestAppContext) {
sort_key: (1, "element_type"),
},
];
- CompletionsMenu::sort_matches(&mut matches, query);
+ CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::default());
assert_eq!(
matches[0].string_match.string.as_str(),
"ElementType",
@@ -335,7 +338,7 @@ fn test_sort_matches_for_unreachable(_cx: &mut TestAppContext) {
sort_key: (2, "unreachable_unchecked"),
},
];
- CompletionsMenu::sort_matches(&mut matches, query);
+ CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::default());
assert_eq!(
matches[0].string_match.string.as_str(),
"unreachable!(β¦)",
@@ -379,7 +382,7 @@ fn test_sort_matches_for_unreachable(_cx: &mut TestAppContext) {
sort_key: (3, "unreachable_unchecked"),
},
];
- CompletionsMenu::sort_matches(&mut matches, query);
+ CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::default());
assert_eq!(
matches[0].string_match.string.as_str(),
"unreachable!(β¦)",
@@ -423,7 +426,7 @@ fn test_sort_matches_for_unreachable(_cx: &mut TestAppContext) {
sort_key: (2, "unreachable_unchecked"),
},
];
- CompletionsMenu::sort_matches(&mut matches, query);
+ CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::default());
assert_eq!(
matches[0].string_match.string.as_str(),
"unreachable!(β¦)",
@@ -467,7 +470,7 @@ fn test_sort_matches_for_unreachable(_cx: &mut TestAppContext) {
sort_key: (2, "unreachable_unchecked"),
},
];
- CompletionsMenu::sort_matches(&mut matches, query);
+ CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::default());
assert_eq!(
matches[0].string_match.string.as_str(),
"unreachable!(β¦)",
@@ -503,7 +506,7 @@ fn test_sort_matches_variable_and_constants_over_function(_cx: &mut TestAppConte
sort_key: (1, "var"), // variable
},
];
- CompletionsMenu::sort_matches(&mut matches, query);
+ CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::default());
assert_eq!(
matches[0].string_match.candidate_id, 1,
"Match order not expected"
@@ -539,7 +542,7 @@ fn test_sort_matches_variable_and_constants_over_function(_cx: &mut TestAppConte
sort_key: (2, "var"), // constant
},
];
- CompletionsMenu::sort_matches(&mut matches, query);
+ CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::default());
assert_eq!(
matches[0].string_match.candidate_id, 1,
"Match order not expected"
@@ -622,7 +625,7 @@ fn test_sort_matches_jsx_event_handler(_cx: &mut TestAppContext) {
sort_key: (3, "className?"),
},
];
- CompletionsMenu::sort_matches(&mut matches, query);
+ CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::default());
assert_eq!(
matches[0].string_match.string, "onCut?",
"Match order not expected"
@@ -944,7 +947,7 @@ fn test_sort_matches_jsx_event_handler(_cx: &mut TestAppContext) {
sort_key: (3, "onLoadedData?"),
},
];
- CompletionsMenu::sort_matches(&mut matches, query);
+ CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::default());
assert_eq!(
matches
.iter()
@@ -996,7 +999,7 @@ fn test_sort_matches_for_snippets(_cx: &mut TestAppContext) {
sort_key: (2, "println!(β¦)"),
},
];
- CompletionsMenu::sort_matches(&mut matches, query);
+ CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::Top);
assert_eq!(
matches[0].string_match.string.as_str(),
"println!(β¦)",
@@ -25,6 +25,7 @@ use task::ResolvedTask;
use ui::{Color, IntoElement, ListItem, Pixels, Popover, Styled, prelude::*};
use util::ResultExt;
+use crate::editor_settings::SnippetSortOrder;
use crate::hover_popover::{hover_markdown_style, open_markdown_url};
use crate::{
CodeActionProvider, CompletionId, CompletionItemKind, CompletionProvider, DisplayRow, Editor,
@@ -184,6 +185,7 @@ pub struct CompletionsMenu {
pub(super) ignore_completion_provider: bool,
last_rendered_range: Rc<RefCell<Option<Range<usize>>>>,
markdown_element: Option<Entity<Markdown>>,
+ snippet_sort_order: SnippetSortOrder,
}
impl CompletionsMenu {
@@ -195,6 +197,7 @@ impl CompletionsMenu {
initial_position: Anchor,
buffer: Entity<Buffer>,
completions: Box<[Completion]>,
+ snippet_sort_order: SnippetSortOrder,
) -> Self {
let match_candidates = completions
.iter()
@@ -217,6 +220,7 @@ impl CompletionsMenu {
resolve_completions: true,
last_rendered_range: RefCell::new(None).into(),
markdown_element: None,
+ snippet_sort_order,
}
}
@@ -226,6 +230,7 @@ impl CompletionsMenu {
choices: &Vec<String>,
selection: Range<Anchor>,
buffer: Entity<Buffer>,
+ snippet_sort_order: SnippetSortOrder,
) -> Self {
let completions = choices
.iter()
@@ -275,6 +280,7 @@ impl CompletionsMenu {
ignore_completion_provider: false,
last_rendered_range: RefCell::new(None).into(),
markdown_element: None,
+ snippet_sort_order,
}
}
@@ -657,7 +663,11 @@ impl CompletionsMenu {
)
}
- pub fn sort_matches(matches: &mut Vec<SortableMatch<'_>>, query: Option<&str>) {
+ pub fn sort_matches(
+ matches: &mut Vec<SortableMatch<'_>>,
+ query: Option<&str>,
+ snippet_sort_order: SnippetSortOrder,
+ ) {
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
enum MatchTier<'a> {
WordStartMatch {
@@ -703,7 +713,11 @@ impl CompletionsMenu {
MatchTier::OtherMatch { sort_score }
} else {
let sort_score_int = Reverse(if score >= FUZZY_THRESHOLD { 1 } else { 0 });
- let sort_snippet = Reverse(if mat.is_snippet { 1 } else { 0 });
+ let sort_snippet = match snippet_sort_order {
+ SnippetSortOrder::Top => Reverse(if mat.is_snippet { 1 } else { 0 }),
+ SnippetSortOrder::Bottom => Reverse(if mat.is_snippet { 0 } else { 1 }),
+ SnippetSortOrder::Inline => Reverse(0),
+ };
MatchTier::WordStartMatch {
sort_score_int,
sort_snippet,
@@ -770,7 +784,7 @@ impl CompletionsMenu {
})
.collect();
- Self::sort_matches(&mut sortable_items, query);
+ Self::sort_matches(&mut sortable_items, query, self.snippet_sort_order);
matches = sortable_items
.into_iter()
@@ -4733,6 +4733,8 @@ impl Editor {
.as_ref()
.map_or(true, |provider| provider.filter_completions());
+ let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
+
let id = post_inc(&mut self.next_completion_id);
let task = cx.spawn_in(window, async move |editor, cx| {
async move {
@@ -4780,6 +4782,7 @@ impl Editor {
position,
buffer.clone(),
completions.into(),
+ snippet_sort_order,
);
menu.filter(
@@ -8229,10 +8232,18 @@ impl Editor {
let buffer_id = selection.start.buffer_id.unwrap();
let buffer = self.buffer().read(cx).buffer(buffer_id);
let id = post_inc(&mut self.next_completion_id);
+ let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
if let Some(buffer) = buffer {
*self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
- CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
+ CompletionsMenu::new_snippet_choices(
+ id,
+ true,
+ choices,
+ selection,
+ buffer,
+ snippet_sort_order,
+ ),
));
}
}
@@ -39,6 +39,7 @@ pub struct EditorSettings {
pub go_to_definition_fallback: GoToDefinitionFallback,
pub jupyter: Jupyter,
pub hide_mouse: Option<HideMouseMode>,
+ pub snippet_sort_order: SnippetSortOrder,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
@@ -239,6 +240,21 @@ pub enum HideMouseMode {
OnTypingAndMovement,
}
+/// Determines how snippets are sorted relative to other completion items.
+///
+/// Default: inline
+#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum SnippetSortOrder {
+ /// Place snippets at the top of the completion list
+ Top,
+ /// Sort snippets normally using the default comparison logic
+ #[default]
+ Inline,
+ /// Place snippets at the bottom of the completion list
+ Bottom,
+}
+
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
pub struct EditorSettingsContent {
/// Whether the cursor blinks in the editor.
@@ -254,6 +270,10 @@ pub struct EditorSettingsContent {
///
/// Default: on_typing_and_movement
pub hide_mouse: Option<HideMouseMode>,
+ /// Determines how snippets are sorted relative to other completion items.
+ ///
+ /// Default: inline
+ pub snippet_sort_order: Option<SnippetSortOrder>,
/// How to highlight the current line in the editor.
///
/// Default: all