@@ -62,6 +62,16 @@ impl SlashCommand for TabSlashCommand {
if has_all_tabs_completion_item {
return Task::ready(Ok(Vec::new()));
}
+
+ let active_item_path = workspace.as_ref().and_then(|workspace| {
+ workspace
+ .update(cx, |workspace, cx| {
+ let snapshot = active_item_buffer(workspace, cx).ok()?;
+ snapshot.resolve_file_path(cx, true)
+ })
+ .ok()
+ .flatten()
+ });
let current_query = arguments.last().cloned().unwrap_or_default();
let tab_items_search =
tab_items_for_queries(workspace, &[current_query], cancel, false, cx);
@@ -73,6 +83,9 @@ impl SlashCommand for TabSlashCommand {
if argument_set.contains(&path_string) {
return None;
}
+ if active_item_path.is_some() && active_item_path == path {
+ return None;
+ }
Some(ArgumentCompletion {
label: path_string.clone().into(),
new_text: path_string,
@@ -81,15 +94,26 @@ impl SlashCommand for TabSlashCommand {
})
});
- Ok(Some(ArgumentCompletion {
- label: ALL_TABS_COMPLETION_ITEM.into(),
- new_text: ALL_TABS_COMPLETION_ITEM.to_owned(),
- replace_previous_arguments: false,
- run_command: true,
- })
- .into_iter()
- .chain(tab_completion_items)
- .collect::<Vec<_>>())
+ let active_item_completion = active_item_path.as_deref().map(|active_item_path| {
+ let path_string = active_item_path.to_string_lossy().to_string();
+ ArgumentCompletion {
+ label: path_string.clone().into(),
+ new_text: path_string,
+ replace_previous_arguments: false,
+ run_command,
+ }
+ });
+
+ Ok(active_item_completion
+ .into_iter()
+ .chain(Some(ArgumentCompletion {
+ label: ALL_TABS_COMPLETION_ITEM.into(),
+ new_text: ALL_TABS_COMPLETION_ITEM.to_owned(),
+ replace_previous_arguments: false,
+ run_command: true,
+ }))
+ .chain(tab_completion_items)
+ .collect())
})
}
@@ -162,19 +186,7 @@ fn tab_items_for_queries(
.context("no workspace")?
.update(&mut cx, |workspace, cx| {
if strict_match && empty_query {
- let active_editor = workspace
- .active_item(cx)
- .context("no active item")?
- .downcast::<Editor>()
- .context("active item is not an editor")?;
- let snapshot = active_editor
- .read(cx)
- .buffer()
- .read(cx)
- .as_singleton()
- .context("active editor is not a singleton buffer")?
- .read(cx)
- .snapshot();
+ let snapshot = active_item_buffer(workspace, cx)?;
let full_path = snapshot.resolve_file_path(cx, true);
return anyhow::Ok(vec![(full_path, snapshot, 0)]);
}
@@ -279,3 +291,23 @@ fn tab_items_for_queries(
.await
})
}
+
+fn active_item_buffer(
+ workspace: &mut Workspace,
+ cx: &mut ui::ViewContext<Workspace>,
+) -> anyhow::Result<BufferSnapshot> {
+ let active_editor = workspace
+ .active_item(cx)
+ .context("no active item")?
+ .downcast::<Editor>()
+ .context("active item is not an editor")?;
+ let snapshot = active_editor
+ .read(cx)
+ .buffer()
+ .read(cx)
+ .as_singleton()
+ .context("active editor is not a singleton buffer")?
+ .read(cx)
+ .snapshot();
+ Ok(snapshot)
+}
@@ -900,6 +900,7 @@ enum ContextMenuOrigin {
#[derive(Clone)]
struct CompletionsMenu {
id: CompletionId,
+ sort_completions: bool,
initial_position: Anchor,
buffer: Model<Buffer>,
completions: Arc<RwLock<Box<[Completion]>>>,
@@ -1225,55 +1226,57 @@ impl CompletionsMenu {
}
let completions = self.completions.read();
- matches.sort_unstable_by_key(|mat| {
- // We do want to strike a balance here between what the language server tells us
- // to sort by (the sort_text) and what are "obvious" good matches (i.e. when you type
- // `Creat` and there is a local variable called `CreateComponent`).
- // So what we do is: we bucket all matches into two buckets
- // - Strong matches
- // - Weak matches
- // Strong matches are the ones with a high fuzzy-matcher score (the "obvious" matches)
- // and the Weak matches are the rest.
- //
- // For the strong matches, we sort by the language-servers score first and for the weak
- // matches, we prefer our fuzzy finder first.
- //
- // The thinking behind that: it's useless to take the sort_text the language-server gives
- // us into account when it's obviously a bad match.
-
- #[derive(PartialEq, Eq, PartialOrd, Ord)]
- enum MatchScore<'a> {
- Strong {
- sort_text: Option<&'a str>,
- score: Reverse<OrderedFloat<f64>>,
- sort_key: (usize, &'a str),
- },
- Weak {
- score: Reverse<OrderedFloat<f64>>,
- sort_text: Option<&'a str>,
- sort_key: (usize, &'a str),
- },
- }
-
- let completion = &completions[mat.candidate_id];
- let sort_key = completion.sort_key();
- let sort_text = completion.lsp_completion.sort_text.as_deref();
- let score = Reverse(OrderedFloat(mat.score));
-
- if mat.score >= 0.2 {
- MatchScore::Strong {
- sort_text,
- score,
- sort_key,
+ if self.sort_completions {
+ matches.sort_unstable_by_key(|mat| {
+ // We do want to strike a balance here between what the language server tells us
+ // to sort by (the sort_text) and what are "obvious" good matches (i.e. when you type
+ // `Creat` and there is a local variable called `CreateComponent`).
+ // So what we do is: we bucket all matches into two buckets
+ // - Strong matches
+ // - Weak matches
+ // Strong matches are the ones with a high fuzzy-matcher score (the "obvious" matches)
+ // and the Weak matches are the rest.
+ //
+ // For the strong matches, we sort by the language-servers score first and for the weak
+ // matches, we prefer our fuzzy finder first.
+ //
+ // The thinking behind that: it's useless to take the sort_text the language-server gives
+ // us into account when it's obviously a bad match.
+
+ #[derive(PartialEq, Eq, PartialOrd, Ord)]
+ enum MatchScore<'a> {
+ Strong {
+ sort_text: Option<&'a str>,
+ score: Reverse<OrderedFloat<f64>>,
+ sort_key: (usize, &'a str),
+ },
+ Weak {
+ score: Reverse<OrderedFloat<f64>>,
+ sort_text: Option<&'a str>,
+ sort_key: (usize, &'a str),
+ },
}
- } else {
- MatchScore::Weak {
- score,
- sort_text,
- sort_key,
+
+ let completion = &completions[mat.candidate_id];
+ let sort_key = completion.sort_key();
+ let sort_text = completion.lsp_completion.sort_text.as_deref();
+ let score = Reverse(OrderedFloat(mat.score));
+
+ if mat.score >= 0.2 {
+ MatchScore::Strong {
+ sort_text,
+ score,
+ sort_key,
+ }
+ } else {
+ MatchScore::Weak {
+ score,
+ sort_text,
+ sort_key,
+ }
}
- }
- });
+ });
+ }
for mat in &mut matches {
let completion = &completions[mat.candidate_id];
@@ -4105,6 +4108,7 @@ impl Editor {
trigger_kind,
};
let completions = provider.completions(&buffer, buffer_position, completion_context, cx);
+ let sort_completions = provider.sort_completions();
let id = post_inc(&mut self.next_completion_id);
let task = cx.spawn(|this, mut cx| {
@@ -4116,6 +4120,7 @@ impl Editor {
let menu = if let Some(completions) = completions {
let mut menu = CompletionsMenu {
id,
+ sort_completions,
initial_position: position,
match_candidates: completions
.iter()
@@ -12045,6 +12050,10 @@ pub trait CompletionProvider {
trigger_in_words: bool,
cx: &mut ViewContext<Editor>,
) -> bool;
+
+ fn sort_completions(&self) -> bool {
+ true
+ }
}
fn snippet_completions(