aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKirill Bulatov <[email protected]>2020-12-03 09:13:28 +0000
committerKirill Bulatov <[email protected]>2020-12-07 21:41:08 +0000
commitf6d2540df09bc0dcd8a748ec0ed7cb33ac76d9f2 (patch)
treea206df6f66f41a9f4840d3e19204d67a46831513
parent68a747efe048e8e92eedafaa27b0c0d2f317f04d (diff)
Simplify import edit calculation
-rw-r--r--crates/completion/src/config.rs10
-rw-r--r--crates/completion/src/item.rs63
-rw-r--r--crates/completion/src/lib.rs2
-rw-r--r--crates/completion/src/render.rs13
-rw-r--r--crates/completion/src/render/enum_variant.rs8
-rw-r--r--crates/completion/src/render/function.rs8
-rw-r--r--crates/completion/src/render/macro_.rs11
-rw-r--r--crates/ide/src/lib.rs2
-rw-r--r--crates/rust-analyzer/src/caps.rs14
-rw-r--r--crates/rust-analyzer/src/global_state.rs4
-rw-r--r--crates/rust-analyzer/src/handlers.rs88
-rw-r--r--crates/rust-analyzer/src/main_loop.rs2
12 files changed, 114 insertions, 111 deletions
diff --git a/crates/completion/src/config.rs b/crates/completion/src/config.rs
index eacdd3449..487c1d0f1 100644
--- a/crates/completion/src/config.rs
+++ b/crates/completion/src/config.rs
@@ -36,12 +36,10 @@ impl CompletionConfig {
36 self.snippet_cap = if yes { Some(SnippetCap { _private: () }) } else { None } 36 self.snippet_cap = if yes { Some(SnippetCap { _private: () }) } else { None }
37 } 37 }
38 38
39 /// Whether the completions' additional edits are calculated later, during a resolve request or not. 39 /// Whether the completions' additional edits are calculated when sending an initional completions list
40 /// See `CompletionResolveCapability` for the details. 40 /// or later, in a separate resolve request.
41 pub fn resolve_edits_immediately(&self) -> bool { 41 pub fn resolve_additional_edits_lazily(&self) -> bool {
42 !self 42 self.active_resolve_capabilities.contains(&CompletionResolveCapability::AdditionalTextEdits)
43 .active_resolve_capabilities
44 .contains(&CompletionResolveCapability::AdditionalTextEdits)
45 } 43 }
46} 44}
47 45
diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs
index 775245b3b..4e56f28f3 100644
--- a/crates/completion/src/item.rs
+++ b/crates/completion/src/item.rs
@@ -68,7 +68,7 @@ pub struct CompletionItem {
68 ref_match: Option<(Mutability, CompletionScore)>, 68 ref_match: Option<(Mutability, CompletionScore)>,
69 69
70 /// The import data to add to completion's edits. 70 /// The import data to add to completion's edits.
71 import_to_add: Option<ImportToAdd>, 71 import_to_add: Option<ImportEdit>,
72} 72}
73 73
74// We use custom debug for CompletionItem to make snapshot tests more readable. 74// We use custom debug for CompletionItem to make snapshot tests more readable.
@@ -209,7 +209,7 @@ impl CompletionItem {
209 score: None, 209 score: None,
210 ref_match: None, 210 ref_match: None,
211 import_to_add: None, 211 import_to_add: None,
212 resolve_import_immediately: true, 212 resolve_import_lazily: false,
213 } 213 }
214 } 214 }
215 215
@@ -262,27 +262,46 @@ impl CompletionItem {
262 self.ref_match 262 self.ref_match
263 } 263 }
264 264
265 pub fn import_to_add(&self) -> Option<&ImportToAdd> { 265 pub fn import_to_add(&self) -> Option<&ImportEdit> {
266 self.import_to_add.as_ref() 266 self.import_to_add.as_ref()
267 } 267 }
268} 268}
269 269
270/// An extra import to add after the completion is applied. 270/// An extra import to add after the completion is applied.
271#[derive(Debug, Clone)] 271#[derive(Debug, Clone)]
272pub struct ImportToAdd { 272pub struct ImportEdit {
273 pub import_path: ModPath, 273 pub import_path: ModPath,
274 pub import_scope: ImportScope, 274 pub import_scope: ImportScope,
275 pub merge_behaviour: Option<MergeBehaviour>, 275 pub merge_behaviour: Option<MergeBehaviour>,
276} 276}
277 277
278impl ImportEdit {
279 /// Attempts to insert the import to the given scope, producing a text edit.
280 /// May return no edit in edge cases, such as scope already containing the import.
281 pub fn to_text_edit(&self) -> Option<TextEdit> {
282 let _p = profile::span("ImportEdit::to_edit");
283
284 let rewriter = insert_use::insert_use(
285 &self.import_scope,
286 mod_path_to_ast(&self.import_path),
287 self.merge_behaviour,
288 );
289 let old_ast = rewriter.rewrite_root()?;
290 let mut import_insert = TextEdit::builder();
291 algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert);
292
293 Some(import_insert.finish())
294 }
295}
296
278/// A helper to make `CompletionItem`s. 297/// A helper to make `CompletionItem`s.
279#[must_use] 298#[must_use]
280#[derive(Clone)] 299#[derive(Clone)]
281pub(crate) struct Builder { 300pub(crate) struct Builder {
282 source_range: TextRange, 301 source_range: TextRange,
283 completion_kind: CompletionKind, 302 completion_kind: CompletionKind,
284 import_to_add: Option<ImportToAdd>, 303 import_to_add: Option<ImportEdit>,
285 resolve_import_immediately: bool, 304 resolve_import_lazily: bool,
286 label: String, 305 label: String,
287 insert_text: Option<String>, 306 insert_text: Option<String>,
288 insert_text_format: InsertTextFormat, 307 insert_text_format: InsertTextFormat,
@@ -304,7 +323,6 @@ impl Builder {
304 let mut label = self.label; 323 let mut label = self.label;
305 let mut lookup = self.lookup; 324 let mut lookup = self.lookup;
306 let mut insert_text = self.insert_text; 325 let mut insert_text = self.insert_text;
307 let mut text_edits = TextEdit::builder();
308 326
309 if let Some(import_to_add) = self.import_to_add.as_ref() { 327 if let Some(import_to_add) = self.import_to_add.as_ref() {
310 let mut import_path_without_last_segment = import_to_add.import_path.to_owned(); 328 let mut import_path_without_last_segment = import_to_add.import_path.to_owned();
@@ -319,35 +337,28 @@ impl Builder {
319 } 337 }
320 label = format!("{}::{}", import_path_without_last_segment, label); 338 label = format!("{}::{}", import_path_without_last_segment, label);
321 } 339 }
322
323 if self.resolve_import_immediately {
324 let rewriter = insert_use::insert_use(
325 &import_to_add.import_scope,
326 mod_path_to_ast(&import_to_add.import_path),
327 import_to_add.merge_behaviour,
328 );
329 if let Some(old_ast) = rewriter.rewrite_root() {
330 algo::diff(&old_ast, &rewriter.rewrite(&old_ast))
331 .into_text_edit(&mut text_edits);
332 }
333 }
334 } 340 }
335 341
336 let original_edit = match self.text_edit { 342 let mut text_edit = match self.text_edit {
337 Some(it) => it, 343 Some(it) => it,
338 None => { 344 None => {
339 TextEdit::replace(self.source_range, insert_text.unwrap_or_else(|| label.clone())) 345 TextEdit::replace(self.source_range, insert_text.unwrap_or_else(|| label.clone()))
340 } 346 }
341 }; 347 };
342 348
343 let mut resulting_edit = text_edits.finish(); 349 if !self.resolve_import_lazily {
344 resulting_edit.union(original_edit).expect("Failed to unite text edits"); 350 if let Some(import_edit) =
351 self.import_to_add.as_ref().and_then(|import_edit| import_edit.to_text_edit())
352 {
353 text_edit.union(import_edit).expect("Failed to unite import and completion edits");
354 }
355 }
345 356
346 CompletionItem { 357 CompletionItem {
347 source_range: self.source_range, 358 source_range: self.source_range,
348 label, 359 label,
349 insert_text_format: self.insert_text_format, 360 insert_text_format: self.insert_text_format,
350 text_edit: resulting_edit, 361 text_edit,
351 detail: self.detail, 362 detail: self.detail,
352 documentation: self.documentation, 363 documentation: self.documentation,
353 lookup, 364 lookup,
@@ -422,11 +433,11 @@ impl Builder {
422 } 433 }
423 pub(crate) fn add_import( 434 pub(crate) fn add_import(
424 mut self, 435 mut self,
425 import_to_add: Option<ImportToAdd>, 436 import_to_add: Option<ImportEdit>,
426 resolve_import_immediately: bool, 437 resolve_import_lazily: bool,
427 ) -> Builder { 438 ) -> Builder {
428 self.import_to_add = import_to_add; 439 self.import_to_add = import_to_add;
429 self.resolve_import_immediately = resolve_import_immediately; 440 self.resolve_import_lazily = resolve_import_lazily;
430 self 441 self
431 } 442 }
432 pub(crate) fn set_ref_match( 443 pub(crate) fn set_ref_match(
diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs
index c689b0dde..c57203c80 100644
--- a/crates/completion/src/lib.rs
+++ b/crates/completion/src/lib.rs
@@ -18,7 +18,7 @@ use crate::{completions::Completions, context::CompletionContext, item::Completi
18 18
19pub use crate::{ 19pub use crate::{
20 config::{CompletionConfig, CompletionResolveCapability}, 20 config::{CompletionConfig, CompletionResolveCapability},
21 item::{CompletionItem, CompletionItemKind, CompletionScore, ImportToAdd, InsertTextFormat}, 21 item::{CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, InsertTextFormat},
22}; 22};
23 23
24//FIXME: split the following feature into fine-grained features. 24//FIXME: split the following feature into fine-grained features.
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs
index 2b4f1ea14..a6faedb18 100644
--- a/crates/completion/src/render.rs
+++ b/crates/completion/src/render.rs
@@ -16,7 +16,7 @@ use syntax::TextRange;
16use test_utils::mark; 16use test_utils::mark;
17 17
18use crate::{ 18use crate::{
19 config::SnippetCap, item::ImportToAdd, CompletionContext, CompletionItem, CompletionItemKind, 19 config::SnippetCap, item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind,
20 CompletionKind, CompletionScore, 20 CompletionKind, CompletionScore,
21}; 21};
22 22
@@ -56,7 +56,7 @@ pub(crate) fn render_resolution_with_import<'a>(
56 let local_name = import_path.segments.last()?.to_string(); 56 let local_name = import_path.segments.last()?.to_string();
57 Render::new(ctx).render_resolution( 57 Render::new(ctx).render_resolution(
58 local_name, 58 local_name,
59 Some(ImportToAdd { import_path, import_scope, merge_behaviour }), 59 Some(ImportEdit { import_path, import_scope, merge_behaviour }),
60 resolution, 60 resolution,
61 ) 61 )
62} 62}
@@ -147,7 +147,7 @@ impl<'a> Render<'a> {
147 fn render_resolution( 147 fn render_resolution(
148 self, 148 self,
149 local_name: String, 149 local_name: String,
150 import_to_add: Option<ImportToAdd>, 150 import_to_add: Option<ImportEdit>,
151 resolution: &ScopeDef, 151 resolution: &ScopeDef,
152 ) -> Option<CompletionItem> { 152 ) -> Option<CompletionItem> {
153 let _p = profile::span("render_resolution"); 153 let _p = profile::span("render_resolution");
@@ -194,7 +194,10 @@ impl<'a> Render<'a> {
194 local_name, 194 local_name,
195 ) 195 )
196 .kind(CompletionItemKind::UnresolvedReference) 196 .kind(CompletionItemKind::UnresolvedReference)
197 .add_import(import_to_add, self.ctx.completion.config.resolve_edits_immediately()) 197 .add_import(
198 import_to_add,
199 self.ctx.completion.config.resolve_additional_edits_lazily(),
200 )
198 .build(); 201 .build();
199 return Some(item); 202 return Some(item);
200 } 203 }
@@ -249,7 +252,7 @@ impl<'a> Render<'a> {
249 252
250 let item = item 253 let item = item
251 .kind(kind) 254 .kind(kind)
252 .add_import(import_to_add, self.ctx.completion.config.resolve_edits_immediately()) 255 .add_import(import_to_add, self.ctx.completion.config.resolve_additional_edits_lazily())
253 .set_documentation(docs) 256 .set_documentation(docs)
254 .set_ref_match(ref_match) 257 .set_ref_match(ref_match)
255 .build(); 258 .build();
diff --git a/crates/completion/src/render/enum_variant.rs b/crates/completion/src/render/enum_variant.rs
index 4a91fe3c7..e979a090b 100644
--- a/crates/completion/src/render/enum_variant.rs
+++ b/crates/completion/src/render/enum_variant.rs
@@ -5,13 +5,13 @@ use itertools::Itertools;
5use test_utils::mark; 5use test_utils::mark;
6 6
7use crate::{ 7use crate::{
8 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportToAdd}, 8 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit},
9 render::{builder_ext::Params, RenderContext}, 9 render::{builder_ext::Params, RenderContext},
10}; 10};
11 11
12pub(crate) fn render_enum_variant<'a>( 12pub(crate) fn render_enum_variant<'a>(
13 ctx: RenderContext<'a>, 13 ctx: RenderContext<'a>,
14 import_to_add: Option<ImportToAdd>, 14 import_to_add: Option<ImportEdit>,
15 local_name: Option<String>, 15 local_name: Option<String>,
16 variant: hir::EnumVariant, 16 variant: hir::EnumVariant,
17 path: Option<ModPath>, 17 path: Option<ModPath>,
@@ -62,7 +62,7 @@ impl<'a> EnumVariantRender<'a> {
62 } 62 }
63 } 63 }
64 64
65 fn render(self, import_to_add: Option<ImportToAdd>) -> CompletionItem { 65 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
66 let mut builder = CompletionItem::new( 66 let mut builder = CompletionItem::new(
67 CompletionKind::Reference, 67 CompletionKind::Reference,
68 self.ctx.source_range(), 68 self.ctx.source_range(),
@@ -71,7 +71,7 @@ impl<'a> EnumVariantRender<'a> {
71 .kind(CompletionItemKind::EnumVariant) 71 .kind(CompletionItemKind::EnumVariant)
72 .set_documentation(self.variant.docs(self.ctx.db())) 72 .set_documentation(self.variant.docs(self.ctx.db()))
73 .set_deprecated(self.ctx.is_deprecated(self.variant)) 73 .set_deprecated(self.ctx.is_deprecated(self.variant))
74 .add_import(import_to_add, self.ctx.completion.config.resolve_edits_immediately()) 74 .add_import(import_to_add, self.ctx.completion.config.resolve_additional_edits_lazily())
75 .detail(self.detail()); 75 .detail(self.detail());
76 76
77 if self.variant_kind == StructKind::Tuple { 77 if self.variant_kind == StructKind::Tuple {
diff --git a/crates/completion/src/render/function.rs b/crates/completion/src/render/function.rs
index 20f2b9b7e..dd2c999ef 100644
--- a/crates/completion/src/render/function.rs
+++ b/crates/completion/src/render/function.rs
@@ -5,13 +5,13 @@ use syntax::{ast::Fn, display::function_declaration};
5use test_utils::mark; 5use test_utils::mark;
6 6
7use crate::{ 7use crate::{
8 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportToAdd}, 8 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit},
9 render::{builder_ext::Params, RenderContext}, 9 render::{builder_ext::Params, RenderContext},
10}; 10};
11 11
12pub(crate) fn render_fn<'a>( 12pub(crate) fn render_fn<'a>(
13 ctx: RenderContext<'a>, 13 ctx: RenderContext<'a>,
14 import_to_add: Option<ImportToAdd>, 14 import_to_add: Option<ImportEdit>,
15 local_name: Option<String>, 15 local_name: Option<String>,
16 fn_: hir::Function, 16 fn_: hir::Function,
17) -> CompletionItem { 17) -> CompletionItem {
@@ -39,7 +39,7 @@ impl<'a> FunctionRender<'a> {
39 FunctionRender { ctx, name, func: fn_, ast_node } 39 FunctionRender { ctx, name, func: fn_, ast_node }
40 } 40 }
41 41
42 fn render(self, import_to_add: Option<ImportToAdd>) -> CompletionItem { 42 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
43 let params = self.params(); 43 let params = self.params();
44 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone()) 44 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone())
45 .kind(self.kind()) 45 .kind(self.kind())
@@ -47,7 +47,7 @@ impl<'a> FunctionRender<'a> {
47 .set_deprecated(self.ctx.is_deprecated(self.func)) 47 .set_deprecated(self.ctx.is_deprecated(self.func))
48 .detail(self.detail()) 48 .detail(self.detail())
49 .add_call_parens(self.ctx.completion, self.name, params) 49 .add_call_parens(self.ctx.completion, self.name, params)
50 .add_import(import_to_add, self.ctx.completion.config.resolve_edits_immediately()) 50 .add_import(import_to_add, self.ctx.completion.config.resolve_additional_edits_lazily())
51 .build() 51 .build()
52 } 52 }
53 53
diff --git a/crates/completion/src/render/macro_.rs b/crates/completion/src/render/macro_.rs
index be7c53659..bdbc642ca 100644
--- a/crates/completion/src/render/macro_.rs
+++ b/crates/completion/src/render/macro_.rs
@@ -5,13 +5,13 @@ use syntax::display::macro_label;
5use test_utils::mark; 5use test_utils::mark;
6 6
7use crate::{ 7use crate::{
8 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportToAdd}, 8 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit},
9 render::RenderContext, 9 render::RenderContext,
10}; 10};
11 11
12pub(crate) fn render_macro<'a>( 12pub(crate) fn render_macro<'a>(
13 ctx: RenderContext<'a>, 13 ctx: RenderContext<'a>,
14 import_to_add: Option<ImportToAdd>, 14 import_to_add: Option<ImportEdit>,
15 name: String, 15 name: String,
16 macro_: hir::MacroDef, 16 macro_: hir::MacroDef,
17) -> Option<CompletionItem> { 17) -> Option<CompletionItem> {
@@ -38,7 +38,7 @@ impl<'a> MacroRender<'a> {
38 MacroRender { ctx, name, macro_, docs, bra, ket } 38 MacroRender { ctx, name, macro_, docs, bra, ket }
39 } 39 }
40 40
41 fn render(&self, import_to_add: Option<ImportToAdd>) -> Option<CompletionItem> { 41 fn render(&self, import_to_add: Option<ImportEdit>) -> Option<CompletionItem> {
42 // FIXME: Currently proc-macro do not have ast-node, 42 // FIXME: Currently proc-macro do not have ast-node,
43 // such that it does not have source 43 // such that it does not have source
44 if self.macro_.is_proc_macro() { 44 if self.macro_.is_proc_macro() {
@@ -50,7 +50,10 @@ impl<'a> MacroRender<'a> {
50 .kind(CompletionItemKind::Macro) 50 .kind(CompletionItemKind::Macro)
51 .set_documentation(self.docs.clone()) 51 .set_documentation(self.docs.clone())
52 .set_deprecated(self.ctx.is_deprecated(self.macro_)) 52 .set_deprecated(self.ctx.is_deprecated(self.macro_))
53 .add_import(import_to_add, self.ctx.completion.config.resolve_edits_immediately()) 53 .add_import(
54 import_to_add,
55 self.ctx.completion.config.resolve_additional_edits_lazily(),
56 )
54 .detail(self.detail()); 57 .detail(self.detail());
55 58
56 let needs_bang = self.needs_bang(); 59 let needs_bang = self.needs_bang();
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index d1a27f3a5..9e38d6506 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -81,7 +81,7 @@ pub use crate::{
81}; 81};
82pub use completion::{ 82pub use completion::{
83 CompletionConfig, CompletionItem, CompletionItemKind, CompletionResolveCapability, 83 CompletionConfig, CompletionItem, CompletionItemKind, CompletionResolveCapability,
84 CompletionScore, ImportToAdd, InsertTextFormat, 84 CompletionScore, ImportEdit, InsertTextFormat,
85}; 85};
86pub use ide_db::{ 86pub use ide_db::{
87 call_info::CallInfo, 87 call_info::CallInfo,
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index 0b6ca76e2..1e0ee10e4 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -104,7 +104,7 @@ fn completions_resolve_provider(client_caps: &ClientCapabilities) -> Option<bool
104} 104}
105 105
106/// Parses client capabilities and returns all completion resolve capabilities rust-analyzer supports. 106/// Parses client capabilities and returns all completion resolve capabilities rust-analyzer supports.
107pub fn enabled_completions_resolve_capabilities( 107pub(crate) fn enabled_completions_resolve_capabilities(
108 caps: &ClientCapabilities, 108 caps: &ClientCapabilities,
109) -> Option<FxHashSet<CompletionResolveCapability>> { 109) -> Option<FxHashSet<CompletionResolveCapability>> {
110 Some( 110 Some(
@@ -118,13 +118,11 @@ pub fn enabled_completions_resolve_capabilities(
118 .as_ref()? 118 .as_ref()?
119 .properties 119 .properties
120 .iter() 120 .iter()
121 .filter_map(|cap_string| { 121 .filter_map(|cap_string| match cap_string.as_str() {
122 Some(match cap_string.as_str() { 122 "additionalTextEdits" => Some(CompletionResolveCapability::AdditionalTextEdits),
123 "additionalTextEdits" => CompletionResolveCapability::AdditionalTextEdits, 123 "detail" => Some(CompletionResolveCapability::Detail),
124 "detail" => CompletionResolveCapability::Detail, 124 "documentation" => Some(CompletionResolveCapability::Documentation),
125 "documentation" => CompletionResolveCapability::Documentation, 125 _unsupported => None,
126 _unsupported => return None,
127 })
128 }) 126 })
129 .collect(), 127 .collect(),
130 ) 128 )
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index e12651937..a35abe86d 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -7,7 +7,7 @@ use std::{sync::Arc, time::Instant};
7 7
8use crossbeam_channel::{unbounded, Receiver, Sender}; 8use crossbeam_channel::{unbounded, Receiver, Sender};
9use flycheck::FlycheckHandle; 9use flycheck::FlycheckHandle;
10use ide::{Analysis, AnalysisHost, Change, CompletionItem, FileId}; 10use ide::{Analysis, AnalysisHost, Change, FileId, ImportEdit};
11use ide_db::base_db::{CrateId, VfsPath}; 11use ide_db::base_db::{CrateId, VfsPath};
12use lsp_types::{SemanticTokens, Url}; 12use lsp_types::{SemanticTokens, Url};
13use parking_lot::{Mutex, RwLock}; 13use parking_lot::{Mutex, RwLock};
@@ -53,7 +53,7 @@ pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>;
53 53
54pub(crate) struct CompletionResolveData { 54pub(crate) struct CompletionResolveData {
55 pub(crate) file_id: FileId, 55 pub(crate) file_id: FileId,
56 pub(crate) item: CompletionItem, 56 pub(crate) import_edit: ImportEdit,
57} 57}
58 58
59/// `GlobalState` is the primary mutable state of the language server 59/// `GlobalState` is the primary mutable state of the language server
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 55c7b0c66..3eb5f26bc 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -8,10 +8,9 @@ use std::{
8}; 8};
9 9
10use ide::{ 10use ide::{
11 FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, ImportToAdd, LineIndex, 11 FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, ImportEdit, LineIndex,
12 NavigationTarget, Query, RangeInfo, Runnable, RunnableKind, SearchScope, TextEdit, 12 NavigationTarget, Query, RangeInfo, Runnable, RunnableKind, SearchScope, TextEdit,
13}; 13};
14use ide_db::helpers::{insert_use, mod_path_to_ast};
15use itertools::Itertools; 14use itertools::Itertools;
16use lsp_server::ErrorCode; 15use lsp_server::ErrorCode;
17use lsp_types::{ 16use lsp_types::{
@@ -581,13 +580,21 @@ pub(crate) fn handle_completion(
581 let mut new_completion_items = 580 let mut new_completion_items =
582 to_proto::completion_item(&line_index, line_endings, item.clone()); 581 to_proto::completion_item(&line_index, line_endings, item.clone());
583 582
584 if !snap.config.completion.active_resolve_capabilities.is_empty() { 583 if snap.config.completion.resolve_additional_edits_lazily() {
585 let item_id = serde_json::to_value(&item_index) 584 if let Some(import_edit) = item.import_to_add() {
586 .expect(&format!("Should be able to serialize usize value {}", item_index)); 585 completion_resolve_data.insert(
587 completion_resolve_data 586 item_index,
588 .insert(item_index, CompletionResolveData { file_id: position.file_id, item }); 587 CompletionResolveData {
589 for new_item in &mut new_completion_items { 588 file_id: position.file_id,
590 new_item.data = Some(item_id.clone()); 589 import_edit: import_edit.clone(),
590 },
591 );
592
593 let item_id = serde_json::to_value(&item_index)
594 .expect(&format!("Should be able to serialize usize value {}", item_index));
595 for new_item in &mut new_completion_items {
596 new_item.data = Some(item_id.clone());
597 }
591 } 598 }
592 } 599 }
593 600
@@ -601,12 +608,17 @@ pub(crate) fn handle_completion(
601 Ok(Some(completion_list.into())) 608 Ok(Some(completion_list.into()))
602} 609}
603 610
604pub(crate) fn handle_resolve_completion( 611pub(crate) fn handle_completion_resolve(
605 global_state: &mut GlobalState, 612 global_state: &mut GlobalState,
606 mut original_completion: lsp_types::CompletionItem, 613 mut original_completion: lsp_types::CompletionItem,
607) -> Result<lsp_types::CompletionItem> { 614) -> Result<lsp_types::CompletionItem> {
608 let _p = profile::span("handle_resolve_completion"); 615 let _p = profile::span("handle_resolve_completion");
609 616
617 let active_resolve_caps = &global_state.config.completion.active_resolve_capabilities;
618 if active_resolve_caps.is_empty() {
619 return Ok(original_completion);
620 }
621
610 let server_completion_data = match original_completion 622 let server_completion_data = match original_completion
611 .data 623 .data
612 .as_ref() 624 .as_ref()
@@ -620,18 +632,16 @@ pub(crate) fn handle_resolve_completion(
620 }; 632 };
621 633
622 let snap = &global_state.snapshot(); 634 let snap = &global_state.snapshot();
623 for supported_completion_resolve_cap in &snap.config.completion.active_resolve_capabilities { 635 for supported_completion_resolve_cap in active_resolve_caps {
624 match supported_completion_resolve_cap { 636 match supported_completion_resolve_cap {
637 // FIXME actually add all additional edits here? see `to_proto::completion_item` for more
625 ide::CompletionResolveCapability::AdditionalTextEdits => { 638 ide::CompletionResolveCapability::AdditionalTextEdits => {
626 // FIXME actually add all additional edits here? 639 append_import_edits(
627 if let Some(import_to_add) = server_completion_data.item.import_to_add() { 640 &mut original_completion,
628 append_import_edits( 641 &server_completion_data.import_edit,
629 &mut original_completion, 642 snap.analysis.file_line_index(server_completion_data.file_id)?.as_ref(),
630 import_to_add, 643 snap.file_line_endings(server_completion_data.file_id),
631 snap.analysis.file_line_index(server_completion_data.file_id)?.as_ref(), 644 );
632 snap.file_line_endings(server_completion_data.file_id),
633 );
634 }
635 } 645 }
636 // FIXME resolve the other capabilities also? 646 // FIXME resolve the other capabilities also?
637 _ => {} 647 _ => {}
@@ -1601,41 +1611,21 @@ fn should_skip_target(runnable: &Runnable, cargo_spec: Option<&CargoTargetSpec>)
1601 1611
1602fn append_import_edits( 1612fn append_import_edits(
1603 completion: &mut lsp_types::CompletionItem, 1613 completion: &mut lsp_types::CompletionItem,
1604 import_to_add: &ImportToAdd, 1614 import_to_add: &ImportEdit,
1605 line_index: &LineIndex, 1615 line_index: &LineIndex,
1606 line_endings: LineEndings, 1616 line_endings: LineEndings,
1607) { 1617) {
1608 let new_edits = import_into_edits(import_to_add, line_index, line_endings); 1618 let import_edits = import_to_add.to_text_edit().map(|import_edit| {
1619 import_edit
1620 .into_iter()
1621 .map(|indel| to_proto::text_edit(line_index, line_endings, indel))
1622 .collect_vec()
1623 });
1609 if let Some(original_additional_edits) = completion.additional_text_edits.as_mut() { 1624 if let Some(original_additional_edits) = completion.additional_text_edits.as_mut() {
1610 if let Some(mut new_edits) = new_edits { 1625 if let Some(mut new_edits) = import_edits {
1611 original_additional_edits.extend(new_edits.drain(..)) 1626 original_additional_edits.extend(new_edits.drain(..))
1612 } 1627 }
1613 } else { 1628 } else {
1614 completion.additional_text_edits = new_edits; 1629 completion.additional_text_edits = import_edits;
1615 } 1630 }
1616} 1631}
1617
1618fn import_into_edits(
1619 import_to_add: &ImportToAdd,
1620 line_index: &LineIndex,
1621 line_endings: LineEndings,
1622) -> Option<Vec<lsp_types::TextEdit>> {
1623 let _p = profile::span("add_import_edits");
1624
1625 let rewriter = insert_use::insert_use(
1626 &import_to_add.import_scope,
1627 mod_path_to_ast(&import_to_add.import_path),
1628 import_to_add.merge_behaviour,
1629 );
1630 let old_ast = rewriter.rewrite_root()?;
1631 let mut import_insert = TextEdit::builder();
1632 algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert);
1633 let import_edit = import_insert.finish();
1634
1635 Some(
1636 import_edit
1637 .into_iter()
1638 .map(|indel| to_proto::text_edit(line_index, line_endings, indel))
1639 .collect_vec(),
1640 )
1641}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 21c58d959..db30b3dce 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -438,7 +438,7 @@ impl GlobalState {
438 .on_sync::<lsp_ext::MemoryUsage>(|s, p| handlers::handle_memory_usage(s, p))? 438 .on_sync::<lsp_ext::MemoryUsage>(|s, p| handlers::handle_memory_usage(s, p))?
439 .on_sync::<lsp_types::request::Completion>(handlers::handle_completion)? 439 .on_sync::<lsp_types::request::Completion>(handlers::handle_completion)?
440 .on_sync::<lsp_types::request::ResolveCompletionItem>( 440 .on_sync::<lsp_types::request::ResolveCompletionItem>(
441 handlers::handle_resolve_completion, 441 handlers::handle_completion_resolve,
442 )? 442 )?
443 .on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status) 443 .on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status)
444 .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree) 444 .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)