Skip to content

Commit 3851f24

Browse files
More completion refactoring around how completions are marshalled (#755)
* Refactor how completion items are marshalled * It can be nice to see completion text and type * Inline logging * This is already being tracked in #764 * We're definitely not doing this and the logging is super annoying * Rescue this comment * Streamline completion item tracking * Go back to keying on label. If we have future concerns about two different CompletionItems having the same label, new logging will alert us and we can dig deeper. * Rename CompletionWithSource to CompletionItemWithSource and only track the first source that contributes the item. * Transfer completion ownership to the finalizer * Let the compiler infer the type Co-authored-by: Davis Vaughan <[email protected]> * Trace not debug Co-authored-by: Davis Vaughan <[email protected]> * Remove comment --------- Co-authored-by: Davis Vaughan <[email protected]> Co-authored-by: Davis Vaughan <[email protected]>
1 parent 0b49cd5 commit 3851f24

File tree

3 files changed

+77
-31
lines changed

3 files changed

+77
-31
lines changed

crates/ark/src/lsp/completions/provide.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,21 @@ use crate::lsp::completions::sources::composite;
1212
use crate::lsp::completions::sources::unique;
1313
use crate::lsp::document_context::DocumentContext;
1414
use crate::lsp::state::WorldState;
15+
use crate::treesitter::node_text;
16+
use crate::treesitter::NodeTypeExt;
1517

1618
// Entry point for completions.
1719
// Must be within an `r_task()`.
1820
pub(crate) fn provide_completions(
1921
document_context: &DocumentContext,
2022
state: &WorldState,
2123
) -> anyhow::Result<Vec<CompletionItem>> {
22-
log::info!("provide_completions()");
24+
log::info!(
25+
"provide_completions() - Completion node text: '{node_text}', Node type: '{node_type:?}'",
26+
node_text = node_text(&document_context.node, &document_context.document.contents)
27+
.unwrap_or_default(),
28+
node_type = document_context.node.node_type()
29+
);
2330

2431
let completion_context = CompletionContext::new(document_context, state);
2532

crates/ark/src/lsp/completions/sources.rs

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ where
3333
S: CompletionSource,
3434
{
3535
let source_name = source.name();
36-
log::trace!("Trying completions from source: {}", source_name);
36+
log::info!("Trying completions from source: {}", source_name);
3737

3838
if let Some(completions) = source.provide_completions(completion_context)? {
3939
log::info!(
@@ -46,17 +46,3 @@ where
4646
Ok(None)
4747
}
4848
}
49-
50-
pub fn push_completions<S>(
51-
source: S,
52-
completion_context: &CompletionContext,
53-
completions: &mut Vec<CompletionItem>,
54-
) -> anyhow::Result<()>
55-
where
56-
S: CompletionSource,
57-
{
58-
if let Some(mut additional_completions) = collect_completions(source, completion_context)? {
59-
completions.append(&mut additional_completions);
60-
}
61-
Ok(())
62-
}

crates/ark/src/lsp/completions/sources/composite.rs

Lines changed: 68 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,33 @@ mod snippets;
1414
mod subset;
1515
mod workspace;
1616

17-
use std::collections::HashSet;
17+
use std::collections::HashMap;
1818

1919
use stdext::*;
2020
use tower_lsp::lsp_types::CompletionItem;
2121
use tower_lsp::lsp_types::CompletionItemKind;
2222
use tree_sitter::Node;
2323

2424
use crate::lsp::completions::completion_context::CompletionContext;
25-
use crate::lsp::completions::sources::push_completions;
25+
use crate::lsp::completions::sources::collect_completions;
26+
use crate::lsp::completions::sources::CompletionSource;
2627
use crate::treesitter::NodeType;
2728
use crate::treesitter::NodeTypeExt;
2829

30+
// Locally useful data structure for tracking completions and their source
31+
#[derive(Clone, Default)]
32+
struct CompletionItemWithSource {
33+
item: CompletionItem,
34+
source: String,
35+
}
36+
2937
/// Gets completions from all composite sources, with deduplication and sorting
3038
pub(crate) fn get_completions(
3139
completion_context: &CompletionContext,
3240
) -> anyhow::Result<Option<Vec<CompletionItem>>> {
3341
log::info!("Getting completions from composite sources");
3442

35-
let mut completions: Vec<CompletionItem> = vec![];
43+
let mut completions = HashMap::new();
3644

3745
// Call, pipe, and subset completions should show up no matter what when
3846
// the user requests completions. This allows them to "tab" their way
@@ -50,7 +58,6 @@ pub(crate) fn get_completions(
5058
// For the rest of the general completions, we require an identifier to
5159
// begin showing anything.
5260
if is_identifier_like(completion_context.document_context.node) {
53-
// Consulted settings.json
5461
push_completions(keyword::KeywordSource, completion_context, &mut completions)?;
5562

5663
push_completions(
@@ -78,16 +85,64 @@ pub(crate) fn get_completions(
7885
)?;
7986
}
8087

81-
// Remove duplicates
82-
let mut uniques = HashSet::new();
83-
completions.retain(|x| uniques.insert(x.label.clone()));
88+
// Simplify to plain old CompletionItems and sort them
89+
let completions = finalize_completions(completions);
90+
91+
Ok(Some(completions))
92+
}
8493

85-
// Sort completions by providing custom 'sort' text to be used when
86-
// ordering completion results. we use some placeholders at the front
87-
// to 'bin' different completion types differently; e.g. we place parameter
88-
// completions at the front, followed by variable completions (like pipe
89-
// completions and subset completions), followed by anything else.
90-
for item in &mut completions {
94+
fn push_completions<S>(
95+
source: S,
96+
completion_context: &CompletionContext,
97+
completions: &mut HashMap<String, CompletionItemWithSource>,
98+
) -> anyhow::Result<()>
99+
where
100+
S: CompletionSource,
101+
{
102+
let source_name = source.name();
103+
104+
if let Some(source_completions) = collect_completions(source, completion_context)? {
105+
for item in source_completions {
106+
if let Some(existing) = completions.get(&item.label) {
107+
log::trace!(
108+
"Completion with label '{}' already exists (first contributed by source: {}, now also from: {})",
109+
item.label,
110+
existing.source,
111+
source_name
112+
);
113+
} else {
114+
completions.insert(item.label.clone(), CompletionItemWithSource {
115+
item,
116+
source: source_name.to_string(),
117+
});
118+
}
119+
}
120+
}
121+
122+
Ok(())
123+
}
124+
125+
/// Produce plain old CompletionItems and sort them
126+
fn finalize_completions(
127+
completions: HashMap<String, CompletionItemWithSource>,
128+
) -> Vec<CompletionItem> {
129+
let mut items: Vec<CompletionItem> = completions
130+
.into_values()
131+
.map(|completion_with_source| completion_with_source.item)
132+
.collect();
133+
134+
sort_completions(&mut items);
135+
136+
items
137+
}
138+
139+
// Sort completions by providing custom 'sort' text to be used when
140+
// ordering completion results. we use some placeholders at the front
141+
// to 'bin' different completion types differently; e.g. we place parameter
142+
// completions at the front, followed by variable completions (like pipe
143+
// completions and subset completions), followed by anything else.
144+
fn sort_completions(completions: &mut Vec<CompletionItem>) {
145+
for item in completions {
91146
// Start with existing `sort_text` if one exists
92147
let sort_text = item.sort_text.take();
93148
let sort_text = match sort_text {
@@ -115,8 +170,6 @@ pub(crate) fn get_completions(
115170
}
116171
}
117172
}
118-
119-
Ok(Some(completions))
120173
}
121174

122175
fn is_identifier_like(x: Node) -> bool {

0 commit comments

Comments
 (0)