diff options
-rw-r--r-- | crates/completion/src/config.rs | 10 | ||||
-rw-r--r-- | crates/completion/src/item.rs | 63 | ||||
-rw-r--r-- | crates/completion/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/completion/src/render.rs | 13 | ||||
-rw-r--r-- | crates/completion/src/render/enum_variant.rs | 8 | ||||
-rw-r--r-- | crates/completion/src/render/function.rs | 8 | ||||
-rw-r--r-- | crates/completion/src/render/macro_.rs | 11 | ||||
-rw-r--r-- | crates/ide/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/caps.rs | 14 | ||||
-rw-r--r-- | crates/rust-analyzer/src/global_state.rs | 4 | ||||
-rw-r--r-- | crates/rust-analyzer/src/handlers.rs | 88 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop.rs | 2 |
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)] |
272 | pub struct ImportToAdd { | 272 | pub 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 | ||
278 | impl 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)] |
281 | pub(crate) struct Builder { | 300 | pub(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 | ||
19 | pub use crate::{ | 19 | pub 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; | |||
16 | use test_utils::mark; | 16 | use test_utils::mark; |
17 | 17 | ||
18 | use crate::{ | 18 | use 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; | |||
5 | use test_utils::mark; | 5 | use test_utils::mark; |
6 | 6 | ||
7 | use crate::{ | 7 | use 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 | ||
12 | pub(crate) fn render_enum_variant<'a>( | 12 | pub(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}; | |||
5 | use test_utils::mark; | 5 | use test_utils::mark; |
6 | 6 | ||
7 | use crate::{ | 7 | use 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 | ||
12 | pub(crate) fn render_fn<'a>( | 12 | pub(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; | |||
5 | use test_utils::mark; | 5 | use test_utils::mark; |
6 | 6 | ||
7 | use crate::{ | 7 | use crate::{ |
8 | item::{CompletionItem, CompletionItemKind, CompletionKind, ImportToAdd}, | 8 | item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit}, |
9 | render::RenderContext, | 9 | render::RenderContext, |
10 | }; | 10 | }; |
11 | 11 | ||
12 | pub(crate) fn render_macro<'a>( | 12 | pub(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 | }; |
82 | pub use completion::{ | 82 | pub use completion::{ |
83 | CompletionConfig, CompletionItem, CompletionItemKind, CompletionResolveCapability, | 83 | CompletionConfig, CompletionItem, CompletionItemKind, CompletionResolveCapability, |
84 | CompletionScore, ImportToAdd, InsertTextFormat, | 84 | CompletionScore, ImportEdit, InsertTextFormat, |
85 | }; | 85 | }; |
86 | pub use ide_db::{ | 86 | pub 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. |
107 | pub fn enabled_completions_resolve_capabilities( | 107 | pub(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 | ||
8 | use crossbeam_channel::{unbounded, Receiver, Sender}; | 8 | use crossbeam_channel::{unbounded, Receiver, Sender}; |
9 | use flycheck::FlycheckHandle; | 9 | use flycheck::FlycheckHandle; |
10 | use ide::{Analysis, AnalysisHost, Change, CompletionItem, FileId}; | 10 | use ide::{Analysis, AnalysisHost, Change, FileId, ImportEdit}; |
11 | use ide_db::base_db::{CrateId, VfsPath}; | 11 | use ide_db::base_db::{CrateId, VfsPath}; |
12 | use lsp_types::{SemanticTokens, Url}; | 12 | use lsp_types::{SemanticTokens, Url}; |
13 | use parking_lot::{Mutex, RwLock}; | 13 | use parking_lot::{Mutex, RwLock}; |
@@ -53,7 +53,7 @@ pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>; | |||
53 | 53 | ||
54 | pub(crate) struct CompletionResolveData { | 54 | pub(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 | ||
10 | use ide::{ | 10 | use 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 | }; |
14 | use ide_db::helpers::{insert_use, mod_path_to_ast}; | ||
15 | use itertools::Itertools; | 14 | use itertools::Itertools; |
16 | use lsp_server::ErrorCode; | 15 | use lsp_server::ErrorCode; |
17 | use lsp_types::{ | 16 | use 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 | ||
604 | pub(crate) fn handle_resolve_completion( | 611 | pub(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 | ||
1602 | fn append_import_edits( | 1612 | fn 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 | |||
1618 | fn 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) |