Skip to content

Commit deda74e

Browse files
Use stateless completion resolve
1 parent 93bc009 commit deda74e

File tree

6 files changed

+124
-71
lines changed

6 files changed

+124
-71
lines changed

crates/completion/src/completions/unqualified_path.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use test_utils::mark;
99

1010
use crate::{
1111
render::{render_resolution_with_import, RenderContext},
12-
CompletionContext, Completions,
12+
CompletionContext, Completions, ImportEdit,
1313
};
1414

1515
pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
@@ -103,9 +103,11 @@ fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()
103103
.filter_map(|(import_path, definition)| {
104104
render_resolution_with_import(
105105
RenderContext::new(ctx),
106-
import_path.clone(),
107-
import_scope.clone(),
108-
ctx.config.merge,
106+
ImportEdit {
107+
import_path: import_path.clone(),
108+
import_scope: import_scope.clone(),
109+
merge_behaviour: ctx.config.merge,
110+
},
109111
&definition,
110112
)
111113
});

crates/completion/src/item.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,6 @@ pub struct ImportEdit {
276276
}
277277

278278
impl ImportEdit {
279-
// TODO kb remove this at all now, since it's used only once?
280279
/// Attempts to insert the import to the given scope, producing a text edit.
281280
/// May return no edit in edge cases, such as scope already containing the import.
282281
pub fn to_text_edit(&self) -> Option<TextEdit> {

crates/completion/src/lib.rs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@ mod render;
1111

1212
mod completions;
1313

14-
use ide_db::base_db::FilePosition;
15-
use ide_db::RootDatabase;
14+
use ide_db::{
15+
base_db::FilePosition, helpers::insert_use::ImportScope, imports_locator, RootDatabase,
16+
};
17+
use syntax::AstNode;
18+
use text_edit::TextEdit;
1619

1720
use crate::{completions::Completions, context::CompletionContext, item::CompletionKind};
1821

@@ -131,6 +134,31 @@ pub fn completions(
131134
Some(acc)
132135
}
133136

137+
/// Resolves additional completion data at the position given.
138+
pub fn resolve_completion_edits(
139+
db: &RootDatabase,
140+
config: &CompletionConfig,
141+
position: FilePosition,
142+
full_import_path: &str,
143+
imported_name: &str,
144+
) -> Option<TextEdit> {
145+
let ctx = CompletionContext::new(db, position, config)?;
146+
let anchor = ctx.name_ref_syntax.as_ref()?;
147+
let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?;
148+
149+
let current_module = ctx.sema.scope(anchor.syntax()).module()?;
150+
let current_crate = current_module.krate();
151+
152+
let import_path = imports_locator::find_exact_imports(&ctx.sema, current_crate, imported_name)
153+
.filter_map(|candidate| {
154+
let item: hir::ItemInNs = candidate.either(Into::into, Into::into);
155+
current_module.find_use_path(db, item)
156+
})
157+
.find(|mod_path| mod_path.to_string() == full_import_path)?;
158+
159+
ImportEdit { import_path, import_scope, merge_behaviour: config.merge }.to_text_edit()
160+
}
161+
134162
#[cfg(test)]
135163
mod tests {
136164
use crate::config::CompletionConfig;

crates/completion/src/render.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ pub(crate) mod type_alias;
99

1010
mod builder_ext;
1111

12-
use hir::{Documentation, HasAttrs, HirDisplay, ModPath, Mutability, ScopeDef, Type};
13-
use ide_db::helpers::insert_use::{ImportScope, MergeBehaviour};
12+
use hir::{Documentation, HasAttrs, HirDisplay, Mutability, ScopeDef, Type};
1413
use ide_db::RootDatabase;
1514
use syntax::TextRange;
1615
use test_utils::mark;
@@ -48,15 +47,12 @@ pub(crate) fn render_resolution<'a>(
4847

4948
pub(crate) fn render_resolution_with_import<'a>(
5049
ctx: RenderContext<'a>,
51-
import_path: ModPath,
52-
import_scope: ImportScope,
53-
merge_behaviour: Option<MergeBehaviour>,
50+
import_edit: ImportEdit,
5451
resolution: &ScopeDef,
5552
) -> Option<CompletionItem> {
56-
let local_name = import_path.segments.last()?.to_string();
5753
Render::new(ctx).render_resolution(
58-
local_name,
59-
Some(ImportEdit { import_path, import_scope, merge_behaviour }),
54+
import_edit.import_path.segments.last()?.to_string(),
55+
Some(import_edit),
6056
resolution,
6157
)
6258
}

crates/ide/src/lib.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,28 @@ impl Analysis {
469469
self.with_db(|db| completion::completions(db, config, position).map(Into::into))
470470
}
471471

472+
/// Resolves additional completion data at the position given.
473+
pub fn resolve_completion_edits(
474+
&self,
475+
config: &CompletionConfig,
476+
position: FilePosition,
477+
full_import_path: &str,
478+
imported_name: &str,
479+
) -> Cancelable<Vec<TextEdit>> {
480+
Ok(self
481+
.with_db(|db| {
482+
completion::resolve_completion_edits(
483+
db,
484+
config,
485+
position,
486+
full_import_path,
487+
imported_name,
488+
)
489+
})?
490+
.map(|edit| vec![edit])
491+
.unwrap_or_default())
492+
}
493+
472494
/// Computes resolved assists with source changes for the given position.
473495
pub fn resolved_assists(
474496
&self,

crates/rust-analyzer/src/handlers.rs

Lines changed: 62 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ use std::{
88
};
99

1010
use ide::{
11-
CompletionResolveCapability, FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData,
12-
ImportEdit, LineIndex, NavigationTarget, Query, RangeInfo, Runnable, RunnableKind, SearchScope,
11+
CompletionConfig, CompletionResolveCapability, FileId, FilePosition, FileRange, HoverAction,
12+
HoverGotoTypeData, NavigationTarget, Query, RangeInfo, Runnable, RunnableKind, SearchScope,
1313
TextEdit,
1414
};
1515
use itertools::Itertools;
@@ -22,7 +22,7 @@ use lsp_types::{
2222
HoverContents, Location, NumberOrString, Position, PrepareRenameResponse, Range, RenameParams,
2323
SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, SemanticTokensParams,
2424
SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation,
25-
SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit,
25+
SymbolTag, TextDocumentIdentifier, TextDocumentPositionParams, Url, WorkspaceEdit,
2626
};
2727
use project_model::TargetKind;
2828
use serde::{Deserialize, Serialize};
@@ -35,7 +35,6 @@ use crate::{
3535
config::RustfmtConfig,
3636
from_json, from_proto,
3737
global_state::{GlobalState, GlobalStateSnapshot},
38-
line_endings::LineEndings,
3938
lsp_ext::{self, InlayHint, InlayHintsParams},
4039
to_proto, LspError, Result,
4140
};
@@ -541,7 +540,7 @@ pub(crate) fn handle_completion(
541540
params: lsp_types::CompletionParams,
542541
) -> Result<Option<lsp_types::CompletionResponse>> {
543542
let _p = profile::span("handle_completion");
544-
let text_document_url = params.text_document_position.text_document.uri.clone();
543+
let text_document_position = params.text_document_position.clone();
545544
let position = from_proto::file_position(&snap, params.text_document_position)?;
546545
let completion_triggered_after_single_colon = {
547546
let mut res = false;
@@ -574,23 +573,18 @@ pub(crate) fn handle_completion(
574573

575574
let items: Vec<CompletionItem> = items
576575
.into_iter()
577-
.enumerate()
578-
.flat_map(|(item_index, item)| {
576+
.flat_map(|item| {
579577
let mut new_completion_items =
580578
to_proto::completion_item(&line_index, line_endings, item.clone());
581579

582-
if snap.config.completion.resolve_additional_edits_lazily() {
583-
// TODO kb add resolve data somehow here
584-
if let Some(import_edit) = item.import_to_add() {
585-
// let data = serde_json::to_value(&CompletionData {
586-
// document_url: text_document_url.clone(),
587-
// import_id: item_index,
588-
// })
589-
// .expect(&format!("Should be able to serialize usize value {}", item_index));
590-
for new_item in &mut new_completion_items {
591-
// new_item.data = Some(data.clone());
592-
}
593-
}
580+
for new_item in &mut new_completion_items {
581+
let _ = fill_resolve_data(
582+
&mut new_item.data,
583+
&item,
584+
&snap.config.completion,
585+
&text_document_position,
586+
)
587+
.take();
594588
}
595589

596590
new_completion_items
@@ -603,8 +597,8 @@ pub(crate) fn handle_completion(
603597

604598
pub(crate) fn handle_completion_resolve(
605599
snap: GlobalStateSnapshot,
606-
mut original_completion: lsp_types::CompletionItem,
607-
) -> Result<lsp_types::CompletionItem> {
600+
mut original_completion: CompletionItem,
601+
) -> Result<CompletionItem> {
608602
let _p = profile::span("handle_resolve_completion");
609603

610604
// FIXME resolve the other capabilities also?
@@ -627,21 +621,30 @@ pub(crate) fn handle_completion_resolve(
627621
None => return Ok(original_completion),
628622
};
629623

630-
// TODO kb get the resolve data and somehow reparse the whole ast again?
631-
// let file_id = from_proto::file_id(&snap, &document_url)?;
632-
// let root = snap.analysis.parse(file_id)?;
633-
634-
// if let Some(import_to_add) =
635-
// import_edit_ptr.and_then(|import_edit| import_edit.into_import_edit(root.syntax()))
636-
// {
637-
// // FIXME actually add all additional edits here? see `to_proto::completion_item` for more
638-
// append_import_edits(
639-
// &mut original_completion,
640-
// &import_to_add,
641-
// snap.analysis.file_line_index(file_id)?.as_ref(),
642-
// snap.file_line_endings(file_id),
643-
// );
644-
// }
624+
let file_id = from_proto::file_id(&snap, &resolve_data.position.text_document.uri)?;
625+
let line_index = snap.analysis.file_line_index(file_id)?;
626+
let line_endings = snap.file_line_endings(file_id);
627+
let offset = from_proto::offset(&line_index, resolve_data.position.position);
628+
629+
let mut additional_edits = snap
630+
.analysis
631+
.resolve_completion_edits(
632+
&snap.config.completion,
633+
FilePosition { file_id, offset },
634+
&resolve_data.full_import_path,
635+
&resolve_data.imported_name,
636+
)?
637+
.into_iter()
638+
.flat_map(|edit| {
639+
edit.into_iter().map(|indel| to_proto::text_edit(&line_index, line_endings, indel))
640+
})
641+
.collect_vec();
642+
643+
if let Some(original_additional_edits) = original_completion.additional_text_edits.as_mut() {
644+
original_additional_edits.extend(additional_edits.drain(..))
645+
} else {
646+
original_completion.additional_text_edits = Some(additional_edits);
647+
}
645648

646649
Ok(original_completion)
647650
}
@@ -1606,27 +1609,30 @@ fn should_skip_target(runnable: &Runnable, cargo_spec: Option<&CargoTargetSpec>)
16061609

16071610
#[derive(Debug, Serialize, Deserialize)]
16081611
struct CompletionResolveData {
1609-
document_url: Url,
1610-
import_id: usize,
1612+
position: lsp_types::TextDocumentPositionParams,
1613+
full_import_path: String,
1614+
imported_name: String,
16111615
}
16121616

1613-
fn append_import_edits(
1614-
completion: &mut lsp_types::CompletionItem,
1615-
import_to_add: &ImportEdit,
1616-
line_index: &LineIndex,
1617-
line_endings: LineEndings,
1618-
) {
1619-
let import_edits = import_to_add.to_text_edit().map(|import_edit| {
1620-
import_edit
1621-
.into_iter()
1622-
.map(|indel| to_proto::text_edit(line_index, line_endings, indel))
1623-
.collect_vec()
1624-
});
1625-
if let Some(original_additional_edits) = completion.additional_text_edits.as_mut() {
1626-
if let Some(mut new_edits) = import_edits {
1627-
original_additional_edits.extend(new_edits.drain(..))
1628-
}
1629-
} else {
1630-
completion.additional_text_edits = import_edits;
1617+
fn fill_resolve_data(
1618+
resolve_data: &mut Option<serde_json::Value>,
1619+
item: &ide::CompletionItem,
1620+
completion_config: &CompletionConfig,
1621+
position: &TextDocumentPositionParams,
1622+
) -> Option<()> {
1623+
if completion_config.resolve_additional_edits_lazily() {
1624+
let import_edit = item.import_to_add()?;
1625+
let full_import_path = import_edit.import_path.to_string();
1626+
let imported_name = import_edit.import_path.segments.clone().pop()?.to_string();
1627+
1628+
*resolve_data = Some(
1629+
serde_json::to_value(CompletionResolveData {
1630+
position: position.to_owned(),
1631+
full_import_path,
1632+
imported_name,
1633+
})
1634+
.expect("Failed to serialize a regular struct with derives"),
1635+
)
16311636
}
1637+
Some(())
16321638
}

0 commit comments

Comments
 (0)