editor: Add setting for snippet sorting behavior for code completion (#29429)
Added `snippet_sort_order`, which determines how snippets are sorted relative to other completion items. It can have the values `top`, `bottom`, or `inline`, with `inline` being the default. This mimics VS Code’s setting: https://code.visualstudio.com/docs/editing/intellisense#_snippets-in-suggestions Release Notes: - Added support for `snippet_sort_order` to control snippet sorting behavior in code completion menus.
This commit is contained in:
parent
c157b1c455
commit
cc57bc7c96
6 changed files with 126 additions and 20 deletions
|
@ -167,7 +167,23 @@
|
||||||
// Default: not set, defaults to "bar"
|
// Default: not set, defaults to "bar"
|
||||||
"cursor_shape": null,
|
"cursor_shape": null,
|
||||||
// Determines when the mouse cursor should be hidden in an editor or input box.
|
// Determines when the mouse cursor should be hidden in an editor or input box.
|
||||||
|
//
|
||||||
|
// 1. Never hide the mouse cursor:
|
||||||
|
// "never"
|
||||||
|
// 2. Hide only when typing:
|
||||||
|
// "on_typing"
|
||||||
|
// 3. Hide on both typing and cursor movement:
|
||||||
|
// "on_typing_and_movement"
|
||||||
"hide_mouse": "on_typing_and_movement",
|
"hide_mouse": "on_typing_and_movement",
|
||||||
|
// Determines how snippets are sorted relative to other completion items.
|
||||||
|
//
|
||||||
|
// 1. Place snippets at the top of the completion list:
|
||||||
|
// "top"
|
||||||
|
// 2. Place snippets normally without any preference:
|
||||||
|
// "inline"
|
||||||
|
// 3. Place snippets at the bottom of the completion list:
|
||||||
|
// "bottom"
|
||||||
|
"snippet_sort_order": "inline",
|
||||||
// How to highlight the current line in the editor.
|
// How to highlight the current line in the editor.
|
||||||
//
|
//
|
||||||
// 1. Don't highlight the current line:
|
// 1. Don't highlight the current line:
|
||||||
|
|
|
@ -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 fuzzy::StringMatch;
|
||||||
use gpui::TestAppContext;
|
use gpui::TestAppContext;
|
||||||
|
|
||||||
|
@ -74,7 +77,7 @@ fn test_sort_matches_local_variable_over_global_variable(_cx: &mut TestAppContex
|
||||||
sort_key: (2, "floorf128"),
|
sort_key: (2, "floorf128"),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
CompletionsMenu::sort_matches(&mut matches, query);
|
CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::default());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
matches[0].string_match.string.as_str(),
|
matches[0].string_match.string.as_str(),
|
||||||
"foo_bar_qux",
|
"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"),
|
sort_key: (1, "foo_bar_qux"),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
CompletionsMenu::sort_matches(&mut matches, query);
|
CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::default());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
matches[0].string_match.string.as_str(),
|
matches[0].string_match.string.as_str(),
|
||||||
"foo_bar_qux",
|
"foo_bar_qux",
|
||||||
|
@ -185,7 +188,7 @@ fn test_sort_matches_local_variable_over_global_enum(_cx: &mut TestAppContext) {
|
||||||
sort_key: (0, "while let"),
|
sort_key: (0, "while let"),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
CompletionsMenu::sort_matches(&mut matches, query);
|
CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::default());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
matches[0].string_match.string.as_str(),
|
matches[0].string_match.string.as_str(),
|
||||||
"element_type",
|
"element_type",
|
||||||
|
@ -234,7 +237,7 @@ fn test_sort_matches_local_variable_over_global_enum(_cx: &mut TestAppContext) {
|
||||||
sort_key: (2, "REPLACEMENT_CHARACTER"),
|
sort_key: (2, "REPLACEMENT_CHARACTER"),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
CompletionsMenu::sort_matches(&mut matches, query);
|
CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::default());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
matches[0].string_match.string.as_str(),
|
matches[0].string_match.string.as_str(),
|
||||||
"element_type",
|
"element_type",
|
||||||
|
@ -272,7 +275,7 @@ fn test_sort_matches_local_variable_over_global_enum(_cx: &mut TestAppContext) {
|
||||||
sort_key: (1, "element_type"),
|
sort_key: (1, "element_type"),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
CompletionsMenu::sort_matches(&mut matches, query);
|
CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::default());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
matches[0].string_match.string.as_str(),
|
matches[0].string_match.string.as_str(),
|
||||||
"ElementType",
|
"ElementType",
|
||||||
|
@ -335,7 +338,7 @@ fn test_sort_matches_for_unreachable(_cx: &mut TestAppContext) {
|
||||||
sort_key: (2, "unreachable_unchecked"),
|
sort_key: (2, "unreachable_unchecked"),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
CompletionsMenu::sort_matches(&mut matches, query);
|
CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::default());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
matches[0].string_match.string.as_str(),
|
matches[0].string_match.string.as_str(),
|
||||||
"unreachable!(…)",
|
"unreachable!(…)",
|
||||||
|
@ -379,7 +382,7 @@ fn test_sort_matches_for_unreachable(_cx: &mut TestAppContext) {
|
||||||
sort_key: (3, "unreachable_unchecked"),
|
sort_key: (3, "unreachable_unchecked"),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
CompletionsMenu::sort_matches(&mut matches, query);
|
CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::default());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
matches[0].string_match.string.as_str(),
|
matches[0].string_match.string.as_str(),
|
||||||
"unreachable!(…)",
|
"unreachable!(…)",
|
||||||
|
@ -423,7 +426,7 @@ fn test_sort_matches_for_unreachable(_cx: &mut TestAppContext) {
|
||||||
sort_key: (2, "unreachable_unchecked"),
|
sort_key: (2, "unreachable_unchecked"),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
CompletionsMenu::sort_matches(&mut matches, query);
|
CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::default());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
matches[0].string_match.string.as_str(),
|
matches[0].string_match.string.as_str(),
|
||||||
"unreachable!(…)",
|
"unreachable!(…)",
|
||||||
|
@ -467,7 +470,7 @@ fn test_sort_matches_for_unreachable(_cx: &mut TestAppContext) {
|
||||||
sort_key: (2, "unreachable_unchecked"),
|
sort_key: (2, "unreachable_unchecked"),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
CompletionsMenu::sort_matches(&mut matches, query);
|
CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::default());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
matches[0].string_match.string.as_str(),
|
matches[0].string_match.string.as_str(),
|
||||||
"unreachable!(…)",
|
"unreachable!(…)",
|
||||||
|
@ -503,7 +506,7 @@ fn test_sort_matches_variable_and_constants_over_function(_cx: &mut TestAppConte
|
||||||
sort_key: (1, "var"), // variable
|
sort_key: (1, "var"), // variable
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
CompletionsMenu::sort_matches(&mut matches, query);
|
CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::default());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
matches[0].string_match.candidate_id, 1,
|
matches[0].string_match.candidate_id, 1,
|
||||||
"Match order not expected"
|
"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
|
sort_key: (2, "var"), // constant
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
CompletionsMenu::sort_matches(&mut matches, query);
|
CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::default());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
matches[0].string_match.candidate_id, 1,
|
matches[0].string_match.candidate_id, 1,
|
||||||
"Match order not expected"
|
"Match order not expected"
|
||||||
|
@ -622,7 +625,7 @@ fn test_sort_matches_jsx_event_handler(_cx: &mut TestAppContext) {
|
||||||
sort_key: (3, "className?"),
|
sort_key: (3, "className?"),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
CompletionsMenu::sort_matches(&mut matches, query);
|
CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::default());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
matches[0].string_match.string, "onCut?",
|
matches[0].string_match.string, "onCut?",
|
||||||
"Match order not expected"
|
"Match order not expected"
|
||||||
|
@ -944,7 +947,7 @@ fn test_sort_matches_jsx_event_handler(_cx: &mut TestAppContext) {
|
||||||
sort_key: (3, "onLoadedData?"),
|
sort_key: (3, "onLoadedData?"),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
CompletionsMenu::sort_matches(&mut matches, query);
|
CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::default());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
matches
|
matches
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -996,7 +999,7 @@ fn test_sort_matches_for_snippets(_cx: &mut TestAppContext) {
|
||||||
sort_key: (2, "println!(…)"),
|
sort_key: (2, "println!(…)"),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
CompletionsMenu::sort_matches(&mut matches, query);
|
CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::Top);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
matches[0].string_match.string.as_str(),
|
matches[0].string_match.string.as_str(),
|
||||||
"println!(…)",
|
"println!(…)",
|
||||||
|
|
|
@ -25,6 +25,7 @@ use task::ResolvedTask;
|
||||||
use ui::{Color, IntoElement, ListItem, Pixels, Popover, Styled, prelude::*};
|
use ui::{Color, IntoElement, ListItem, Pixels, Popover, Styled, prelude::*};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
|
use crate::editor_settings::SnippetSortOrder;
|
||||||
use crate::hover_popover::{hover_markdown_style, open_markdown_url};
|
use crate::hover_popover::{hover_markdown_style, open_markdown_url};
|
||||||
use crate::{
|
use crate::{
|
||||||
CodeActionProvider, CompletionId, CompletionItemKind, CompletionProvider, DisplayRow, Editor,
|
CodeActionProvider, CompletionId, CompletionItemKind, CompletionProvider, DisplayRow, Editor,
|
||||||
|
@ -184,6 +185,7 @@ pub struct CompletionsMenu {
|
||||||
pub(super) ignore_completion_provider: bool,
|
pub(super) ignore_completion_provider: bool,
|
||||||
last_rendered_range: Rc<RefCell<Option<Range<usize>>>>,
|
last_rendered_range: Rc<RefCell<Option<Range<usize>>>>,
|
||||||
markdown_element: Option<Entity<Markdown>>,
|
markdown_element: Option<Entity<Markdown>>,
|
||||||
|
snippet_sort_order: SnippetSortOrder,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CompletionsMenu {
|
impl CompletionsMenu {
|
||||||
|
@ -195,6 +197,7 @@ impl CompletionsMenu {
|
||||||
initial_position: Anchor,
|
initial_position: Anchor,
|
||||||
buffer: Entity<Buffer>,
|
buffer: Entity<Buffer>,
|
||||||
completions: Box<[Completion]>,
|
completions: Box<[Completion]>,
|
||||||
|
snippet_sort_order: SnippetSortOrder,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let match_candidates = completions
|
let match_candidates = completions
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -217,6 +220,7 @@ impl CompletionsMenu {
|
||||||
resolve_completions: true,
|
resolve_completions: true,
|
||||||
last_rendered_range: RefCell::new(None).into(),
|
last_rendered_range: RefCell::new(None).into(),
|
||||||
markdown_element: None,
|
markdown_element: None,
|
||||||
|
snippet_sort_order,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,6 +230,7 @@ impl CompletionsMenu {
|
||||||
choices: &Vec<String>,
|
choices: &Vec<String>,
|
||||||
selection: Range<Anchor>,
|
selection: Range<Anchor>,
|
||||||
buffer: Entity<Buffer>,
|
buffer: Entity<Buffer>,
|
||||||
|
snippet_sort_order: SnippetSortOrder,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let completions = choices
|
let completions = choices
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -275,6 +280,7 @@ impl CompletionsMenu {
|
||||||
ignore_completion_provider: false,
|
ignore_completion_provider: false,
|
||||||
last_rendered_range: RefCell::new(None).into(),
|
last_rendered_range: RefCell::new(None).into(),
|
||||||
markdown_element: None,
|
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)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
enum MatchTier<'a> {
|
enum MatchTier<'a> {
|
||||||
WordStartMatch {
|
WordStartMatch {
|
||||||
|
@ -703,7 +713,11 @@ impl CompletionsMenu {
|
||||||
MatchTier::OtherMatch { sort_score }
|
MatchTier::OtherMatch { sort_score }
|
||||||
} else {
|
} else {
|
||||||
let sort_score_int = Reverse(if score >= FUZZY_THRESHOLD { 1 } else { 0 });
|
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 {
|
MatchTier::WordStartMatch {
|
||||||
sort_score_int,
|
sort_score_int,
|
||||||
sort_snippet,
|
sort_snippet,
|
||||||
|
@ -770,7 +784,7 @@ impl CompletionsMenu {
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Self::sort_matches(&mut sortable_items, query);
|
Self::sort_matches(&mut sortable_items, query, self.snippet_sort_order);
|
||||||
|
|
||||||
matches = sortable_items
|
matches = sortable_items
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
|
@ -4733,6 +4733,8 @@ impl Editor {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(true, |provider| provider.filter_completions());
|
.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 id = post_inc(&mut self.next_completion_id);
|
||||||
let task = cx.spawn_in(window, async move |editor, cx| {
|
let task = cx.spawn_in(window, async move |editor, cx| {
|
||||||
async move {
|
async move {
|
||||||
|
@ -4780,6 +4782,7 @@ impl Editor {
|
||||||
position,
|
position,
|
||||||
buffer.clone(),
|
buffer.clone(),
|
||||||
completions.into(),
|
completions.into(),
|
||||||
|
snippet_sort_order,
|
||||||
);
|
);
|
||||||
|
|
||||||
menu.filter(
|
menu.filter(
|
||||||
|
@ -8229,10 +8232,18 @@ impl Editor {
|
||||||
let buffer_id = selection.start.buffer_id.unwrap();
|
let buffer_id = selection.start.buffer_id.unwrap();
|
||||||
let buffer = self.buffer().read(cx).buffer(buffer_id);
|
let buffer = self.buffer().read(cx).buffer(buffer_id);
|
||||||
let id = post_inc(&mut self.next_completion_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 {
|
if let Some(buffer) = buffer {
|
||||||
*self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
|
*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 go_to_definition_fallback: GoToDefinitionFallback,
|
||||||
pub jupyter: Jupyter,
|
pub jupyter: Jupyter,
|
||||||
pub hide_mouse: Option<HideMouseMode>,
|
pub hide_mouse: Option<HideMouseMode>,
|
||||||
|
pub snippet_sort_order: SnippetSortOrder,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||||
|
@ -239,6 +240,21 @@ pub enum HideMouseMode {
|
||||||
OnTypingAndMovement,
|
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)]
|
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct EditorSettingsContent {
|
pub struct EditorSettingsContent {
|
||||||
/// Whether the cursor blinks in the editor.
|
/// Whether the cursor blinks in the editor.
|
||||||
|
@ -254,6 +270,10 @@ pub struct EditorSettingsContent {
|
||||||
///
|
///
|
||||||
/// Default: on_typing_and_movement
|
/// Default: on_typing_and_movement
|
||||||
pub hide_mouse: Option<HideMouseMode>,
|
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.
|
/// How to highlight the current line in the editor.
|
||||||
///
|
///
|
||||||
/// Default: all
|
/// Default: all
|
||||||
|
|
|
@ -592,7 +592,49 @@ List of `string` values
|
||||||
|
|
||||||
**Options**
|
**Options**
|
||||||
|
|
||||||
`boolean` values
|
1. Never hide the mouse cursor:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"hide_mouse": "never"
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Hide only when typing:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"hide_mouse": "on_typing"
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Hide on both typing and cursor movement:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"hide_mouse": "on_typing_and_movement"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Snippet Sort Order
|
||||||
|
|
||||||
|
- Description: Determines how snippets are sorted relative to other completion items.
|
||||||
|
- Setting: `snippet_sort_order`
|
||||||
|
- Default: `inline`
|
||||||
|
|
||||||
|
**Options**
|
||||||
|
|
||||||
|
1. Place snippets at the top of the completion list:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"snippet_sort_order": "top"
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Place snippets normally without any preference:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"snippet_sort_order": "inline"
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Place snippets at the bottom of the completion list:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"snippet_sort_order": "bottom"
|
||||||
|
```
|
||||||
|
|
||||||
## Editor Scrollbar
|
## Editor Scrollbar
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue