aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock38
-rw-r--r--crates/assists/src/handlers/remove_unused_param.rs83
-rw-r--r--crates/assists/src/utils/insert_use.rs96
-rw-r--r--crates/hir_ty/Cargo.toml1
-rw-r--r--crates/hir_ty/src/tests.rs7
-rw-r--r--crates/ide/src/syntax_highlighting.rs5
-rw-r--r--crates/ide/src/syntax_highlighting/tags.rs4
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html56
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html2
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_injection.html2
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs28
-rw-r--r--crates/rust-analyzer/src/caps.rs6
-rw-r--r--crates/rust-analyzer/src/config.rs14
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt27
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt9
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt36
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt9
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt9
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt20
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt20
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt20
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt18
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt29
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs2
-rw-r--r--crates/rust-analyzer/src/handlers.rs23
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs35
-rw-r--r--crates/rust-analyzer/src/main_loop.rs2
-rw-r--r--crates/rust-analyzer/src/to_proto.rs27
-rw-r--r--crates/stdx/src/lib.rs30
-rw-r--r--crates/syntax/Cargo.toml2
-rw-r--r--crates/syntax/src/algo.rs125
-rw-r--r--crates/syntax/src/ast.rs19
-rw-r--r--crates/syntax/src/ast/token_ext.rs51
-rw-r--r--crates/syntax/src/parsing/text_tree_sink.rs27
-rw-r--r--crates/syntax/test_data/parser/ok/0037_mod.rast6
-rw-r--r--docs/dev/lsp-extensions.md26
-rw-r--r--editors/code/rust.tmGrammar.json15
-rw-r--r--editors/code/src/client.ts21
-rw-r--r--editors/code/src/commands.ts11
-rw-r--r--editors/code/src/lsp_ext.ts6
40 files changed, 698 insertions, 269 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 1a4a63550..494011068 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -139,9 +139,9 @@ dependencies = [
139 139
140[[package]] 140[[package]]
141name = "cc" 141name = "cc"
142version = "1.0.61" 142version = "1.0.62"
143source = "registry+https://github.com/rust-lang/crates.io-index" 143source = "registry+https://github.com/rust-lang/crates.io-index"
144checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d" 144checksum = "f1770ced377336a88a67c473594ccc14eca6f4559217c34f64aac8f83d641b40"
145 145
146[[package]] 146[[package]]
147name = "cfg" 147name = "cfg"
@@ -398,11 +398,11 @@ dependencies = [
398 398
399[[package]] 399[[package]]
400name = "filetime" 400name = "filetime"
401version = "0.2.12" 401version = "0.2.13"
402source = "registry+https://github.com/rust-lang/crates.io-index" 402source = "registry+https://github.com/rust-lang/crates.io-index"
403checksum = "3ed85775dcc68644b5c950ac06a2b23768d3bc9390464151aaf27136998dcf9e" 403checksum = "0c122a393ea57648015bf06fbd3d372378992e86b9ff5a7a497b076a28c79efe"
404dependencies = [ 404dependencies = [
405 "cfg-if 0.1.10", 405 "cfg-if 1.0.0",
406 "libc", 406 "libc",
407 "redox_syscall", 407 "redox_syscall",
408 "winapi 0.3.9", 408 "winapi 0.3.9",
@@ -439,6 +439,16 @@ dependencies = [
439] 439]
440 440
441[[package]] 441[[package]]
442name = "form_urlencoded"
443version = "1.0.0"
444source = "registry+https://github.com/rust-lang/crates.io-index"
445checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00"
446dependencies = [
447 "matches",
448 "percent-encoding",
449]
450
451[[package]]
442name = "fsevent" 452name = "fsevent"
443version = "2.0.2" 453version = "2.0.2"
444source = "registry+https://github.com/rust-lang/crates.io-index" 454source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -612,6 +622,7 @@ dependencies = [
612 "hir_expand", 622 "hir_expand",
613 "itertools", 623 "itertools",
614 "log", 624 "log",
625 "once_cell",
615 "profile", 626 "profile",
616 "rustc-hash", 627 "rustc-hash",
617 "scoped-tls", 628 "scoped-tls",
@@ -714,9 +725,9 @@ dependencies = [
714 725
715[[package]] 726[[package]]
716name = "inotify-sys" 727name = "inotify-sys"
717version = "0.1.3" 728version = "0.1.4"
718source = "registry+https://github.com/rust-lang/crates.io-index" 729source = "registry+https://github.com/rust-lang/crates.io-index"
719checksum = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0" 730checksum = "c4563555856585ab3180a5bf0b2f9f8d301a728462afffc8195b3f5394229c55"
720dependencies = [ 731dependencies = [
721 "libc", 732 "libc",
722] 733]
@@ -1053,9 +1064,9 @@ checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397"
1053 1064
1054[[package]] 1065[[package]]
1055name = "once_cell" 1066name = "once_cell"
1056version = "1.4.1" 1067version = "1.5.1"
1057source = "registry+https://github.com/rust-lang/crates.io-index" 1068source = "registry+https://github.com/rust-lang/crates.io-index"
1058checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" 1069checksum = "f53cef67919d7d247eb9a2f128ca9e522789967ef1eb4ccd8c71a95a8aedf596"
1059 1070
1060[[package]] 1071[[package]]
1061name = "oorandom" 1072name = "oorandom"
@@ -1383,9 +1394,9 @@ dependencies = [
1383 1394
1384[[package]] 1395[[package]]
1385name = "rustc-ap-rustc_lexer" 1396name = "rustc-ap-rustc_lexer"
1386version = "686.0.0" 1397version = "688.0.0"
1387source = "registry+https://github.com/rust-lang/crates.io-index" 1398source = "registry+https://github.com/rust-lang/crates.io-index"
1388checksum = "a5b04cd2159495584d976d501c5394498470c2e94e4f0cebb8186562d407a678" 1399checksum = "ebbdcc99bd015349093fcbae4780fda21416fec5d8843acfb3d1733e130cd4db"
1389dependencies = [ 1400dependencies = [
1390 "unicode-xid", 1401 "unicode-xid",
1391] 1402]
@@ -1888,10 +1899,11 @@ checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
1888 1899
1889[[package]] 1900[[package]]
1890name = "url" 1901name = "url"
1891version = "2.1.1" 1902version = "2.2.0"
1892source = "registry+https://github.com/rust-lang/crates.io-index" 1903source = "registry+https://github.com/rust-lang/crates.io-index"
1893checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" 1904checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e"
1894dependencies = [ 1905dependencies = [
1906 "form_urlencoded",
1895 "idna", 1907 "idna",
1896 "matches", 1908 "matches",
1897 "percent-encoding", 1909 "percent-encoding",
diff --git a/crates/assists/src/handlers/remove_unused_param.rs b/crates/assists/src/handlers/remove_unused_param.rs
index 5fccca54b..1ff5e92b0 100644
--- a/crates/assists/src/handlers/remove_unused_param.rs
+++ b/crates/assists/src/handlers/remove_unused_param.rs
@@ -73,7 +73,8 @@ fn process_usage(
73 let source_file = ctx.sema.parse(usage.file_range.file_id); 73 let source_file = ctx.sema.parse(usage.file_range.file_id);
74 let call_expr: ast::CallExpr = 74 let call_expr: ast::CallExpr =
75 find_node_at_range(source_file.syntax(), usage.file_range.range)?; 75 find_node_at_range(source_file.syntax(), usage.file_range.range)?;
76 if call_expr.expr()?.syntax().text_range() != usage.file_range.range { 76 let call_expr_range = call_expr.expr()?.syntax().text_range();
77 if !call_expr_range.contains_range(usage.file_range.range) {
77 return None; 78 return None;
78 } 79 }
79 let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?; 80 let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?;
@@ -118,6 +119,53 @@ fn b() { foo(9, ) }
118 } 119 }
119 120
120 #[test] 121 #[test]
122 fn remove_unused_qualified_call() {
123 check_assist(
124 remove_unused_param,
125 r#"
126mod bar { pub fn foo(x: i32, <|>y: i32) { x; } }
127fn b() { bar::foo(9, 2) }
128"#,
129 r#"
130mod bar { pub fn foo(x: i32) { x; } }
131fn b() { bar::foo(9) }
132"#,
133 );
134 }
135
136 #[test]
137 fn remove_unused_turbofished_func() {
138 check_assist(
139 remove_unused_param,
140 r#"
141pub fn foo<T>(x: T, <|>y: i32) { x; }
142fn b() { foo::<i32>(9, 2) }
143"#,
144 r#"
145pub fn foo<T>(x: T) { x; }
146fn b() { foo::<i32>(9) }
147"#,
148 );
149 }
150
151 #[test]
152 fn remove_unused_generic_unused_param_func() {
153 check_assist(
154 remove_unused_param,
155 r#"
156pub fn foo<T>(x: i32, <|>y: T) { x; }
157fn b() { foo::<i32>(9, 2) }
158fn b2() { foo(9, 2) }
159"#,
160 r#"
161pub fn foo<T>(x: i32) { x; }
162fn b() { foo::<i32>(9) }
163fn b2() { foo(9) }
164"#,
165 );
166 }
167
168 #[test]
121 fn keep_used() { 169 fn keep_used() {
122 mark::check!(keep_used); 170 mark::check!(keep_used);
123 check_assist_not_applicable( 171 check_assist_not_applicable(
@@ -128,4 +176,37 @@ fn main() { foo(9, 2) }
128"#, 176"#,
129 ); 177 );
130 } 178 }
179
180 #[test]
181 fn remove_across_files() {
182 check_assist(
183 remove_unused_param,
184 r#"
185//- /main.rs
186fn foo(x: i32, <|>y: i32) { x; }
187
188mod foo;
189
190//- /foo.rs
191use super::foo;
192
193fn bar() {
194 let _ = foo(1, 2);
195}
196"#,
197 r#"
198//- /main.rs
199fn foo(x: i32) { x; }
200
201mod foo;
202
203//- /foo.rs
204use super::foo;
205
206fn bar() {
207 let _ = foo(1);
208}
209"#,
210 )
211 }
131} 212}
diff --git a/crates/assists/src/utils/insert_use.rs b/crates/assists/src/utils/insert_use.rs
index 84a0dffdd..af3fc96b6 100644
--- a/crates/assists/src/utils/insert_use.rs
+++ b/crates/assists/src/utils/insert_use.rs
@@ -9,7 +9,7 @@ use syntax::{
9 edit::{AstNodeEdit, IndentLevel}, 9 edit::{AstNodeEdit, IndentLevel},
10 make, AstNode, PathSegmentKind, VisibilityOwner, 10 make, AstNode, PathSegmentKind, VisibilityOwner,
11 }, 11 },
12 InsertPosition, SyntaxElement, SyntaxNode, 12 AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken,
13}; 13};
14use test_utils::mark; 14use test_utils::mark;
15 15
@@ -63,27 +63,30 @@ impl ImportScope {
63 } 63 }
64 } 64 }
65 65
66 fn insert_pos_after_inner_attribute(&self) -> (InsertPosition<SyntaxElement>, AddBlankLine) { 66 fn insert_pos_after_last_inner_element(&self) -> (InsertPosition<SyntaxElement>, AddBlankLine) {
67 // check if the scope has inner attributes, we dont want to insert in front of them 67 self.as_syntax_node()
68 match self 68 .children_with_tokens()
69 .as_syntax_node() 69 .filter(|child| match child {
70 .children() 70 NodeOrToken::Node(node) => is_inner_attribute(node.clone()),
71 // no flat_map here cause we want to short circuit the iterator 71 NodeOrToken::Token(token) => is_inner_comment(token.clone()),
72 .map(ast::Attr::cast)
73 .take_while(|attr| {
74 attr.as_ref().map(|attr| attr.kind() == ast::AttrKind::Inner).unwrap_or(false)
75 }) 72 })
76 .last() 73 .last()
77 .flatten() 74 .map(|last_inner_element| {
78 { 75 (InsertPosition::After(last_inner_element.into()), AddBlankLine::BeforeTwice)
79 Some(attr) => { 76 })
80 (InsertPosition::After(attr.syntax().clone().into()), AddBlankLine::BeforeTwice) 77 .unwrap_or_else(|| self.first_insert_pos())
81 }
82 None => self.first_insert_pos(),
83 }
84 } 78 }
85} 79}
86 80
81fn is_inner_attribute(node: SyntaxNode) -> bool {
82 ast::Attr::cast(node).map(|attr| attr.kind()) == Some(ast::AttrKind::Inner)
83}
84
85fn is_inner_comment(token: SyntaxToken) -> bool {
86 ast::Comment::cast(token).and_then(|comment| comment.kind().doc)
87 == Some(ast::CommentPlacement::Inner)
88}
89
87/// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. 90/// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur.
88pub(crate) fn insert_use<'a>( 91pub(crate) fn insert_use<'a>(
89 scope: &ImportScope, 92 scope: &ImportScope,
@@ -558,7 +561,7 @@ fn find_insert_position(
558 (InsertPosition::After(node.into()), AddBlankLine::BeforeTwice) 561 (InsertPosition::After(node.into()), AddBlankLine::BeforeTwice)
559 } 562 }
560 // there are no imports in this file at all 563 // there are no imports in this file at all
561 None => scope.insert_pos_after_inner_attribute(), 564 None => scope.insert_pos_after_last_inner_element(),
562 }, 565 },
563 } 566 }
564 } 567 }
@@ -830,12 +833,67 @@ use foo::bar;",
830 "foo::bar", 833 "foo::bar",
831 r"#![allow(unused_imports)] 834 r"#![allow(unused_imports)]
832 835
836#![no_std]
833fn main() {}", 837fn main() {}",
834 r"#![allow(unused_imports)] 838 r"#![allow(unused_imports)]
835 839
836use foo::bar; 840#![no_std]
837 841
842use foo::bar;
838fn main() {}", 843fn main() {}",
844 );
845 }
846
847 #[test]
848 fn inserts_after_single_line_inner_comments() {
849 check_none(
850 "foo::bar::Baz",
851 "//! Single line inner comments do not allow any code before them.",
852 r#"//! Single line inner comments do not allow any code before them.
853
854use foo::bar::Baz;"#,
855 );
856 }
857
858 #[test]
859 fn inserts_after_multiline_inner_comments() {
860 check_none(
861 "foo::bar::Baz",
862 r#"/*! Multiline inner comments do not allow any code before them. */
863
864/*! Still an inner comment, cannot place any code before. */
865fn main() {}"#,
866 r#"/*! Multiline inner comments do not allow any code before them. */
867
868/*! Still an inner comment, cannot place any code before. */
869
870use foo::bar::Baz;
871fn main() {}"#,
872 )
873 }
874
875 #[test]
876 fn inserts_after_all_inner_items() {
877 check_none(
878 "foo::bar::Baz",
879 r#"#![allow(unused_imports)]
880/*! Multiline line comment 2 */
881
882
883//! Single line comment 1
884#![no_std]
885//! Single line comment 2
886fn main() {}"#,
887 r#"#![allow(unused_imports)]
888/*! Multiline line comment 2 */
889
890
891//! Single line comment 1
892#![no_std]
893//! Single line comment 2
894
895use foo::bar::Baz;
896fn main() {}"#,
839 ) 897 )
840 } 898 }
841 899
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index fdc65a5c3..cf5c38a23 100644
--- a/crates/hir_ty/Cargo.toml
+++ b/crates/hir_ty/Cargo.toml
@@ -35,3 +35,4 @@ expect-test = "1.0"
35tracing = "0.1" 35tracing = "0.1"
36tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] } 36tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] }
37tracing-tree = { version = "0.1.4" } 37tracing-tree = { version = "0.1.4" }
38once_cell = { version = "1.5.0", features = ["unstable"] }
diff --git a/crates/hir_ty/src/tests.rs b/crates/hir_ty/src/tests.rs
index 104ef334c..0a400cb70 100644
--- a/crates/hir_ty/src/tests.rs
+++ b/crates/hir_ty/src/tests.rs
@@ -22,7 +22,8 @@ use hir_def::{
22 AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, 22 AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId,
23}; 23};
24use hir_expand::{db::AstDatabase, InFile}; 24use hir_expand::{db::AstDatabase, InFile};
25use stdx::{format_to, RacyFlag}; 25use once_cell::race::OnceBool;
26use stdx::format_to;
26use syntax::{ 27use syntax::{
27 algo, 28 algo,
28 ast::{self, AstNode}, 29 ast::{self, AstNode},
@@ -40,8 +41,8 @@ use crate::{
40// `env UPDATE_EXPECT=1 cargo test -p hir_ty` to update the snapshots. 41// `env UPDATE_EXPECT=1 cargo test -p hir_ty` to update the snapshots.
41 42
42fn setup_tracing() -> Option<tracing::subscriber::DefaultGuard> { 43fn setup_tracing() -> Option<tracing::subscriber::DefaultGuard> {
43 static ENABLE: RacyFlag = RacyFlag::new(); 44 static ENABLE: OnceBool = OnceBool::new();
44 if !ENABLE.get(|| env::var("CHALK_DEBUG").is_ok()) { 45 if !ENABLE.get_or_init(|| env::var("CHALK_DEBUG").is_ok()) {
45 return None; 46 return None;
46 } 47 }
47 48
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 37ab7a8f6..1ed77b40b 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -6,7 +6,7 @@ pub(crate) mod tags;
6#[cfg(test)] 6#[cfg(test)]
7mod tests; 7mod tests;
8 8
9use hir::{Local, Name, Semantics, VariantDef}; 9use hir::{AsAssocItem, Local, Name, Semantics, VariantDef};
10use ide_db::{ 10use ide_db::{
11 defs::{Definition, NameClass, NameRefClass}, 11 defs::{Definition, NameClass, NameRefClass},
12 RootDatabase, 12 RootDatabase,
@@ -746,6 +746,9 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
746 if func.is_unsafe(db) { 746 if func.is_unsafe(db) {
747 h |= HighlightModifier::Unsafe; 747 h |= HighlightModifier::Unsafe;
748 } 748 }
749 if func.as_assoc_item(db).is_some() && func.self_param(db).is_none() {
750 h |= HighlightModifier::Static;
751 }
749 return h; 752 return h;
750 } 753 }
751 hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct, 754 hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct,
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs
index e8f78ad52..65e0671a5 100644
--- a/crates/ide/src/syntax_highlighting/tags.rs
+++ b/crates/ide/src/syntax_highlighting/tags.rs
@@ -65,6 +65,8 @@ pub enum HighlightModifier {
65 Consuming, 65 Consuming,
66 Unsafe, 66 Unsafe,
67 Callable, 67 Callable,
68 /// Used for associated functions
69 Static,
68} 70}
69 71
70impl HighlightTag { 72impl HighlightTag {
@@ -124,6 +126,7 @@ impl HighlightModifier {
124 HighlightModifier::Consuming, 126 HighlightModifier::Consuming,
125 HighlightModifier::Unsafe, 127 HighlightModifier::Unsafe,
126 HighlightModifier::Callable, 128 HighlightModifier::Callable,
129 HighlightModifier::Static,
127 ]; 130 ];
128 131
129 fn as_str(self) -> &'static str { 132 fn as_str(self) -> &'static str {
@@ -137,6 +140,7 @@ impl HighlightModifier {
137 HighlightModifier::Consuming => "consuming", 140 HighlightModifier::Consuming => "consuming",
138 HighlightModifier::Unsafe => "unsafe", 141 HighlightModifier::Unsafe => "unsafe",
139 HighlightModifier::Callable => "callable", 142 HighlightModifier::Callable => "callable",
143 HighlightModifier::Static => "static",
140 } 144 }
141 } 145 }
142 146
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
new file mode 100644
index 000000000..cd80d72b7
--- /dev/null
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
@@ -0,0 +1,56 @@
1
2<style>
3body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5
6.lifetime { color: #DFAF8F; font-style: italic; }
7.comment { color: #7F9F7F; }
8.documentation { color: #629755; }
9.injected { opacity: 0.65 ; }
10.struct, .enum { color: #7CB8BB; }
11.enum_variant { color: #BDE0F3; }
12.string_literal { color: #CC9393; }
13.field { color: #94BFF3; }
14.function { color: #93E0E3; }
15.function.unsafe { color: #BC8383; }
16.operator.unsafe { color: #BC8383; }
17.parameter { color: #94BFF3; }
18.text { color: #DCDCCC; }
19.type { color: #7CB8BB; }
20.builtin_type { color: #8CD0D3; }
21.type_param { color: #DFAF8F; }
22.attribute { color: #94BFF3; }
23.numeric_literal { color: #BFEBBF; }
24.bool_literal { color: #BFE6EB; }
25.macro { color: #94BFF3; }
26.module { color: #AFD8AF; }
27.value_param { color: #DCDCCC; }
28.variable { color: #DCDCCC; }
29.format_specifier { color: #CC696B; }
30.mutable { text-decoration: underline; }
31.escape_sequence { color: #94BFF3; }
32.keyword { color: #F0DFAF; font-weight: bold; }
33.keyword.unsafe { color: #BC8383; font-weight: bold; }
34.control { font-style: italic; }
35
36.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
37</style>
38<pre><code><span class="keyword">fn</span> <span class="function declaration">not_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
39
40<span class="keyword">struct</span> <span class="struct declaration">foo</span> <span class="punctuation">{</span><span class="punctuation">}</span>
41
42<span class="keyword">impl</span> <span class="struct">foo</span> <span class="punctuation">{</span>
43 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static">is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
44 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">is_not_static</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
45<span class="punctuation">}</span>
46
47<span class="keyword">trait</span> <span class="trait declaration">t</span> <span class="punctuation">{</span>
48 <span class="keyword">fn</span> <span class="function declaration static">t_is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
49 <span class="keyword">fn</span> <span class="function declaration">t_is_not_static</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
50<span class="punctuation">}</span>
51
52<span class="keyword">impl</span> <span class="trait">t</span> <span class="keyword">for</span> <span class="struct">foo</span> <span class="punctuation">{</span>
53 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static">is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
54 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">is_not_static</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
55<span class="punctuation">}</span>
56 </code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index f44fe457d..6be88f856 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -53,7 +53,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
53 <span class="comment documentation">/// #</span><span class="generic injected"> </span><span class="attribute injected">#</span><span class="attribute injected">!</span><span class="attribute injected">[</span><span class="function attribute injected">allow</span><span class="punctuation injected">(</span><span class="attribute injected">unused_mut</span><span class="punctuation injected">)</span><span class="attribute injected">]</span> 53 <span class="comment documentation">/// #</span><span class="generic injected"> </span><span class="attribute injected">#</span><span class="attribute injected">!</span><span class="attribute injected">[</span><span class="function attribute injected">allow</span><span class="punctuation injected">(</span><span class="attribute injected">unused_mut</span><span class="punctuation injected">)</span><span class="attribute injected">]</span>
54 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="keyword injected">mut</span><span class="generic injected"> </span><span class="variable declaration injected mutable">foo</span><span class="punctuation injected">:</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected"> 54 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="keyword injected">mut</span><span class="generic injected"> </span><span class="variable declaration injected mutable">foo</span><span class="punctuation injected">:</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected">
55</span> <span class="comment documentation">/// ```</span> 55</span> <span class="comment documentation">/// ```</span>
56 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration">new</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="struct">Foo</span> <span class="punctuation">{</span> 56 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration static">new</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="struct">Foo</span> <span class="punctuation">{</span>
57 <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">bar</span><span class="punctuation">:</span> <span class="bool_literal">true</span> <span class="punctuation">}</span> 57 <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">bar</span><span class="punctuation">:</span> <span class="bool_literal">true</span> <span class="punctuation">}</span>
58 <span class="punctuation">}</span> 58 <span class="punctuation">}</span>
59 59
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
index 18addd00d..57c178916 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
@@ -40,7 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
40<span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> 40<span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
41 <span class="function">fixture</span><span class="punctuation">(</span><span class="string_literal">r#"</span> 41 <span class="function">fixture</span><span class="punctuation">(</span><span class="string_literal">r#"</span>
42 <span class="keyword">trait</span> <span class="trait declaration">Foo</span> <span class="punctuation">{</span> 42 <span class="keyword">trait</span> <span class="trait declaration">Foo</span> <span class="punctuation">{</span>
43 <span class="keyword">fn</span> <span class="function declaration">foo</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> 43 <span class="keyword">fn</span> <span class="function declaration static">foo</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
44 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"2 + 2 = {}"</span><span class="punctuation">,</span> <span class="numeric_literal">4</span><span class="punctuation">)</span><span class="punctuation">;</span> 44 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"2 + 2 = {}"</span><span class="punctuation">,</span> <span class="numeric_literal">4</span><span class="punctuation">)</span><span class="punctuation">;</span>
45 <span class="punctuation">}</span> 45 <span class="punctuation">}</span>
46 <span class="punctuation">}</span><span class="string_literal">"#</span> 46 <span class="punctuation">}</span><span class="string_literal">"#</span>
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 2b667b0d4..5c22e2fce 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -513,6 +513,34 @@ fn test_extern_crate() {
513 ); 513 );
514} 514}
515 515
516#[test]
517fn test_associated_function() {
518 check_highlighting(
519 r#"
520fn not_static() {}
521
522struct foo {}
523
524impl foo {
525 pub fn is_static() {}
526 pub fn is_not_static(&self) {}
527}
528
529trait t {
530 fn t_is_static() {}
531 fn t_is_not_static(&self) {}
532}
533
534impl t for foo {
535 pub fn is_static() {}
536 pub fn is_not_static(&self) {}
537}
538 "#,
539 expect_file!["./test_data/highlight_assoc_functions.html"],
540 false,
541 )
542}
543
516/// Highlights the code given by the `ra_fixture` argument, renders the 544/// Highlights the code given by the `ra_fixture` argument, renders the
517/// result as HTML, and compares it with the HTML file given as `snapshot`. 545/// result as HTML, and compares it with the HTML file given as `snapshot`.
518/// Note that the `snapshot` file is overwritten by the rendered HTML. 546/// Note that the `snapshot` file is overwritten by the rendered HTML.
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index ff1ae9575..9a8870053 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -16,8 +16,6 @@ use serde_json::json;
16use crate::semantic_tokens; 16use crate::semantic_tokens;
17 17
18pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabilities { 18pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabilities {
19 let code_action_provider = code_action_capabilities(client_caps);
20
21 ServerCapabilities { 19 ServerCapabilities {
22 text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions { 20 text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions {
23 open_close: Some(true), 21 open_close: Some(true),
@@ -49,7 +47,7 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
49 document_highlight_provider: Some(OneOf::Left(true)), 47 document_highlight_provider: Some(OneOf::Left(true)),
50 document_symbol_provider: Some(OneOf::Left(true)), 48 document_symbol_provider: Some(OneOf::Left(true)),
51 workspace_symbol_provider: Some(OneOf::Left(true)), 49 workspace_symbol_provider: Some(OneOf::Left(true)),
52 code_action_provider: Some(code_action_provider), 50 code_action_provider: Some(code_action_capabilities(client_caps)),
53 code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }), 51 code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }),
54 document_formatting_provider: Some(OneOf::Left(true)), 52 document_formatting_provider: Some(OneOf::Left(true)),
55 document_range_formatting_provider: None, 53 document_range_formatting_provider: None,
@@ -113,7 +111,7 @@ fn code_action_capabilities(client_caps: &ClientCapabilities) -> CodeActionProvi
113 CodeActionKind::REFACTOR_INLINE, 111 CodeActionKind::REFACTOR_INLINE,
114 CodeActionKind::REFACTOR_REWRITE, 112 CodeActionKind::REFACTOR_REWRITE,
115 ]), 113 ]),
116 resolve_provider: None, 114 resolve_provider: Some(true),
117 work_done_progress_options: Default::default(), 115 work_done_progress_options: Default::default(),
118 }) 116 })
119 }) 117 })
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 2ed6a0d82..b4c738272 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -144,7 +144,7 @@ pub struct ClientCapsConfig {
144 pub code_action_literals: bool, 144 pub code_action_literals: bool,
145 pub work_done_progress: bool, 145 pub work_done_progress: bool,
146 pub code_action_group: bool, 146 pub code_action_group: bool,
147 pub resolve_code_action: bool, 147 pub code_action_resolve: bool,
148 pub hover_actions: bool, 148 pub hover_actions: bool,
149 pub status_notification: bool, 149 pub status_notification: bool,
150 pub signature_help_label_offsets: bool, 150 pub signature_help_label_offsets: bool,
@@ -383,6 +383,17 @@ impl Config {
383 } 383 }
384 } 384 }
385 } 385 }
386
387 if let Some(code_action) = &doc_caps.code_action {
388 match (code_action.data_support, &code_action.resolve_support) {
389 (Some(true), Some(resolve_support)) => {
390 if resolve_support.properties.iter().any(|it| it == "edit") {
391 self.client_caps.code_action_resolve = true;
392 }
393 }
394 _ => (),
395 }
396 }
386 } 397 }
387 398
388 if let Some(window_caps) = caps.window.as_ref() { 399 if let Some(window_caps) = caps.window.as_ref() {
@@ -400,7 +411,6 @@ impl Config {
400 self.assist.allow_snippets(snippet_text_edit); 411 self.assist.allow_snippets(snippet_text_edit);
401 412
402 self.client_caps.code_action_group = get_bool("codeActionGroup"); 413 self.client_caps.code_action_group = get_bool("codeActionGroup");
403 self.client_caps.resolve_code_action = get_bool("resolveCodeAction");
404 self.client_caps.hover_actions = get_bool("hoverActions"); 414 self.client_caps.hover_actions = get_bool("hoverActions");
405 self.client_caps.status_notification = get_bool("statusNotification"); 415 self.client_caps.status_notification = get_bool("statusNotification");
406 } 416 }
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt
index 58d47d32a..5b2e5187a 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt
@@ -1,6 +1,13 @@
1[ 1[
2 MappedRustDiagnostic { 2 MappedRustDiagnostic {
3 url: "file:///test/compiler/mir/tagset.rs", 3 url: Url {
4 scheme: "file",
5 host: None,
6 port: None,
7 path: "/test/compiler/mir/tagset.rs",
8 query: None,
9 fragment: None,
10 },
4 diagnostic: Diagnostic { 11 diagnostic: Diagnostic {
5 range: Range { 12 range: Range {
6 start: Position { 13 start: Position {
@@ -29,7 +36,14 @@
29 [ 36 [
30 DiagnosticRelatedInformation { 37 DiagnosticRelatedInformation {
31 location: Location { 38 location: Location {
32 uri: "file:///test/compiler/lib.rs", 39 uri: Url {
40 scheme: "file",
41 host: None,
42 port: None,
43 path: "/test/compiler/lib.rs",
44 query: None,
45 fragment: None,
46 },
33 range: Range { 47 range: Range {
34 start: Position { 48 start: Position {
35 line: 0, 49 line: 0,
@@ -45,7 +59,14 @@
45 }, 59 },
46 DiagnosticRelatedInformation { 60 DiagnosticRelatedInformation {
47 location: Location { 61 location: Location {
48 uri: "file:///test/compiler/mir/tagset.rs", 62 uri: Url {
63 scheme: "file",
64 host: None,
65 port: None,
66 path: "/test/compiler/mir/tagset.rs",
67 query: None,
68 fragment: None,
69 },
49 range: Range { 70 range: Range {
50 start: Position { 71 start: Position {
51 line: 41, 72 line: 41,
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt b/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt
index 6aa26bf63..116f0ff73 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt
@@ -1,6 +1,13 @@
1[ 1[
2 MappedRustDiagnostic { 2 MappedRustDiagnostic {
3 url: "file:///test/src/main.rs", 3 url: Url {
4 scheme: "file",
5 host: None,
6 port: None,
7 path: "/test/src/main.rs",
8 query: None,
9 fragment: None,
10 },
4 diagnostic: Diagnostic { 11 diagnostic: Diagnostic {
5 range: Range { 12 range: Range {
6 start: Position { 13 start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt b/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt
index 7aaffaba2..bbec6a796 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt
@@ -1,6 +1,13 @@
1[ 1[
2 MappedRustDiagnostic { 2 MappedRustDiagnostic {
3 url: "file:///test/crates/hir_def/src/data.rs", 3 url: Url {
4 scheme: "file",
5 host: None,
6 port: None,
7 path: "/test/crates/hir_def/src/data.rs",
8 query: None,
9 fragment: None,
10 },
4 diagnostic: Diagnostic { 11 diagnostic: Diagnostic {
5 range: Range { 12 range: Range {
6 start: Position { 13 start: Position {
@@ -25,7 +32,14 @@
25 [ 32 [
26 DiagnosticRelatedInformation { 33 DiagnosticRelatedInformation {
27 location: Location { 34 location: Location {
28 uri: "file:///test/crates/hir_def/src/path.rs", 35 uri: Url {
36 scheme: "file",
37 host: None,
38 port: None,
39 path: "/test/crates/hir_def/src/path.rs",
40 query: None,
41 fragment: None,
42 },
29 range: Range { 43 range: Range {
30 start: Position { 44 start: Position {
31 line: 264, 45 line: 264,
@@ -47,7 +61,14 @@
47 fixes: [], 61 fixes: [],
48 }, 62 },
49 MappedRustDiagnostic { 63 MappedRustDiagnostic {
50 url: "file:///test/crates/hir_def/src/path.rs", 64 url: Url {
65 scheme: "file",
66 host: None,
67 port: None,
68 path: "/test/crates/hir_def/src/path.rs",
69 query: None,
70 fragment: None,
71 },
51 diagnostic: Diagnostic { 72 diagnostic: Diagnostic {
52 range: Range { 73 range: Range {
53 start: Position { 74 start: Position {
@@ -72,7 +93,14 @@
72 [ 93 [
73 DiagnosticRelatedInformation { 94 DiagnosticRelatedInformation {
74 location: Location { 95 location: Location {
75 uri: "file:///test/crates/hir_def/src/data.rs", 96 uri: Url {
97 scheme: "file",
98 host: None,
99 port: None,
100 path: "/test/crates/hir_def/src/data.rs",
101 query: None,
102 fragment: None,
103 },
76 range: Range { 104 range: Range {
77 start: Position { 105 start: Position {
78 line: 79, 106 line: 79,
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt
index 584213420..2cbf657e5 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt
@@ -1,6 +1,13 @@
1[ 1[
2 MappedRustDiagnostic { 2 MappedRustDiagnostic {
3 url: "file:///test/compiler/ty/list_iter.rs", 3 url: Url {
4 scheme: "file",
5 host: None,
6 port: None,
7 path: "/test/compiler/ty/list_iter.rs",
8 query: None,
9 fragment: None,
10 },
4 diagnostic: Diagnostic { 11 diagnostic: Diagnostic {
5 range: Range { 12 range: Range {
6 start: Position { 13 start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt
index 2610e4e20..1142dc2ac 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt
@@ -1,6 +1,13 @@
1[ 1[
2 MappedRustDiagnostic { 2 MappedRustDiagnostic {
3 url: "file:///test/runtime/compiler_support.rs", 3 url: Url {
4 scheme: "file",
5 host: None,
6 port: None,
7 path: "/test/runtime/compiler_support.rs",
8 query: None,
9 fragment: None,
10 },
4 diagnostic: Diagnostic { 11 diagnostic: Diagnostic {
5 range: Range { 12 range: Range {
6 start: Position { 13 start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt
index 8dc53391e..c709de95f 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt
@@ -1,6 +1,13 @@
1[ 1[
2 MappedRustDiagnostic { 2 MappedRustDiagnostic {
3 url: "file:///test/driver/subcommand/repl.rs", 3 url: Url {
4 scheme: "file",
5 host: None,
6 port: None,
7 path: "/test/driver/subcommand/repl.rs",
8 query: None,
9 fragment: None,
10 },
4 diagnostic: Diagnostic { 11 diagnostic: Diagnostic {
5 range: Range { 12 range: Range {
6 start: Position { 13 start: Position {
@@ -36,7 +43,6 @@
36 fixes: [ 43 fixes: [
37 CodeAction { 44 CodeAction {
38 title: "consider prefixing with an underscore", 45 title: "consider prefixing with an underscore",
39 id: None,
40 group: None, 46 group: None,
41 kind: Some( 47 kind: Some(
42 CodeActionKind( 48 CodeActionKind(
@@ -47,7 +53,14 @@
47 SnippetWorkspaceEdit { 53 SnippetWorkspaceEdit {
48 changes: Some( 54 changes: Some(
49 { 55 {
50 "file:///test/driver/subcommand/repl.rs": [ 56 Url {
57 scheme: "file",
58 host: None,
59 port: None,
60 path: "/test/driver/subcommand/repl.rs",
61 query: None,
62 fragment: None,
63 }: [
51 TextEdit { 64 TextEdit {
52 range: Range { 65 range: Range {
53 start: Position { 66 start: Position {
@@ -70,6 +83,7 @@
70 is_preferred: Some( 83 is_preferred: Some(
71 true, 84 true,
72 ), 85 ),
86 data: None,
73 }, 87 },
74 ], 88 ],
75 }, 89 },
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt
index c8703194c..632f438d7 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt
@@ -1,6 +1,13 @@
1[ 1[
2 MappedRustDiagnostic { 2 MappedRustDiagnostic {
3 url: "file:///test/driver/subcommand/repl.rs", 3 url: Url {
4 scheme: "file",
5 host: None,
6 port: None,
7 path: "/test/driver/subcommand/repl.rs",
8 query: None,
9 fragment: None,
10 },
4 diagnostic: Diagnostic { 11 diagnostic: Diagnostic {
5 range: Range { 12 range: Range {
6 start: Position { 13 start: Position {
@@ -36,7 +43,6 @@
36 fixes: [ 43 fixes: [
37 CodeAction { 44 CodeAction {
38 title: "consider prefixing with an underscore", 45 title: "consider prefixing with an underscore",
39 id: None,
40 group: None, 46 group: None,
41 kind: Some( 47 kind: Some(
42 CodeActionKind( 48 CodeActionKind(
@@ -47,7 +53,14 @@
47 SnippetWorkspaceEdit { 53 SnippetWorkspaceEdit {
48 changes: Some( 54 changes: Some(
49 { 55 {
50 "file:///test/driver/subcommand/repl.rs": [ 56 Url {
57 scheme: "file",
58 host: None,
59 port: None,
60 path: "/test/driver/subcommand/repl.rs",
61 query: None,
62 fragment: None,
63 }: [
51 TextEdit { 64 TextEdit {
52 range: Range { 65 range: Range {
53 start: Position { 66 start: Position {
@@ -70,6 +83,7 @@
70 is_preferred: Some( 83 is_preferred: Some(
71 true, 84 true,
72 ), 85 ),
86 data: None,
73 }, 87 },
74 ], 88 ],
75 }, 89 },
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt
index dc93227ad..c0b79428d 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt
@@ -1,6 +1,13 @@
1[ 1[
2 MappedRustDiagnostic { 2 MappedRustDiagnostic {
3 url: "file:///test/driver/subcommand/repl.rs", 3 url: Url {
4 scheme: "file",
5 host: None,
6 port: None,
7 path: "/test/driver/subcommand/repl.rs",
8 query: None,
9 fragment: None,
10 },
4 diagnostic: Diagnostic { 11 diagnostic: Diagnostic {
5 range: Range { 12 range: Range {
6 start: Position { 13 start: Position {
@@ -36,7 +43,6 @@
36 fixes: [ 43 fixes: [
37 CodeAction { 44 CodeAction {
38 title: "consider prefixing with an underscore", 45 title: "consider prefixing with an underscore",
39 id: None,
40 group: None, 46 group: None,
41 kind: Some( 47 kind: Some(
42 CodeActionKind( 48 CodeActionKind(
@@ -47,7 +53,14 @@
47 SnippetWorkspaceEdit { 53 SnippetWorkspaceEdit {
48 changes: Some( 54 changes: Some(
49 { 55 {
50 "file:///test/driver/subcommand/repl.rs": [ 56 Url {
57 scheme: "file",
58 host: None,
59 port: None,
60 path: "/test/driver/subcommand/repl.rs",
61 query: None,
62 fragment: None,
63 }: [
51 TextEdit { 64 TextEdit {
52 range: Range { 65 range: Range {
53 start: Position { 66 start: Position {
@@ -70,6 +83,7 @@
70 is_preferred: Some( 83 is_preferred: Some(
71 true, 84 true,
72 ), 85 ),
86 data: None,
73 }, 87 },
74 ], 88 ],
75 }, 89 },
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt
index ba1b98b33..782c72dbd 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt
@@ -1,6 +1,13 @@
1[ 1[
2 MappedRustDiagnostic { 2 MappedRustDiagnostic {
3 url: "file:///test/compiler/ty/select.rs", 3 url: Url {
4 scheme: "file",
5 host: None,
6 port: None,
7 path: "/test/compiler/ty/select.rs",
8 query: None,
9 fragment: None,
10 },
4 diagnostic: Diagnostic { 11 diagnostic: Diagnostic {
5 range: Range { 12 range: Range {
6 start: Position { 13 start: Position {
@@ -29,7 +36,14 @@
29 [ 36 [
30 DiagnosticRelatedInformation { 37 DiagnosticRelatedInformation {
31 location: Location { 38 location: Location {
32 uri: "file:///test/compiler/ty/select.rs", 39 uri: Url {
40 scheme: "file",
41 host: None,
42 port: None,
43 path: "/test/compiler/ty/select.rs",
44 query: None,
45 fragment: None,
46 },
33 range: Range { 47 range: Range {
34 start: Position { 48 start: Position {
35 line: 218, 49 line: 218,
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt
index 81f752672..d3f27ab6a 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt
@@ -1,6 +1,13 @@
1[ 1[
2 MappedRustDiagnostic { 2 MappedRustDiagnostic {
3 url: "file:///test/src/main.rs", 3 url: Url {
4 scheme: "file",
5 host: None,
6 port: None,
7 path: "/test/src/main.rs",
8 query: None,
9 fragment: None,
10 },
4 diagnostic: Diagnostic { 11 diagnostic: Diagnostic {
5 range: Range { 12 range: Range {
6 start: Position { 13 start: Position {
@@ -29,7 +36,14 @@
29 [ 36 [
30 DiagnosticRelatedInformation { 37 DiagnosticRelatedInformation {
31 location: Location { 38 location: Location {
32 uri: "file:///test/src/main.rs", 39 uri: Url {
40 scheme: "file",
41 host: None,
42 port: None,
43 path: "/test/src/main.rs",
44 query: None,
45 fragment: None,
46 },
33 range: Range { 47 range: Range {
34 start: Position { 48 start: Position {
35 line: 2, 49 line: 2,
@@ -51,7 +65,6 @@
51 fixes: [ 65 fixes: [
52 CodeAction { 66 CodeAction {
53 title: "return the expression directly", 67 title: "return the expression directly",
54 id: None,
55 group: None, 68 group: None,
56 kind: Some( 69 kind: Some(
57 CodeActionKind( 70 CodeActionKind(
@@ -62,7 +75,14 @@
62 SnippetWorkspaceEdit { 75 SnippetWorkspaceEdit {
63 changes: Some( 76 changes: Some(
64 { 77 {
65 "file:///test/src/main.rs": [ 78 Url {
79 scheme: "file",
80 host: None,
81 port: None,
82 path: "/test/src/main.rs",
83 query: None,
84 fragment: None,
85 }: [
66 TextEdit { 86 TextEdit {
67 range: Range { 87 range: Range {
68 start: Position { 88 start: Position {
@@ -98,6 +118,7 @@
98 is_preferred: Some( 118 is_preferred: Some(
99 true, 119 true,
100 ), 120 ),
121 data: None,
101 }, 122 },
102 ], 123 ],
103 }, 124 },
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index b949577c1..15145415b 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -110,7 +110,6 @@ fn map_rust_child_diagnostic(
110 } else { 110 } else {
111 MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction { 111 MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction {
112 title: rd.message.clone(), 112 title: rd.message.clone(),
113 id: None,
114 group: None, 113 group: None,
115 kind: Some(lsp_types::CodeActionKind::QUICKFIX), 114 kind: Some(lsp_types::CodeActionKind::QUICKFIX),
116 edit: Some(lsp_ext::SnippetWorkspaceEdit { 115 edit: Some(lsp_ext::SnippetWorkspaceEdit {
@@ -119,6 +118,7 @@ fn map_rust_child_diagnostic(
119 document_changes: None, 118 document_changes: None,
120 }), 119 }),
121 is_preferred: Some(true), 120 is_preferred: Some(true),
121 data: None,
122 }) 122 })
123 } 123 }
124} 124}
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 049c583a4..95659b0db 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -806,11 +806,11 @@ fn handle_fixes(
806 let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?; 806 let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?;
807 let action = lsp_ext::CodeAction { 807 let action = lsp_ext::CodeAction {
808 title: fix.label.to_string(), 808 title: fix.label.to_string(),
809 id: None,
810 group: None, 809 group: None,
811 kind: Some(CodeActionKind::QUICKFIX), 810 kind: Some(CodeActionKind::QUICKFIX),
812 edit: Some(edit), 811 edit: Some(edit),
813 is_preferred: Some(false), 812 is_preferred: Some(false),
813 data: None,
814 }; 814 };
815 res.push(action); 815 res.push(action);
816 } 816 }
@@ -852,11 +852,11 @@ pub(crate) fn handle_code_action(
852 852
853 handle_fixes(&snap, &params, &mut res)?; 853 handle_fixes(&snap, &params, &mut res)?;
854 854
855 if snap.config.client_caps.resolve_code_action { 855 if snap.config.client_caps.code_action_resolve {
856 for (index, assist) in 856 for (index, assist) in
857 snap.analysis.unresolved_assists(&snap.config.assist, frange)?.into_iter().enumerate() 857 snap.analysis.unresolved_assists(&snap.config.assist, frange)?.into_iter().enumerate()
858 { 858 {
859 res.push(to_proto::unresolved_code_action(&snap, assist, index)?); 859 res.push(to_proto::unresolved_code_action(&snap, params.clone(), assist, index)?);
860 } 860 }
861 } else { 861 } else {
862 for assist in snap.analysis.resolved_assists(&snap.config.assist, frange)?.into_iter() { 862 for assist in snap.analysis.resolved_assists(&snap.config.assist, frange)?.into_iter() {
@@ -867,11 +867,16 @@ pub(crate) fn handle_code_action(
867 Ok(Some(res)) 867 Ok(Some(res))
868} 868}
869 869
870pub(crate) fn handle_resolve_code_action( 870pub(crate) fn handle_code_action_resolve(
871 mut snap: GlobalStateSnapshot, 871 mut snap: GlobalStateSnapshot,
872 params: lsp_ext::ResolveCodeActionParams, 872 mut code_action: lsp_ext::CodeAction,
873) -> Result<Option<lsp_ext::SnippetWorkspaceEdit>> { 873) -> Result<lsp_ext::CodeAction> {
874 let _p = profile::span("handle_resolve_code_action"); 874 let _p = profile::span("handle_code_action_resolve");
875 let params = match code_action.data.take() {
876 Some(it) => it,
877 None => Err("can't resolve code action without data")?,
878 };
879
875 let file_id = from_proto::file_id(&snap, &params.code_action_params.text_document.uri)?; 880 let file_id = from_proto::file_id(&snap, &params.code_action_params.text_document.uri)?;
876 let line_index = snap.analysis.file_line_index(file_id)?; 881 let line_index = snap.analysis.file_line_index(file_id)?;
877 let range = from_proto::text_range(&line_index, params.code_action_params.range); 882 let range = from_proto::text_range(&line_index, params.code_action_params.range);
@@ -888,7 +893,9 @@ pub(crate) fn handle_resolve_code_action(
888 let index = index.parse::<usize>().unwrap(); 893 let index = index.parse::<usize>().unwrap();
889 let assist = &assists[index]; 894 let assist = &assists[index];
890 assert!(assist.assist.id.0 == id); 895 assert!(assist.assist.id.0 == id);
891 Ok(to_proto::resolved_code_action(&snap, assist.clone())?.edit) 896 let edit = to_proto::resolved_code_action(&snap, assist.clone())?.edit;
897 code_action.edit = edit;
898 Ok(code_action)
892} 899}
893 900
894pub(crate) fn handle_code_lens( 901pub(crate) fn handle_code_lens(
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index f31f8d900..a7c3028e4 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -113,22 +113,6 @@ pub struct JoinLinesParams {
113 pub ranges: Vec<Range>, 113 pub ranges: Vec<Range>,
114} 114}
115 115
116pub enum ResolveCodeActionRequest {}
117
118impl Request for ResolveCodeActionRequest {
119 type Params = ResolveCodeActionParams;
120 type Result = Option<SnippetWorkspaceEdit>;
121 const METHOD: &'static str = "experimental/resolveCodeAction";
122}
123
124/// Params for the ResolveCodeActionRequest
125#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
126#[serde(rename_all = "camelCase")]
127pub struct ResolveCodeActionParams {
128 pub code_action_params: lsp_types::CodeActionParams,
129 pub id: String,
130}
131
132pub enum OnEnter {} 116pub enum OnEnter {}
133 117
134impl Request for OnEnter { 118impl Request for OnEnter {
@@ -265,13 +249,18 @@ impl Request for CodeActionRequest {
265 const METHOD: &'static str = "textDocument/codeAction"; 249 const METHOD: &'static str = "textDocument/codeAction";
266} 250}
267 251
252pub enum CodeActionResolveRequest {}
253impl Request for CodeActionResolveRequest {
254 type Params = CodeAction;
255 type Result = CodeAction;
256 const METHOD: &'static str = "codeAction/resolve";
257}
258
268#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)] 259#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)]
269#[serde(rename_all = "camelCase")] 260#[serde(rename_all = "camelCase")]
270pub struct CodeAction { 261pub struct CodeAction {
271 pub title: String, 262 pub title: String,
272 #[serde(skip_serializing_if = "Option::is_none")] 263 #[serde(skip_serializing_if = "Option::is_none")]
273 pub id: Option<String>,
274 #[serde(skip_serializing_if = "Option::is_none")]
275 pub group: Option<String>, 264 pub group: Option<String>,
276 #[serde(skip_serializing_if = "Option::is_none")] 265 #[serde(skip_serializing_if = "Option::is_none")]
277 pub kind: Option<CodeActionKind>, 266 pub kind: Option<CodeActionKind>,
@@ -282,6 +271,16 @@ pub struct CodeAction {
282 pub edit: Option<SnippetWorkspaceEdit>, 271 pub edit: Option<SnippetWorkspaceEdit>,
283 #[serde(skip_serializing_if = "Option::is_none")] 272 #[serde(skip_serializing_if = "Option::is_none")]
284 pub is_preferred: Option<bool>, 273 pub is_preferred: Option<bool>,
274
275 #[serde(skip_serializing_if = "Option::is_none")]
276 pub data: Option<CodeActionData>,
277}
278
279#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
280#[serde(rename_all = "camelCase")]
281pub struct CodeActionData {
282 pub code_action_params: lsp_types::CodeActionParams,
283 pub id: String,
285} 284}
286 285
287#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)] 286#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index d504572a8..6e6cac42e 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -435,7 +435,7 @@ impl GlobalState {
435 .on::<lsp_ext::Runnables>(handlers::handle_runnables) 435 .on::<lsp_ext::Runnables>(handlers::handle_runnables)
436 .on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints) 436 .on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints)
437 .on::<lsp_ext::CodeActionRequest>(handlers::handle_code_action) 437 .on::<lsp_ext::CodeActionRequest>(handlers::handle_code_action)
438 .on::<lsp_ext::ResolveCodeActionRequest>(handlers::handle_resolve_code_action) 438 .on::<lsp_ext::CodeActionResolveRequest>(handlers::handle_code_action_resolve)
439 .on::<lsp_ext::HoverRequest>(handlers::handle_hover) 439 .on::<lsp_ext::HoverRequest>(handlers::handle_hover)
440 .on::<lsp_ext::ExternalDocs>(handlers::handle_open_docs) 440 .on::<lsp_ext::ExternalDocs>(handlers::handle_open_docs)
441 .on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting) 441 .on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 92b7c7b68..4bdf4bf0f 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -426,6 +426,7 @@ fn semantic_token_type_and_modifiers(
426 HighlightModifier::Consuming => semantic_tokens::CONSUMING, 426 HighlightModifier::Consuming => semantic_tokens::CONSUMING,
427 HighlightModifier::Unsafe => semantic_tokens::UNSAFE, 427 HighlightModifier::Unsafe => semantic_tokens::UNSAFE,
428 HighlightModifier::Callable => semantic_tokens::CALLABLE, 428 HighlightModifier::Callable => semantic_tokens::CALLABLE,
429 HighlightModifier::Static => lsp_types::SemanticTokenModifier::STATIC,
429 }; 430 };
430 mods |= modifier; 431 mods |= modifier;
431 } 432 }
@@ -734,16 +735,20 @@ pub(crate) fn code_action_kind(kind: AssistKind) -> lsp_types::CodeActionKind {
734 735
735pub(crate) fn unresolved_code_action( 736pub(crate) fn unresolved_code_action(
736 snap: &GlobalStateSnapshot, 737 snap: &GlobalStateSnapshot,
738 code_action_params: lsp_types::CodeActionParams,
737 assist: Assist, 739 assist: Assist,
738 index: usize, 740 index: usize,
739) -> Result<lsp_ext::CodeAction> { 741) -> Result<lsp_ext::CodeAction> {
740 let res = lsp_ext::CodeAction { 742 let res = lsp_ext::CodeAction {
741 title: assist.label.to_string(), 743 title: assist.label.to_string(),
742 id: Some(format!("{}:{}", assist.id.0, index.to_string())),
743 group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0), 744 group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0),
744 kind: Some(code_action_kind(assist.id.1)), 745 kind: Some(code_action_kind(assist.id.1)),
745 edit: None, 746 edit: None,
746 is_preferred: None, 747 is_preferred: None,
748 data: Some(lsp_ext::CodeActionData {
749 id: format!("{}:{}", assist.id.0, index.to_string()),
750 code_action_params,
751 }),
747 }; 752 };
748 Ok(res) 753 Ok(res)
749} 754}
@@ -753,13 +758,19 @@ pub(crate) fn resolved_code_action(
753 assist: ResolvedAssist, 758 assist: ResolvedAssist,
754) -> Result<lsp_ext::CodeAction> { 759) -> Result<lsp_ext::CodeAction> {
755 let change = assist.source_change; 760 let change = assist.source_change;
756 unresolved_code_action(snap, assist.assist, 0).and_then(|it| { 761 let res = lsp_ext::CodeAction {
757 Ok(lsp_ext::CodeAction { 762 edit: Some(snippet_workspace_edit(snap, change)?),
758 id: None, 763 title: assist.assist.label.to_string(),
759 edit: Some(snippet_workspace_edit(snap, change)?), 764 group: assist
760 ..it 765 .assist
761 }) 766 .group
762 }) 767 .filter(|_| snap.config.client_caps.code_action_group)
768 .map(|gr| gr.0),
769 kind: Some(code_action_kind(assist.assist.id.1)),
770 is_preferred: None,
771 data: None,
772 };
773 Ok(res)
763} 774}
764 775
765pub(crate) fn runnable( 776pub(crate) fn runnable(
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index 59d89f47d..374ed5910 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -1,8 +1,5 @@
1//! Missing batteries for standard libraries. 1//! Missing batteries for standard libraries.
2use std::{ 2use std::time::Instant;
3 sync::atomic::{AtomicUsize, Ordering},
4 time::Instant,
5};
6 3
7mod macros; 4mod macros;
8pub mod panic_context; 5pub mod panic_context;
@@ -150,31 +147,6 @@ where
150 left 147 left
151} 148}
152 149
153pub struct RacyFlag(AtomicUsize);
154
155impl RacyFlag {
156 pub const fn new() -> RacyFlag {
157 RacyFlag(AtomicUsize::new(!0))
158 }
159
160 pub fn get(&self, init: impl FnMut() -> bool) -> bool {
161 let mut init = Some(init);
162 self.get_impl(&mut || init.take().map_or(false, |mut f| f()))
163 }
164
165 fn get_impl(&self, init: &mut dyn FnMut() -> bool) -> bool {
166 match self.0.load(Ordering::Relaxed) {
167 0 => false,
168 1 => true,
169 _ => {
170 let res = init();
171 self.0.store(if res { 1 } else { 0 }, Ordering::Relaxed);
172 res
173 }
174 }
175 }
176}
177
178#[cfg(test)] 150#[cfg(test)]
179mod tests { 151mod tests {
180 use super::*; 152 use super::*;
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 61d2acb49..1fe907753 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -13,7 +13,7 @@ doctest = false
13[dependencies] 13[dependencies]
14itertools = "0.9.0" 14itertools = "0.9.0"
15rowan = "0.10.0" 15rowan = "0.10.0"
16rustc_lexer = { version = "686.0.0", package = "rustc-ap-rustc_lexer" } 16rustc_lexer = { version = "688.0.0", package = "rustc-ap-rustc_lexer" }
17rustc-hash = "1.1.0" 17rustc-hash = "1.1.0"
18arrayvec = "0.5.1" 18arrayvec = "0.5.1"
19once_cell = "1.3.1" 19once_cell = "1.3.1"
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs
index 30adb7217..320c430c9 100644
--- a/crates/syntax/src/algo.rs
+++ b/crates/syntax/src/algo.rs
@@ -111,18 +111,28 @@ pub enum InsertPosition<T> {
111 111
112type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<rustc_hash::FxHasher>>; 112type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<rustc_hash::FxHasher>>;
113 113
114#[derive(Debug, Hash, PartialEq, Eq)]
115enum TreeDiffInsertPos {
116 After(SyntaxElement),
117 AsFirstChild(SyntaxElement),
118}
119
114#[derive(Debug)] 120#[derive(Debug)]
115pub struct TreeDiff { 121pub struct TreeDiff {
116 replacements: FxHashMap<SyntaxElement, SyntaxElement>, 122 replacements: FxHashMap<SyntaxElement, SyntaxElement>,
117 deletions: Vec<SyntaxElement>, 123 deletions: Vec<SyntaxElement>,
118 // the vec as well as the indexmap are both here to preserve order 124 // the vec as well as the indexmap are both here to preserve order
119 insertions: FxIndexMap<SyntaxElement, Vec<SyntaxElement>>, 125 insertions: FxIndexMap<TreeDiffInsertPos, Vec<SyntaxElement>>,
120} 126}
121 127
122impl TreeDiff { 128impl TreeDiff {
123 pub fn into_text_edit(&self, builder: &mut TextEditBuilder) { 129 pub fn into_text_edit(&self, builder: &mut TextEditBuilder) {
124 for (anchor, to) in self.insertions.iter() { 130 for (anchor, to) in self.insertions.iter() {
125 to.iter().for_each(|to| builder.insert(anchor.text_range().end(), to.to_string())); 131 let offset = match anchor {
132 TreeDiffInsertPos::After(it) => it.text_range().end(),
133 TreeDiffInsertPos::AsFirstChild(it) => it.text_range().start(),
134 };
135 to.iter().for_each(|to| builder.insert(offset, to.to_string()));
126 } 136 }
127 for (from, to) in self.replacements.iter() { 137 for (from, to) in self.replacements.iter() {
128 builder.replace(from.text_range(), to.to_string()) 138 builder.replace(from.text_range(), to.to_string())
@@ -188,19 +198,20 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff {
188 let lhs_child = lhs_children.next(); 198 let lhs_child = lhs_children.next();
189 match (lhs_child.clone(), rhs_children.next()) { 199 match (lhs_child.clone(), rhs_children.next()) {
190 (None, None) => break, 200 (None, None) => break,
191 (None, Some(element)) => match last_lhs.clone() { 201 (None, Some(element)) => {
192 Some(prev) => { 202 let insert_pos = match last_lhs.clone() {
193 mark::hit!(diff_insert); 203 Some(prev) => {
194 diff.insertions.entry(prev).or_insert_with(Vec::new).push(element); 204 mark::hit!(diff_insert);
195 } 205 TreeDiffInsertPos::After(prev)
196 // first iteration, this means we got no anchor element to insert after 206 }
197 // therefor replace the parent node instead 207 // first iteration, insert into out parent as the first child
198 None => { 208 None => {
199 mark::hit!(diff_replace_parent); 209 mark::hit!(diff_insert_as_first_child);
200 diff.replacements.insert(lhs.clone().into(), rhs.clone().into()); 210 TreeDiffInsertPos::AsFirstChild(lhs.clone().into())
201 break; 211 }
202 } 212 };
203 }, 213 diff.insertions.entry(insert_pos).or_insert_with(Vec::new).push(element);
214 }
204 (Some(element), None) => { 215 (Some(element), None) => {
205 mark::hit!(diff_delete); 216 mark::hit!(diff_delete);
206 diff.deletions.push(element); 217 diff.deletions.push(element);
@@ -224,8 +235,15 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff {
224 } 235 }
225 } 236 }
226 let drain = look_ahead_scratch.drain(..); 237 let drain = look_ahead_scratch.drain(..);
227 if let Some(prev) = last_lhs.clone().filter(|_| insert) { 238 if insert {
228 diff.insertions.entry(prev).or_insert_with(Vec::new).extend(drain); 239 let insert_pos = if let Some(prev) = last_lhs.clone().filter(|_| insert) {
240 TreeDiffInsertPos::After(prev)
241 } else {
242 mark::hit!(insert_first_child);
243 TreeDiffInsertPos::AsFirstChild(lhs.clone().into())
244 };
245
246 diff.insertions.entry(insert_pos).or_insert_with(Vec::new).extend(drain);
229 rhs_children = rhs_children_clone; 247 rhs_children = rhs_children_clone;
230 } else { 248 } else {
231 go(diff, lhs_ele, rhs_ele) 249 go(diff, lhs_ele, rhs_ele)
@@ -632,18 +650,19 @@ mod tests {
632 650
633 #[test] 651 #[test]
634 fn replace_parent() { 652 fn replace_parent() {
635 mark::check!(diff_replace_parent); 653 mark::check!(diff_insert_as_first_child);
636 check_diff( 654 check_diff(
637 r#""#, 655 r#""#,
638 r#"use foo::bar;"#, 656 r#"use foo::bar;"#,
639 expect![[r#" 657 expect![[r#"
640 insertions: 658 insertions:
641 659
642 660 Line 0: AsFirstChild(Node([email protected]))
661 -> use foo::bar;
643 662
644 replacements: 663 replacements:
645 664
646 Line 0: Node([email protected]) -> use foo::bar; 665
647 666
648 deletions: 667 deletions:
649 668
@@ -666,7 +685,7 @@ use baz;"#,
666 expect![[r#" 685 expect![[r#"
667 insertions: 686 insertions:
668 687
669 Line 2: Node([email protected]) 688 Line 2: After(Node([email protected]))
670 -> "\n" 689 -> "\n"
671 -> use baz; 690 -> use baz;
672 691
@@ -694,7 +713,7 @@ use baz;"#,
694 expect![[r#" 713 expect![[r#"
695 insertions: 714 insertions:
696 715
697 Line 2: Token([email protected] "\n") 716 Line 2: After(Token([email protected] "\n"))
698 -> use bar; 717 -> use bar;
699 -> "\n" 718 -> "\n"
700 719
@@ -722,7 +741,7 @@ use baz;"#,
722 expect![[r#" 741 expect![[r#"
723 insertions: 742 insertions:
724 743
725 Line 0: Token([email protected] "\n") 744 Line 0: After(Token([email protected] "\n"))
726 -> use foo; 745 -> use foo;
727 -> "\n" 746 -> "\n"
728 747
@@ -738,6 +757,36 @@ use baz;"#,
738 } 757 }
739 758
740 #[test] 759 #[test]
760 fn first_child_insertion() {
761 mark::check!(insert_first_child);
762 check_diff(
763 r#"fn main() {
764 stdi
765 }"#,
766 r#"use foo::bar;
767
768 fn main() {
769 stdi
770 }"#,
771 expect![[r#"
772 insertions:
773
774 Line 0: AsFirstChild(Node([email protected]))
775 -> use foo::bar;
776 -> "\n\n "
777
778 replacements:
779
780
781
782 deletions:
783
784
785 "#]],
786 );
787 }
788
789 #[test]
741 fn delete_last() { 790 fn delete_last() {
742 mark::check!(diff_delete); 791 mark::check!(diff_delete);
743 check_diff( 792 check_diff(
@@ -779,7 +828,7 @@ use crate::AstNode;
779 expect![[r#" 828 expect![[r#"
780 insertions: 829 insertions:
781 830
782 Line 1: Node([email protected]) 831 Line 1: After(Node([email protected]))
783 -> "\n\n" 832 -> "\n\n"
784 -> use crate::AstNode; 833 -> use crate::AstNode;
785 834
@@ -845,10 +894,10 @@ use std::ops::{self, RangeInclusive};
845 expect![[r#" 894 expect![[r#"
846 insertions: 895 insertions:
847 896
848 Line 2: Node([email protected]) 897 Line 2: After(Node([email protected]))
849 -> :: 898 -> ::
850 -> fmt 899 -> fmt
851 Line 6: Token([email protected] "\n") 900 Line 6: After(Token([email protected] "\n"))
852 -> use std::hash::BuildHasherDefault; 901 -> use std::hash::BuildHasherDefault;
853 -> "\n" 902 -> "\n"
854 -> use std::ops::{self, RangeInclusive}; 903 -> use std::ops::{self, RangeInclusive};
@@ -892,14 +941,14 @@ fn main() {
892 expect![[r#" 941 expect![[r#"
893 insertions: 942 insertions:
894 943
895 Line 3: Node([email protected]) 944 Line 3: After(Node([email protected]))
896 -> " " 945 -> " "
897 -> match Err(92) { 946 -> match Err(92) {
898 Ok(it) => it, 947 Ok(it) => it,
899 _ => return, 948 _ => return,
900 } 949 }
901 -> ; 950 -> ;
902 Line 3: Node([email protected]) 951 Line 3: After(Node([email protected]))
903 -> "\n " 952 -> "\n "
904 -> foo(x); 953 -> foo(x);
905 954
@@ -934,14 +983,18 @@ fn main() {
934 _ => format!("{}", syn), 983 _ => format!("{}", syn),
935 }; 984 };
936 985
937 let insertions = diff.insertions.iter().format_with("\n", |(k, v), f| { 986 let insertions =
938 f(&format!( 987 diff.insertions.iter().format_with("\n", |(k, v), f| -> Result<(), std::fmt::Error> {
939 "Line {}: {:?}\n-> {}", 988 f(&format!(
940 line_number(k), 989 "Line {}: {:?}\n-> {}",
941 k, 990 line_number(match k {
942 v.iter().format_with("\n-> ", |v, f| f(&fmt_syntax(v))) 991 super::TreeDiffInsertPos::After(syn) => syn,
943 )) 992 super::TreeDiffInsertPos::AsFirstChild(syn) => syn,
944 }); 993 }),
994 k,
995 v.iter().format_with("\n-> ", |v, f| f(&fmt_syntax(v)))
996 ))
997 });
945 998
946 let replacements = diff 999 let replacements = diff
947 .replacements 1000 .replacements
diff --git a/crates/syntax/src/ast.rs b/crates/syntax/src/ast.rs
index 8a0e3d27b..7844f9ed6 100644
--- a/crates/syntax/src/ast.rs
+++ b/crates/syntax/src/ast.rs
@@ -115,10 +115,10 @@ fn test_doc_comment_none() {
115} 115}
116 116
117#[test] 117#[test]
118fn test_doc_comment_of_items() { 118fn test_outer_doc_comment_of_items() {
119 let file = SourceFile::parse( 119 let file = SourceFile::parse(
120 r#" 120 r#"
121 //! doc 121 /// doc
122 // non-doc 122 // non-doc
123 mod foo {} 123 mod foo {}
124 "#, 124 "#,
@@ -130,6 +130,21 @@ fn test_doc_comment_of_items() {
130} 130}
131 131
132#[test] 132#[test]
133fn test_inner_doc_comment_of_items() {
134 let file = SourceFile::parse(
135 r#"
136 //! doc
137 // non-doc
138 mod foo {}
139 "#,
140 )
141 .ok()
142 .unwrap();
143 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
144 assert!(module.doc_comment_text().is_none());
145}
146
147#[test]
133fn test_doc_comment_of_statics() { 148fn test_doc_comment_of_statics() {
134 let file = SourceFile::parse( 149 let file = SourceFile::parse(
135 r#" 150 r#"
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs
index e4e512f2e..2661c753e 100644
--- a/crates/syntax/src/ast/token_ext.rs
+++ b/crates/syntax/src/ast/token_ext.rs
@@ -14,16 +14,15 @@ use crate::{
14 14
15impl ast::Comment { 15impl ast::Comment {
16 pub fn kind(&self) -> CommentKind { 16 pub fn kind(&self) -> CommentKind {
17 kind_by_prefix(self.text()) 17 CommentKind::from_text(self.text())
18 } 18 }
19 19
20 pub fn prefix(&self) -> &'static str { 20 pub fn prefix(&self) -> &'static str {
21 for (prefix, k) in COMMENT_PREFIX_TO_KIND.iter() { 21 let &(prefix, _kind) = CommentKind::BY_PREFIX
22 if *k == self.kind() && self.text().starts_with(prefix) { 22 .iter()
23 return prefix; 23 .find(|&(prefix, kind)| self.kind() == *kind && self.text().starts_with(prefix))
24 } 24 .unwrap();
25 } 25 prefix
26 unreachable!()
27 } 26 }
28} 27}
29 28
@@ -55,29 +54,25 @@ pub enum CommentPlacement {
55 Outer, 54 Outer,
56} 55}
57 56
58const COMMENT_PREFIX_TO_KIND: &[(&str, CommentKind)] = { 57impl CommentKind {
59 use {CommentPlacement::*, CommentShape::*}; 58 const BY_PREFIX: [(&'static str, CommentKind); 8] = [
60 &[ 59 ("/**/", CommentKind { shape: CommentShape::Block, doc: None }),
61 ("////", CommentKind { shape: Line, doc: None }), 60 ("////", CommentKind { shape: CommentShape::Line, doc: None }),
62 ("///", CommentKind { shape: Line, doc: Some(Outer) }), 61 ("///", CommentKind { shape: CommentShape::Line, doc: Some(CommentPlacement::Outer) }),
63 ("//!", CommentKind { shape: Line, doc: Some(Inner) }), 62 ("//!", CommentKind { shape: CommentShape::Line, doc: Some(CommentPlacement::Inner) }),
64 ("/**", CommentKind { shape: Block, doc: Some(Outer) }), 63 ("/**", CommentKind { shape: CommentShape::Block, doc: Some(CommentPlacement::Outer) }),
65 ("/*!", CommentKind { shape: Block, doc: Some(Inner) }), 64 ("/*!", CommentKind { shape: CommentShape::Block, doc: Some(CommentPlacement::Inner) }),
66 ("//", CommentKind { shape: Line, doc: None }), 65 ("//", CommentKind { shape: CommentShape::Line, doc: None }),
67 ("/*", CommentKind { shape: Block, doc: None }), 66 ("/*", CommentKind { shape: CommentShape::Block, doc: None }),
68 ] 67 ];
69};
70 68
71fn kind_by_prefix(text: &str) -> CommentKind { 69 pub(crate) fn from_text(text: &str) -> CommentKind {
72 if text == "/**/" { 70 let &(_prefix, kind) = CommentKind::BY_PREFIX
73 return CommentKind { shape: CommentShape::Block, doc: None }; 71 .iter()
74 } 72 .find(|&(prefix, _kind)| text.starts_with(prefix))
75 for (prefix, kind) in COMMENT_PREFIX_TO_KIND.iter() { 73 .unwrap();
76 if text.starts_with(prefix) { 74 kind
77 return *kind;
78 }
79 } 75 }
80 panic!("bad comment text: {:?}", text)
81} 76}
82 77
83impl ast::Whitespace { 78impl ast::Whitespace {
diff --git a/crates/syntax/src/parsing/text_tree_sink.rs b/crates/syntax/src/parsing/text_tree_sink.rs
index c1b5f246d..997bc5d28 100644
--- a/crates/syntax/src/parsing/text_tree_sink.rs
+++ b/crates/syntax/src/parsing/text_tree_sink.rs
@@ -5,6 +5,7 @@ use std::mem;
5use parser::{ParseError, TreeSink}; 5use parser::{ParseError, TreeSink};
6 6
7use crate::{ 7use crate::{
8 ast,
8 parsing::Token, 9 parsing::Token,
9 syntax_node::GreenNode, 10 syntax_node::GreenNode,
10 SmolStr, SyntaxError, 11 SmolStr, SyntaxError,
@@ -153,24 +154,22 @@ fn n_attached_trivias<'a>(
153 154
154 while let Some((i, (kind, text))) = trivias.next() { 155 while let Some((i, (kind, text))) = trivias.next() {
155 match kind { 156 match kind {
156 WHITESPACE => { 157 WHITESPACE if text.contains("\n\n") => {
157 if text.contains("\n\n") { 158 // we check whether the next token is a doc-comment
158 // we check whether the next token is a doc-comment 159 // and skip the whitespace in this case
159 // and skip the whitespace in this case 160 if let Some((COMMENT, peek_text)) = trivias.peek().map(|(_, pair)| pair) {
160 if let Some((peek_kind, peek_text)) = 161 let comment_kind = ast::CommentKind::from_text(peek_text);
161 trivias.peek().map(|(_, pair)| pair) 162 if comment_kind.doc == Some(ast::CommentPlacement::Outer) {
162 { 163 continue;
163 if *peek_kind == COMMENT
164 && peek_text.starts_with("///")
165 && !peek_text.starts_with("////")
166 {
167 continue;
168 }
169 } 164 }
170 break;
171 } 165 }
166 break;
172 } 167 }
173 COMMENT => { 168 COMMENT => {
169 let comment_kind = ast::CommentKind::from_text(text);
170 if comment_kind.doc == Some(ast::CommentPlacement::Inner) {
171 break;
172 }
174 res = i + 1; 173 res = i + 1;
175 } 174 }
176 _ => (), 175 _ => (),
diff --git a/crates/syntax/test_data/parser/ok/0037_mod.rast b/crates/syntax/test_data/parser/ok/0037_mod.rast
index 1d5d94bde..35577272e 100644
--- a/crates/syntax/test_data/parser/ok/0037_mod.rast
+++ b/crates/syntax/test_data/parser/ok/0037_mod.rast
@@ -1,9 +1,9 @@
1[email protected] 1[email protected]
2 [email protected] "// https://github.com ..." 2 [email protected] "// https://github.com ..."
3 [email protected] "\n\n" 3 [email protected] "\n\n"
4 MODULE@62..93 4 COMMENT@62..70 "//! docs"
5 COMMENT@62..70 "//! docs" 5 WHITESPACE@70..71 "\n"
6 WHITESPACE@70..71 "\n" 6 MODULE@71..93
7 [email protected] "// non-docs" 7 [email protected] "// non-docs"
8 [email protected] "\n" 8 [email protected] "\n"
9 [email protected] "mod" 9 [email protected] "mod"
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index 780f5cb91..77d4e0ec9 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
1<!--- 1<!---
2lsp_ext.rs hash: 286f8bbac885531a 2lsp_ext.rs hash: 4f86fb54e4b2870e
3 3
4If you need to change the above hash to make the test pass, please check if you 4If you need to change the above hash to make the test pass, please check if you
5need to adjust this doc as well and ping this issue: 5need to adjust this doc as well and ping this issue:
@@ -109,30 +109,6 @@ Invoking code action at this position will yield two code actions for importing
109* Is a fixed two-level structure enough? 109* Is a fixed two-level structure enough?
110* Should we devise a general way to encode custom interaction protocols for GUI refactorings? 110* Should we devise a general way to encode custom interaction protocols for GUI refactorings?
111 111
112## Lazy assists with `ResolveCodeAction`
113
114**Issue:** https://github.com/microsoft/language-server-protocol/issues/787
115
116**Client Capability** `{ "resolveCodeAction": boolean }`
117
118If this capability is set, the assists will be computed lazily. Thus `CodeAction` returned from the server will only contain `id` but not `edit` or `command` fields. The only exclusion from the rule is the diagnostic edits.
119
120After the client got the id, it should then call `experimental/resolveCodeAction` command on the server and provide the following payload:
121
122```typescript
123interface ResolveCodeActionParams {
124 id: string;
125 codeActionParams: lc.CodeActionParams;
126}
127```
128
129As a result of the command call the client will get the respective workspace edit (`lc.WorkspaceEdit`).
130
131### Unresolved Questions
132
133* Apply smarter filtering for ids?
134* Upon `resolveCodeAction` command only call the assits which should be resolved and not all of them?
135
136## Parent Module 112## Parent Module
137 113
138**Issue:** https://github.com/microsoft/language-server-protocol/issues/1002 114**Issue:** https://github.com/microsoft/language-server-protocol/issues/1002
diff --git a/editors/code/rust.tmGrammar.json b/editors/code/rust.tmGrammar.json
index b3a10795e..cd4775d27 100644
--- a/editors/code/rust.tmGrammar.json
+++ b/editors/code/rust.tmGrammar.json
@@ -307,9 +307,14 @@
307 "block-comments": { 307 "block-comments": {
308 "patterns": [ 308 "patterns": [
309 { 309 {
310 "comment": "block comments", 310 "comment": "empty block comments",
311 "name": "comment.block.rust", 311 "name": "comment.block.rust",
312 "begin": "/\\*(?!\\*)", 312 "match": "/\\*\\*/"
313 },
314 {
315 "comment": "block documentation comments",
316 "name": "comment.block.documentation.rust",
317 "begin": "/\\*\\*",
313 "end": "\\*/", 318 "end": "\\*/",
314 "patterns": [ 319 "patterns": [
315 { 320 {
@@ -318,9 +323,9 @@
318 ] 323 ]
319 }, 324 },
320 { 325 {
321 "comment": "block documentation comments", 326 "comment": "block comments",
322 "name": "comment.block.documentation.rust", 327 "name": "comment.block.rust",
323 "begin": "/\\*\\*", 328 "begin": "/\\*(?!\\*)",
324 "end": "\\*/", 329 "end": "\\*/",
325 "patterns": [ 330 "patterns": [
326 { 331 {
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index d032b45b7..c9d032ead 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -4,6 +4,7 @@ import * as ra from '../src/lsp_ext';
4import * as Is from 'vscode-languageclient/lib/common/utils/is'; 4import * as Is from 'vscode-languageclient/lib/common/utils/is';
5import { DocumentSemanticsTokensSignature, DocumentSemanticsTokensEditsSignature, DocumentRangeSemanticTokensSignature } from 'vscode-languageclient/lib/common/semanticTokens'; 5import { DocumentSemanticsTokensSignature, DocumentSemanticsTokensEditsSignature, DocumentRangeSemanticTokensSignature } from 'vscode-languageclient/lib/common/semanticTokens';
6import { assert } from './util'; 6import { assert } from './util';
7import { WorkspaceEdit } from 'vscode';
7 8
8function renderCommand(cmd: ra.CommandLink) { 9function renderCommand(cmd: ra.CommandLink) {
9 return `[${cmd.title}](command:${cmd.command}?${encodeURIComponent(JSON.stringify(cmd.arguments))} '${cmd.tooltip!}')`; 10 return `[${cmd.title}](command:${cmd.command}?${encodeURIComponent(JSON.stringify(cmd.arguments))} '${cmd.tooltip!}')`;
@@ -75,8 +76,8 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient
75 return Promise.resolve(null); 76 return Promise.resolve(null);
76 }); 77 });
77 }, 78 },
78 // Using custom handling of CodeActions where each code action is resolved lazily 79 // Using custom handling of CodeActions to support action groups and snippet edits.
79 // That's why we are not waiting for any command or edits 80 // Note that this means we have to re-implement lazy edit resolving ourselves as well.
80 async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) { 81 async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) {
81 const params: lc.CodeActionParams = { 82 const params: lc.CodeActionParams = {
82 textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), 83 textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
@@ -99,16 +100,15 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient
99 const kind = client.protocol2CodeConverter.asCodeActionKind((item as any).kind); 100 const kind = client.protocol2CodeConverter.asCodeActionKind((item as any).kind);
100 const action = new vscode.CodeAction(item.title, kind); 101 const action = new vscode.CodeAction(item.title, kind);
101 const group = (item as any).group; 102 const group = (item as any).group;
102 const id = (item as any).id;
103 const resolveParams: ra.ResolveCodeActionParams = {
104 id: id,
105 codeActionParams: params
106 };
107 action.command = { 103 action.command = {
108 command: "rust-analyzer.resolveCodeAction", 104 command: "rust-analyzer.resolveCodeAction",
109 title: item.title, 105 title: item.title,
110 arguments: [resolveParams], 106 arguments: [item],
111 }; 107 };
108
109 // Set a dummy edit, so that VS Code doesn't try to resolve this.
110 action.edit = new WorkspaceEdit();
111
112 if (group) { 112 if (group) {
113 let entry = groups.get(group); 113 let entry = groups.get(group);
114 if (!entry) { 114 if (!entry) {
@@ -134,6 +134,10 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient
134 return { label: item.title, arguments: item.command!!.arguments!![0] }; 134 return { label: item.title, arguments: item.command!!.arguments!![0] };
135 })], 135 })],
136 }; 136 };
137
138 // Set a dummy edit, so that VS Code doesn't try to resolve this.
139 action.edit = new WorkspaceEdit();
140
137 result[index] = action; 141 result[index] = action;
138 } 142 }
139 } 143 }
@@ -164,7 +168,6 @@ class ExperimentalFeatures implements lc.StaticFeature {
164 const caps: any = capabilities.experimental ?? {}; 168 const caps: any = capabilities.experimental ?? {};
165 caps.snippetTextEdit = true; 169 caps.snippetTextEdit = true;
166 caps.codeActionGroup = true; 170 caps.codeActionGroup = true;
167 caps.resolveCodeAction = true;
168 caps.hoverActions = true; 171 caps.hoverActions = true;
169 caps.statusNotification = true; 172 caps.statusNotification = true;
170 capabilities.experimental = caps; 173 capabilities.experimental = caps;
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index 22509e874..cf34622c3 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -395,7 +395,7 @@ export function showReferences(ctx: Ctx): Cmd {
395} 395}
396 396
397export function applyActionGroup(_ctx: Ctx): Cmd { 397export function applyActionGroup(_ctx: Ctx): Cmd {
398 return async (actions: { label: string; arguments: ra.ResolveCodeActionParams }[]) => { 398 return async (actions: { label: string; arguments: lc.CodeAction }[]) => {
399 const selectedAction = await vscode.window.showQuickPick(actions); 399 const selectedAction = await vscode.window.showQuickPick(actions);
400 if (!selectedAction) return; 400 if (!selectedAction) return;
401 vscode.commands.executeCommand( 401 vscode.commands.executeCommand(
@@ -442,12 +442,13 @@ export function openDocs(ctx: Ctx): Cmd {
442 442
443export function resolveCodeAction(ctx: Ctx): Cmd { 443export function resolveCodeAction(ctx: Ctx): Cmd {
444 const client = ctx.client; 444 const client = ctx.client;
445 return async (params: ra.ResolveCodeActionParams) => { 445 return async (params: lc.CodeAction) => {
446 const item: lc.WorkspaceEdit = await client.sendRequest(ra.resolveCodeAction, params); 446 params.command = undefined;
447 if (!item) { 447 const item = await client.sendRequest(lc.CodeActionResolveRequest.type, params);
448 if (!item.edit) {
448 return; 449 return;
449 } 450 }
450 const edit = client.protocol2CodeConverter.asWorkspaceEdit(item); 451 const edit = client.protocol2CodeConverter.asWorkspaceEdit(item.edit);
451 await applySnippetWorkspaceEdit(edit); 452 await applySnippetWorkspaceEdit(edit);
452 }; 453 };
453} 454}
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts
index fc8e120b3..d320c3cd7 100644
--- a/editors/code/src/lsp_ext.ts
+++ b/editors/code/src/lsp_ext.ts
@@ -43,12 +43,6 @@ export const matchingBrace = new lc.RequestType<MatchingBraceParams, lc.Position
43 43
44export const parentModule = new lc.RequestType<lc.TextDocumentPositionParams, lc.LocationLink[], void>("experimental/parentModule"); 44export const parentModule = new lc.RequestType<lc.TextDocumentPositionParams, lc.LocationLink[], void>("experimental/parentModule");
45 45
46export interface ResolveCodeActionParams {
47 id: string;
48 codeActionParams: lc.CodeActionParams;
49}
50export const resolveCodeAction = new lc.RequestType<ResolveCodeActionParams, lc.WorkspaceEdit, unknown>('experimental/resolveCodeAction');
51
52export interface JoinLinesParams { 46export interface JoinLinesParams {
53 textDocument: lc.TextDocumentIdentifier; 47 textDocument: lc.TextDocumentIdentifier;
54 ranges: lc.Range[]; 48 ranges: lc.Range[];