aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-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
34 files changed, 644 insertions, 206 deletions
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"