diff options
47 files changed, 1767 insertions, 1400 deletions
diff --git a/Cargo.lock b/Cargo.lock index 41855f22e..eefa9a676 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -89,9 +89,9 @@ dependencies = [ | |||
89 | 89 | ||
90 | [[package]] | 90 | [[package]] |
91 | name = "cargo_metadata" | 91 | name = "cargo_metadata" |
92 | version = "0.9.1" | 92 | version = "0.10.0" |
93 | source = "registry+https://github.com/rust-lang/crates.io-index" | 93 | source = "registry+https://github.com/rust-lang/crates.io-index" |
94 | checksum = "46e3374c604fb39d1a2f35ed5e4a4e30e60d01fab49446e08f1b3e9a90aef202" | 94 | checksum = "b8de60b887edf6d74370fc8eb177040da4847d971d6234c7b13a6da324ef0caf" |
95 | dependencies = [ | 95 | dependencies = [ |
96 | "semver", | 96 | "semver", |
97 | "serde", | 97 | "serde", |
@@ -1514,18 +1514,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" | |||
1514 | 1514 | ||
1515 | [[package]] | 1515 | [[package]] |
1516 | name = "serde" | 1516 | name = "serde" |
1517 | version = "1.0.106" | 1517 | version = "1.0.107" |
1518 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1518 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1519 | checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" | 1519 | checksum = "eba7550f2cdf88ffc23ab0f1607133486c390a8c0f89b57e589b9654ee15e04d" |
1520 | dependencies = [ | 1520 | dependencies = [ |
1521 | "serde_derive", | 1521 | "serde_derive", |
1522 | ] | 1522 | ] |
1523 | 1523 | ||
1524 | [[package]] | 1524 | [[package]] |
1525 | name = "serde_derive" | 1525 | name = "serde_derive" |
1526 | version = "1.0.106" | 1526 | version = "1.0.107" |
1527 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1527 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1528 | checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" | 1528 | checksum = "10be45e22e5597d4b88afcc71f9d7bfadcd604bf0c78a3ab4582b8d2b37f39f3" |
1529 | dependencies = [ | 1529 | dependencies = [ |
1530 | "proc-macro2", | 1530 | "proc-macro2", |
1531 | "quote", | 1531 | "quote", |
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index 3085c4330..a680f752b 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs | |||
@@ -49,7 +49,7 @@ use crate::{Assist, AssistId, GroupLabel, ResolvedAssist}; | |||
49 | /// easier to just compute the edit eagerly :-) | 49 | /// easier to just compute the edit eagerly :-) |
50 | pub(crate) struct AssistContext<'a> { | 50 | pub(crate) struct AssistContext<'a> { |
51 | pub(crate) sema: Semantics<'a, RootDatabase>, | 51 | pub(crate) sema: Semantics<'a, RootDatabase>, |
52 | pub(super) db: &'a RootDatabase, | 52 | pub(crate) db: &'a RootDatabase, |
53 | pub(crate) frange: FileRange, | 53 | pub(crate) frange: FileRange, |
54 | source_file: SourceFile, | 54 | source_file: SourceFile, |
55 | } | 55 | } |
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs index 55409e501..7ced00626 100644 --- a/crates/ra_assists/src/handlers/add_explicit_type.rs +++ b/crates/ra_assists/src/handlers/add_explicit_type.rs | |||
@@ -23,6 +23,7 @@ use crate::{AssistContext, AssistId, Assists}; | |||
23 | // ``` | 23 | // ``` |
24 | pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 24 | pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
25 | let stmt = ctx.find_node_at_offset::<LetStmt>()?; | 25 | let stmt = ctx.find_node_at_offset::<LetStmt>()?; |
26 | let module = ctx.sema.scope(stmt.syntax()).module()?; | ||
26 | let expr = stmt.initializer()?; | 27 | let expr = stmt.initializer()?; |
27 | let pat = stmt.pat()?; | 28 | let pat = stmt.pat()?; |
28 | // Must be a binding | 29 | // Must be a binding |
@@ -57,17 +58,17 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio | |||
57 | return None; | 58 | return None; |
58 | } | 59 | } |
59 | 60 | ||
60 | let db = ctx.db; | 61 | let inferred_type = ty.display_source_code(ctx.db, module.into()).ok()?; |
61 | let new_type_string = ty.display_truncated(db, None).to_string(); | ||
62 | acc.add( | 62 | acc.add( |
63 | AssistId("add_explicit_type"), | 63 | AssistId("add_explicit_type"), |
64 | format!("Insert explicit type '{}'", new_type_string), | 64 | format!("Insert explicit type '{}'", inferred_type), |
65 | pat_range, | 65 | pat_range, |
66 | |edit| { | 66 | |builder| match ascribed_ty { |
67 | if let Some(ascribed_ty) = ascribed_ty { | 67 | Some(ascribed_ty) => { |
68 | edit.replace(ascribed_ty.syntax().text_range(), new_type_string); | 68 | builder.replace(ascribed_ty.syntax().text_range(), inferred_type); |
69 | } else { | 69 | } |
70 | edit.insert(name_range.end(), format!(": {}", new_type_string)); | 70 | None => { |
71 | builder.insert(name_range.end(), format!(": {}", inferred_type)); | ||
71 | } | 72 | } |
72 | }, | 73 | }, |
73 | ) | 74 | ) |
diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs index 275184e24..6a49b7dbd 100644 --- a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs +++ b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs | |||
@@ -1,8 +1,5 @@ | |||
1 | use ra_ide_db::RootDatabase; | 1 | use ra_ide_db::RootDatabase; |
2 | use ra_syntax::{ | 2 | use ra_syntax::ast::{self, AstNode, NameOwner}; |
3 | ast::{self, AstNode, NameOwner}, | ||
4 | TextSize, | ||
5 | }; | ||
6 | use stdx::format_to; | 3 | use stdx::format_to; |
7 | use test_utils::tested_by; | 4 | use test_utils::tested_by; |
8 | 5 | ||
@@ -69,7 +66,6 @@ impl From<{0}> for {1} {{ | |||
69 | variant_name | 66 | variant_name |
70 | ); | 67 | ); |
71 | edit.insert(start_offset, buf); | 68 | edit.insert(start_offset, buf); |
72 | edit.set_cursor(start_offset + TextSize::of("\n\n")); | ||
73 | }, | 69 | }, |
74 | ) | 70 | ) |
75 | } | 71 | } |
@@ -97,19 +93,20 @@ fn existing_from_impl( | |||
97 | 93 | ||
98 | #[cfg(test)] | 94 | #[cfg(test)] |
99 | mod tests { | 95 | mod tests { |
100 | use super::*; | 96 | use test_utils::covers; |
101 | 97 | ||
102 | use crate::tests::{check_assist, check_assist_not_applicable}; | 98 | use crate::tests::{check_assist, check_assist_not_applicable}; |
103 | use test_utils::covers; | 99 | |
100 | use super::*; | ||
104 | 101 | ||
105 | #[test] | 102 | #[test] |
106 | fn test_add_from_impl_for_enum() { | 103 | fn test_add_from_impl_for_enum() { |
107 | check_assist( | 104 | check_assist( |
108 | add_from_impl_for_enum, | 105 | add_from_impl_for_enum, |
109 | "enum A { <|>One(u32) }", | 106 | "enum A { <|>One(u32) }", |
110 | r#"enum A { One(u32) } | 107 | r#"enum A { <|>One(u32) } |
111 | 108 | ||
112 | <|>impl From<u32> for A { | 109 | impl From<u32> for A { |
113 | fn from(v: u32) -> Self { | 110 | fn from(v: u32) -> Self { |
114 | A::One(v) | 111 | A::One(v) |
115 | } | 112 | } |
@@ -121,10 +118,10 @@ mod tests { | |||
121 | fn test_add_from_impl_for_enum_complicated_path() { | 118 | fn test_add_from_impl_for_enum_complicated_path() { |
122 | check_assist( | 119 | check_assist( |
123 | add_from_impl_for_enum, | 120 | add_from_impl_for_enum, |
124 | "enum A { <|>One(foo::bar::baz::Boo) }", | 121 | r#"enum A { <|>One(foo::bar::baz::Boo) }"#, |
125 | r#"enum A { One(foo::bar::baz::Boo) } | 122 | r#"enum A { <|>One(foo::bar::baz::Boo) } |
126 | 123 | ||
127 | <|>impl From<foo::bar::baz::Boo> for A { | 124 | impl From<foo::bar::baz::Boo> for A { |
128 | fn from(v: foo::bar::baz::Boo) -> Self { | 125 | fn from(v: foo::bar::baz::Boo) -> Self { |
129 | A::One(v) | 126 | A::One(v) |
130 | } | 127 | } |
@@ -184,9 +181,9 @@ impl From<String> for A { | |||
184 | pub trait From<T> { | 181 | pub trait From<T> { |
185 | fn from(T) -> Self; | 182 | fn from(T) -> Self; |
186 | }"#, | 183 | }"#, |
187 | r#"enum A { One(u32), Two(String), } | 184 | r#"enum A { <|>One(u32), Two(String), } |
188 | 185 | ||
189 | <|>impl From<u32> for A { | 186 | impl From<u32> for A { |
190 | fn from(v: u32) -> Self { | 187 | fn from(v: u32) -> Self { |
191 | A::One(v) | 188 | A::One(v) |
192 | } | 189 | } |
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs index 6b5616aa9..de016ae4e 100644 --- a/crates/ra_assists/src/handlers/add_function.rs +++ b/crates/ra_assists/src/handlers/add_function.rs | |||
@@ -1,7 +1,11 @@ | |||
1 | use hir::HirDisplay; | 1 | use hir::HirDisplay; |
2 | use ra_db::FileId; | 2 | use ra_db::FileId; |
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | ast::{self, edit::IndentLevel, ArgListOwner, AstNode, ModuleItemOwner}, | 4 | ast::{ |
5 | self, | ||
6 | edit::{AstNodeEdit, IndentLevel}, | ||
7 | ArgListOwner, AstNode, ModuleItemOwner, | ||
8 | }, | ||
5 | SyntaxKind, SyntaxNode, TextSize, | 9 | SyntaxKind, SyntaxNode, TextSize, |
6 | }; | 10 | }; |
7 | use rustc_hash::{FxHashMap, FxHashSet}; | 11 | use rustc_hash::{FxHashMap, FxHashSet}; |
@@ -43,16 +47,12 @@ pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
43 | return None; | 47 | return None; |
44 | } | 48 | } |
45 | 49 | ||
46 | let target_module = if let Some(qualifier) = path.qualifier() { | 50 | let target_module = match path.qualifier() { |
47 | if let Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) = | 51 | Some(qualifier) => match ctx.sema.resolve_path(&qualifier) { |
48 | ctx.sema.resolve_path(&qualifier) | 52 | Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) => Some(module), |
49 | { | 53 | _ => return None, |
50 | Some(module.definition_source(ctx.sema.db)) | 54 | }, |
51 | } else { | 55 | None => None, |
52 | return None; | ||
53 | } | ||
54 | } else { | ||
55 | None | ||
56 | }; | 56 | }; |
57 | 57 | ||
58 | let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; | 58 | let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; |
@@ -83,25 +83,29 @@ struct FunctionBuilder { | |||
83 | } | 83 | } |
84 | 84 | ||
85 | impl FunctionBuilder { | 85 | impl FunctionBuilder { |
86 | /// Prepares a generated function that matches `call` in `generate_in` | 86 | /// Prepares a generated function that matches `call`. |
87 | /// (or as close to `call` as possible, if `generate_in` is `None`) | 87 | /// The function is generated in `target_module` or next to `call` |
88 | fn from_call( | 88 | fn from_call( |
89 | ctx: &AssistContext, | 89 | ctx: &AssistContext, |
90 | call: &ast::CallExpr, | 90 | call: &ast::CallExpr, |
91 | path: &ast::Path, | 91 | path: &ast::Path, |
92 | target_module: Option<hir::InFile<hir::ModuleSource>>, | 92 | target_module: Option<hir::Module>, |
93 | ) -> Option<Self> { | 93 | ) -> Option<Self> { |
94 | let needs_pub = target_module.is_some(); | ||
95 | let mut file = ctx.frange.file_id; | 94 | let mut file = ctx.frange.file_id; |
96 | let target = if let Some(target_module) = target_module { | 95 | let target = match &target_module { |
97 | let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, target_module)?; | 96 | Some(target_module) => { |
98 | file = in_file; | 97 | let module_source = target_module.definition_source(ctx.db); |
99 | target | 98 | let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, &module_source)?; |
100 | } else { | 99 | file = in_file; |
101 | next_space_for_fn_after_call_site(&call)? | 100 | target |
101 | } | ||
102 | None => next_space_for_fn_after_call_site(&call)?, | ||
102 | }; | 103 | }; |
104 | let needs_pub = target_module.is_some(); | ||
105 | let target_module = target_module.or_else(|| ctx.sema.scope(target.syntax()).module())?; | ||
103 | let fn_name = fn_name(&path)?; | 106 | let fn_name = fn_name(&path)?; |
104 | let (type_params, params) = fn_args(ctx, &call)?; | 107 | let (type_params, params) = fn_args(ctx, target_module, &call)?; |
108 | |||
105 | Some(Self { target, fn_name, type_params, params, file, needs_pub }) | 109 | Some(Self { target, fn_name, type_params, params, file, needs_pub }) |
106 | } | 110 | } |
107 | 111 | ||
@@ -116,17 +120,16 @@ impl FunctionBuilder { | |||
116 | let (fn_def, insert_offset) = match self.target { | 120 | let (fn_def, insert_offset) = match self.target { |
117 | GeneratedFunctionTarget::BehindItem(it) => { | 121 | GeneratedFunctionTarget::BehindItem(it) => { |
118 | let with_leading_blank_line = ast::make::add_leading_newlines(2, fn_def); | 122 | let with_leading_blank_line = ast::make::add_leading_newlines(2, fn_def); |
119 | let indented = IndentLevel::from_node(&it).increase_indent(with_leading_blank_line); | 123 | let indented = with_leading_blank_line.indent(IndentLevel::from_node(&it)); |
120 | (indented, it.text_range().end()) | 124 | (indented, it.text_range().end()) |
121 | } | 125 | } |
122 | GeneratedFunctionTarget::InEmptyItemList(it) => { | 126 | GeneratedFunctionTarget::InEmptyItemList(it) => { |
123 | let indent_once = IndentLevel(1); | 127 | let indent_once = IndentLevel(1); |
124 | let indent = IndentLevel::from_node(it.syntax()); | 128 | let indent = IndentLevel::from_node(it.syntax()); |
125 | |||
126 | let fn_def = ast::make::add_leading_newlines(1, fn_def); | 129 | let fn_def = ast::make::add_leading_newlines(1, fn_def); |
127 | let fn_def = indent_once.increase_indent(fn_def); | 130 | let fn_def = fn_def.indent(indent_once); |
128 | let fn_def = ast::make::add_trailing_newlines(1, fn_def); | 131 | let fn_def = ast::make::add_trailing_newlines(1, fn_def); |
129 | let fn_def = indent.increase_indent(fn_def); | 132 | let fn_def = fn_def.indent(indent); |
130 | (fn_def, it.syntax().text_range().start() + TextSize::of('{')) | 133 | (fn_def, it.syntax().text_range().start() + TextSize::of('{')) |
131 | } | 134 | } |
132 | }; | 135 | }; |
@@ -144,6 +147,15 @@ enum GeneratedFunctionTarget { | |||
144 | InEmptyItemList(ast::ItemList), | 147 | InEmptyItemList(ast::ItemList), |
145 | } | 148 | } |
146 | 149 | ||
150 | impl GeneratedFunctionTarget { | ||
151 | fn syntax(&self) -> &SyntaxNode { | ||
152 | match self { | ||
153 | GeneratedFunctionTarget::BehindItem(it) => it, | ||
154 | GeneratedFunctionTarget::InEmptyItemList(it) => it.syntax(), | ||
155 | } | ||
156 | } | ||
157 | } | ||
158 | |||
147 | fn fn_name(call: &ast::Path) -> Option<ast::Name> { | 159 | fn fn_name(call: &ast::Path) -> Option<ast::Name> { |
148 | let name = call.segment()?.syntax().to_string(); | 160 | let name = call.segment()?.syntax().to_string(); |
149 | Some(ast::make::name(&name)) | 161 | Some(ast::make::name(&name)) |
@@ -152,17 +164,17 @@ fn fn_name(call: &ast::Path) -> Option<ast::Name> { | |||
152 | /// Computes the type variables and arguments required for the generated function | 164 | /// Computes the type variables and arguments required for the generated function |
153 | fn fn_args( | 165 | fn fn_args( |
154 | ctx: &AssistContext, | 166 | ctx: &AssistContext, |
167 | target_module: hir::Module, | ||
155 | call: &ast::CallExpr, | 168 | call: &ast::CallExpr, |
156 | ) -> Option<(Option<ast::TypeParamList>, ast::ParamList)> { | 169 | ) -> Option<(Option<ast::TypeParamList>, ast::ParamList)> { |
157 | let mut arg_names = Vec::new(); | 170 | let mut arg_names = Vec::new(); |
158 | let mut arg_types = Vec::new(); | 171 | let mut arg_types = Vec::new(); |
159 | for arg in call.arg_list()?.args() { | 172 | for arg in call.arg_list()?.args() { |
160 | let arg_name = match fn_arg_name(&arg) { | 173 | arg_names.push(match fn_arg_name(&arg) { |
161 | Some(name) => name, | 174 | Some(name) => name, |
162 | None => String::from("arg"), | 175 | None => String::from("arg"), |
163 | }; | 176 | }); |
164 | arg_names.push(arg_name); | 177 | arg_types.push(match fn_arg_type(ctx, target_module, &arg) { |
165 | arg_types.push(match fn_arg_type(ctx, &arg) { | ||
166 | Some(ty) => ty, | 178 | Some(ty) => ty, |
167 | None => String::from("()"), | 179 | None => String::from("()"), |
168 | }); | 180 | }); |
@@ -218,12 +230,21 @@ fn fn_arg_name(fn_arg: &ast::Expr) -> Option<String> { | |||
218 | } | 230 | } |
219 | } | 231 | } |
220 | 232 | ||
221 | fn fn_arg_type(ctx: &AssistContext, fn_arg: &ast::Expr) -> Option<String> { | 233 | fn fn_arg_type( |
234 | ctx: &AssistContext, | ||
235 | target_module: hir::Module, | ||
236 | fn_arg: &ast::Expr, | ||
237 | ) -> Option<String> { | ||
222 | let ty = ctx.sema.type_of_expr(fn_arg)?; | 238 | let ty = ctx.sema.type_of_expr(fn_arg)?; |
223 | if ty.is_unknown() { | 239 | if ty.is_unknown() { |
224 | return None; | 240 | return None; |
225 | } | 241 | } |
226 | Some(ty.display(ctx.sema.db).to_string()) | 242 | |
243 | if let Ok(rendered) = ty.display_source_code(ctx.db, target_module.into()) { | ||
244 | Some(rendered) | ||
245 | } else { | ||
246 | None | ||
247 | } | ||
227 | } | 248 | } |
228 | 249 | ||
229 | /// Returns the position inside the current mod or file | 250 | /// Returns the position inside the current mod or file |
@@ -252,10 +273,10 @@ fn next_space_for_fn_after_call_site(expr: &ast::CallExpr) -> Option<GeneratedFu | |||
252 | 273 | ||
253 | fn next_space_for_fn_in_module( | 274 | fn next_space_for_fn_in_module( |
254 | db: &dyn hir::db::AstDatabase, | 275 | db: &dyn hir::db::AstDatabase, |
255 | module: hir::InFile<hir::ModuleSource>, | 276 | module_source: &hir::InFile<hir::ModuleSource>, |
256 | ) -> Option<(FileId, GeneratedFunctionTarget)> { | 277 | ) -> Option<(FileId, GeneratedFunctionTarget)> { |
257 | let file = module.file_id.original_file(db); | 278 | let file = module_source.file_id.original_file(db); |
258 | let assist_item = match module.value { | 279 | let assist_item = match &module_source.value { |
259 | hir::ModuleSource::SourceFile(it) => { | 280 | hir::ModuleSource::SourceFile(it) => { |
260 | if let Some(last_item) = it.items().last() { | 281 | if let Some(last_item) = it.items().last() { |
261 | GeneratedFunctionTarget::BehindItem(last_item.syntax().clone()) | 282 | GeneratedFunctionTarget::BehindItem(last_item.syntax().clone()) |
@@ -599,8 +620,33 @@ fn bar(foo: impl Foo) { | |||
599 | } | 620 | } |
600 | 621 | ||
601 | #[test] | 622 | #[test] |
602 | #[ignore] | 623 | fn borrowed_arg() { |
603 | // FIXME print paths properly to make this test pass | 624 | check_assist( |
625 | add_function, | ||
626 | r" | ||
627 | struct Baz; | ||
628 | fn baz() -> Baz { todo!() } | ||
629 | |||
630 | fn foo() { | ||
631 | bar<|>(&baz()) | ||
632 | } | ||
633 | ", | ||
634 | r" | ||
635 | struct Baz; | ||
636 | fn baz() -> Baz { todo!() } | ||
637 | |||
638 | fn foo() { | ||
639 | bar(&baz()) | ||
640 | } | ||
641 | |||
642 | fn bar(baz: &Baz) { | ||
643 | <|>todo!() | ||
644 | } | ||
645 | ", | ||
646 | ) | ||
647 | } | ||
648 | |||
649 | #[test] | ||
604 | fn add_function_with_qualified_path_arg() { | 650 | fn add_function_with_qualified_path_arg() { |
605 | check_assist( | 651 | check_assist( |
606 | add_function, | 652 | add_function, |
@@ -609,10 +655,8 @@ mod Baz { | |||
609 | pub struct Bof; | 655 | pub struct Bof; |
610 | pub fn baz() -> Bof { Bof } | 656 | pub fn baz() -> Bof { Bof } |
611 | } | 657 | } |
612 | mod Foo { | 658 | fn foo() { |
613 | fn foo() { | 659 | <|>bar(Baz::baz()) |
614 | <|>bar(super::Baz::baz()) | ||
615 | } | ||
616 | } | 660 | } |
617 | ", | 661 | ", |
618 | r" | 662 | r" |
@@ -620,14 +664,12 @@ mod Baz { | |||
620 | pub struct Bof; | 664 | pub struct Bof; |
621 | pub fn baz() -> Bof { Bof } | 665 | pub fn baz() -> Bof { Bof } |
622 | } | 666 | } |
623 | mod Foo { | 667 | fn foo() { |
624 | fn foo() { | 668 | bar(Baz::baz()) |
625 | bar(super::Baz::baz()) | 669 | } |
626 | } | ||
627 | 670 | ||
628 | fn bar(baz: super::Baz::Bof) { | 671 | fn bar(baz: Baz::Bof) { |
629 | <|>todo!() | 672 | <|>todo!() |
630 | } | ||
631 | } | 673 | } |
632 | ", | 674 | ", |
633 | ) | 675 | ) |
@@ -809,6 +851,40 @@ fn foo() { | |||
809 | } | 851 | } |
810 | 852 | ||
811 | #[test] | 853 | #[test] |
854 | #[ignore] | ||
855 | // Ignored until local imports are supported. | ||
856 | // See https://github.com/rust-analyzer/rust-analyzer/issues/1165 | ||
857 | fn qualified_path_uses_correct_scope() { | ||
858 | check_assist( | ||
859 | add_function, | ||
860 | " | ||
861 | mod foo { | ||
862 | pub struct Foo; | ||
863 | } | ||
864 | fn bar() { | ||
865 | use foo::Foo; | ||
866 | let foo = Foo; | ||
867 | baz<|>(foo) | ||
868 | } | ||
869 | ", | ||
870 | " | ||
871 | mod foo { | ||
872 | pub struct Foo; | ||
873 | } | ||
874 | fn bar() { | ||
875 | use foo::Foo; | ||
876 | let foo = Foo; | ||
877 | baz(foo) | ||
878 | } | ||
879 | |||
880 | fn baz(foo: foo::Foo) { | ||
881 | <|>todo!() | ||
882 | } | ||
883 | ", | ||
884 | ) | ||
885 | } | ||
886 | |||
887 | #[test] | ||
812 | fn add_function_in_module_containing_other_items() { | 888 | fn add_function_in_module_containing_other_items() { |
813 | check_assist( | 889 | check_assist( |
814 | add_function, | 890 | add_function, |
@@ -920,21 +996,6 @@ fn bar(baz: ()) {} | |||
920 | } | 996 | } |
921 | 997 | ||
922 | #[test] | 998 | #[test] |
923 | fn add_function_not_applicable_if_function_path_not_singleton() { | ||
924 | // In the future this assist could be extended to generate functions | ||
925 | // if the path is in the same crate (or even the same workspace). | ||
926 | // For the beginning, I think this is fine. | ||
927 | check_assist_not_applicable( | ||
928 | add_function, | ||
929 | r" | ||
930 | fn foo() { | ||
931 | other_crate::bar<|>(); | ||
932 | } | ||
933 | ", | ||
934 | ) | ||
935 | } | ||
936 | |||
937 | #[test] | ||
938 | #[ignore] | 999 | #[ignore] |
939 | fn create_method_with_no_args() { | 1000 | fn create_method_with_no_args() { |
940 | check_assist( | 1001 | check_assist( |
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs index 3482a75bf..c1ce87914 100644 --- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs | |||
@@ -2,7 +2,7 @@ use hir::HasSource; | |||
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | ast::{ | 3 | ast::{ |
4 | self, | 4 | self, |
5 | edit::{self, IndentLevel}, | 5 | edit::{self, AstNodeEdit, IndentLevel}, |
6 | make, AstNode, NameOwner, | 6 | make, AstNode, NameOwner, |
7 | }, | 7 | }, |
8 | SmolStr, | 8 | SmolStr, |
@@ -176,8 +176,7 @@ fn add_body(fn_def: ast::FnDef) -> ast::FnDef { | |||
176 | if fn_def.body().is_some() { | 176 | if fn_def.body().is_some() { |
177 | return fn_def; | 177 | return fn_def; |
178 | } | 178 | } |
179 | let body = make::block_expr(None, Some(make::expr_todo())); | 179 | let body = make::block_expr(None, Some(make::expr_todo())).indent(IndentLevel(1)); |
180 | let body = IndentLevel(1).increase_indent(body); | ||
181 | fn_def.with_body(body) | 180 | fn_def.with_body(body) |
182 | } | 181 | } |
183 | 182 | ||
diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs index 810784ad5..66b296081 100644 --- a/crates/ra_assists/src/handlers/early_return.rs +++ b/crates/ra_assists/src/handlers/early_return.rs | |||
@@ -2,7 +2,11 @@ use std::{iter::once, ops::RangeInclusive}; | |||
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | algo::replace_children, | 4 | algo::replace_children, |
5 | ast::{self, edit::IndentLevel, make}, | 5 | ast::{ |
6 | self, | ||
7 | edit::{AstNodeEdit, IndentLevel}, | ||
8 | make, | ||
9 | }, | ||
6 | AstNode, | 10 | AstNode, |
7 | SyntaxKind::{FN_DEF, LOOP_EXPR, L_CURLY, R_CURLY, WHILE_EXPR, WHITESPACE}, | 11 | SyntaxKind::{FN_DEF, LOOP_EXPR, L_CURLY, R_CURLY, WHILE_EXPR, WHITESPACE}, |
8 | SyntaxNode, | 12 | SyntaxNode, |
@@ -105,8 +109,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) | |||
105 | let then_branch = | 109 | let then_branch = |
106 | make::block_expr(once(make::expr_stmt(early_expression).into()), None); | 110 | make::block_expr(once(make::expr_stmt(early_expression).into()), None); |
107 | let cond = invert_boolean_expression(cond_expr); | 111 | let cond = invert_boolean_expression(cond_expr); |
108 | let e = make::expr_if(make::condition(cond, None), then_branch); | 112 | make::expr_if(make::condition(cond, None), then_branch).indent(if_indent_level) |
109 | if_indent_level.increase_indent(e) | ||
110 | }; | 113 | }; |
111 | replace(new_expr.syntax(), &then_block, &parent_block, &if_expr) | 114 | replace(new_expr.syntax(), &then_block, &parent_block, &if_expr) |
112 | } | 115 | } |
@@ -140,7 +143,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) | |||
140 | make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(), | 143 | make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(), |
141 | Some(match_expr), | 144 | Some(match_expr), |
142 | ); | 145 | ); |
143 | let let_stmt = if_indent_level.increase_indent(let_stmt); | 146 | let let_stmt = let_stmt.indent(if_indent_level); |
144 | replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr) | 147 | replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr) |
145 | } | 148 | } |
146 | }; | 149 | }; |
@@ -153,7 +156,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) | |||
153 | parent_block: &ast::BlockExpr, | 156 | parent_block: &ast::BlockExpr, |
154 | if_expr: &ast::IfExpr, | 157 | if_expr: &ast::IfExpr, |
155 | ) -> SyntaxNode { | 158 | ) -> SyntaxNode { |
156 | let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone()); | 159 | let then_block_items = then_block.dedent(IndentLevel::from(1)); |
157 | let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); | 160 | let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); |
158 | let end_of_then = | 161 | let end_of_then = |
159 | if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { | 162 | if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { |
diff --git a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs index a59a06efa..65f5fc6ab 100644 --- a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs | |||
@@ -1,6 +1,10 @@ | |||
1 | use ra_fmt::unwrap_trivial_block; | 1 | use ra_fmt::unwrap_trivial_block; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | ast::{self, edit::IndentLevel, make}, | 3 | ast::{ |
4 | self, | ||
5 | edit::{AstNodeEdit, IndentLevel}, | ||
6 | make, | ||
7 | }, | ||
4 | AstNode, | 8 | AstNode, |
5 | }; | 9 | }; |
6 | 10 | ||
@@ -61,10 +65,9 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext) | |||
61 | make::match_arm(vec![pattern], else_expr) | 65 | make::match_arm(vec![pattern], else_expr) |
62 | }; | 66 | }; |
63 | make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm])) | 67 | make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm])) |
68 | .indent(IndentLevel::from_node(if_expr.syntax())) | ||
64 | }; | 69 | }; |
65 | 70 | ||
66 | let match_expr = IndentLevel::from_node(if_expr.syntax()).increase_indent(match_expr); | ||
67 | |||
68 | edit.set_cursor(if_expr.syntax().text_range().start()); | 71 | edit.set_cursor(if_expr.syntax().text_range().start()); |
69 | edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr); | 72 | edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr); |
70 | }) | 73 | }) |
diff --git a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs index d3f214591..482957dc6 100644 --- a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs +++ b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs | |||
@@ -53,8 +53,7 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) -> | |||
53 | ) | 53 | ) |
54 | .into(), | 54 | .into(), |
55 | }; | 55 | }; |
56 | let block = | 56 | let block = make::block_expr(None, None).indent(IndentLevel::from_node(let_stmt.syntax())); |
57 | IndentLevel::from_node(let_stmt.syntax()).increase_indent(make::block_expr(None, None)); | ||
58 | let if_ = make::expr_if(make::condition(init, Some(with_placeholder)), block); | 57 | let if_ = make::expr_if(make::condition(init, Some(with_placeholder)), block); |
59 | let stmt = make::expr_stmt(if_); | 58 | let stmt = make::expr_stmt(if_); |
60 | 59 | ||
diff --git a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs index a46998b8e..c4b56f6e9 100644 --- a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs +++ b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs | |||
@@ -1,7 +1,11 @@ | |||
1 | use std::iter; | 1 | use std::iter; |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | ast::{self, edit::IndentLevel, make}, | 4 | ast::{ |
5 | self, | ||
6 | edit::{AstNodeEdit, IndentLevel}, | ||
7 | make, | ||
8 | }, | ||
5 | AstNode, | 9 | AstNode, |
6 | }; | 10 | }; |
7 | 11 | ||
@@ -51,8 +55,8 @@ pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext) | |||
51 | let err_arm = make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call); | 55 | let err_arm = make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call); |
52 | 56 | ||
53 | let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); | 57 | let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); |
54 | let match_expr = make::expr_match(caller.clone(), match_arm_list); | 58 | let match_expr = make::expr_match(caller.clone(), match_arm_list) |
55 | let match_expr = IndentLevel::from_node(method_call.syntax()).increase_indent(match_expr); | 59 | .indent(IndentLevel::from_node(method_call.syntax())); |
56 | 60 | ||
57 | edit.set_cursor(caller.syntax().text_range().start()); | 61 | edit.set_cursor(caller.syntax().text_range().start()); |
58 | edit.replace_ast::<ast::Expr>(method_call.into(), match_expr); | 62 | edit.replace_ast::<ast::Expr>(method_call.into(), match_expr); |
diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs index eba0631a4..e52ec557e 100644 --- a/crates/ra_assists/src/handlers/unwrap_block.rs +++ b/crates/ra_assists/src/handlers/unwrap_block.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use crate::{AssistContext, AssistId, Assists}; | 1 | use crate::{AssistContext, AssistId, Assists}; |
2 | 2 | ||
3 | use ast::LoopBodyOwner; | 3 | use ast::{ElseBranch, Expr, LoopBodyOwner}; |
4 | use ra_fmt::unwrap_trivial_block; | 4 | use ra_fmt::unwrap_trivial_block; |
5 | use ra_syntax::{ast, match_ast, AstNode, TextRange, T}; | 5 | use ra_syntax::{ast, match_ast, AstNode, TextRange, T}; |
6 | 6 | ||
@@ -25,19 +25,11 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
25 | let l_curly_token = ctx.find_token_at_offset(T!['{'])?; | 25 | let l_curly_token = ctx.find_token_at_offset(T!['{'])?; |
26 | let block = ast::BlockExpr::cast(l_curly_token.parent())?; | 26 | let block = ast::BlockExpr::cast(l_curly_token.parent())?; |
27 | let parent = block.syntax().parent()?; | 27 | let parent = block.syntax().parent()?; |
28 | let assist_id = AssistId("unwrap_block"); | ||
29 | let assist_label = "Unwrap block"; | ||
30 | |||
28 | let (expr, expr_to_unwrap) = match_ast! { | 31 | let (expr, expr_to_unwrap) = match_ast! { |
29 | match parent { | 32 | match parent { |
30 | ast::IfExpr(if_expr) => { | ||
31 | let expr_to_unwrap = if_expr.blocks().find_map(|expr| extract_expr(ctx.frange.range, expr)); | ||
32 | let expr_to_unwrap = expr_to_unwrap?; | ||
33 | // Find if we are in a else if block | ||
34 | let ancestor = if_expr.syntax().parent().and_then(ast::IfExpr::cast); | ||
35 | |||
36 | match ancestor { | ||
37 | None => (ast::Expr::IfExpr(if_expr), expr_to_unwrap), | ||
38 | Some(ancestor) => (ast::Expr::IfExpr(ancestor), expr_to_unwrap), | ||
39 | } | ||
40 | }, | ||
41 | ast::ForExpr(for_expr) => { | 33 | ast::ForExpr(for_expr) => { |
42 | let block_expr = for_expr.loop_body()?; | 34 | let block_expr = for_expr.loop_body()?; |
43 | let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; | 35 | let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; |
@@ -53,27 +45,62 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
53 | let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; | 45 | let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; |
54 | (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap) | 46 | (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap) |
55 | }, | 47 | }, |
48 | ast::IfExpr(if_expr) => { | ||
49 | let mut resp = None; | ||
50 | |||
51 | let then_branch = if_expr.then_branch()?; | ||
52 | if then_branch.l_curly_token()?.text_range().contains_range(ctx.frange.range) { | ||
53 | if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) { | ||
54 | // For `else if` blocks | ||
55 | let ancestor_then_branch = ancestor.then_branch()?; | ||
56 | let l_curly_token = then_branch.l_curly_token()?; | ||
57 | |||
58 | let target = then_branch.syntax().text_range(); | ||
59 | return acc.add(assist_id, assist_label, target, |edit| { | ||
60 | let range_to_del_else_if = TextRange::new(ancestor_then_branch.syntax().text_range().end(), l_curly_token.text_range().start()); | ||
61 | let range_to_del_rest = TextRange::new(then_branch.syntax().text_range().end(), if_expr.syntax().text_range().end()); | ||
62 | |||
63 | edit.set_cursor(ancestor_then_branch.syntax().text_range().end()); | ||
64 | edit.delete(range_to_del_rest); | ||
65 | edit.delete(range_to_del_else_if); | ||
66 | edit.replace(target, update_expr_string(then_branch.to_string(), &[' ', '{'])); | ||
67 | }); | ||
68 | } else { | ||
69 | resp = Some((ast::Expr::IfExpr(if_expr.clone()), Expr::BlockExpr(then_branch))); | ||
70 | } | ||
71 | } else if let Some(else_branch) = if_expr.else_branch() { | ||
72 | match else_branch { | ||
73 | ElseBranch::Block(else_block) => { | ||
74 | let l_curly_token = else_block.l_curly_token()?; | ||
75 | if l_curly_token.text_range().contains_range(ctx.frange.range) { | ||
76 | let target = else_block.syntax().text_range(); | ||
77 | return acc.add(assist_id, assist_label, target, |edit| { | ||
78 | let range_to_del = TextRange::new(then_branch.syntax().text_range().end(), l_curly_token.text_range().start()); | ||
79 | |||
80 | edit.set_cursor(then_branch.syntax().text_range().end()); | ||
81 | edit.delete(range_to_del); | ||
82 | edit.replace(target, update_expr_string(else_block.to_string(), &[' ', '{'])); | ||
83 | }); | ||
84 | } | ||
85 | }, | ||
86 | ElseBranch::IfExpr(_) => {}, | ||
87 | } | ||
88 | } | ||
89 | |||
90 | resp? | ||
91 | }, | ||
56 | _ => return None, | 92 | _ => return None, |
57 | } | 93 | } |
58 | }; | 94 | }; |
59 | 95 | ||
60 | let target = expr_to_unwrap.syntax().text_range(); | 96 | let target = expr_to_unwrap.syntax().text_range(); |
61 | acc.add(AssistId("unwrap_block"), "Unwrap block", target, |edit| { | 97 | acc.add(assist_id, assist_label, target, |edit| { |
62 | edit.set_cursor(expr.syntax().text_range().start()); | 98 | edit.set_cursor(expr.syntax().text_range().start()); |
63 | 99 | ||
64 | let pat_start: &[_] = &[' ', '{', '\n']; | 100 | edit.replace( |
65 | let expr_to_unwrap = expr_to_unwrap.to_string(); | 101 | expr.syntax().text_range(), |
66 | let expr_string = expr_to_unwrap.trim_start_matches(pat_start); | 102 | update_expr_string(expr_to_unwrap.to_string(), &[' ', '{', '\n']), |
67 | let mut expr_string_lines: Vec<&str> = expr_string.lines().collect(); | 103 | ); |
68 | expr_string_lines.pop(); // Delete last line | ||
69 | |||
70 | let expr_string = expr_string_lines | ||
71 | .into_iter() | ||
72 | .map(|line| line.replacen(" ", "", 1)) // Delete indentation | ||
73 | .collect::<Vec<String>>() | ||
74 | .join("\n"); | ||
75 | |||
76 | edit.replace(expr.syntax().text_range(), expr_string); | ||
77 | }) | 104 | }) |
78 | } | 105 | } |
79 | 106 | ||
@@ -87,6 +114,18 @@ fn extract_expr(cursor_range: TextRange, block: ast::BlockExpr) -> Option<ast::E | |||
87 | } | 114 | } |
88 | } | 115 | } |
89 | 116 | ||
117 | fn update_expr_string(expr_str: String, trim_start_pat: &[char]) -> String { | ||
118 | let expr_string = expr_str.trim_start_matches(trim_start_pat); | ||
119 | let mut expr_string_lines: Vec<&str> = expr_string.lines().collect(); | ||
120 | expr_string_lines.pop(); // Delete last line | ||
121 | |||
122 | expr_string_lines | ||
123 | .into_iter() | ||
124 | .map(|line| line.replacen(" ", "", 1)) // Delete indentation | ||
125 | .collect::<Vec<String>>() | ||
126 | .join("\n") | ||
127 | } | ||
128 | |||
90 | #[cfg(test)] | 129 | #[cfg(test)] |
91 | mod tests { | 130 | mod tests { |
92 | use crate::tests::{check_assist, check_assist_not_applicable}; | 131 | use crate::tests::{check_assist, check_assist_not_applicable}; |
@@ -142,7 +181,13 @@ mod tests { | |||
142 | r#" | 181 | r#" |
143 | fn main() { | 182 | fn main() { |
144 | bar(); | 183 | bar(); |
145 | <|>println!("bar"); | 184 | if true { |
185 | foo(); | ||
186 | |||
187 | //comment | ||
188 | bar(); | ||
189 | }<|> | ||
190 | println!("bar"); | ||
146 | } | 191 | } |
147 | "#, | 192 | "#, |
148 | ); | 193 | ); |
@@ -170,7 +215,127 @@ mod tests { | |||
170 | r#" | 215 | r#" |
171 | fn main() { | 216 | fn main() { |
172 | //bar(); | 217 | //bar(); |
173 | <|>println!("bar"); | 218 | if true { |
219 | println!("true"); | ||
220 | |||
221 | //comment | ||
222 | //bar(); | ||
223 | }<|> | ||
224 | println!("bar"); | ||
225 | } | ||
226 | "#, | ||
227 | ); | ||
228 | } | ||
229 | |||
230 | #[test] | ||
231 | fn simple_if_else_if_nested() { | ||
232 | check_assist( | ||
233 | unwrap_block, | ||
234 | r#" | ||
235 | fn main() { | ||
236 | //bar(); | ||
237 | if true { | ||
238 | println!("true"); | ||
239 | |||
240 | //comment | ||
241 | //bar(); | ||
242 | } else if false { | ||
243 | println!("bar"); | ||
244 | } else if true {<|> | ||
245 | println!("foo"); | ||
246 | } | ||
247 | } | ||
248 | "#, | ||
249 | r#" | ||
250 | fn main() { | ||
251 | //bar(); | ||
252 | if true { | ||
253 | println!("true"); | ||
254 | |||
255 | //comment | ||
256 | //bar(); | ||
257 | } else if false { | ||
258 | println!("bar"); | ||
259 | }<|> | ||
260 | println!("foo"); | ||
261 | } | ||
262 | "#, | ||
263 | ); | ||
264 | } | ||
265 | |||
266 | #[test] | ||
267 | fn simple_if_else_if_nested_else() { | ||
268 | check_assist( | ||
269 | unwrap_block, | ||
270 | r#" | ||
271 | fn main() { | ||
272 | //bar(); | ||
273 | if true { | ||
274 | println!("true"); | ||
275 | |||
276 | //comment | ||
277 | //bar(); | ||
278 | } else if false { | ||
279 | println!("bar"); | ||
280 | } else if true { | ||
281 | println!("foo"); | ||
282 | } else {<|> | ||
283 | println!("else"); | ||
284 | } | ||
285 | } | ||
286 | "#, | ||
287 | r#" | ||
288 | fn main() { | ||
289 | //bar(); | ||
290 | if true { | ||
291 | println!("true"); | ||
292 | |||
293 | //comment | ||
294 | //bar(); | ||
295 | } else if false { | ||
296 | println!("bar"); | ||
297 | } else if true { | ||
298 | println!("foo"); | ||
299 | }<|> | ||
300 | println!("else"); | ||
301 | } | ||
302 | "#, | ||
303 | ); | ||
304 | } | ||
305 | |||
306 | #[test] | ||
307 | fn simple_if_else_if_nested_middle() { | ||
308 | check_assist( | ||
309 | unwrap_block, | ||
310 | r#" | ||
311 | fn main() { | ||
312 | //bar(); | ||
313 | if true { | ||
314 | println!("true"); | ||
315 | |||
316 | //comment | ||
317 | //bar(); | ||
318 | } else if false { | ||
319 | println!("bar"); | ||
320 | } else if true {<|> | ||
321 | println!("foo"); | ||
322 | } else { | ||
323 | println!("else"); | ||
324 | } | ||
325 | } | ||
326 | "#, | ||
327 | r#" | ||
328 | fn main() { | ||
329 | //bar(); | ||
330 | if true { | ||
331 | println!("true"); | ||
332 | |||
333 | //comment | ||
334 | //bar(); | ||
335 | } else if false { | ||
336 | println!("bar"); | ||
337 | }<|> | ||
338 | println!("foo"); | ||
174 | } | 339 | } |
175 | "#, | 340 | "#, |
176 | ); | 341 | ); |
diff --git a/crates/ra_flycheck/Cargo.toml b/crates/ra_flycheck/Cargo.toml index 03e557148..eac502da5 100644 --- a/crates/ra_flycheck/Cargo.toml +++ b/crates/ra_flycheck/Cargo.toml | |||
@@ -11,7 +11,7 @@ doctest = false | |||
11 | crossbeam-channel = "0.4.0" | 11 | crossbeam-channel = "0.4.0" |
12 | lsp-types = { version = "0.74.0", features = ["proposed"] } | 12 | lsp-types = { version = "0.74.0", features = ["proposed"] } |
13 | log = "0.4.8" | 13 | log = "0.4.8" |
14 | cargo_metadata = "0.9.1" | 14 | cargo_metadata = "0.10.0" |
15 | serde_json = "1.0.48" | 15 | serde_json = "1.0.48" |
16 | jod-thread = "0.1.1" | 16 | jod-thread = "0.1.1" |
17 | ra_toolchain = { path = "../ra_toolchain" } | 17 | ra_toolchain = { path = "../ra_toolchain" } |
diff --git a/crates/ra_flycheck/src/lib.rs b/crates/ra_flycheck/src/lib.rs index 68dcee285..24af75c95 100644 --- a/crates/ra_flycheck/src/lib.rs +++ b/crates/ra_flycheck/src/lib.rs | |||
@@ -204,6 +204,8 @@ impl FlycheckThread { | |||
204 | } | 204 | } |
205 | 205 | ||
206 | CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {} | 206 | CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {} |
207 | CheckEvent::Msg(Message::BuildFinished(_)) => {} | ||
208 | CheckEvent::Msg(Message::TextLine(_)) => {} | ||
207 | CheckEvent::Msg(Message::Unknown) => {} | 209 | CheckEvent::Msg(Message::Unknown) => {} |
208 | } | 210 | } |
209 | } | 211 | } |
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 5f480c304..3fc2eccdd 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs | |||
@@ -22,8 +22,11 @@ use hir_expand::{ | |||
22 | MacroDefId, MacroDefKind, | 22 | MacroDefId, MacroDefKind, |
23 | }; | 23 | }; |
24 | use hir_ty::{ | 24 | use hir_ty::{ |
25 | autoderef, display::HirFormatter, expr::ExprValidator, method_resolution, ApplicationTy, | 25 | autoderef, |
26 | Canonical, InEnvironment, Substs, TraitEnvironment, Ty, TyDefId, TypeCtor, | 26 | display::{HirDisplayError, HirFormatter}, |
27 | expr::ExprValidator, | ||
28 | method_resolution, ApplicationTy, Canonical, InEnvironment, Substs, TraitEnvironment, Ty, | ||
29 | TyDefId, TypeCtor, | ||
27 | }; | 30 | }; |
28 | use ra_db::{CrateId, CrateName, Edition, FileId}; | 31 | use ra_db::{CrateId, CrateName, Edition, FileId}; |
29 | use ra_prof::profile; | 32 | use ra_prof::profile; |
@@ -675,6 +678,10 @@ impl Static { | |||
675 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | 678 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { |
676 | db.static_data(self.id).name.clone() | 679 | db.static_data(self.id).name.clone() |
677 | } | 680 | } |
681 | |||
682 | pub fn is_mut(self, db: &dyn HirDatabase) -> bool { | ||
683 | db.static_data(self.id).mutable | ||
684 | } | ||
678 | } | 685 | } |
679 | 686 | ||
680 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 687 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
@@ -1319,7 +1326,7 @@ impl Type { | |||
1319 | } | 1326 | } |
1320 | 1327 | ||
1321 | impl HirDisplay for Type { | 1328 | impl HirDisplay for Type { |
1322 | fn hir_fmt(&self, f: &mut HirFormatter) -> std::fmt::Result { | 1329 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
1323 | self.ty.value.hir_fmt(f) | 1330 | self.ty.value.hir_fmt(f) |
1324 | } | 1331 | } |
1325 | } | 1332 | } |
diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs index e7eb2bb11..e2130d931 100644 --- a/crates/ra_hir_def/src/data.rs +++ b/crates/ra_hir_def/src/data.rs | |||
@@ -251,11 +251,6 @@ impl ConstData { | |||
251 | Arc::new(ConstData::new(db, vis_default, node)) | 251 | Arc::new(ConstData::new(db, vis_default, node)) |
252 | } | 252 | } |
253 | 253 | ||
254 | pub(crate) fn static_data_query(db: &dyn DefDatabase, konst: StaticId) -> Arc<ConstData> { | ||
255 | let node = konst.lookup(db).source(db); | ||
256 | Arc::new(ConstData::new(db, RawVisibility::private(), node)) | ||
257 | } | ||
258 | |||
259 | fn new<N: NameOwner + TypeAscriptionOwner + VisibilityOwner>( | 254 | fn new<N: NameOwner + TypeAscriptionOwner + VisibilityOwner>( |
260 | db: &dyn DefDatabase, | 255 | db: &dyn DefDatabase, |
261 | vis_default: RawVisibility, | 256 | vis_default: RawVisibility, |
@@ -270,6 +265,32 @@ impl ConstData { | |||
270 | } | 265 | } |
271 | } | 266 | } |
272 | 267 | ||
268 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
269 | pub struct StaticData { | ||
270 | pub name: Option<Name>, | ||
271 | pub type_ref: TypeRef, | ||
272 | pub visibility: RawVisibility, | ||
273 | pub mutable: bool, | ||
274 | } | ||
275 | |||
276 | impl StaticData { | ||
277 | pub(crate) fn static_data_query(db: &dyn DefDatabase, konst: StaticId) -> Arc<StaticData> { | ||
278 | let node = konst.lookup(db).source(db); | ||
279 | let ctx = LowerCtx::new(db, node.file_id); | ||
280 | |||
281 | let name = node.value.name().map(|n| n.as_name()); | ||
282 | let type_ref = TypeRef::from_ast_opt(&ctx, node.value.ascribed_type()); | ||
283 | let mutable = node.value.mut_token().is_some(); | ||
284 | let visibility = RawVisibility::from_ast_with_default( | ||
285 | db, | ||
286 | RawVisibility::private(), | ||
287 | node.map(|n| n.visibility()), | ||
288 | ); | ||
289 | |||
290 | Arc::new(StaticData { name, type_ref, visibility, mutable }) | ||
291 | } | ||
292 | } | ||
293 | |||
273 | fn collect_items_in_macros( | 294 | fn collect_items_in_macros( |
274 | db: &dyn DefDatabase, | 295 | db: &dyn DefDatabase, |
275 | expander: &mut Expander, | 296 | expander: &mut Expander, |
diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs index 5dc7395f5..e665ab45d 100644 --- a/crates/ra_hir_def/src/db.rs +++ b/crates/ra_hir_def/src/db.rs | |||
@@ -10,7 +10,7 @@ use crate::{ | |||
10 | adt::{EnumData, StructData}, | 10 | adt::{EnumData, StructData}, |
11 | attr::Attrs, | 11 | attr::Attrs, |
12 | body::{scope::ExprScopes, Body, BodySourceMap}, | 12 | body::{scope::ExprScopes, Body, BodySourceMap}, |
13 | data::{ConstData, FunctionData, ImplData, TraitData, TypeAliasData}, | 13 | data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData}, |
14 | docs::Documentation, | 14 | docs::Documentation, |
15 | generics::GenericParams, | 15 | generics::GenericParams, |
16 | lang_item::{LangItemTarget, LangItems}, | 16 | lang_item::{LangItemTarget, LangItems}, |
@@ -77,8 +77,8 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { | |||
77 | #[salsa::invoke(ConstData::const_data_query)] | 77 | #[salsa::invoke(ConstData::const_data_query)] |
78 | fn const_data(&self, konst: ConstId) -> Arc<ConstData>; | 78 | fn const_data(&self, konst: ConstId) -> Arc<ConstData>; |
79 | 79 | ||
80 | #[salsa::invoke(ConstData::static_data_query)] | 80 | #[salsa::invoke(StaticData::static_data_query)] |
81 | fn static_data(&self, konst: StaticId) -> Arc<ConstData>; | 81 | fn static_data(&self, konst: StaticId) -> Arc<StaticData>; |
82 | 82 | ||
83 | #[salsa::invoke(Body::body_with_source_map_query)] | 83 | #[salsa::invoke(Body::body_with_source_map_query)] |
84 | fn body_with_source_map(&self, def: DefWithBodyId) -> (Arc<Body>, Arc<BodySourceMap>); | 84 | fn body_with_source_map(&self, def: DefWithBodyId) -> (Arc<Body>, Arc<BodySourceMap>); |
diff --git a/crates/ra_hir_ty/src/display.rs b/crates/ra_hir_ty/src/display.rs index d03bbd5a7..b9c4d2e89 100644 --- a/crates/ra_hir_ty/src/display.rs +++ b/crates/ra_hir_ty/src/display.rs | |||
@@ -6,28 +6,42 @@ use crate::{ | |||
6 | db::HirDatabase, utils::generics, ApplicationTy, CallableDef, FnSig, GenericPredicate, | 6 | db::HirDatabase, utils::generics, ApplicationTy, CallableDef, FnSig, GenericPredicate, |
7 | Obligation, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, | 7 | Obligation, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, |
8 | }; | 8 | }; |
9 | use hir_def::{generics::TypeParamProvenance, AdtId, AssocContainerId, Lookup}; | 9 | use hir_def::{ |
10 | find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId, AssocContainerId, | ||
11 | Lookup, ModuleId, | ||
12 | }; | ||
10 | use hir_expand::name::Name; | 13 | use hir_expand::name::Name; |
11 | 14 | ||
12 | pub struct HirFormatter<'a, 'b> { | 15 | pub struct HirFormatter<'a> { |
13 | pub db: &'a dyn HirDatabase, | 16 | pub db: &'a dyn HirDatabase, |
14 | fmt: &'a mut fmt::Formatter<'b>, | 17 | fmt: &'a mut dyn fmt::Write, |
15 | buf: String, | 18 | buf: String, |
16 | curr_size: usize, | 19 | curr_size: usize, |
17 | pub(crate) max_size: Option<usize>, | 20 | pub(crate) max_size: Option<usize>, |
18 | omit_verbose_types: bool, | 21 | omit_verbose_types: bool, |
22 | display_target: DisplayTarget, | ||
19 | } | 23 | } |
20 | 24 | ||
21 | pub trait HirDisplay { | 25 | pub trait HirDisplay { |
22 | fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result; | 26 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError>; |
23 | 27 | ||
28 | /// Returns a `Display`able type that is human-readable. | ||
29 | /// Use this for showing types to the user (e.g. diagnostics) | ||
24 | fn display<'a>(&'a self, db: &'a dyn HirDatabase) -> HirDisplayWrapper<'a, Self> | 30 | fn display<'a>(&'a self, db: &'a dyn HirDatabase) -> HirDisplayWrapper<'a, Self> |
25 | where | 31 | where |
26 | Self: Sized, | 32 | Self: Sized, |
27 | { | 33 | { |
28 | HirDisplayWrapper(db, self, None, false) | 34 | HirDisplayWrapper { |
35 | db, | ||
36 | t: self, | ||
37 | max_size: None, | ||
38 | omit_verbose_types: false, | ||
39 | display_target: DisplayTarget::Diagnostics, | ||
40 | } | ||
29 | } | 41 | } |
30 | 42 | ||
43 | /// Returns a `Display`able type that is human-readable and tries to be succinct. | ||
44 | /// Use this for showing types to the user where space is constrained (e.g. doc popups) | ||
31 | fn display_truncated<'a>( | 45 | fn display_truncated<'a>( |
32 | &'a self, | 46 | &'a self, |
33 | db: &'a dyn HirDatabase, | 47 | db: &'a dyn HirDatabase, |
@@ -36,16 +50,46 @@ pub trait HirDisplay { | |||
36 | where | 50 | where |
37 | Self: Sized, | 51 | Self: Sized, |
38 | { | 52 | { |
39 | HirDisplayWrapper(db, self, max_size, true) | 53 | HirDisplayWrapper { |
54 | db, | ||
55 | t: self, | ||
56 | max_size, | ||
57 | omit_verbose_types: true, | ||
58 | display_target: DisplayTarget::Diagnostics, | ||
59 | } | ||
60 | } | ||
61 | |||
62 | /// Returns a String representation of `self` that can be inserted into the given module. | ||
63 | /// Use this when generating code (e.g. assists) | ||
64 | fn display_source_code<'a>( | ||
65 | &'a self, | ||
66 | db: &'a dyn HirDatabase, | ||
67 | module_id: ModuleId, | ||
68 | ) -> Result<String, DisplaySourceCodeError> { | ||
69 | let mut result = String::new(); | ||
70 | match self.hir_fmt(&mut HirFormatter { | ||
71 | db, | ||
72 | fmt: &mut result, | ||
73 | buf: String::with_capacity(20), | ||
74 | curr_size: 0, | ||
75 | max_size: None, | ||
76 | omit_verbose_types: false, | ||
77 | display_target: DisplayTarget::SourceCode { module_id }, | ||
78 | }) { | ||
79 | Ok(()) => {} | ||
80 | Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"), | ||
81 | Err(HirDisplayError::DisplaySourceCodeError(e)) => return Err(e), | ||
82 | }; | ||
83 | Ok(result) | ||
40 | } | 84 | } |
41 | } | 85 | } |
42 | 86 | ||
43 | impl<'a, 'b> HirFormatter<'a, 'b> { | 87 | impl<'a> HirFormatter<'a> { |
44 | pub fn write_joined<T: HirDisplay>( | 88 | pub fn write_joined<T: HirDisplay>( |
45 | &mut self, | 89 | &mut self, |
46 | iter: impl IntoIterator<Item = T>, | 90 | iter: impl IntoIterator<Item = T>, |
47 | sep: &str, | 91 | sep: &str, |
48 | ) -> fmt::Result { | 92 | ) -> Result<(), HirDisplayError> { |
49 | let mut first = true; | 93 | let mut first = true; |
50 | for e in iter { | 94 | for e in iter { |
51 | if !first { | 95 | if !first { |
@@ -58,14 +102,14 @@ impl<'a, 'b> HirFormatter<'a, 'b> { | |||
58 | } | 102 | } |
59 | 103 | ||
60 | /// This allows using the `write!` macro directly with a `HirFormatter`. | 104 | /// This allows using the `write!` macro directly with a `HirFormatter`. |
61 | pub fn write_fmt(&mut self, args: fmt::Arguments) -> fmt::Result { | 105 | pub fn write_fmt(&mut self, args: fmt::Arguments) -> Result<(), HirDisplayError> { |
62 | // We write to a buffer first to track output size | 106 | // We write to a buffer first to track output size |
63 | self.buf.clear(); | 107 | self.buf.clear(); |
64 | fmt::write(&mut self.buf, args)?; | 108 | fmt::write(&mut self.buf, args)?; |
65 | self.curr_size += self.buf.len(); | 109 | self.curr_size += self.buf.len(); |
66 | 110 | ||
67 | // Then we write to the internal formatter from the buffer | 111 | // Then we write to the internal formatter from the buffer |
68 | self.fmt.write_str(&self.buf) | 112 | self.fmt.write_str(&self.buf).map_err(HirDisplayError::from) |
69 | } | 113 | } |
70 | 114 | ||
71 | pub fn should_truncate(&self) -> bool { | 115 | pub fn should_truncate(&self) -> bool { |
@@ -81,34 +125,82 @@ impl<'a, 'b> HirFormatter<'a, 'b> { | |||
81 | } | 125 | } |
82 | } | 126 | } |
83 | 127 | ||
84 | pub struct HirDisplayWrapper<'a, T>(&'a dyn HirDatabase, &'a T, Option<usize>, bool); | 128 | #[derive(Clone, Copy)] |
129 | enum DisplayTarget { | ||
130 | /// Display types for inlays, doc popups, autocompletion, etc... | ||
131 | /// Showing `{unknown}` or not qualifying paths is fine here. | ||
132 | /// There's no reason for this to fail. | ||
133 | Diagnostics, | ||
134 | /// Display types for inserting them in source files. | ||
135 | /// The generated code should compile, so paths need to be qualified. | ||
136 | SourceCode { module_id: ModuleId }, | ||
137 | } | ||
138 | |||
139 | impl DisplayTarget { | ||
140 | fn is_source_code(&self) -> bool { | ||
141 | matches!(self, Self::SourceCode {..}) | ||
142 | } | ||
143 | } | ||
144 | |||
145 | #[derive(Debug)] | ||
146 | pub enum DisplaySourceCodeError { | ||
147 | PathNotFound, | ||
148 | } | ||
149 | |||
150 | pub enum HirDisplayError { | ||
151 | /// Errors that can occur when generating source code | ||
152 | DisplaySourceCodeError(DisplaySourceCodeError), | ||
153 | /// `FmtError` is required to be compatible with std::fmt::Display | ||
154 | FmtError, | ||
155 | } | ||
156 | impl From<fmt::Error> for HirDisplayError { | ||
157 | fn from(_: fmt::Error) -> Self { | ||
158 | Self::FmtError | ||
159 | } | ||
160 | } | ||
161 | |||
162 | pub struct HirDisplayWrapper<'a, T> { | ||
163 | db: &'a dyn HirDatabase, | ||
164 | t: &'a T, | ||
165 | max_size: Option<usize>, | ||
166 | omit_verbose_types: bool, | ||
167 | display_target: DisplayTarget, | ||
168 | } | ||
85 | 169 | ||
86 | impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T> | 170 | impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T> |
87 | where | 171 | where |
88 | T: HirDisplay, | 172 | T: HirDisplay, |
89 | { | 173 | { |
90 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 174 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
91 | self.1.hir_fmt(&mut HirFormatter { | 175 | match self.t.hir_fmt(&mut HirFormatter { |
92 | db: self.0, | 176 | db: self.db, |
93 | fmt: f, | 177 | fmt: f, |
94 | buf: String::with_capacity(20), | 178 | buf: String::with_capacity(20), |
95 | curr_size: 0, | 179 | curr_size: 0, |
96 | max_size: self.2, | 180 | max_size: self.max_size, |
97 | omit_verbose_types: self.3, | 181 | omit_verbose_types: self.omit_verbose_types, |
98 | }) | 182 | display_target: self.display_target, |
183 | }) { | ||
184 | Ok(()) => Ok(()), | ||
185 | Err(HirDisplayError::FmtError) => Err(fmt::Error), | ||
186 | Err(HirDisplayError::DisplaySourceCodeError(_)) => { | ||
187 | // This should never happen | ||
188 | panic!("HirDisplay failed when calling Display::fmt!") | ||
189 | } | ||
190 | } | ||
99 | } | 191 | } |
100 | } | 192 | } |
101 | 193 | ||
102 | const TYPE_HINT_TRUNCATION: &str = "…"; | 194 | const TYPE_HINT_TRUNCATION: &str = "…"; |
103 | 195 | ||
104 | impl HirDisplay for &Ty { | 196 | impl HirDisplay for &Ty { |
105 | fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { | 197 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
106 | HirDisplay::hir_fmt(*self, f) | 198 | HirDisplay::hir_fmt(*self, f) |
107 | } | 199 | } |
108 | } | 200 | } |
109 | 201 | ||
110 | impl HirDisplay for ApplicationTy { | 202 | impl HirDisplay for ApplicationTy { |
111 | fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { | 203 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
112 | if f.should_truncate() { | 204 | if f.should_truncate() { |
113 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | 205 | return write!(f, "{}", TYPE_HINT_TRUNCATION); |
114 | } | 206 | } |
@@ -191,45 +283,66 @@ impl HirDisplay for ApplicationTy { | |||
191 | } | 283 | } |
192 | } | 284 | } |
193 | TypeCtor::Adt(def_id) => { | 285 | TypeCtor::Adt(def_id) => { |
194 | let name = match def_id { | 286 | match f.display_target { |
195 | AdtId::StructId(it) => f.db.struct_data(it).name.clone(), | 287 | DisplayTarget::Diagnostics => { |
196 | AdtId::UnionId(it) => f.db.union_data(it).name.clone(), | 288 | let name = match def_id { |
197 | AdtId::EnumId(it) => f.db.enum_data(it).name.clone(), | 289 | AdtId::StructId(it) => f.db.struct_data(it).name.clone(), |
198 | }; | 290 | AdtId::UnionId(it) => f.db.union_data(it).name.clone(), |
199 | write!(f, "{}", name)?; | 291 | AdtId::EnumId(it) => f.db.enum_data(it).name.clone(), |
292 | }; | ||
293 | write!(f, "{}", name)?; | ||
294 | } | ||
295 | DisplayTarget::SourceCode { module_id } => { | ||
296 | if let Some(path) = find_path::find_path( | ||
297 | f.db.upcast(), | ||
298 | ItemInNs::Types(def_id.into()), | ||
299 | module_id, | ||
300 | ) { | ||
301 | write!(f, "{}", path)?; | ||
302 | } else { | ||
303 | return Err(HirDisplayError::DisplaySourceCodeError( | ||
304 | DisplaySourceCodeError::PathNotFound, | ||
305 | )); | ||
306 | } | ||
307 | } | ||
308 | } | ||
309 | |||
200 | if self.parameters.len() > 0 { | 310 | if self.parameters.len() > 0 { |
201 | let mut non_default_parameters = Vec::with_capacity(self.parameters.len()); | 311 | let mut non_default_parameters = Vec::with_capacity(self.parameters.len()); |
202 | let parameters_to_write = if f.omit_verbose_types() { | 312 | let parameters_to_write = |
203 | match self | 313 | if f.display_target.is_source_code() || f.omit_verbose_types() { |
204 | .ctor | 314 | match self |
205 | .as_generic_def() | 315 | .ctor |
206 | .map(|generic_def_id| f.db.generic_defaults(generic_def_id)) | 316 | .as_generic_def() |
207 | .filter(|defaults| !defaults.is_empty()) | 317 | .map(|generic_def_id| f.db.generic_defaults(generic_def_id)) |
208 | { | 318 | .filter(|defaults| !defaults.is_empty()) |
209 | None => self.parameters.0.as_ref(), | 319 | { |
210 | Some(default_parameters) => { | 320 | None => self.parameters.0.as_ref(), |
211 | for (i, parameter) in self.parameters.iter().enumerate() { | 321 | Some(default_parameters) => { |
212 | match (parameter, default_parameters.get(i)) { | 322 | for (i, parameter) in self.parameters.iter().enumerate() { |
213 | (&Ty::Unknown, _) | (_, None) => { | 323 | match (parameter, default_parameters.get(i)) { |
214 | non_default_parameters.push(parameter.clone()) | 324 | (&Ty::Unknown, _) | (_, None) => { |
325 | non_default_parameters.push(parameter.clone()) | ||
326 | } | ||
327 | (_, Some(default_parameter)) | ||
328 | if parameter != default_parameter => | ||
329 | { | ||
330 | non_default_parameters.push(parameter.clone()) | ||
331 | } | ||
332 | _ => (), | ||
215 | } | 333 | } |
216 | (_, Some(default_parameter)) | ||
217 | if parameter != default_parameter => | ||
218 | { | ||
219 | non_default_parameters.push(parameter.clone()) | ||
220 | } | ||
221 | _ => (), | ||
222 | } | 334 | } |
335 | &non_default_parameters | ||
223 | } | 336 | } |
224 | &non_default_parameters | ||
225 | } | 337 | } |
226 | } | 338 | } else { |
227 | } else { | 339 | self.parameters.0.as_ref() |
228 | self.parameters.0.as_ref() | 340 | }; |
229 | }; | 341 | if !parameters_to_write.is_empty() { |
230 | write!(f, "<")?; | 342 | write!(f, "<")?; |
231 | f.write_joined(parameters_to_write, ", ")?; | 343 | f.write_joined(parameters_to_write, ", ")?; |
232 | write!(f, ">")?; | 344 | write!(f, ">")?; |
345 | } | ||
233 | } | 346 | } |
234 | } | 347 | } |
235 | TypeCtor::AssociatedType(type_alias) => { | 348 | TypeCtor::AssociatedType(type_alias) => { |
@@ -269,7 +382,7 @@ impl HirDisplay for ApplicationTy { | |||
269 | } | 382 | } |
270 | 383 | ||
271 | impl HirDisplay for ProjectionTy { | 384 | impl HirDisplay for ProjectionTy { |
272 | fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { | 385 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
273 | if f.should_truncate() { | 386 | if f.should_truncate() { |
274 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | 387 | return write!(f, "{}", TYPE_HINT_TRUNCATION); |
275 | } | 388 | } |
@@ -287,7 +400,7 @@ impl HirDisplay for ProjectionTy { | |||
287 | } | 400 | } |
288 | 401 | ||
289 | impl HirDisplay for Ty { | 402 | impl HirDisplay for Ty { |
290 | fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { | 403 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
291 | if f.should_truncate() { | 404 | if f.should_truncate() { |
292 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | 405 | return write!(f, "{}", TYPE_HINT_TRUNCATION); |
293 | } | 406 | } |
@@ -332,7 +445,7 @@ impl HirDisplay for Ty { | |||
332 | fn write_bounds_like_dyn_trait( | 445 | fn write_bounds_like_dyn_trait( |
333 | predicates: &[GenericPredicate], | 446 | predicates: &[GenericPredicate], |
334 | f: &mut HirFormatter, | 447 | f: &mut HirFormatter, |
335 | ) -> fmt::Result { | 448 | ) -> Result<(), HirDisplayError> { |
336 | // Note: This code is written to produce nice results (i.e. | 449 | // Note: This code is written to produce nice results (i.e. |
337 | // corresponding to surface Rust) for types that can occur in | 450 | // corresponding to surface Rust) for types that can occur in |
338 | // actual Rust. It will have weird results if the predicates | 451 | // actual Rust. It will have weird results if the predicates |
@@ -394,7 +507,7 @@ fn write_bounds_like_dyn_trait( | |||
394 | } | 507 | } |
395 | 508 | ||
396 | impl TraitRef { | 509 | impl TraitRef { |
397 | fn hir_fmt_ext(&self, f: &mut HirFormatter, use_as: bool) -> fmt::Result { | 510 | fn hir_fmt_ext(&self, f: &mut HirFormatter, use_as: bool) -> Result<(), HirDisplayError> { |
398 | if f.should_truncate() { | 511 | if f.should_truncate() { |
399 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | 512 | return write!(f, "{}", TYPE_HINT_TRUNCATION); |
400 | } | 513 | } |
@@ -416,19 +529,19 @@ impl TraitRef { | |||
416 | } | 529 | } |
417 | 530 | ||
418 | impl HirDisplay for TraitRef { | 531 | impl HirDisplay for TraitRef { |
419 | fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { | 532 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
420 | self.hir_fmt_ext(f, false) | 533 | self.hir_fmt_ext(f, false) |
421 | } | 534 | } |
422 | } | 535 | } |
423 | 536 | ||
424 | impl HirDisplay for &GenericPredicate { | 537 | impl HirDisplay for &GenericPredicate { |
425 | fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { | 538 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
426 | HirDisplay::hir_fmt(*self, f) | 539 | HirDisplay::hir_fmt(*self, f) |
427 | } | 540 | } |
428 | } | 541 | } |
429 | 542 | ||
430 | impl HirDisplay for GenericPredicate { | 543 | impl HirDisplay for GenericPredicate { |
431 | fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { | 544 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
432 | if f.should_truncate() { | 545 | if f.should_truncate() { |
433 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | 546 | return write!(f, "{}", TYPE_HINT_TRUNCATION); |
434 | } | 547 | } |
@@ -452,15 +565,15 @@ impl HirDisplay for GenericPredicate { | |||
452 | } | 565 | } |
453 | 566 | ||
454 | impl HirDisplay for Obligation { | 567 | impl HirDisplay for Obligation { |
455 | fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { | 568 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
456 | match self { | 569 | Ok(match self { |
457 | Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db)), | 570 | Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db))?, |
458 | Obligation::Projection(proj) => write!( | 571 | Obligation::Projection(proj) => write!( |
459 | f, | 572 | f, |
460 | "Normalize({} => {})", | 573 | "Normalize({} => {})", |
461 | proj.projection_ty.display(f.db), | 574 | proj.projection_ty.display(f.db), |
462 | proj.ty.display(f.db) | 575 | proj.ty.display(f.db) |
463 | ), | 576 | )?, |
464 | } | 577 | }) |
465 | } | 578 | } |
466 | } | 579 | } |
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index a21ad8d86..fb7c6cd8c 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs | |||
@@ -22,7 +22,7 @@ use rustc_hash::FxHashMap; | |||
22 | 22 | ||
23 | use hir_def::{ | 23 | use hir_def::{ |
24 | body::Body, | 24 | body::Body, |
25 | data::{ConstData, FunctionData}, | 25 | data::{ConstData, FunctionData, StaticData}, |
26 | expr::{BindingAnnotation, ExprId, PatId}, | 26 | expr::{BindingAnnotation, ExprId, PatId}, |
27 | lang_item::LangItemTarget, | 27 | lang_item::LangItemTarget, |
28 | path::{path, Path}, | 28 | path::{path, Path}, |
@@ -71,7 +71,7 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer | |||
71 | match def { | 71 | match def { |
72 | DefWithBodyId::ConstId(c) => ctx.collect_const(&db.const_data(c)), | 72 | DefWithBodyId::ConstId(c) => ctx.collect_const(&db.const_data(c)), |
73 | DefWithBodyId::FunctionId(f) => ctx.collect_fn(&db.function_data(f)), | 73 | DefWithBodyId::FunctionId(f) => ctx.collect_fn(&db.function_data(f)), |
74 | DefWithBodyId::StaticId(s) => ctx.collect_const(&db.static_data(s)), | 74 | DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_data(s)), |
75 | } | 75 | } |
76 | 76 | ||
77 | ctx.infer_body(); | 77 | ctx.infer_body(); |
@@ -485,6 +485,10 @@ impl<'a> InferenceContext<'a> { | |||
485 | self.return_ty = self.make_ty(&data.type_ref); | 485 | self.return_ty = self.make_ty(&data.type_ref); |
486 | } | 486 | } |
487 | 487 | ||
488 | fn collect_static(&mut self, data: &StaticData) { | ||
489 | self.return_ty = self.make_ty(&data.type_ref); | ||
490 | } | ||
491 | |||
488 | fn collect_fn(&mut self, data: &FunctionData) { | 492 | fn collect_fn(&mut self, data: &FunctionData) { |
489 | let body = Arc::clone(&self.body); // avoid borrow checker problem | 493 | let body = Arc::clone(&self.body); // avoid borrow checker problem |
490 | let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver) | 494 | let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver) |
diff --git a/crates/ra_hir_ty/src/op.rs b/crates/ra_hir_ty/src/op.rs index 54e2bd05a..0870874fc 100644 --- a/crates/ra_hir_ty/src/op.rs +++ b/crates/ra_hir_ty/src/op.rs | |||
@@ -30,7 +30,8 @@ pub(super) fn binary_op_return_ty(op: BinaryOp, lhs_ty: Ty, rhs_ty: Ty) -> Ty { | |||
30 | pub(super) fn binary_op_rhs_expectation(op: BinaryOp, lhs_ty: Ty) -> Ty { | 30 | pub(super) fn binary_op_rhs_expectation(op: BinaryOp, lhs_ty: Ty) -> Ty { |
31 | match op { | 31 | match op { |
32 | BinaryOp::LogicOp(..) => Ty::simple(TypeCtor::Bool), | 32 | BinaryOp::LogicOp(..) => Ty::simple(TypeCtor::Bool), |
33 | BinaryOp::Assignment { op: None } | BinaryOp::CmpOp(CmpOp::Eq { .. }) => match lhs_ty { | 33 | BinaryOp::Assignment { op: None } => lhs_ty, |
34 | BinaryOp::CmpOp(CmpOp::Eq { .. }) => match lhs_ty { | ||
34 | Ty::Apply(ApplicationTy { ctor, .. }) => match ctor { | 35 | Ty::Apply(ApplicationTy { ctor, .. }) => match ctor { |
35 | TypeCtor::Int(..) | 36 | TypeCtor::Int(..) |
36 | | TypeCtor::Float(..) | 37 | | TypeCtor::Float(..) |
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs index 5af88b368..1fe05c70c 100644 --- a/crates/ra_hir_ty/src/tests.rs +++ b/crates/ra_hir_ty/src/tests.rs | |||
@@ -6,6 +6,7 @@ mod patterns; | |||
6 | mod traits; | 6 | mod traits; |
7 | mod method_resolution; | 7 | mod method_resolution; |
8 | mod macros; | 8 | mod macros; |
9 | mod display_source_code; | ||
9 | 10 | ||
10 | use std::sync::Arc; | 11 | use std::sync::Arc; |
11 | 12 | ||
@@ -16,7 +17,7 @@ use hir_def::{ | |||
16 | item_scope::ItemScope, | 17 | item_scope::ItemScope, |
17 | keys, | 18 | keys, |
18 | nameres::CrateDefMap, | 19 | nameres::CrateDefMap, |
19 | AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, | 20 | AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, ModuleId, |
20 | }; | 21 | }; |
21 | use hir_expand::{db::AstDatabase, InFile}; | 22 | use hir_expand::{db::AstDatabase, InFile}; |
22 | use insta::assert_snapshot; | 23 | use insta::assert_snapshot; |
@@ -37,6 +38,18 @@ use crate::{ | |||
37 | // update the snapshots. | 38 | // update the snapshots. |
38 | 39 | ||
39 | fn type_at_pos(db: &TestDB, pos: FilePosition) -> String { | 40 | fn type_at_pos(db: &TestDB, pos: FilePosition) -> String { |
41 | type_at_pos_displayed(db, pos, |ty, _| ty.display(db).to_string()) | ||
42 | } | ||
43 | |||
44 | fn displayed_source_at_pos(db: &TestDB, pos: FilePosition) -> String { | ||
45 | type_at_pos_displayed(db, pos, |ty, module_id| ty.display_source_code(db, module_id).unwrap()) | ||
46 | } | ||
47 | |||
48 | fn type_at_pos_displayed( | ||
49 | db: &TestDB, | ||
50 | pos: FilePosition, | ||
51 | display_fn: impl FnOnce(&Ty, ModuleId) -> String, | ||
52 | ) -> String { | ||
40 | let file = db.parse(pos.file_id).ok().unwrap(); | 53 | let file = db.parse(pos.file_id).ok().unwrap(); |
41 | let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap(); | 54 | let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap(); |
42 | let fn_def = expr.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); | 55 | let fn_def = expr.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); |
@@ -49,7 +62,7 @@ fn type_at_pos(db: &TestDB, pos: FilePosition) -> String { | |||
49 | if let Some(expr_id) = source_map.node_expr(InFile::new(pos.file_id.into(), &expr)) { | 62 | if let Some(expr_id) = source_map.node_expr(InFile::new(pos.file_id.into(), &expr)) { |
50 | let infer = db.infer(func.into()); | 63 | let infer = db.infer(func.into()); |
51 | let ty = &infer[expr_id]; | 64 | let ty = &infer[expr_id]; |
52 | return ty.display(db).to_string(); | 65 | return display_fn(ty, module); |
53 | } | 66 | } |
54 | panic!("Can't find expression") | 67 | panic!("Can't find expression") |
55 | } | 68 | } |
diff --git a/crates/ra_hir_ty/src/tests/display_source_code.rs b/crates/ra_hir_ty/src/tests/display_source_code.rs new file mode 100644 index 000000000..4088b1d22 --- /dev/null +++ b/crates/ra_hir_ty/src/tests/display_source_code.rs | |||
@@ -0,0 +1,50 @@ | |||
1 | use super::displayed_source_at_pos; | ||
2 | use crate::test_db::TestDB; | ||
3 | use ra_db::fixture::WithFixture; | ||
4 | |||
5 | #[test] | ||
6 | fn qualify_path_to_submodule() { | ||
7 | let (db, pos) = TestDB::with_position( | ||
8 | r#" | ||
9 | //- /main.rs | ||
10 | |||
11 | mod foo { | ||
12 | pub struct Foo; | ||
13 | } | ||
14 | |||
15 | fn bar() { | ||
16 | let foo: foo::Foo = foo::Foo; | ||
17 | foo<|> | ||
18 | } | ||
19 | |||
20 | "#, | ||
21 | ); | ||
22 | assert_eq!("foo::Foo", displayed_source_at_pos(&db, pos)); | ||
23 | } | ||
24 | |||
25 | #[test] | ||
26 | fn omit_default_type_parameters() { | ||
27 | let (db, pos) = TestDB::with_position( | ||
28 | r" | ||
29 | //- /main.rs | ||
30 | struct Foo<T = u8> { t: T } | ||
31 | fn main() { | ||
32 | let foo = Foo { t: 5 }; | ||
33 | foo<|>; | ||
34 | } | ||
35 | ", | ||
36 | ); | ||
37 | assert_eq!("Foo", displayed_source_at_pos(&db, pos)); | ||
38 | |||
39 | let (db, pos) = TestDB::with_position( | ||
40 | r" | ||
41 | //- /main.rs | ||
42 | struct Foo<K, T = u8> { k: K, t: T } | ||
43 | fn main() { | ||
44 | let foo = Foo { k: 400, t: 5 }; | ||
45 | foo<|>; | ||
46 | } | ||
47 | ", | ||
48 | ); | ||
49 | assert_eq!("Foo<i32>", displayed_source_at_pos(&db, pos)); | ||
50 | } | ||
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs index 3820175f6..322838f02 100644 --- a/crates/ra_hir_ty/src/tests/simple.rs +++ b/crates/ra_hir_ty/src/tests/simple.rs | |||
@@ -1787,3 +1787,32 @@ fn main() { | |||
1787 | "### | 1787 | "### |
1788 | ) | 1788 | ) |
1789 | } | 1789 | } |
1790 | |||
1791 | #[test] | ||
1792 | fn infer_generic_from_later_assignment() { | ||
1793 | assert_snapshot!( | ||
1794 | infer(r#" | ||
1795 | enum Option<T> { Some(T), None } | ||
1796 | use Option::*; | ||
1797 | |||
1798 | fn test() { | ||
1799 | let mut end = None; | ||
1800 | loop { | ||
1801 | end = Some(true); | ||
1802 | } | ||
1803 | } | ||
1804 | "#), | ||
1805 | @r###" | ||
1806 | 60..130 '{ ... } }': () | ||
1807 | 70..77 'mut end': Option<bool> | ||
1808 | 80..84 'None': Option<bool> | ||
1809 | 90..128 'loop {... }': ! | ||
1810 | 95..128 '{ ... }': () | ||
1811 | 105..108 'end': Option<bool> | ||
1812 | 105..121 'end = ...(true)': () | ||
1813 | 111..115 'Some': Some<bool>(bool) -> Option<bool> | ||
1814 | 111..121 'Some(true)': Option<bool> | ||
1815 | 116..120 'true': bool | ||
1816 | "### | ||
1817 | ); | ||
1818 | } | ||
diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs index f16d42276..9572debd8 100644 --- a/crates/ra_ide/src/display/function_signature.rs +++ b/crates/ra_ide/src/display/function_signature.rs | |||
@@ -84,8 +84,8 @@ impl FunctionSignature { | |||
84 | let ty = field.signature_ty(db); | 84 | let ty = field.signature_ty(db); |
85 | let raw_param = format!("{}", ty.display(db)); | 85 | let raw_param = format!("{}", ty.display(db)); |
86 | 86 | ||
87 | if let Some(param_type) = raw_param.split(':').nth(1) { | 87 | if let Some(param_type) = raw_param.split(':').nth(1).and_then(|it| it.get(1..)) { |
88 | parameter_types.push(param_type[1..].to_string()); | 88 | parameter_types.push(param_type.to_string()); |
89 | } else { | 89 | } else { |
90 | // useful when you have tuple struct | 90 | // useful when you have tuple struct |
91 | parameter_types.push(raw_param.clone()); | 91 | parameter_types.push(raw_param.clone()); |
@@ -129,8 +129,8 @@ impl FunctionSignature { | |||
129 | for field in variant.fields(db).into_iter() { | 129 | for field in variant.fields(db).into_iter() { |
130 | let ty = field.signature_ty(db); | 130 | let ty = field.signature_ty(db); |
131 | let raw_param = format!("{}", ty.display(db)); | 131 | let raw_param = format!("{}", ty.display(db)); |
132 | if let Some(param_type) = raw_param.split(':').nth(1) { | 132 | if let Some(param_type) = raw_param.split(':').nth(1).and_then(|it| it.get(1..)) { |
133 | parameter_types.push(param_type[1..].to_string()); | 133 | parameter_types.push(param_type.to_string()); |
134 | } else { | 134 | } else { |
135 | // The unwrap_or_else is useful when you have tuple | 135 | // The unwrap_or_else is useful when you have tuple |
136 | parameter_types.push(raw_param); | 136 | parameter_types.push(raw_param); |
@@ -197,7 +197,12 @@ impl From<&'_ ast::FnDef> for FunctionSignature { | |||
197 | let raw_param = self_param.syntax().text().to_string(); | 197 | let raw_param = self_param.syntax().text().to_string(); |
198 | 198 | ||
199 | res_types.push( | 199 | res_types.push( |
200 | raw_param.split(':').nth(1).unwrap_or_else(|| " Self")[1..].to_string(), | 200 | raw_param |
201 | .split(':') | ||
202 | .nth(1) | ||
203 | .and_then(|it| it.get(1..)) | ||
204 | .unwrap_or_else(|| "Self") | ||
205 | .to_string(), | ||
201 | ); | 206 | ); |
202 | res.push(raw_param); | 207 | res.push(raw_param); |
203 | } | 208 | } |
@@ -205,8 +210,8 @@ impl From<&'_ ast::FnDef> for FunctionSignature { | |||
205 | res.extend(param_list.params().map(|param| param.syntax().text().to_string())); | 210 | res.extend(param_list.params().map(|param| param.syntax().text().to_string())); |
206 | res_types.extend(param_list.params().map(|param| { | 211 | res_types.extend(param_list.params().map(|param| { |
207 | let param_text = param.syntax().text().to_string(); | 212 | let param_text = param.syntax().text().to_string(); |
208 | match param_text.split(':').nth(1) { | 213 | match param_text.split(':').nth(1).and_then(|it| it.get(1..)) { |
209 | Some(it) => it[1..].to_string(), | 214 | Some(it) => it.to_string(), |
210 | None => param_text, | 215 | None => param_text, |
211 | } | 216 | } |
212 | })); | 217 | })); |
diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs index de35c6711..5da28edd2 100644 --- a/crates/ra_ide/src/display/navigation_target.rs +++ b/crates/ra_ide/src/display/navigation_target.rs | |||
@@ -11,7 +11,7 @@ use ra_syntax::{ | |||
11 | TextRange, | 11 | TextRange, |
12 | }; | 12 | }; |
13 | 13 | ||
14 | use crate::FileSymbol; | 14 | use crate::{FileRange, FileSymbol}; |
15 | 15 | ||
16 | use super::short_label::ShortLabel; | 16 | use super::short_label::ShortLabel; |
17 | 17 | ||
@@ -22,10 +22,11 @@ use super::short_label::ShortLabel; | |||
22 | /// code, like a function or a struct, but this is not strictly required. | 22 | /// code, like a function or a struct, but this is not strictly required. |
23 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 23 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
24 | pub struct NavigationTarget { | 24 | pub struct NavigationTarget { |
25 | // FIXME: use FileRange? | ||
25 | file_id: FileId, | 26 | file_id: FileId, |
27 | full_range: TextRange, | ||
26 | name: SmolStr, | 28 | name: SmolStr, |
27 | kind: SyntaxKind, | 29 | kind: SyntaxKind, |
28 | full_range: TextRange, | ||
29 | focus_range: Option<TextRange>, | 30 | focus_range: Option<TextRange>, |
30 | container_name: Option<SmolStr>, | 31 | container_name: Option<SmolStr>, |
31 | description: Option<String>, | 32 | description: Option<String>, |
@@ -63,6 +64,10 @@ impl NavigationTarget { | |||
63 | self.file_id | 64 | self.file_id |
64 | } | 65 | } |
65 | 66 | ||
67 | pub fn file_range(&self) -> FileRange { | ||
68 | FileRange { file_id: self.file_id, range: self.full_range } | ||
69 | } | ||
70 | |||
66 | pub fn full_range(&self) -> TextRange { | 71 | pub fn full_range(&self) -> TextRange { |
67 | self.full_range | 72 | self.full_range |
68 | } | 73 | } |
diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/src/snapshots/highlight_strings.html index de06daf72..752b487e8 100644 --- a/crates/ra_ide/src/snapshots/highlight_strings.html +++ b/crates/ra_ide/src/snapshots/highlight_strings.html | |||
@@ -27,13 +27,13 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
27 | .keyword.unsafe { color: #BC8383; font-weight: bold; } | 27 | .keyword.unsafe { color: #BC8383; font-weight: bold; } |
28 | .control { font-style: italic; } | 28 | .control { font-style: italic; } |
29 | </style> | 29 | </style> |
30 | <pre><code><span class="macro">macro_rules!</span> println { | 30 | <pre><code><span class="macro">macro_rules!</span> <span class="macro declaration">println</span> { |
31 | ($($arg:tt)*) => ({ | 31 | ($($arg:tt)*) => ({ |
32 | $<span class="keyword">crate</span>::io::_print($<span class="keyword">crate</span>::format_args_nl!($($arg)*)); | 32 | $<span class="keyword">crate</span>::io::_print($<span class="keyword">crate</span>::format_args_nl!($($arg)*)); |
33 | }) | 33 | }) |
34 | } | 34 | } |
35 | #[rustc_builtin_macro] | 35 | #[rustc_builtin_macro] |
36 | <span class="macro">macro_rules!</span> format_args_nl { | 36 | <span class="macro">macro_rules!</span> <span class="macro declaration">format_args_nl</span> { |
37 | ($fmt:expr) => {{ <span class="comment">/* compiler built-in */</span> }}; | 37 | ($fmt:expr) => {{ <span class="comment">/* compiler built-in */</span> }}; |
38 | ($fmt:expr, $($args:tt)*) => {{ <span class="comment">/* compiler built-in */</span> }}; | 38 | ($fmt:expr, $($args:tt)*) => {{ <span class="comment">/* compiler built-in */</span> }}; |
39 | } | 39 | } |
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html index 4b12fe823..4c27aade4 100644 --- a/crates/ra_ide/src/snapshots/highlighting.html +++ b/crates/ra_ide/src/snapshots/highlighting.html | |||
@@ -33,11 +33,13 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
33 | <span class="keyword">pub</span> <span class="field declaration">y</span>: <span class="builtin_type">i32</span>, | 33 | <span class="keyword">pub</span> <span class="field declaration">y</span>: <span class="builtin_type">i32</span>, |
34 | } | 34 | } |
35 | 35 | ||
36 | <span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable">STATIC_MUT</span>: <span class="builtin_type">i32</span> = <span class="numeric_literal">0</span>; | ||
37 | |||
36 | <span class="keyword">fn</span> <span class="function declaration">foo</span><<span class="lifetime declaration">'a</span>, <span class="type_param declaration">T</span>>() -> <span class="type_param">T</span> { | 38 | <span class="keyword">fn</span> <span class="function declaration">foo</span><<span class="lifetime declaration">'a</span>, <span class="type_param declaration">T</span>>() -> <span class="type_param">T</span> { |
37 | <span class="function">foo</span>::<<span class="lifetime">'a</span>, <span class="builtin_type">i32</span>>() | 39 | <span class="function">foo</span>::<<span class="lifetime">'a</span>, <span class="builtin_type">i32</span>>() |
38 | } | 40 | } |
39 | 41 | ||
40 | <span class="macro">macro_rules!</span> def_fn { | 42 | <span class="macro">macro_rules!</span> <span class="macro declaration">def_fn</span> { |
41 | ($($tt:tt)*) => {$($tt)*} | 43 | ($($tt:tt)*) => {$($tt)*} |
42 | } | 44 | } |
43 | 45 | ||
@@ -56,7 +58,10 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
56 | <span class="keyword">let</span> <span class="variable declaration">x</span> = <span class="numeric_literal">92</span>; | 58 | <span class="keyword">let</span> <span class="variable declaration">x</span> = <span class="numeric_literal">92</span>; |
57 | <span class="variable mutable">vec</span>.<span class="unresolved_reference">push</span>(<span class="struct">Foo</span> { <span class="field">x</span>, <span class="field">y</span>: <span class="numeric_literal">1</span> }); | 59 | <span class="variable mutable">vec</span>.<span class="unresolved_reference">push</span>(<span class="struct">Foo</span> { <span class="field">x</span>, <span class="field">y</span>: <span class="numeric_literal">1</span> }); |
58 | } | 60 | } |
59 | <span class="keyword unsafe">unsafe</span> { <span class="variable mutable">vec</span>.<span class="unresolved_reference">set_len</span>(<span class="numeric_literal">0</span>); } | 61 | <span class="keyword unsafe">unsafe</span> { |
62 | <span class="variable mutable">vec</span>.<span class="unresolved_reference">set_len</span>(<span class="numeric_literal">0</span>); | ||
63 | <span class="static mutable">STATIC_MUT</span> = <span class="numeric_literal">1</span>; | ||
64 | } | ||
60 | 65 | ||
61 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">x</span> = <span class="numeric_literal">42</span>; | 66 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">x</span> = <span class="numeric_literal">42</span>; |
62 | <span class="keyword">let</span> <span class="variable declaration mutable">y</span> = &<span class="keyword">mut</span> <span class="variable mutable">x</span>; | 67 | <span class="keyword">let</span> <span class="variable declaration mutable">y</span> = &<span class="keyword">mut</span> <span class="variable mutable">x</span>; |
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 6658c7bb2..d53a39f57 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs | |||
@@ -167,6 +167,19 @@ pub(crate) fn highlight( | |||
167 | binding_hash: None, | 167 | binding_hash: None, |
168 | }); | 168 | }); |
169 | } | 169 | } |
170 | if let Some(name) = mc.is_macro_rules() { | ||
171 | if let Some((highlight, binding_hash)) = highlight_element( | ||
172 | &sema, | ||
173 | &mut bindings_shadow_count, | ||
174 | name.syntax().clone().into(), | ||
175 | ) { | ||
176 | stack.add(HighlightedRange { | ||
177 | range: name.syntax().text_range(), | ||
178 | highlight, | ||
179 | binding_hash, | ||
180 | }); | ||
181 | } | ||
182 | } | ||
170 | continue; | 183 | continue; |
171 | } | 184 | } |
172 | WalkEvent::Leave(Some(mc)) => { | 185 | WalkEvent::Leave(Some(mc)) => { |
@@ -431,10 +444,16 @@ fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight { | |||
431 | hir::ModuleDef::Adt(hir::Adt::Union(_)) => HighlightTag::Union, | 444 | hir::ModuleDef::Adt(hir::Adt::Union(_)) => HighlightTag::Union, |
432 | hir::ModuleDef::EnumVariant(_) => HighlightTag::EnumVariant, | 445 | hir::ModuleDef::EnumVariant(_) => HighlightTag::EnumVariant, |
433 | hir::ModuleDef::Const(_) => HighlightTag::Constant, | 446 | hir::ModuleDef::Const(_) => HighlightTag::Constant, |
434 | hir::ModuleDef::Static(_) => HighlightTag::Static, | ||
435 | hir::ModuleDef::Trait(_) => HighlightTag::Trait, | 447 | hir::ModuleDef::Trait(_) => HighlightTag::Trait, |
436 | hir::ModuleDef::TypeAlias(_) => HighlightTag::TypeAlias, | 448 | hir::ModuleDef::TypeAlias(_) => HighlightTag::TypeAlias, |
437 | hir::ModuleDef::BuiltinType(_) => HighlightTag::BuiltinType, | 449 | hir::ModuleDef::BuiltinType(_) => HighlightTag::BuiltinType, |
450 | hir::ModuleDef::Static(s) => { | ||
451 | let mut h = Highlight::new(HighlightTag::Static); | ||
452 | if s.is_mut(db) { | ||
453 | h |= HighlightModifier::Mutable; | ||
454 | } | ||
455 | return h; | ||
456 | } | ||
438 | }, | 457 | }, |
439 | Definition::SelfType(_) => HighlightTag::SelfType, | 458 | Definition::SelfType(_) => HighlightTag::SelfType, |
440 | Definition::TypeParam(_) => HighlightTag::TypeParam, | 459 | Definition::TypeParam(_) => HighlightTag::TypeParam, |
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs index d2926ba78..13894869c 100644 --- a/crates/ra_ide/src/syntax_highlighting/tests.rs +++ b/crates/ra_ide/src/syntax_highlighting/tests.rs | |||
@@ -17,6 +17,8 @@ struct Foo { | |||
17 | pub y: i32, | 17 | pub y: i32, |
18 | } | 18 | } |
19 | 19 | ||
20 | static mut STATIC_MUT: i32 = 0; | ||
21 | |||
20 | fn foo<'a, T>() -> T { | 22 | fn foo<'a, T>() -> T { |
21 | foo::<'a, i32>() | 23 | foo::<'a, i32>() |
22 | } | 24 | } |
@@ -40,7 +42,10 @@ fn main() { | |||
40 | let x = 92; | 42 | let x = 92; |
41 | vec.push(Foo { x, y: 1 }); | 43 | vec.push(Foo { x, y: 1 }); |
42 | } | 44 | } |
43 | unsafe { vec.set_len(0); } | 45 | unsafe { |
46 | vec.set_len(0); | ||
47 | STATIC_MUT = 1; | ||
48 | } | ||
44 | 49 | ||
45 | let mut x = 42; | 50 | let mut x = 42; |
46 | let y = &mut x; | 51 | let y = &mut x; |
diff --git a/crates/ra_proc_macro_srv/Cargo.toml b/crates/ra_proc_macro_srv/Cargo.toml index 886e14870..bb3003278 100644 --- a/crates/ra_proc_macro_srv/Cargo.toml +++ b/crates/ra_proc_macro_srv/Cargo.toml | |||
@@ -18,7 +18,7 @@ memmap = "0.7" | |||
18 | test_utils = { path = "../test_utils" } | 18 | test_utils = { path = "../test_utils" } |
19 | 19 | ||
20 | [dev-dependencies] | 20 | [dev-dependencies] |
21 | cargo_metadata = "0.9.1" | 21 | cargo_metadata = "0.10.0" |
22 | difference = "2.0.0" | 22 | difference = "2.0.0" |
23 | # used as proc macro test target | 23 | # used as proc macro test target |
24 | serde_derive = "=1.0.106" | 24 | serde_derive = "1.0.106" |
diff --git a/crates/ra_proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt b/crates/ra_proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt index 6776f5231..bc010cfe9 100644 --- a/crates/ra_proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt +++ b/crates/ra_proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt | |||
@@ -23,23 +23,12 @@ SUBTREE $ | |||
23 | SUBTREE [] 4294967295 | 23 | SUBTREE [] 4294967295 |
24 | IDENT allow 4294967295 | 24 | IDENT allow 4294967295 |
25 | SUBTREE () 4294967295 | 25 | SUBTREE () 4294967295 |
26 | IDENT unknown_lints 4294967295 | ||
27 | PUNCH # [alone] 4294967295 | ||
28 | SUBTREE [] 4294967295 | ||
29 | IDENT cfg_attr 4294967295 | ||
30 | SUBTREE () 4294967295 | ||
31 | IDENT feature 4294967295 | ||
32 | PUNCH = [alone] 4294967295 | ||
33 | LITERAL "cargo-clippy" 0 | ||
34 | PUNCH , [alone] 4294967295 | ||
35 | IDENT allow 4294967295 | ||
36 | SUBTREE () 4294967295 | ||
37 | IDENT useless_attribute 4294967295 | ||
38 | PUNCH # [alone] 4294967295 | ||
39 | SUBTREE [] 4294967295 | ||
40 | IDENT allow 4294967295 | ||
41 | SUBTREE () 4294967295 | ||
42 | IDENT rust_2018_idioms 4294967295 | 26 | IDENT rust_2018_idioms 4294967295 |
27 | PUNCH , [alone] 4294967295 | ||
28 | IDENT clippy 4294967295 | ||
29 | PUNCH : [joint] 4294967295 | ||
30 | PUNCH : [alone] 4294967295 | ||
31 | IDENT useless_attribute 4294967295 | ||
43 | IDENT extern 4294967295 | 32 | IDENT extern 4294967295 |
44 | IDENT crate 4294967295 | 33 | IDENT crate 4294967295 |
45 | IDENT serde 4294967295 | 34 | IDENT serde 4294967295 |
diff --git a/crates/ra_proc_macro_srv/src/tests/mod.rs b/crates/ra_proc_macro_srv/src/tests/mod.rs index 9cf58511c..82cefbb29 100644 --- a/crates/ra_proc_macro_srv/src/tests/mod.rs +++ b/crates/ra_proc_macro_srv/src/tests/mod.rs | |||
@@ -10,7 +10,7 @@ fn test_derive_serialize_proc_macro() { | |||
10 | assert_expand( | 10 | assert_expand( |
11 | "serde_derive", | 11 | "serde_derive", |
12 | "Serialize", | 12 | "Serialize", |
13 | "1.0.106", | 13 | "1.0", |
14 | r##"struct Foo {}"##, | 14 | r##"struct Foo {}"##, |
15 | include_str!("fixtures/test_serialize_proc_macro.txt"), | 15 | include_str!("fixtures/test_serialize_proc_macro.txt"), |
16 | ); | 16 | ); |
@@ -21,7 +21,7 @@ fn test_derive_serialize_proc_macro_failed() { | |||
21 | assert_expand( | 21 | assert_expand( |
22 | "serde_derive", | 22 | "serde_derive", |
23 | "Serialize", | 23 | "Serialize", |
24 | "1.0.106", | 24 | "1.0", |
25 | r##" | 25 | r##" |
26 | struct {} | 26 | struct {} |
27 | "##, | 27 | "##, |
@@ -37,7 +37,7 @@ SUBTREE $ | |||
37 | 37 | ||
38 | #[test] | 38 | #[test] |
39 | fn test_derive_proc_macro_list() { | 39 | fn test_derive_proc_macro_list() { |
40 | let res = list("serde_derive", "1.0.106").join("\n"); | 40 | let res = list("serde_derive", "1.0").join("\n"); |
41 | 41 | ||
42 | assert_eq_text!( | 42 | assert_eq_text!( |
43 | &res, | 43 | &res, |
diff --git a/crates/ra_proc_macro_srv/src/tests/utils.rs b/crates/ra_proc_macro_srv/src/tests/utils.rs index 646a427c5..84348b5de 100644 --- a/crates/ra_proc_macro_srv/src/tests/utils.rs +++ b/crates/ra_proc_macro_srv/src/tests/utils.rs | |||
@@ -8,7 +8,7 @@ use std::str::FromStr; | |||
8 | use test_utils::assert_eq_text; | 8 | use test_utils::assert_eq_text; |
9 | 9 | ||
10 | mod fixtures { | 10 | mod fixtures { |
11 | use cargo_metadata::{parse_messages, Message}; | 11 | use cargo_metadata::Message; |
12 | use std::process::Command; | 12 | use std::process::Command; |
13 | 13 | ||
14 | // Use current project metadata to get the proc-macro dylib path | 14 | // Use current project metadata to get the proc-macro dylib path |
@@ -19,7 +19,7 @@ mod fixtures { | |||
19 | .unwrap() | 19 | .unwrap() |
20 | .stdout; | 20 | .stdout; |
21 | 21 | ||
22 | for message in parse_messages(command.as_slice()) { | 22 | for message in Message::parse_stream(command.as_slice()) { |
23 | match message.unwrap() { | 23 | match message.unwrap() { |
24 | Message::CompilerArtifact(artifact) => { | 24 | Message::CompilerArtifact(artifact) => { |
25 | if artifact.target.kind.contains(&"proc-macro".to_string()) { | 25 | if artifact.target.kind.contains(&"proc-macro".to_string()) { |
diff --git a/crates/ra_project_model/Cargo.toml b/crates/ra_project_model/Cargo.toml index a32a5daab..e4a60f4c0 100644 --- a/crates/ra_project_model/Cargo.toml +++ b/crates/ra_project_model/Cargo.toml | |||
@@ -11,7 +11,7 @@ doctest = false | |||
11 | log = "0.4.8" | 11 | log = "0.4.8" |
12 | rustc-hash = "1.1.0" | 12 | rustc-hash = "1.1.0" |
13 | 13 | ||
14 | cargo_metadata = "0.9.1" | 14 | cargo_metadata = "0.10.0" |
15 | 15 | ||
16 | ra_arena = { path = "../ra_arena" } | 16 | ra_arena = { path = "../ra_arena" } |
17 | ra_cfg = { path = "../ra_cfg" } | 17 | ra_cfg = { path = "../ra_cfg" } |
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index 082af4f96..a306ce95f 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs | |||
@@ -161,7 +161,7 @@ impl CargoWorkspace { | |||
161 | meta.current_dir(parent); | 161 | meta.current_dir(parent); |
162 | } | 162 | } |
163 | if let Some(target) = cargo_features.target.as_ref() { | 163 | if let Some(target) = cargo_features.target.as_ref() { |
164 | meta.other_options(&[String::from("--filter-platform"), target.clone()]); | 164 | meta.other_options(vec![String::from("--filter-platform"), target.clone()]); |
165 | } | 165 | } |
166 | let meta = meta.exec().with_context(|| { | 166 | let meta = meta.exec().with_context(|| { |
167 | format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display()) | 167 | format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display()) |
@@ -304,19 +304,13 @@ pub fn load_extern_resources( | |||
304 | 304 | ||
305 | let mut res = ExternResources::default(); | 305 | let mut res = ExternResources::default(); |
306 | 306 | ||
307 | for message in cargo_metadata::parse_messages(output.stdout.as_slice()) { | 307 | for message in cargo_metadata::Message::parse_stream(output.stdout.as_slice()) { |
308 | if let Ok(message) = message { | 308 | if let Ok(message) = message { |
309 | match message { | 309 | match message { |
310 | Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => { | 310 | Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => { |
311 | res.out_dirs.insert(package_id.clone(), out_dir); | 311 | res.out_dirs.insert(package_id.clone(), out_dir); |
312 | res.cfgs.insert( | 312 | res.cfgs.insert(package_id, cfgs); |
313 | package_id, | ||
314 | // FIXME: Current `cargo_metadata` uses `PathBuf` instead of `String`, | ||
315 | // change when https://github.com/oli-obk/cargo_metadata/pulls/112 reaches crates.io | ||
316 | cfgs.iter().filter_map(|c| c.to_str().map(|s| s.to_owned())).collect(), | ||
317 | ); | ||
318 | } | 313 | } |
319 | |||
320 | Message::CompilerArtifact(message) => { | 314 | Message::CompilerArtifact(message) => { |
321 | if message.target.kind.contains(&"proc-macro".to_string()) { | 315 | if message.target.kind.contains(&"proc-macro".to_string()) { |
322 | let package_id = message.package_id; | 316 | let package_id = message.package_id; |
@@ -329,6 +323,8 @@ pub fn load_extern_resources( | |||
329 | } | 323 | } |
330 | Message::CompilerMessage(_) => (), | 324 | Message::CompilerMessage(_) => (), |
331 | Message::Unknown => (), | 325 | Message::Unknown => (), |
326 | Message::BuildFinished(_) => {} | ||
327 | Message::TextLine(_) => {} | ||
332 | } | 328 | } |
333 | } | 329 | } |
334 | } | 330 | } |
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs index 3e6dd6061..24a1e1d91 100644 --- a/crates/ra_syntax/src/ast/edit.rs +++ b/crates/ra_syntax/src/ast/edit.rs | |||
@@ -453,11 +453,7 @@ impl IndentLevel { | |||
453 | IndentLevel(0) | 453 | IndentLevel(0) |
454 | } | 454 | } |
455 | 455 | ||
456 | pub fn increase_indent<N: AstNode>(self, node: N) -> N { | 456 | fn increase_indent(self, node: SyntaxNode) -> SyntaxNode { |
457 | N::cast(self._increase_indent(node.syntax().clone())).unwrap() | ||
458 | } | ||
459 | |||
460 | fn _increase_indent(self, node: SyntaxNode) -> SyntaxNode { | ||
461 | let mut rewriter = SyntaxRewriter::default(); | 457 | let mut rewriter = SyntaxRewriter::default(); |
462 | node.descendants_with_tokens() | 458 | node.descendants_with_tokens() |
463 | .filter_map(|el| el.into_token()) | 459 | .filter_map(|el| el.into_token()) |
@@ -478,11 +474,7 @@ impl IndentLevel { | |||
478 | rewriter.rewrite(&node) | 474 | rewriter.rewrite(&node) |
479 | } | 475 | } |
480 | 476 | ||
481 | pub fn decrease_indent<N: AstNode>(self, node: N) -> N { | 477 | fn decrease_indent(self, node: SyntaxNode) -> SyntaxNode { |
482 | N::cast(self._decrease_indent(node.syntax().clone())).unwrap() | ||
483 | } | ||
484 | |||
485 | fn _decrease_indent(self, node: SyntaxNode) -> SyntaxNode { | ||
486 | let mut rewriter = SyntaxRewriter::default(); | 478 | let mut rewriter = SyntaxRewriter::default(); |
487 | node.descendants_with_tokens() | 479 | node.descendants_with_tokens() |
488 | .filter_map(|el| el.into_token()) | 480 | .filter_map(|el| el.into_token()) |
@@ -521,7 +513,7 @@ fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> { | |||
521 | iter::successors(Some(token), |token| token.prev_token()) | 513 | iter::successors(Some(token), |token| token.prev_token()) |
522 | } | 514 | } |
523 | 515 | ||
524 | pub trait AstNodeEdit: AstNode + Sized { | 516 | pub trait AstNodeEdit: AstNode + Clone + Sized { |
525 | #[must_use] | 517 | #[must_use] |
526 | fn insert_children( | 518 | fn insert_children( |
527 | &self, | 519 | &self, |
@@ -558,9 +550,17 @@ pub trait AstNodeEdit: AstNode + Sized { | |||
558 | } | 550 | } |
559 | rewriter.rewrite_ast(self) | 551 | rewriter.rewrite_ast(self) |
560 | } | 552 | } |
553 | #[must_use] | ||
554 | fn indent(&self, indent: IndentLevel) -> Self { | ||
555 | Self::cast(indent.increase_indent(self.syntax().clone())).unwrap() | ||
556 | } | ||
557 | #[must_use] | ||
558 | fn dedent(&self, indent: IndentLevel) -> Self { | ||
559 | Self::cast(indent.decrease_indent(self.syntax().clone())).unwrap() | ||
560 | } | ||
561 | } | 561 | } |
562 | 562 | ||
563 | impl<N: AstNode> AstNodeEdit for N {} | 563 | impl<N: AstNode + Clone> AstNodeEdit for N {} |
564 | 564 | ||
565 | fn single_node(element: impl Into<SyntaxElement>) -> RangeInclusive<SyntaxElement> { | 565 | fn single_node(element: impl Into<SyntaxElement>) -> RangeInclusive<SyntaxElement> { |
566 | let element = element.into(); | 566 | let element = element.into(); |
@@ -580,7 +580,7 @@ fn test_increase_indent() { | |||
580 | _ => (), | 580 | _ => (), |
581 | }" | 581 | }" |
582 | ); | 582 | ); |
583 | let indented = IndentLevel(2).increase_indent(arm_list); | 583 | let indented = arm_list.indent(IndentLevel(2)); |
584 | assert_eq!( | 584 | assert_eq!( |
585 | indented.syntax().to_string(), | 585 | indented.syntax().to_string(), |
586 | "{ | 586 | "{ |
diff --git a/crates/ra_text_edit/src/lib.rs b/crates/ra_text_edit/src/lib.rs index 3409713ff..37f77cc47 100644 --- a/crates/ra_text_edit/src/lib.rs +++ b/crates/ra_text_edit/src/lib.rs | |||
@@ -75,6 +75,7 @@ impl TextEdit { | |||
75 | self.indels.is_empty() | 75 | self.indels.is_empty() |
76 | } | 76 | } |
77 | 77 | ||
78 | // FXME: impl IntoIter instead | ||
78 | pub fn as_indels(&self) -> &[Indel] { | 79 | pub fn as_indels(&self) -> &[Indel] { |
79 | &self.indels | 80 | &self.indels |
80 | } | 81 | } |
diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs deleted file mode 100644 index f64c90b5b..000000000 --- a/crates/rust-analyzer/src/conv.rs +++ /dev/null | |||
@@ -1,726 +0,0 @@ | |||
1 | //! Convenience module responsible for translating between rust-analyzer's types | ||
2 | //! and LSP types. | ||
3 | |||
4 | use lsp_types::{ | ||
5 | self, CreateFile, DiagnosticSeverity, DocumentChangeOperation, DocumentChanges, Documentation, | ||
6 | Location, LocationLink, MarkupContent, MarkupKind, ParameterInformation, ParameterLabel, | ||
7 | Position, Range, RenameFile, ResourceOp, SemanticTokenModifier, SemanticTokenType, | ||
8 | SignatureInformation, SymbolKind, TextDocumentEdit, TextDocumentIdentifier, TextDocumentItem, | ||
9 | TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, WorkspaceEdit, | ||
10 | }; | ||
11 | use ra_ide::{ | ||
12 | translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition, | ||
13 | FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HighlightModifier, HighlightTag, | ||
14 | InlayHint, InlayKind, InsertTextFormat, LineCol, LineIndex, NavigationTarget, RangeInfo, | ||
15 | ReferenceAccess, Severity, SourceChange, SourceFileEdit, | ||
16 | }; | ||
17 | use ra_syntax::{SyntaxKind, TextRange, TextSize}; | ||
18 | use ra_text_edit::{Indel, TextEdit}; | ||
19 | use ra_vfs::LineEndings; | ||
20 | |||
21 | use crate::{ | ||
22 | req, | ||
23 | semantic_tokens::{self, ModifierSet, CONSTANT, CONTROL_FLOW, MUTABLE, UNSAFE}, | ||
24 | world::WorldSnapshot, | ||
25 | Result, | ||
26 | }; | ||
27 | use semantic_tokens::{ | ||
28 | ATTRIBUTE, BUILTIN_TYPE, ENUM_MEMBER, FORMAT_SPECIFIER, LIFETIME, TYPE_ALIAS, UNION, | ||
29 | UNRESOLVED_REFERENCE, | ||
30 | }; | ||
31 | |||
32 | pub trait Conv { | ||
33 | type Output; | ||
34 | fn conv(self) -> Self::Output; | ||
35 | } | ||
36 | |||
37 | pub trait ConvWith<CTX> { | ||
38 | type Output; | ||
39 | fn conv_with(self, ctx: CTX) -> Self::Output; | ||
40 | } | ||
41 | |||
42 | pub trait TryConvWith<CTX> { | ||
43 | type Output; | ||
44 | fn try_conv_with(self, ctx: CTX) -> Result<Self::Output>; | ||
45 | } | ||
46 | |||
47 | impl Conv for SyntaxKind { | ||
48 | type Output = SymbolKind; | ||
49 | |||
50 | fn conv(self) -> <Self as Conv>::Output { | ||
51 | match self { | ||
52 | SyntaxKind::FN_DEF => SymbolKind::Function, | ||
53 | SyntaxKind::STRUCT_DEF => SymbolKind::Struct, | ||
54 | SyntaxKind::ENUM_DEF => SymbolKind::Enum, | ||
55 | SyntaxKind::ENUM_VARIANT => SymbolKind::EnumMember, | ||
56 | SyntaxKind::TRAIT_DEF => SymbolKind::Interface, | ||
57 | SyntaxKind::MACRO_CALL => SymbolKind::Function, | ||
58 | SyntaxKind::MODULE => SymbolKind::Module, | ||
59 | SyntaxKind::TYPE_ALIAS_DEF => SymbolKind::TypeParameter, | ||
60 | SyntaxKind::RECORD_FIELD_DEF => SymbolKind::Field, | ||
61 | SyntaxKind::STATIC_DEF => SymbolKind::Constant, | ||
62 | SyntaxKind::CONST_DEF => SymbolKind::Constant, | ||
63 | SyntaxKind::IMPL_DEF => SymbolKind::Object, | ||
64 | _ => SymbolKind::Variable, | ||
65 | } | ||
66 | } | ||
67 | } | ||
68 | |||
69 | impl Conv for ReferenceAccess { | ||
70 | type Output = ::lsp_types::DocumentHighlightKind; | ||
71 | |||
72 | fn conv(self) -> Self::Output { | ||
73 | use lsp_types::DocumentHighlightKind; | ||
74 | match self { | ||
75 | ReferenceAccess::Read => DocumentHighlightKind::Read, | ||
76 | ReferenceAccess::Write => DocumentHighlightKind::Write, | ||
77 | } | ||
78 | } | ||
79 | } | ||
80 | |||
81 | impl Conv for CompletionItemKind { | ||
82 | type Output = ::lsp_types::CompletionItemKind; | ||
83 | |||
84 | fn conv(self) -> <Self as Conv>::Output { | ||
85 | use lsp_types::CompletionItemKind::*; | ||
86 | match self { | ||
87 | CompletionItemKind::Keyword => Keyword, | ||
88 | CompletionItemKind::Snippet => Snippet, | ||
89 | CompletionItemKind::Module => Module, | ||
90 | CompletionItemKind::Function => Function, | ||
91 | CompletionItemKind::Struct => Struct, | ||
92 | CompletionItemKind::Enum => Enum, | ||
93 | CompletionItemKind::EnumVariant => EnumMember, | ||
94 | CompletionItemKind::BuiltinType => Struct, | ||
95 | CompletionItemKind::Binding => Variable, | ||
96 | CompletionItemKind::Field => Field, | ||
97 | CompletionItemKind::Trait => Interface, | ||
98 | CompletionItemKind::TypeAlias => Struct, | ||
99 | CompletionItemKind::Const => Constant, | ||
100 | CompletionItemKind::Static => Value, | ||
101 | CompletionItemKind::Method => Method, | ||
102 | CompletionItemKind::TypeParam => TypeParameter, | ||
103 | CompletionItemKind::Macro => Method, | ||
104 | CompletionItemKind::Attribute => EnumMember, | ||
105 | } | ||
106 | } | ||
107 | } | ||
108 | |||
109 | impl Conv for Severity { | ||
110 | type Output = DiagnosticSeverity; | ||
111 | fn conv(self) -> DiagnosticSeverity { | ||
112 | match self { | ||
113 | Severity::Error => DiagnosticSeverity::Error, | ||
114 | Severity::WeakWarning => DiagnosticSeverity::Hint, | ||
115 | } | ||
116 | } | ||
117 | } | ||
118 | |||
119 | impl ConvWith<(&LineIndex, LineEndings)> for CompletionItem { | ||
120 | type Output = ::lsp_types::CompletionItem; | ||
121 | |||
122 | fn conv_with(self, ctx: (&LineIndex, LineEndings)) -> ::lsp_types::CompletionItem { | ||
123 | let mut additional_text_edits = Vec::new(); | ||
124 | let mut text_edit = None; | ||
125 | // LSP does not allow arbitrary edits in completion, so we have to do a | ||
126 | // non-trivial mapping here. | ||
127 | for indel in self.text_edit().as_indels() { | ||
128 | if indel.delete.contains_range(self.source_range()) { | ||
129 | text_edit = Some(if indel.delete == self.source_range() { | ||
130 | indel.conv_with((ctx.0, ctx.1)) | ||
131 | } else { | ||
132 | assert!(self.source_range().end() == indel.delete.end()); | ||
133 | let range1 = TextRange::new(indel.delete.start(), self.source_range().start()); | ||
134 | let range2 = self.source_range(); | ||
135 | let edit1 = Indel::replace(range1, String::new()); | ||
136 | let edit2 = Indel::replace(range2, indel.insert.clone()); | ||
137 | additional_text_edits.push(edit1.conv_with((ctx.0, ctx.1))); | ||
138 | edit2.conv_with((ctx.0, ctx.1)) | ||
139 | }) | ||
140 | } else { | ||
141 | assert!(self.source_range().intersect(indel.delete).is_none()); | ||
142 | additional_text_edits.push(indel.conv_with((ctx.0, ctx.1))); | ||
143 | } | ||
144 | } | ||
145 | let text_edit = text_edit.unwrap(); | ||
146 | |||
147 | let mut res = lsp_types::CompletionItem { | ||
148 | label: self.label().to_string(), | ||
149 | detail: self.detail().map(|it| it.to_string()), | ||
150 | filter_text: Some(self.lookup().to_string()), | ||
151 | kind: self.kind().map(|it| it.conv()), | ||
152 | text_edit: Some(text_edit.into()), | ||
153 | additional_text_edits: Some(additional_text_edits), | ||
154 | documentation: self.documentation().map(|it| it.conv()), | ||
155 | deprecated: Some(self.deprecated()), | ||
156 | command: if self.trigger_call_info() { | ||
157 | let cmd = lsp_types::Command { | ||
158 | title: "triggerParameterHints".into(), | ||
159 | command: "editor.action.triggerParameterHints".into(), | ||
160 | arguments: None, | ||
161 | }; | ||
162 | Some(cmd) | ||
163 | } else { | ||
164 | None | ||
165 | }, | ||
166 | ..Default::default() | ||
167 | }; | ||
168 | |||
169 | if self.score().is_some() { | ||
170 | res.preselect = Some(true) | ||
171 | } | ||
172 | |||
173 | if self.deprecated() { | ||
174 | res.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated]) | ||
175 | } | ||
176 | |||
177 | res.insert_text_format = Some(match self.insert_text_format() { | ||
178 | InsertTextFormat::Snippet => lsp_types::InsertTextFormat::Snippet, | ||
179 | InsertTextFormat::PlainText => lsp_types::InsertTextFormat::PlainText, | ||
180 | }); | ||
181 | |||
182 | res | ||
183 | } | ||
184 | } | ||
185 | |||
186 | impl ConvWith<&LineIndex> for Position { | ||
187 | type Output = TextSize; | ||
188 | |||
189 | fn conv_with(self, line_index: &LineIndex) -> TextSize { | ||
190 | let line_col = LineCol { line: self.line as u32, col_utf16: self.character as u32 }; | ||
191 | line_index.offset(line_col) | ||
192 | } | ||
193 | } | ||
194 | |||
195 | impl ConvWith<&LineIndex> for TextSize { | ||
196 | type Output = Position; | ||
197 | |||
198 | fn conv_with(self, line_index: &LineIndex) -> Position { | ||
199 | let line_col = line_index.line_col(self); | ||
200 | Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16)) | ||
201 | } | ||
202 | } | ||
203 | |||
204 | impl ConvWith<&LineIndex> for TextRange { | ||
205 | type Output = Range; | ||
206 | |||
207 | fn conv_with(self, line_index: &LineIndex) -> Range { | ||
208 | Range::new(self.start().conv_with(line_index), self.end().conv_with(line_index)) | ||
209 | } | ||
210 | } | ||
211 | |||
212 | impl ConvWith<&LineIndex> for Range { | ||
213 | type Output = TextRange; | ||
214 | |||
215 | fn conv_with(self, line_index: &LineIndex) -> TextRange { | ||
216 | TextRange::new(self.start.conv_with(line_index), self.end.conv_with(line_index)) | ||
217 | } | ||
218 | } | ||
219 | |||
220 | impl Conv for ra_ide::Documentation { | ||
221 | type Output = lsp_types::Documentation; | ||
222 | fn conv(self) -> Documentation { | ||
223 | Documentation::MarkupContent(MarkupContent { | ||
224 | kind: MarkupKind::Markdown, | ||
225 | value: crate::markdown::format_docs(self.as_str()), | ||
226 | }) | ||
227 | } | ||
228 | } | ||
229 | |||
230 | impl ConvWith<bool> for ra_ide::FunctionSignature { | ||
231 | type Output = lsp_types::SignatureInformation; | ||
232 | fn conv_with(self, concise: bool) -> Self::Output { | ||
233 | let (label, documentation, params) = if concise { | ||
234 | let mut params = self.parameters; | ||
235 | if self.has_self_param { | ||
236 | params.remove(0); | ||
237 | } | ||
238 | (params.join(", "), None, params) | ||
239 | } else { | ||
240 | (self.to_string(), self.doc.map(|it| it.conv()), self.parameters) | ||
241 | }; | ||
242 | |||
243 | let parameters: Vec<ParameterInformation> = params | ||
244 | .into_iter() | ||
245 | .map(|param| ParameterInformation { | ||
246 | label: ParameterLabel::Simple(param), | ||
247 | documentation: None, | ||
248 | }) | ||
249 | .collect(); | ||
250 | |||
251 | SignatureInformation { label, documentation, parameters: Some(parameters) } | ||
252 | } | ||
253 | } | ||
254 | |||
255 | impl ConvWith<(&LineIndex, LineEndings)> for TextEdit { | ||
256 | type Output = Vec<lsp_types::TextEdit>; | ||
257 | |||
258 | fn conv_with(self, ctx: (&LineIndex, LineEndings)) -> Vec<lsp_types::TextEdit> { | ||
259 | self.as_indels().iter().map_conv_with(ctx).collect() | ||
260 | } | ||
261 | } | ||
262 | |||
263 | impl ConvWith<(&LineIndex, LineEndings)> for &Indel { | ||
264 | type Output = lsp_types::TextEdit; | ||
265 | |||
266 | fn conv_with( | ||
267 | self, | ||
268 | (line_index, line_endings): (&LineIndex, LineEndings), | ||
269 | ) -> lsp_types::TextEdit { | ||
270 | let mut new_text = self.insert.clone(); | ||
271 | if line_endings == LineEndings::Dos { | ||
272 | new_text = new_text.replace('\n', "\r\n"); | ||
273 | } | ||
274 | lsp_types::TextEdit { range: self.delete.conv_with(line_index), new_text } | ||
275 | } | ||
276 | } | ||
277 | |||
278 | pub(crate) struct FoldConvCtx<'a> { | ||
279 | pub(crate) text: &'a str, | ||
280 | pub(crate) line_index: &'a LineIndex, | ||
281 | pub(crate) line_folding_only: bool, | ||
282 | } | ||
283 | |||
284 | impl ConvWith<&FoldConvCtx<'_>> for Fold { | ||
285 | type Output = lsp_types::FoldingRange; | ||
286 | |||
287 | fn conv_with(self, ctx: &FoldConvCtx) -> lsp_types::FoldingRange { | ||
288 | let kind = match self.kind { | ||
289 | FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment), | ||
290 | FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports), | ||
291 | FoldKind::Mods => None, | ||
292 | FoldKind::Block => None, | ||
293 | }; | ||
294 | |||
295 | let range = self.range.conv_with(&ctx.line_index); | ||
296 | |||
297 | if ctx.line_folding_only { | ||
298 | // Clients with line_folding_only == true (such as VSCode) will fold the whole end line | ||
299 | // even if it contains text not in the folding range. To prevent that we exclude | ||
300 | // range.end.line from the folding region if there is more text after range.end | ||
301 | // on the same line. | ||
302 | let has_more_text_on_end_line = ctx.text | ||
303 | [TextRange::new(self.range.end(), TextSize::of(ctx.text))] | ||
304 | .chars() | ||
305 | .take_while(|it| *it != '\n') | ||
306 | .any(|it| !it.is_whitespace()); | ||
307 | |||
308 | let end_line = if has_more_text_on_end_line { | ||
309 | range.end.line.saturating_sub(1) | ||
310 | } else { | ||
311 | range.end.line | ||
312 | }; | ||
313 | |||
314 | lsp_types::FoldingRange { | ||
315 | start_line: range.start.line, | ||
316 | start_character: None, | ||
317 | end_line, | ||
318 | end_character: None, | ||
319 | kind, | ||
320 | } | ||
321 | } else { | ||
322 | lsp_types::FoldingRange { | ||
323 | start_line: range.start.line, | ||
324 | start_character: Some(range.start.character), | ||
325 | end_line: range.end.line, | ||
326 | end_character: Some(range.end.character), | ||
327 | kind, | ||
328 | } | ||
329 | } | ||
330 | } | ||
331 | } | ||
332 | |||
333 | impl ConvWith<&LineIndex> for InlayHint { | ||
334 | type Output = req::InlayHint; | ||
335 | fn conv_with(self, line_index: &LineIndex) -> Self::Output { | ||
336 | req::InlayHint { | ||
337 | label: self.label.to_string(), | ||
338 | range: self.range.conv_with(line_index), | ||
339 | kind: match self.kind { | ||
340 | InlayKind::ParameterHint => req::InlayKind::ParameterHint, | ||
341 | InlayKind::TypeHint => req::InlayKind::TypeHint, | ||
342 | InlayKind::ChainingHint => req::InlayKind::ChainingHint, | ||
343 | }, | ||
344 | } | ||
345 | } | ||
346 | } | ||
347 | |||
348 | impl Conv for Highlight { | ||
349 | type Output = (u32, u32); | ||
350 | |||
351 | fn conv(self) -> Self::Output { | ||
352 | let mut mods = ModifierSet::default(); | ||
353 | let type_ = match self.tag { | ||
354 | HighlightTag::Struct => SemanticTokenType::STRUCT, | ||
355 | HighlightTag::Enum => SemanticTokenType::ENUM, | ||
356 | HighlightTag::Union => UNION, | ||
357 | HighlightTag::TypeAlias => TYPE_ALIAS, | ||
358 | HighlightTag::Trait => SemanticTokenType::INTERFACE, | ||
359 | HighlightTag::BuiltinType => BUILTIN_TYPE, | ||
360 | HighlightTag::SelfType => SemanticTokenType::TYPE, | ||
361 | HighlightTag::Field => SemanticTokenType::MEMBER, | ||
362 | HighlightTag::Function => SemanticTokenType::FUNCTION, | ||
363 | HighlightTag::Module => SemanticTokenType::NAMESPACE, | ||
364 | HighlightTag::Constant => { | ||
365 | mods |= CONSTANT; | ||
366 | mods |= SemanticTokenModifier::STATIC; | ||
367 | SemanticTokenType::VARIABLE | ||
368 | } | ||
369 | HighlightTag::Static => { | ||
370 | mods |= SemanticTokenModifier::STATIC; | ||
371 | SemanticTokenType::VARIABLE | ||
372 | } | ||
373 | HighlightTag::EnumVariant => ENUM_MEMBER, | ||
374 | HighlightTag::Macro => SemanticTokenType::MACRO, | ||
375 | HighlightTag::Local => SemanticTokenType::VARIABLE, | ||
376 | HighlightTag::TypeParam => SemanticTokenType::TYPE_PARAMETER, | ||
377 | HighlightTag::Lifetime => LIFETIME, | ||
378 | HighlightTag::ByteLiteral | HighlightTag::NumericLiteral => SemanticTokenType::NUMBER, | ||
379 | HighlightTag::CharLiteral | HighlightTag::StringLiteral => SemanticTokenType::STRING, | ||
380 | HighlightTag::Comment => SemanticTokenType::COMMENT, | ||
381 | HighlightTag::Attribute => ATTRIBUTE, | ||
382 | HighlightTag::Keyword => SemanticTokenType::KEYWORD, | ||
383 | HighlightTag::UnresolvedReference => UNRESOLVED_REFERENCE, | ||
384 | HighlightTag::FormatSpecifier => FORMAT_SPECIFIER, | ||
385 | }; | ||
386 | |||
387 | for modifier in self.modifiers.iter() { | ||
388 | let modifier = match modifier { | ||
389 | HighlightModifier::Definition => SemanticTokenModifier::DECLARATION, | ||
390 | HighlightModifier::ControlFlow => CONTROL_FLOW, | ||
391 | HighlightModifier::Mutable => MUTABLE, | ||
392 | HighlightModifier::Unsafe => UNSAFE, | ||
393 | }; | ||
394 | mods |= modifier; | ||
395 | } | ||
396 | |||
397 | (semantic_tokens::type_index(type_), mods.0) | ||
398 | } | ||
399 | } | ||
400 | |||
401 | impl<T: ConvWith<CTX>, CTX> ConvWith<CTX> for Option<T> { | ||
402 | type Output = Option<T::Output>; | ||
403 | |||
404 | fn conv_with(self, ctx: CTX) -> Self::Output { | ||
405 | self.map(|x| ConvWith::conv_with(x, ctx)) | ||
406 | } | ||
407 | } | ||
408 | |||
409 | impl TryConvWith<&WorldSnapshot> for &Url { | ||
410 | type Output = FileId; | ||
411 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileId> { | ||
412 | world.uri_to_file_id(self) | ||
413 | } | ||
414 | } | ||
415 | |||
416 | impl TryConvWith<&WorldSnapshot> for FileId { | ||
417 | type Output = Url; | ||
418 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<Url> { | ||
419 | world.file_id_to_uri(self) | ||
420 | } | ||
421 | } | ||
422 | |||
423 | impl TryConvWith<&WorldSnapshot> for &TextDocumentItem { | ||
424 | type Output = FileId; | ||
425 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileId> { | ||
426 | self.uri.try_conv_with(world) | ||
427 | } | ||
428 | } | ||
429 | |||
430 | impl TryConvWith<&WorldSnapshot> for &VersionedTextDocumentIdentifier { | ||
431 | type Output = FileId; | ||
432 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileId> { | ||
433 | self.uri.try_conv_with(world) | ||
434 | } | ||
435 | } | ||
436 | |||
437 | impl TryConvWith<&WorldSnapshot> for &TextDocumentIdentifier { | ||
438 | type Output = FileId; | ||
439 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileId> { | ||
440 | world.uri_to_file_id(&self.uri) | ||
441 | } | ||
442 | } | ||
443 | |||
444 | impl TryConvWith<&WorldSnapshot> for &TextDocumentPositionParams { | ||
445 | type Output = FilePosition; | ||
446 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<FilePosition> { | ||
447 | let file_id = self.text_document.try_conv_with(world)?; | ||
448 | let line_index = world.analysis().file_line_index(file_id)?; | ||
449 | let offset = self.position.conv_with(&line_index); | ||
450 | Ok(FilePosition { file_id, offset }) | ||
451 | } | ||
452 | } | ||
453 | |||
454 | impl TryConvWith<&WorldSnapshot> for (&TextDocumentIdentifier, Range) { | ||
455 | type Output = FileRange; | ||
456 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileRange> { | ||
457 | let file_id = self.0.try_conv_with(world)?; | ||
458 | let line_index = world.analysis().file_line_index(file_id)?; | ||
459 | let range = self.1.conv_with(&line_index); | ||
460 | Ok(FileRange { file_id, range }) | ||
461 | } | ||
462 | } | ||
463 | |||
464 | impl<T: TryConvWith<CTX>, CTX: Copy> TryConvWith<CTX> for Vec<T> { | ||
465 | type Output = Vec<<T as TryConvWith<CTX>>::Output>; | ||
466 | fn try_conv_with(self, ctx: CTX) -> Result<Self::Output> { | ||
467 | let mut res = Vec::with_capacity(self.len()); | ||
468 | for item in self { | ||
469 | res.push(item.try_conv_with(ctx)?); | ||
470 | } | ||
471 | Ok(res) | ||
472 | } | ||
473 | } | ||
474 | |||
475 | impl TryConvWith<&WorldSnapshot> for SourceChange { | ||
476 | type Output = req::SourceChange; | ||
477 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<req::SourceChange> { | ||
478 | let cursor_position = match self.cursor_position { | ||
479 | None => None, | ||
480 | Some(pos) => { | ||
481 | let line_index = world.analysis().file_line_index(pos.file_id)?; | ||
482 | let edit = self | ||
483 | .source_file_edits | ||
484 | .iter() | ||
485 | .find(|it| it.file_id == pos.file_id) | ||
486 | .map(|it| &it.edit); | ||
487 | let line_col = match edit { | ||
488 | Some(edit) => translate_offset_with_edit(&*line_index, pos.offset, edit), | ||
489 | None => line_index.line_col(pos.offset), | ||
490 | }; | ||
491 | let position = | ||
492 | Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16)); | ||
493 | Some(TextDocumentPositionParams { | ||
494 | text_document: TextDocumentIdentifier::new(pos.file_id.try_conv_with(world)?), | ||
495 | position, | ||
496 | }) | ||
497 | } | ||
498 | }; | ||
499 | let mut document_changes: Vec<DocumentChangeOperation> = Vec::new(); | ||
500 | for resource_op in self.file_system_edits.try_conv_with(world)? { | ||
501 | document_changes.push(DocumentChangeOperation::Op(resource_op)); | ||
502 | } | ||
503 | for text_document_edit in self.source_file_edits.try_conv_with(world)? { | ||
504 | document_changes.push(DocumentChangeOperation::Edit(text_document_edit)); | ||
505 | } | ||
506 | let workspace_edit = WorkspaceEdit { | ||
507 | changes: None, | ||
508 | document_changes: Some(DocumentChanges::Operations(document_changes)), | ||
509 | }; | ||
510 | Ok(req::SourceChange { label: self.label, workspace_edit, cursor_position }) | ||
511 | } | ||
512 | } | ||
513 | |||
514 | impl TryConvWith<&WorldSnapshot> for SourceFileEdit { | ||
515 | type Output = TextDocumentEdit; | ||
516 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<TextDocumentEdit> { | ||
517 | let text_document = VersionedTextDocumentIdentifier { | ||
518 | uri: self.file_id.try_conv_with(world)?, | ||
519 | version: None, | ||
520 | }; | ||
521 | let line_index = world.analysis().file_line_index(self.file_id)?; | ||
522 | let line_endings = world.file_line_endings(self.file_id); | ||
523 | let edits = | ||
524 | self.edit.as_indels().iter().map_conv_with((&line_index, line_endings)).collect(); | ||
525 | Ok(TextDocumentEdit { text_document, edits }) | ||
526 | } | ||
527 | } | ||
528 | |||
529 | impl TryConvWith<&WorldSnapshot> for FileSystemEdit { | ||
530 | type Output = ResourceOp; | ||
531 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<ResourceOp> { | ||
532 | let res = match self { | ||
533 | FileSystemEdit::CreateFile { source_root, path } => { | ||
534 | let uri = world.path_to_uri(source_root, &path)?; | ||
535 | ResourceOp::Create(CreateFile { uri, options: None }) | ||
536 | } | ||
537 | FileSystemEdit::MoveFile { src, dst_source_root, dst_path } => { | ||
538 | let old_uri = world.file_id_to_uri(src)?; | ||
539 | let new_uri = world.path_to_uri(dst_source_root, &dst_path)?; | ||
540 | ResourceOp::Rename(RenameFile { old_uri, new_uri, options: None }) | ||
541 | } | ||
542 | }; | ||
543 | Ok(res) | ||
544 | } | ||
545 | } | ||
546 | |||
547 | impl TryConvWith<&WorldSnapshot> for &NavigationTarget { | ||
548 | type Output = Location; | ||
549 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<Location> { | ||
550 | let line_index = world.analysis().file_line_index(self.file_id())?; | ||
551 | let range = self.range(); | ||
552 | to_location(self.file_id(), range, &world, &line_index) | ||
553 | } | ||
554 | } | ||
555 | |||
556 | impl TryConvWith<&WorldSnapshot> for (FileId, RangeInfo<NavigationTarget>) { | ||
557 | type Output = LocationLink; | ||
558 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<LocationLink> { | ||
559 | let (src_file_id, target) = self; | ||
560 | |||
561 | let target_uri = target.info.file_id().try_conv_with(world)?; | ||
562 | let src_line_index = world.analysis().file_line_index(src_file_id)?; | ||
563 | let tgt_line_index = world.analysis().file_line_index(target.info.file_id())?; | ||
564 | |||
565 | let target_range = target.info.full_range().conv_with(&tgt_line_index); | ||
566 | |||
567 | let target_selection_range = target | ||
568 | .info | ||
569 | .focus_range() | ||
570 | .map(|it| it.conv_with(&tgt_line_index)) | ||
571 | .unwrap_or(target_range); | ||
572 | |||
573 | let res = LocationLink { | ||
574 | origin_selection_range: Some(target.range.conv_with(&src_line_index)), | ||
575 | target_uri, | ||
576 | target_range, | ||
577 | target_selection_range, | ||
578 | }; | ||
579 | Ok(res) | ||
580 | } | ||
581 | } | ||
582 | |||
583 | impl TryConvWith<&WorldSnapshot> for (FileId, RangeInfo<Vec<NavigationTarget>>) { | ||
584 | type Output = req::GotoDefinitionResponse; | ||
585 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<req::GotoTypeDefinitionResponse> { | ||
586 | let (file_id, RangeInfo { range, info: navs }) = self; | ||
587 | let links = navs | ||
588 | .into_iter() | ||
589 | .map(|nav| (file_id, RangeInfo::new(range, nav))) | ||
590 | .try_conv_with_to_vec(world)?; | ||
591 | if world.config.client_caps.location_link { | ||
592 | Ok(links.into()) | ||
593 | } else { | ||
594 | let locations: Vec<Location> = links | ||
595 | .into_iter() | ||
596 | .map(|link| Location { uri: link.target_uri, range: link.target_selection_range }) | ||
597 | .collect(); | ||
598 | Ok(locations.into()) | ||
599 | } | ||
600 | } | ||
601 | } | ||
602 | |||
603 | pub fn to_call_hierarchy_item( | ||
604 | file_id: FileId, | ||
605 | range: TextRange, | ||
606 | world: &WorldSnapshot, | ||
607 | line_index: &LineIndex, | ||
608 | nav: NavigationTarget, | ||
609 | ) -> Result<lsp_types::CallHierarchyItem> { | ||
610 | Ok(lsp_types::CallHierarchyItem { | ||
611 | name: nav.name().to_string(), | ||
612 | kind: nav.kind().conv(), | ||
613 | tags: None, | ||
614 | detail: nav.description().map(|it| it.to_string()), | ||
615 | uri: file_id.try_conv_with(&world)?, | ||
616 | range: nav.range().conv_with(&line_index), | ||
617 | selection_range: range.conv_with(&line_index), | ||
618 | }) | ||
619 | } | ||
620 | |||
621 | pub fn to_location( | ||
622 | file_id: FileId, | ||
623 | range: TextRange, | ||
624 | world: &WorldSnapshot, | ||
625 | line_index: &LineIndex, | ||
626 | ) -> Result<Location> { | ||
627 | let url = file_id.try_conv_with(world)?; | ||
628 | let loc = Location::new(url, range.conv_with(line_index)); | ||
629 | Ok(loc) | ||
630 | } | ||
631 | |||
632 | pub trait MapConvWith<CTX>: Sized { | ||
633 | type Output; | ||
634 | |||
635 | fn map_conv_with(self, ctx: CTX) -> ConvWithIter<Self, CTX> { | ||
636 | ConvWithIter { iter: self, ctx } | ||
637 | } | ||
638 | } | ||
639 | |||
640 | impl<CTX, I> MapConvWith<CTX> for I | ||
641 | where | ||
642 | I: Iterator, | ||
643 | I::Item: ConvWith<CTX>, | ||
644 | { | ||
645 | type Output = <I::Item as ConvWith<CTX>>::Output; | ||
646 | } | ||
647 | |||
648 | pub struct ConvWithIter<I, CTX> { | ||
649 | iter: I, | ||
650 | ctx: CTX, | ||
651 | } | ||
652 | |||
653 | impl<I, CTX> Iterator for ConvWithIter<I, CTX> | ||
654 | where | ||
655 | I: Iterator, | ||
656 | I::Item: ConvWith<CTX>, | ||
657 | CTX: Copy, | ||
658 | { | ||
659 | type Item = <I::Item as ConvWith<CTX>>::Output; | ||
660 | |||
661 | fn next(&mut self) -> Option<Self::Item> { | ||
662 | self.iter.next().map(|item| item.conv_with(self.ctx)) | ||
663 | } | ||
664 | } | ||
665 | |||
666 | pub trait TryConvWithToVec<CTX>: Sized { | ||
667 | type Output; | ||
668 | |||
669 | fn try_conv_with_to_vec(self, ctx: CTX) -> Result<Vec<Self::Output>>; | ||
670 | } | ||
671 | |||
672 | impl<I, CTX> TryConvWithToVec<CTX> for I | ||
673 | where | ||
674 | I: Iterator, | ||
675 | I::Item: TryConvWith<CTX>, | ||
676 | CTX: Copy, | ||
677 | { | ||
678 | type Output = <I::Item as TryConvWith<CTX>>::Output; | ||
679 | |||
680 | fn try_conv_with_to_vec(self, ctx: CTX) -> Result<Vec<Self::Output>> { | ||
681 | self.map(|it| it.try_conv_with(ctx)).collect() | ||
682 | } | ||
683 | } | ||
684 | |||
685 | #[cfg(test)] | ||
686 | mod tests { | ||
687 | use super::*; | ||
688 | use test_utils::extract_ranges; | ||
689 | |||
690 | #[test] | ||
691 | fn conv_fold_line_folding_only_fixup() { | ||
692 | let text = r#"<fold>mod a; | ||
693 | mod b; | ||
694 | mod c;</fold> | ||
695 | |||
696 | fn main() <fold>{ | ||
697 | if cond <fold>{ | ||
698 | a::do_a(); | ||
699 | }</fold> else <fold>{ | ||
700 | b::do_b(); | ||
701 | }</fold> | ||
702 | }</fold>"#; | ||
703 | |||
704 | let (ranges, text) = extract_ranges(text, "fold"); | ||
705 | assert_eq!(ranges.len(), 4); | ||
706 | let folds = vec![ | ||
707 | Fold { range: ranges[0], kind: FoldKind::Mods }, | ||
708 | Fold { range: ranges[1], kind: FoldKind::Block }, | ||
709 | Fold { range: ranges[2], kind: FoldKind::Block }, | ||
710 | Fold { range: ranges[3], kind: FoldKind::Block }, | ||
711 | ]; | ||
712 | |||
713 | let line_index = LineIndex::new(&text); | ||
714 | let ctx = FoldConvCtx { text: &text, line_index: &line_index, line_folding_only: true }; | ||
715 | let converted: Vec<_> = folds.into_iter().map_conv_with(&ctx).collect(); | ||
716 | |||
717 | let expected_lines = [(0, 2), (4, 10), (5, 6), (7, 9)]; | ||
718 | assert_eq!(converted.len(), expected_lines.len()); | ||
719 | for (folding_range, (start_line, end_line)) in converted.iter().zip(expected_lines.iter()) { | ||
720 | assert_eq!(folding_range.start_line, *start_line); | ||
721 | assert_eq!(folding_range.start_character, None); | ||
722 | assert_eq!(folding_range.end_line, *end_line); | ||
723 | assert_eq!(folding_range.end_character, None); | ||
724 | } | ||
725 | } | ||
726 | } | ||
diff --git a/crates/rust-analyzer/src/from_proto.rs b/crates/rust-analyzer/src/from_proto.rs new file mode 100644 index 000000000..4bb16a496 --- /dev/null +++ b/crates/rust-analyzer/src/from_proto.rs | |||
@@ -0,0 +1,42 @@ | |||
1 | //! Conversion lsp_types types to rust-analyzer specific ones. | ||
2 | use ra_db::{FileId, FilePosition, FileRange}; | ||
3 | use ra_ide::{LineCol, LineIndex}; | ||
4 | use ra_syntax::{TextRange, TextSize}; | ||
5 | |||
6 | use crate::{world::WorldSnapshot, Result}; | ||
7 | |||
8 | pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> TextSize { | ||
9 | let line_col = LineCol { line: position.line as u32, col_utf16: position.character as u32 }; | ||
10 | line_index.offset(line_col) | ||
11 | } | ||
12 | |||
13 | pub(crate) fn text_range(line_index: &LineIndex, range: lsp_types::Range) -> TextRange { | ||
14 | let start = offset(line_index, range.start); | ||
15 | let end = offset(line_index, range.end); | ||
16 | TextRange::new(start, end) | ||
17 | } | ||
18 | |||
19 | pub(crate) fn file_id(world: &WorldSnapshot, url: &lsp_types::Url) -> Result<FileId> { | ||
20 | world.uri_to_file_id(url) | ||
21 | } | ||
22 | |||
23 | pub(crate) fn file_position( | ||
24 | world: &WorldSnapshot, | ||
25 | tdpp: lsp_types::TextDocumentPositionParams, | ||
26 | ) -> Result<FilePosition> { | ||
27 | let file_id = file_id(world, &tdpp.text_document.uri)?; | ||
28 | let line_index = world.analysis().file_line_index(file_id)?; | ||
29 | let offset = offset(&*line_index, tdpp.position); | ||
30 | Ok(FilePosition { file_id, offset }) | ||
31 | } | ||
32 | |||
33 | pub(crate) fn file_range( | ||
34 | world: &WorldSnapshot, | ||
35 | text_document_identifier: lsp_types::TextDocumentIdentifier, | ||
36 | range: lsp_types::Range, | ||
37 | ) -> Result<FileRange> { | ||
38 | let file_id = file_id(world, &text_document_identifier.uri)?; | ||
39 | let line_index = world.analysis().file_line_index(file_id)?; | ||
40 | let range = text_range(&line_index, range); | ||
41 | Ok(FileRange { file_id, range }) | ||
42 | } | ||
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs index 036bf62a7..57d0e9218 100644 --- a/crates/rust-analyzer/src/lib.rs +++ b/crates/rust-analyzer/src/lib.rs | |||
@@ -20,10 +20,11 @@ macro_rules! eprintln { | |||
20 | mod vfs_glob; | 20 | mod vfs_glob; |
21 | mod caps; | 21 | mod caps; |
22 | mod cargo_target_spec; | 22 | mod cargo_target_spec; |
23 | mod conv; | 23 | mod to_proto; |
24 | mod from_proto; | ||
24 | mod main_loop; | 25 | mod main_loop; |
25 | mod markdown; | 26 | mod markdown; |
26 | pub mod req; | 27 | pub mod lsp_ext; |
27 | pub mod config; | 28 | pub mod config; |
28 | mod world; | 29 | mod world; |
29 | mod diagnostics; | 30 | mod diagnostics; |
diff --git a/crates/rust-analyzer/src/req.rs b/crates/rust-analyzer/src/lsp_ext.rs index 0dae6bad4..313a8c769 100644 --- a/crates/rust-analyzer/src/req.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs | |||
@@ -1,25 +1,12 @@ | |||
1 | //! Defines `rust-analyzer` specific custom messages. | 1 | //! rust-analyzer extensions to the LSP. |
2 | 2 | ||
3 | use std::path::PathBuf; | ||
4 | |||
5 | use lsp_types::request::Request; | ||
3 | use lsp_types::{Location, Position, Range, TextDocumentIdentifier}; | 6 | use lsp_types::{Location, Position, Range, TextDocumentIdentifier}; |
4 | use rustc_hash::FxHashMap; | 7 | use rustc_hash::FxHashMap; |
5 | use serde::{Deserialize, Serialize}; | 8 | use serde::{Deserialize, Serialize}; |
6 | 9 | ||
7 | pub use lsp_types::{ | ||
8 | notification::*, request::*, ApplyWorkspaceEditParams, CodeActionParams, CodeLens, | ||
9 | CodeLensParams, CompletionParams, CompletionResponse, ConfigurationItem, ConfigurationParams, | ||
10 | DiagnosticTag, DidChangeConfigurationParams, DidChangeWatchedFilesParams, | ||
11 | DidChangeWatchedFilesRegistrationOptions, DocumentHighlightParams, | ||
12 | DocumentOnTypeFormattingParams, DocumentSymbolParams, DocumentSymbolResponse, | ||
13 | FileSystemWatcher, GotoDefinitionParams, GotoDefinitionResponse, Hover, HoverParams, | ||
14 | InitializeResult, MessageType, PartialResultParams, ProgressParams, ProgressParamsValue, | ||
15 | ProgressToken, PublishDiagnosticsParams, ReferenceParams, Registration, RegistrationParams, | ||
16 | SelectionRange, SelectionRangeParams, SemanticTokensParams, SemanticTokensRangeParams, | ||
17 | SemanticTokensRangeResult, SemanticTokensResult, ServerCapabilities, ShowMessageParams, | ||
18 | SignatureHelp, SignatureHelpParams, SymbolKind, TextDocumentEdit, TextDocumentPositionParams, | ||
19 | TextEdit, WorkDoneProgressParams, WorkspaceEdit, WorkspaceSymbolParams, | ||
20 | }; | ||
21 | use std::path::PathBuf; | ||
22 | |||
23 | pub enum AnalyzerStatus {} | 10 | pub enum AnalyzerStatus {} |
24 | 11 | ||
25 | impl Request for AnalyzerStatus { | 12 | impl Request for AnalyzerStatus { |
@@ -91,7 +78,7 @@ pub struct FindMatchingBraceParams { | |||
91 | pub enum ParentModule {} | 78 | pub enum ParentModule {} |
92 | 79 | ||
93 | impl Request for ParentModule { | 80 | impl Request for ParentModule { |
94 | type Params = TextDocumentPositionParams; | 81 | type Params = lsp_types::TextDocumentPositionParams; |
95 | type Result = Vec<Location>; | 82 | type Result = Vec<Location>; |
96 | const METHOD: &'static str = "rust-analyzer/parentModule"; | 83 | const METHOD: &'static str = "rust-analyzer/parentModule"; |
97 | } | 84 | } |
@@ -114,7 +101,7 @@ pub struct JoinLinesParams { | |||
114 | pub enum OnEnter {} | 101 | pub enum OnEnter {} |
115 | 102 | ||
116 | impl Request for OnEnter { | 103 | impl Request for OnEnter { |
117 | type Params = TextDocumentPositionParams; | 104 | type Params = lsp_types::TextDocumentPositionParams; |
118 | type Result = Option<SourceChange>; | 105 | type Result = Option<SourceChange>; |
119 | const METHOD: &'static str = "rust-analyzer/onEnter"; | 106 | const METHOD: &'static str = "rust-analyzer/onEnter"; |
120 | } | 107 | } |
@@ -150,8 +137,8 @@ pub struct Runnable { | |||
150 | #[serde(rename_all = "camelCase")] | 137 | #[serde(rename_all = "camelCase")] |
151 | pub struct SourceChange { | 138 | pub struct SourceChange { |
152 | pub label: String, | 139 | pub label: String, |
153 | pub workspace_edit: WorkspaceEdit, | 140 | pub workspace_edit: lsp_types::WorkspaceEdit, |
154 | pub cursor_position: Option<TextDocumentPositionParams>, | 141 | pub cursor_position: Option<lsp_types::TextDocumentPositionParams>, |
155 | } | 142 | } |
156 | 143 | ||
157 | pub enum InlayHints {} | 144 | pub enum InlayHints {} |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index a8fa2af08..3f12dd718 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -37,13 +37,12 @@ use threadpool::ThreadPool; | |||
37 | 37 | ||
38 | use crate::{ | 38 | use crate::{ |
39 | config::{Config, FilesWatcher}, | 39 | config::{Config, FilesWatcher}, |
40 | conv::{ConvWith, TryConvWith}, | ||
41 | diagnostics::DiagnosticTask, | 40 | diagnostics::DiagnosticTask, |
41 | from_proto, lsp_ext, | ||
42 | main_loop::{ | 42 | main_loop::{ |
43 | pending_requests::{PendingRequest, PendingRequests}, | 43 | pending_requests::{PendingRequest, PendingRequests}, |
44 | subscriptions::Subscriptions, | 44 | subscriptions::Subscriptions, |
45 | }, | 45 | }, |
46 | req, | ||
47 | world::{WorldSnapshot, WorldState}, | 46 | world::{WorldSnapshot, WorldState}, |
48 | Result, | 47 | Result, |
49 | }; | 48 | }; |
@@ -104,7 +103,7 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection) | |||
104 | 103 | ||
105 | if project_roots.is_empty() && config.notifications.cargo_toml_not_found { | 104 | if project_roots.is_empty() && config.notifications.cargo_toml_not_found { |
106 | show_message( | 105 | show_message( |
107 | req::MessageType::Error, | 106 | lsp_types::MessageType::Error, |
108 | format!( | 107 | format!( |
109 | "rust-analyzer failed to discover workspace, no Cargo.toml found, dirs searched: {}", | 108 | "rust-analyzer failed to discover workspace, no Cargo.toml found, dirs searched: {}", |
110 | ws_roots.iter().format_with(", ", |it, f| f(&it.display())) | 109 | ws_roots.iter().format_with(", ", |it, f| f(&it.display())) |
@@ -124,7 +123,7 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection) | |||
124 | .map_err(|err| { | 123 | .map_err(|err| { |
125 | log::error!("failed to load workspace: {:#}", err); | 124 | log::error!("failed to load workspace: {:#}", err); |
126 | show_message( | 125 | show_message( |
127 | req::MessageType::Error, | 126 | lsp_types::MessageType::Error, |
128 | format!("rust-analyzer failed to load workspace: {:#}", err), | 127 | format!("rust-analyzer failed to load workspace: {:#}", err), |
129 | &connection.sender, | 128 | &connection.sender, |
130 | ); | 129 | ); |
@@ -142,23 +141,25 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection) | |||
142 | .collect::<std::result::Result<Vec<_>, _>>()?; | 141 | .collect::<std::result::Result<Vec<_>, _>>()?; |
143 | 142 | ||
144 | if let FilesWatcher::Client = config.files.watcher { | 143 | if let FilesWatcher::Client = config.files.watcher { |
145 | let registration_options = req::DidChangeWatchedFilesRegistrationOptions { | 144 | let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions { |
146 | watchers: workspaces | 145 | watchers: workspaces |
147 | .iter() | 146 | .iter() |
148 | .flat_map(ProjectWorkspace::to_roots) | 147 | .flat_map(ProjectWorkspace::to_roots) |
149 | .filter(PackageRoot::is_member) | 148 | .filter(PackageRoot::is_member) |
150 | .map(|root| format!("{}/**/*.rs", root.path().display())) | 149 | .map(|root| format!("{}/**/*.rs", root.path().display())) |
151 | .map(|glob_pattern| req::FileSystemWatcher { glob_pattern, kind: None }) | 150 | .map(|glob_pattern| lsp_types::FileSystemWatcher { glob_pattern, kind: None }) |
152 | .collect(), | 151 | .collect(), |
153 | }; | 152 | }; |
154 | let registration = req::Registration { | 153 | let registration = lsp_types::Registration { |
155 | id: "file-watcher".to_string(), | 154 | id: "file-watcher".to_string(), |
156 | method: "workspace/didChangeWatchedFiles".to_string(), | 155 | method: "workspace/didChangeWatchedFiles".to_string(), |
157 | register_options: Some(serde_json::to_value(registration_options).unwrap()), | 156 | register_options: Some(serde_json::to_value(registration_options).unwrap()), |
158 | }; | 157 | }; |
159 | let params = req::RegistrationParams { registrations: vec![registration] }; | 158 | let params = lsp_types::RegistrationParams { registrations: vec![registration] }; |
160 | let request = | 159 | let request = request_new::<lsp_types::request::RegisterCapability>( |
161 | request_new::<req::RegisterCapability>(loop_state.next_request_id(), params); | 160 | loop_state.next_request_id(), |
161 | params, | ||
162 | ); | ||
162 | connection.sender.send(request.into()).unwrap(); | 163 | connection.sender.send(request.into()).unwrap(); |
163 | } | 164 | } |
164 | 165 | ||
@@ -257,14 +258,14 @@ impl fmt::Debug for Event { | |||
257 | 258 | ||
258 | match self { | 259 | match self { |
259 | Event::Msg(Message::Notification(not)) => { | 260 | Event::Msg(Message::Notification(not)) => { |
260 | if notification_is::<req::DidOpenTextDocument>(not) | 261 | if notification_is::<lsp_types::notification::DidOpenTextDocument>(not) |
261 | || notification_is::<req::DidChangeTextDocument>(not) | 262 | || notification_is::<lsp_types::notification::DidChangeTextDocument>(not) |
262 | { | 263 | { |
263 | return debug_verbose_not(not, f); | 264 | return debug_verbose_not(not, f); |
264 | } | 265 | } |
265 | } | 266 | } |
266 | Event::Task(Task::Notify(not)) => { | 267 | Event::Task(Task::Notify(not)) => { |
267 | if notification_is::<req::PublishDiagnostics>(not) { | 268 | if notification_is::<lsp_types::notification::PublishDiagnostics>(not) { |
268 | return debug_verbose_not(not, f); | 269 | return debug_verbose_not(not, f); |
269 | } | 270 | } |
270 | } | 271 | } |
@@ -451,7 +452,7 @@ fn loop_turn( | |||
451 | log::error!("overly long loop turn: {:?}", loop_duration); | 452 | log::error!("overly long loop turn: {:?}", loop_duration); |
452 | if env::var("RA_PROFILE").is_ok() { | 453 | if env::var("RA_PROFILE").is_ok() { |
453 | show_message( | 454 | show_message( |
454 | req::MessageType::Error, | 455 | lsp_types::MessageType::Error, |
455 | format!("overly long loop turn: {:?}", loop_duration), | 456 | format!("overly long loop turn: {:?}", loop_duration), |
456 | &connection.sender, | 457 | &connection.sender, |
457 | ); | 458 | ); |
@@ -501,45 +502,51 @@ fn on_request( | |||
501 | request_received, | 502 | request_received, |
502 | }; | 503 | }; |
503 | pool_dispatcher | 504 | pool_dispatcher |
504 | .on_sync::<req::CollectGarbage>(|s, ()| Ok(s.collect_garbage()))? | 505 | .on_sync::<lsp_ext::CollectGarbage>(|s, ()| Ok(s.collect_garbage()))? |
505 | .on_sync::<req::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))? | 506 | .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))? |
506 | .on_sync::<req::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))? | 507 | .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))? |
507 | .on_sync::<req::SelectionRangeRequest>(|s, p| { | 508 | .on_sync::<lsp_types::request::SelectionRangeRequest>(|s, p| { |
508 | handlers::handle_selection_range(s.snapshot(), p) | 509 | handlers::handle_selection_range(s.snapshot(), p) |
509 | })? | 510 | })? |
510 | .on_sync::<req::FindMatchingBrace>(|s, p| { | 511 | .on_sync::<lsp_ext::FindMatchingBrace>(|s, p| { |
511 | handlers::handle_find_matching_brace(s.snapshot(), p) | 512 | handlers::handle_find_matching_brace(s.snapshot(), p) |
512 | })? | 513 | })? |
513 | .on::<req::AnalyzerStatus>(handlers::handle_analyzer_status)? | 514 | .on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status)? |
514 | .on::<req::SyntaxTree>(handlers::handle_syntax_tree)? | 515 | .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)? |
515 | .on::<req::ExpandMacro>(handlers::handle_expand_macro)? | 516 | .on::<lsp_ext::ExpandMacro>(handlers::handle_expand_macro)? |
516 | .on::<req::OnTypeFormatting>(handlers::handle_on_type_formatting)? | 517 | .on::<lsp_ext::ParentModule>(handlers::handle_parent_module)? |
517 | .on::<req::DocumentSymbolRequest>(handlers::handle_document_symbol)? | 518 | .on::<lsp_ext::Runnables>(handlers::handle_runnables)? |
518 | .on::<req::WorkspaceSymbol>(handlers::handle_workspace_symbol)? | 519 | .on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints)? |
519 | .on::<req::GotoDefinition>(handlers::handle_goto_definition)? | 520 | .on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)? |
520 | .on::<req::GotoImplementation>(handlers::handle_goto_implementation)? | 521 | .on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)? |
521 | .on::<req::GotoTypeDefinition>(handlers::handle_goto_type_definition)? | 522 | .on::<lsp_types::request::WorkspaceSymbol>(handlers::handle_workspace_symbol)? |
522 | .on::<req::ParentModule>(handlers::handle_parent_module)? | 523 | .on::<lsp_types::request::GotoDefinition>(handlers::handle_goto_definition)? |
523 | .on::<req::Runnables>(handlers::handle_runnables)? | 524 | .on::<lsp_types::request::GotoImplementation>(handlers::handle_goto_implementation)? |
524 | .on::<req::Completion>(handlers::handle_completion)? | 525 | .on::<lsp_types::request::GotoTypeDefinition>(handlers::handle_goto_type_definition)? |
525 | .on::<req::CodeActionRequest>(handlers::handle_code_action)? | 526 | .on::<lsp_types::request::Completion>(handlers::handle_completion)? |
526 | .on::<req::CodeLensRequest>(handlers::handle_code_lens)? | 527 | .on::<lsp_types::request::CodeActionRequest>(handlers::handle_code_action)? |
527 | .on::<req::CodeLensResolve>(handlers::handle_code_lens_resolve)? | 528 | .on::<lsp_types::request::CodeLensRequest>(handlers::handle_code_lens)? |
528 | .on::<req::FoldingRangeRequest>(handlers::handle_folding_range)? | 529 | .on::<lsp_types::request::CodeLensResolve>(handlers::handle_code_lens_resolve)? |
529 | .on::<req::SignatureHelpRequest>(handlers::handle_signature_help)? | 530 | .on::<lsp_types::request::FoldingRangeRequest>(handlers::handle_folding_range)? |
530 | .on::<req::HoverRequest>(handlers::handle_hover)? | 531 | .on::<lsp_types::request::SignatureHelpRequest>(handlers::handle_signature_help)? |
531 | .on::<req::PrepareRenameRequest>(handlers::handle_prepare_rename)? | 532 | .on::<lsp_types::request::HoverRequest>(handlers::handle_hover)? |
532 | .on::<req::Rename>(handlers::handle_rename)? | 533 | .on::<lsp_types::request::PrepareRenameRequest>(handlers::handle_prepare_rename)? |
533 | .on::<req::References>(handlers::handle_references)? | 534 | .on::<lsp_types::request::Rename>(handlers::handle_rename)? |
534 | .on::<req::Formatting>(handlers::handle_formatting)? | 535 | .on::<lsp_types::request::References>(handlers::handle_references)? |
535 | .on::<req::DocumentHighlightRequest>(handlers::handle_document_highlight)? | 536 | .on::<lsp_types::request::Formatting>(handlers::handle_formatting)? |
536 | .on::<req::InlayHints>(handlers::handle_inlay_hints)? | 537 | .on::<lsp_types::request::DocumentHighlightRequest>(handlers::handle_document_highlight)? |
537 | .on::<req::CallHierarchyPrepare>(handlers::handle_call_hierarchy_prepare)? | 538 | .on::<lsp_types::request::CallHierarchyPrepare>(handlers::handle_call_hierarchy_prepare)? |
538 | .on::<req::CallHierarchyIncomingCalls>(handlers::handle_call_hierarchy_incoming)? | 539 | .on::<lsp_types::request::CallHierarchyIncomingCalls>( |
539 | .on::<req::CallHierarchyOutgoingCalls>(handlers::handle_call_hierarchy_outgoing)? | 540 | handlers::handle_call_hierarchy_incoming, |
540 | .on::<req::SemanticTokensRequest>(handlers::handle_semantic_tokens)? | 541 | )? |
541 | .on::<req::SemanticTokensRangeRequest>(handlers::handle_semantic_tokens_range)? | 542 | .on::<lsp_types::request::CallHierarchyOutgoingCalls>( |
542 | .on::<req::Ssr>(handlers::handle_ssr)? | 543 | handlers::handle_call_hierarchy_outgoing, |
544 | )? | ||
545 | .on::<lsp_types::request::SemanticTokensRequest>(handlers::handle_semantic_tokens)? | ||
546 | .on::<lsp_types::request::SemanticTokensRangeRequest>( | ||
547 | handlers::handle_semantic_tokens_range, | ||
548 | )? | ||
549 | .on::<lsp_ext::Ssr>(handlers::handle_ssr)? | ||
543 | .finish(); | 550 | .finish(); |
544 | Ok(()) | 551 | Ok(()) |
545 | } | 552 | } |
@@ -550,7 +557,7 @@ fn on_notification( | |||
550 | loop_state: &mut LoopState, | 557 | loop_state: &mut LoopState, |
551 | not: Notification, | 558 | not: Notification, |
552 | ) -> Result<()> { | 559 | ) -> Result<()> { |
553 | let not = match notification_cast::<req::Cancel>(not) { | 560 | let not = match notification_cast::<lsp_types::notification::Cancel>(not) { |
554 | Ok(params) => { | 561 | Ok(params) => { |
555 | let id: RequestId = match params.id { | 562 | let id: RequestId = match params.id { |
556 | NumberOrString::Number(id) => id.into(), | 563 | NumberOrString::Number(id) => id.into(), |
@@ -568,7 +575,7 @@ fn on_notification( | |||
568 | } | 575 | } |
569 | Err(not) => not, | 576 | Err(not) => not, |
570 | }; | 577 | }; |
571 | let not = match notification_cast::<req::DidOpenTextDocument>(not) { | 578 | let not = match notification_cast::<lsp_types::notification::DidOpenTextDocument>(not) { |
572 | Ok(params) => { | 579 | Ok(params) => { |
573 | let uri = params.text_document.uri; | 580 | let uri = params.text_document.uri; |
574 | let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; | 581 | let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; |
@@ -581,11 +588,11 @@ fn on_notification( | |||
581 | } | 588 | } |
582 | Err(not) => not, | 589 | Err(not) => not, |
583 | }; | 590 | }; |
584 | let not = match notification_cast::<req::DidChangeTextDocument>(not) { | 591 | let not = match notification_cast::<lsp_types::notification::DidChangeTextDocument>(not) { |
585 | Ok(params) => { | 592 | Ok(params) => { |
586 | let DidChangeTextDocumentParams { text_document, content_changes } = params; | 593 | let DidChangeTextDocumentParams { text_document, content_changes } = params; |
587 | let world = state.snapshot(); | 594 | let world = state.snapshot(); |
588 | let file_id = text_document.try_conv_with(&world)?; | 595 | let file_id = from_proto::file_id(&world, &text_document.uri)?; |
589 | let line_index = world.analysis().file_line_index(file_id)?; | 596 | let line_index = world.analysis().file_line_index(file_id)?; |
590 | let uri = text_document.uri; | 597 | let uri = text_document.uri; |
591 | let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; | 598 | let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; |
@@ -596,7 +603,7 @@ fn on_notification( | |||
596 | } | 603 | } |
597 | Err(not) => not, | 604 | Err(not) => not, |
598 | }; | 605 | }; |
599 | let not = match notification_cast::<req::DidSaveTextDocument>(not) { | 606 | let not = match notification_cast::<lsp_types::notification::DidSaveTextDocument>(not) { |
600 | Ok(_params) => { | 607 | Ok(_params) => { |
601 | if let Some(flycheck) = &state.flycheck { | 608 | if let Some(flycheck) = &state.flycheck { |
602 | flycheck.update(); | 609 | flycheck.update(); |
@@ -605,7 +612,7 @@ fn on_notification( | |||
605 | } | 612 | } |
606 | Err(not) => not, | 613 | Err(not) => not, |
607 | }; | 614 | }; |
608 | let not = match notification_cast::<req::DidCloseTextDocument>(not) { | 615 | let not = match notification_cast::<lsp_types::notification::DidCloseTextDocument>(not) { |
609 | Ok(params) => { | 616 | Ok(params) => { |
610 | let uri = params.text_document.uri; | 617 | let uri = params.text_document.uri; |
611 | let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; | 618 | let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; |
@@ -613,22 +620,22 @@ fn on_notification( | |||
613 | loop_state.subscriptions.remove_sub(FileId(file_id.0)); | 620 | loop_state.subscriptions.remove_sub(FileId(file_id.0)); |
614 | } | 621 | } |
615 | let params = | 622 | let params = |
616 | req::PublishDiagnosticsParams { uri, diagnostics: Vec::new(), version: None }; | 623 | lsp_types::PublishDiagnosticsParams { uri, diagnostics: Vec::new(), version: None }; |
617 | let not = notification_new::<req::PublishDiagnostics>(params); | 624 | let not = notification_new::<lsp_types::notification::PublishDiagnostics>(params); |
618 | msg_sender.send(not.into()).unwrap(); | 625 | msg_sender.send(not.into()).unwrap(); |
619 | return Ok(()); | 626 | return Ok(()); |
620 | } | 627 | } |
621 | Err(not) => not, | 628 | Err(not) => not, |
622 | }; | 629 | }; |
623 | let not = match notification_cast::<req::DidChangeConfiguration>(not) { | 630 | let not = match notification_cast::<lsp_types::notification::DidChangeConfiguration>(not) { |
624 | Ok(_) => { | 631 | Ok(_) => { |
625 | // As stated in https://github.com/microsoft/language-server-protocol/issues/676, | 632 | // As stated in https://github.com/microsoft/language-server-protocol/issues/676, |
626 | // this notification's parameters should be ignored and the actual config queried separately. | 633 | // this notification's parameters should be ignored and the actual config queried separately. |
627 | let request_id = loop_state.next_request_id(); | 634 | let request_id = loop_state.next_request_id(); |
628 | let request = request_new::<req::WorkspaceConfiguration>( | 635 | let request = request_new::<lsp_types::request::WorkspaceConfiguration>( |
629 | request_id.clone(), | 636 | request_id.clone(), |
630 | req::ConfigurationParams { | 637 | lsp_types::ConfigurationParams { |
631 | items: vec![req::ConfigurationItem { | 638 | items: vec![lsp_types::ConfigurationItem { |
632 | scope_uri: None, | 639 | scope_uri: None, |
633 | section: Some("rust-analyzer".to_string()), | 640 | section: Some("rust-analyzer".to_string()), |
634 | }], | 641 | }], |
@@ -641,7 +648,7 @@ fn on_notification( | |||
641 | } | 648 | } |
642 | Err(not) => not, | 649 | Err(not) => not, |
643 | }; | 650 | }; |
644 | let not = match notification_cast::<req::DidChangeWatchedFiles>(not) { | 651 | let not = match notification_cast::<lsp_types::notification::DidChangeWatchedFiles>(not) { |
645 | Ok(params) => { | 652 | Ok(params) => { |
646 | let mut vfs = state.vfs.write(); | 653 | let mut vfs = state.vfs.write(); |
647 | for change in params.changes { | 654 | for change in params.changes { |
@@ -695,7 +702,7 @@ fn apply_document_changes( | |||
695 | line_index = Cow::Owned(LineIndex::new(&old_text)); | 702 | line_index = Cow::Owned(LineIndex::new(&old_text)); |
696 | } | 703 | } |
697 | index_valid = IndexValid::UpToLineExclusive(range.start.line); | 704 | index_valid = IndexValid::UpToLineExclusive(range.start.line); |
698 | let range = range.conv_with(&line_index); | 705 | let range = from_proto::text_range(&line_index, range); |
699 | let mut text = old_text.to_owned(); | 706 | let mut text = old_text.to_owned(); |
700 | match std::panic::catch_unwind(move || { | 707 | match std::panic::catch_unwind(move || { |
701 | text.replace_range(Range::<usize>::from(range), &change.text); | 708 | text.replace_range(Range::<usize>::from(range), &change.text); |
@@ -743,11 +750,11 @@ fn on_check_task( | |||
743 | } | 750 | } |
744 | 751 | ||
745 | CheckTask::Status(progress) => { | 752 | CheckTask::Status(progress) => { |
746 | let params = req::ProgressParams { | 753 | let params = lsp_types::ProgressParams { |
747 | token: req::ProgressToken::String("rustAnalyzer/cargoWatcher".to_string()), | 754 | token: lsp_types::ProgressToken::String("rustAnalyzer/cargoWatcher".to_string()), |
748 | value: req::ProgressParamsValue::WorkDone(progress), | 755 | value: lsp_types::ProgressParamsValue::WorkDone(progress), |
749 | }; | 756 | }; |
750 | let not = notification_new::<req::Progress>(params); | 757 | let not = notification_new::<lsp_types::notification::Progress>(params); |
751 | task_sender.send(Task::Notify(not)).unwrap(); | 758 | task_sender.send(Task::Notify(not)).unwrap(); |
752 | } | 759 | } |
753 | }; | 760 | }; |
@@ -769,8 +776,8 @@ fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender<Message>, state: | |||
769 | }; | 776 | }; |
770 | 777 | ||
771 | let diagnostics = state.diagnostics.diagnostics_for(file_id).cloned().collect(); | 778 | let diagnostics = state.diagnostics.diagnostics_for(file_id).cloned().collect(); |
772 | let params = req::PublishDiagnosticsParams { uri, diagnostics, version: None }; | 779 | let params = lsp_types::PublishDiagnosticsParams { uri, diagnostics, version: None }; |
773 | let not = notification_new::<req::PublishDiagnostics>(params); | 780 | let not = notification_new::<lsp_types::notification::PublishDiagnostics>(params); |
774 | msg_sender.send(not.into()).unwrap(); | 781 | msg_sender.send(not.into()).unwrap(); |
775 | } | 782 | } |
776 | } | 783 | } |
@@ -783,10 +790,10 @@ fn send_startup_progress(sender: &Sender<Message>, loop_state: &mut LoopState) { | |||
783 | 790 | ||
784 | match (prev, loop_state.workspace_loaded) { | 791 | match (prev, loop_state.workspace_loaded) { |
785 | (None, false) => { | 792 | (None, false) => { |
786 | let work_done_progress_create = request_new::<req::WorkDoneProgressCreate>( | 793 | let work_done_progress_create = request_new::<lsp_types::request::WorkDoneProgressCreate>( |
787 | loop_state.next_request_id(), | 794 | loop_state.next_request_id(), |
788 | WorkDoneProgressCreateParams { | 795 | WorkDoneProgressCreateParams { |
789 | token: req::ProgressToken::String("rustAnalyzer/startup".into()), | 796 | token: lsp_types::ProgressToken::String("rustAnalyzer/startup".into()), |
790 | }, | 797 | }, |
791 | ); | 798 | ); |
792 | sender.send(work_done_progress_create.into()).unwrap(); | 799 | sender.send(work_done_progress_create.into()).unwrap(); |
@@ -818,10 +825,11 @@ fn send_startup_progress(sender: &Sender<Message>, loop_state: &mut LoopState) { | |||
818 | } | 825 | } |
819 | 826 | ||
820 | fn send_startup_progress_notif(sender: &Sender<Message>, work_done_progress: WorkDoneProgress) { | 827 | fn send_startup_progress_notif(sender: &Sender<Message>, work_done_progress: WorkDoneProgress) { |
821 | let notif = notification_new::<req::Progress>(req::ProgressParams { | 828 | let notif = |
822 | token: req::ProgressToken::String("rustAnalyzer/startup".into()), | 829 | notification_new::<lsp_types::notification::Progress>(lsp_types::ProgressParams { |
823 | value: req::ProgressParamsValue::WorkDone(work_done_progress), | 830 | token: lsp_types::ProgressToken::String("rustAnalyzer/startup".into()), |
824 | }); | 831 | value: lsp_types::ProgressParamsValue::WorkDone(work_done_progress), |
832 | }); | ||
825 | sender.send(notif.into()).unwrap(); | 833 | sender.send(notif.into()).unwrap(); |
826 | } | 834 | } |
827 | } | 835 | } |
@@ -843,7 +851,7 @@ impl<'a> PoolDispatcher<'a> { | |||
843 | f: fn(&mut WorldState, R::Params) -> Result<R::Result>, | 851 | f: fn(&mut WorldState, R::Params) -> Result<R::Result>, |
844 | ) -> Result<&mut Self> | 852 | ) -> Result<&mut Self> |
845 | where | 853 | where |
846 | R: req::Request + 'static, | 854 | R: lsp_types::request::Request + 'static, |
847 | R::Params: DeserializeOwned + panic::UnwindSafe + 'static, | 855 | R::Params: DeserializeOwned + panic::UnwindSafe + 'static, |
848 | R::Result: Serialize + 'static, | 856 | R::Result: Serialize + 'static, |
849 | { | 857 | { |
@@ -866,7 +874,7 @@ impl<'a> PoolDispatcher<'a> { | |||
866 | /// Dispatches the request onto thread pool | 874 | /// Dispatches the request onto thread pool |
867 | fn on<R>(&mut self, f: fn(WorldSnapshot, R::Params) -> Result<R::Result>) -> Result<&mut Self> | 875 | fn on<R>(&mut self, f: fn(WorldSnapshot, R::Params) -> Result<R::Result>) -> Result<&mut Self> |
868 | where | 876 | where |
869 | R: req::Request + 'static, | 877 | R: lsp_types::request::Request + 'static, |
870 | R::Params: DeserializeOwned + Send + 'static, | 878 | R::Params: DeserializeOwned + Send + 'static, |
871 | R::Result: Serialize + 'static, | 879 | R::Result: Serialize + 'static, |
872 | { | 880 | { |
@@ -892,7 +900,7 @@ impl<'a> PoolDispatcher<'a> { | |||
892 | 900 | ||
893 | fn parse<R>(&mut self) -> Option<(RequestId, R::Params)> | 901 | fn parse<R>(&mut self) -> Option<(RequestId, R::Params)> |
894 | where | 902 | where |
895 | R: req::Request + 'static, | 903 | R: lsp_types::request::Request + 'static, |
896 | R::Params: DeserializeOwned + 'static, | 904 | R::Params: DeserializeOwned + 'static, |
897 | { | 905 | { |
898 | let req = self.req.take()?; | 906 | let req = self.req.take()?; |
@@ -929,7 +937,7 @@ impl<'a> PoolDispatcher<'a> { | |||
929 | 937 | ||
930 | fn result_to_task<R>(id: RequestId, result: Result<R::Result>) -> Task | 938 | fn result_to_task<R>(id: RequestId, result: Result<R::Result>) -> Task |
931 | where | 939 | where |
932 | R: req::Request + 'static, | 940 | R: lsp_types::request::Request + 'static, |
933 | R::Params: DeserializeOwned + 'static, | 941 | R::Params: DeserializeOwned + 'static, |
934 | R::Result: Serialize + 'static, | 942 | R::Result: Serialize + 'static, |
935 | { | 943 | { |
@@ -985,10 +993,14 @@ fn update_file_notifications_on_threadpool( | |||
985 | } | 993 | } |
986 | } | 994 | } |
987 | 995 | ||
988 | pub fn show_message(typ: req::MessageType, message: impl Into<String>, sender: &Sender<Message>) { | 996 | pub fn show_message( |
997 | typ: lsp_types::MessageType, | ||
998 | message: impl Into<String>, | ||
999 | sender: &Sender<Message>, | ||
1000 | ) { | ||
989 | let message = message.into(); | 1001 | let message = message.into(); |
990 | let params = req::ShowMessageParams { typ, message }; | 1002 | let params = lsp_types::ShowMessageParams { typ, message }; |
991 | let not = notification_new::<req::ShowMessage>(params); | 1003 | let not = notification_new::<lsp_types::notification::ShowMessage>(params); |
992 | sender.send(not.into()).unwrap(); | 1004 | sender.send(not.into()).unwrap(); |
993 | } | 1005 | } |
994 | 1006 | ||
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index f4353af64..be8688bc3 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs | |||
@@ -22,6 +22,7 @@ use ra_ide::{ | |||
22 | Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, | 22 | Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, |
23 | }; | 23 | }; |
24 | use ra_prof::profile; | 24 | use ra_prof::profile; |
25 | use ra_project_model::TargetKind; | ||
25 | use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize}; | 26 | use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize}; |
26 | use rustc_hash::FxHashMap; | 27 | use rustc_hash::FxHashMap; |
27 | use serde::{Deserialize, Serialize}; | 28 | use serde::{Deserialize, Serialize}; |
@@ -31,18 +32,13 @@ use stdx::format_to; | |||
31 | use crate::{ | 32 | use crate::{ |
32 | cargo_target_spec::CargoTargetSpec, | 33 | cargo_target_spec::CargoTargetSpec, |
33 | config::RustfmtConfig, | 34 | config::RustfmtConfig, |
34 | conv::{ | ||
35 | to_call_hierarchy_item, to_location, Conv, ConvWith, FoldConvCtx, MapConvWith, TryConvWith, | ||
36 | TryConvWithToVec, | ||
37 | }, | ||
38 | diagnostics::DiagnosticTask, | 35 | diagnostics::DiagnosticTask, |
39 | from_json, | 36 | from_json, from_proto, |
40 | req::{self, InlayHint, InlayHintsParams}, | 37 | lsp_ext::{self, InlayHint, InlayHintsParams}, |
41 | semantic_tokens::SemanticTokensBuilder, | 38 | to_proto, |
42 | world::WorldSnapshot, | 39 | world::WorldSnapshot, |
43 | LspError, Result, | 40 | LspError, Result, |
44 | }; | 41 | }; |
45 | use ra_project_model::TargetKind; | ||
46 | 42 | ||
47 | pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> { | 43 | pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> { |
48 | let _p = profile("handle_analyzer_status"); | 44 | let _p = profile("handle_analyzer_status"); |
@@ -56,48 +52,51 @@ pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> { | |||
56 | Ok(buf) | 52 | Ok(buf) |
57 | } | 53 | } |
58 | 54 | ||
59 | pub fn handle_syntax_tree(world: WorldSnapshot, params: req::SyntaxTreeParams) -> Result<String> { | 55 | pub fn handle_syntax_tree( |
56 | world: WorldSnapshot, | ||
57 | params: lsp_ext::SyntaxTreeParams, | ||
58 | ) -> Result<String> { | ||
60 | let _p = profile("handle_syntax_tree"); | 59 | let _p = profile("handle_syntax_tree"); |
61 | let id = params.text_document.try_conv_with(&world)?; | 60 | let id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
62 | let line_index = world.analysis().file_line_index(id)?; | 61 | let line_index = world.analysis().file_line_index(id)?; |
63 | let text_range = params.range.map(|p| p.conv_with(&line_index)); | 62 | let text_range = params.range.map(|r| from_proto::text_range(&line_index, r)); |
64 | let res = world.analysis().syntax_tree(id, text_range)?; | 63 | let res = world.analysis().syntax_tree(id, text_range)?; |
65 | Ok(res) | 64 | Ok(res) |
66 | } | 65 | } |
67 | 66 | ||
68 | pub fn handle_expand_macro( | 67 | pub fn handle_expand_macro( |
69 | world: WorldSnapshot, | 68 | world: WorldSnapshot, |
70 | params: req::ExpandMacroParams, | 69 | params: lsp_ext::ExpandMacroParams, |
71 | ) -> Result<Option<req::ExpandedMacro>> { | 70 | ) -> Result<Option<lsp_ext::ExpandedMacro>> { |
72 | let _p = profile("handle_expand_macro"); | 71 | let _p = profile("handle_expand_macro"); |
73 | let file_id = params.text_document.try_conv_with(&world)?; | 72 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
74 | let line_index = world.analysis().file_line_index(file_id)?; | 73 | let line_index = world.analysis().file_line_index(file_id)?; |
75 | let offset = params.position.map(|p| p.conv_with(&line_index)); | 74 | let offset = params.position.map(|p| from_proto::offset(&line_index, p)); |
76 | 75 | ||
77 | match offset { | 76 | match offset { |
78 | None => Ok(None), | 77 | None => Ok(None), |
79 | Some(offset) => { | 78 | Some(offset) => { |
80 | let res = world.analysis().expand_macro(FilePosition { file_id, offset })?; | 79 | let res = world.analysis().expand_macro(FilePosition { file_id, offset })?; |
81 | Ok(res.map(|it| req::ExpandedMacro { name: it.name, expansion: it.expansion })) | 80 | Ok(res.map(|it| lsp_ext::ExpandedMacro { name: it.name, expansion: it.expansion })) |
82 | } | 81 | } |
83 | } | 82 | } |
84 | } | 83 | } |
85 | 84 | ||
86 | pub fn handle_selection_range( | 85 | pub fn handle_selection_range( |
87 | world: WorldSnapshot, | 86 | world: WorldSnapshot, |
88 | params: req::SelectionRangeParams, | 87 | params: lsp_types::SelectionRangeParams, |
89 | ) -> Result<Option<Vec<req::SelectionRange>>> { | 88 | ) -> Result<Option<Vec<lsp_types::SelectionRange>>> { |
90 | let _p = profile("handle_selection_range"); | 89 | let _p = profile("handle_selection_range"); |
91 | let file_id = params.text_document.try_conv_with(&world)?; | 90 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
92 | let line_index = world.analysis().file_line_index(file_id)?; | 91 | let line_index = world.analysis().file_line_index(file_id)?; |
93 | let res: Result<Vec<req::SelectionRange>> = params | 92 | let res: Result<Vec<lsp_types::SelectionRange>> = params |
94 | .positions | 93 | .positions |
95 | .into_iter() | 94 | .into_iter() |
96 | .map_conv_with(&line_index) | ||
97 | .map(|position| { | 95 | .map(|position| { |
96 | let offset = from_proto::offset(&line_index, position); | ||
98 | let mut ranges = Vec::new(); | 97 | let mut ranges = Vec::new(); |
99 | { | 98 | { |
100 | let mut range = TextRange::new(position, position); | 99 | let mut range = TextRange::new(offset, offset); |
101 | loop { | 100 | loop { |
102 | ranges.push(range); | 101 | ranges.push(range); |
103 | let frange = FileRange { file_id, range }; | 102 | let frange = FileRange { file_id, range }; |
@@ -109,13 +108,13 @@ pub fn handle_selection_range( | |||
109 | } | 108 | } |
110 | } | 109 | } |
111 | } | 110 | } |
112 | let mut range = req::SelectionRange { | 111 | let mut range = lsp_types::SelectionRange { |
113 | range: ranges.last().unwrap().conv_with(&line_index), | 112 | range: to_proto::range(&line_index, *ranges.last().unwrap()), |
114 | parent: None, | 113 | parent: None, |
115 | }; | 114 | }; |
116 | for r in ranges.iter().rev().skip(1) { | 115 | for &r in ranges.iter().rev().skip(1) { |
117 | range = req::SelectionRange { | 116 | range = lsp_types::SelectionRange { |
118 | range: r.conv_with(&line_index), | 117 | range: to_proto::range(&line_index, r), |
119 | parent: Some(Box::new(range)), | 118 | parent: Some(Box::new(range)), |
120 | } | 119 | } |
121 | } | 120 | } |
@@ -128,57 +127,55 @@ pub fn handle_selection_range( | |||
128 | 127 | ||
129 | pub fn handle_find_matching_brace( | 128 | pub fn handle_find_matching_brace( |
130 | world: WorldSnapshot, | 129 | world: WorldSnapshot, |
131 | params: req::FindMatchingBraceParams, | 130 | params: lsp_ext::FindMatchingBraceParams, |
132 | ) -> Result<Vec<Position>> { | 131 | ) -> Result<Vec<Position>> { |
133 | let _p = profile("handle_find_matching_brace"); | 132 | let _p = profile("handle_find_matching_brace"); |
134 | let file_id = params.text_document.try_conv_with(&world)?; | 133 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
135 | let line_index = world.analysis().file_line_index(file_id)?; | 134 | let line_index = world.analysis().file_line_index(file_id)?; |
136 | let res = params | 135 | let res = params |
137 | .offsets | 136 | .offsets |
138 | .into_iter() | 137 | .into_iter() |
139 | .map_conv_with(&line_index) | 138 | .map(|position| { |
140 | .map(|offset| { | 139 | let offset = from_proto::offset(&line_index, position); |
141 | if let Ok(Some(matching_brace_offset)) = | 140 | let offset = match world.analysis().matching_brace(FilePosition { file_id, offset }) { |
142 | world.analysis().matching_brace(FilePosition { file_id, offset }) | 141 | Ok(Some(matching_brace_offset)) => matching_brace_offset, |
143 | { | 142 | Err(_) | Ok(None) => offset, |
144 | matching_brace_offset | 143 | }; |
145 | } else { | 144 | to_proto::position(&line_index, offset) |
146 | offset | ||
147 | } | ||
148 | }) | 145 | }) |
149 | .map_conv_with(&line_index) | ||
150 | .collect(); | 146 | .collect(); |
151 | Ok(res) | 147 | Ok(res) |
152 | } | 148 | } |
153 | 149 | ||
154 | pub fn handle_join_lines( | 150 | pub fn handle_join_lines( |
155 | world: WorldSnapshot, | 151 | world: WorldSnapshot, |
156 | params: req::JoinLinesParams, | 152 | params: lsp_ext::JoinLinesParams, |
157 | ) -> Result<req::SourceChange> { | 153 | ) -> Result<lsp_ext::SourceChange> { |
158 | let _p = profile("handle_join_lines"); | 154 | let _p = profile("handle_join_lines"); |
159 | let frange = (¶ms.text_document, params.range).try_conv_with(&world)?; | 155 | let frange = from_proto::file_range(&world, params.text_document, params.range)?; |
160 | world.analysis().join_lines(frange)?.try_conv_with(&world) | 156 | let source_change = world.analysis().join_lines(frange)?; |
157 | to_proto::source_change(&world, source_change) | ||
161 | } | 158 | } |
162 | 159 | ||
163 | pub fn handle_on_enter( | 160 | pub fn handle_on_enter( |
164 | world: WorldSnapshot, | 161 | world: WorldSnapshot, |
165 | params: req::TextDocumentPositionParams, | 162 | params: lsp_types::TextDocumentPositionParams, |
166 | ) -> Result<Option<req::SourceChange>> { | 163 | ) -> Result<Option<lsp_ext::SourceChange>> { |
167 | let _p = profile("handle_on_enter"); | 164 | let _p = profile("handle_on_enter"); |
168 | let position = params.try_conv_with(&world)?; | 165 | let position = from_proto::file_position(&world, params)?; |
169 | match world.analysis().on_enter(position)? { | 166 | match world.analysis().on_enter(position)? { |
170 | None => Ok(None), | 167 | None => Ok(None), |
171 | Some(edit) => Ok(Some(edit.try_conv_with(&world)?)), | 168 | Some(source_change) => to_proto::source_change(&world, source_change).map(Some), |
172 | } | 169 | } |
173 | } | 170 | } |
174 | 171 | ||
175 | // Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`. | 172 | // Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`. |
176 | pub fn handle_on_type_formatting( | 173 | pub fn handle_on_type_formatting( |
177 | world: WorldSnapshot, | 174 | world: WorldSnapshot, |
178 | params: req::DocumentOnTypeFormattingParams, | 175 | params: lsp_types::DocumentOnTypeFormattingParams, |
179 | ) -> Result<Option<Vec<TextEdit>>> { | 176 | ) -> Result<Option<Vec<TextEdit>>> { |
180 | let _p = profile("handle_on_type_formatting"); | 177 | let _p = profile("handle_on_type_formatting"); |
181 | let mut position = params.text_document_position.try_conv_with(&world)?; | 178 | let mut position = from_proto::file_position(&world, params.text_document_position)?; |
182 | let line_index = world.analysis().file_line_index(position.file_id)?; | 179 | let line_index = world.analysis().file_line_index(position.file_id)?; |
183 | let line_endings = world.file_line_endings(position.file_id); | 180 | let line_endings = world.file_line_endings(position.file_id); |
184 | 181 | ||
@@ -208,18 +205,17 @@ pub fn handle_on_type_formatting( | |||
208 | // This should be a single-file edit | 205 | // This should be a single-file edit |
209 | let edit = edit.source_file_edits.pop().unwrap(); | 206 | let edit = edit.source_file_edits.pop().unwrap(); |
210 | 207 | ||
211 | let change: Vec<TextEdit> = edit.edit.conv_with((&line_index, line_endings)); | 208 | let change = to_proto::text_edit_vec(&line_index, line_endings, edit.edit); |
212 | Ok(Some(change)) | 209 | Ok(Some(change)) |
213 | } | 210 | } |
214 | 211 | ||
215 | pub fn handle_document_symbol( | 212 | pub fn handle_document_symbol( |
216 | world: WorldSnapshot, | 213 | world: WorldSnapshot, |
217 | params: req::DocumentSymbolParams, | 214 | params: lsp_types::DocumentSymbolParams, |
218 | ) -> Result<Option<req::DocumentSymbolResponse>> { | 215 | ) -> Result<Option<lsp_types::DocumentSymbolResponse>> { |
219 | let _p = profile("handle_document_symbol"); | 216 | let _p = profile("handle_document_symbol"); |
220 | let file_id = params.text_document.try_conv_with(&world)?; | 217 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
221 | let line_index = world.analysis().file_line_index(file_id)?; | 218 | let line_index = world.analysis().file_line_index(file_id)?; |
222 | let url = file_id.try_conv_with(&world)?; | ||
223 | 219 | ||
224 | let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new(); | 220 | let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new(); |
225 | 221 | ||
@@ -227,10 +223,10 @@ pub fn handle_document_symbol( | |||
227 | let doc_symbol = DocumentSymbol { | 223 | let doc_symbol = DocumentSymbol { |
228 | name: symbol.label, | 224 | name: symbol.label, |
229 | detail: symbol.detail, | 225 | detail: symbol.detail, |
230 | kind: symbol.kind.conv(), | 226 | kind: to_proto::symbol_kind(symbol.kind), |
231 | deprecated: Some(symbol.deprecated), | 227 | deprecated: Some(symbol.deprecated), |
232 | range: symbol.node_range.conv_with(&line_index), | 228 | range: to_proto::range(&line_index, symbol.node_range), |
233 | selection_range: symbol.navigation_range.conv_with(&line_index), | 229 | selection_range: to_proto::range(&line_index, symbol.navigation_range), |
234 | children: None, | 230 | children: None, |
235 | }; | 231 | }; |
236 | parents.push((doc_symbol, symbol.parent)); | 232 | parents.push((doc_symbol, symbol.parent)); |
@@ -249,40 +245,41 @@ pub fn handle_document_symbol( | |||
249 | } | 245 | } |
250 | } | 246 | } |
251 | 247 | ||
252 | if world.config.client_caps.hierarchical_symbols { | 248 | let res = if world.config.client_caps.hierarchical_symbols { |
253 | Ok(Some(document_symbols.into())) | 249 | document_symbols.into() |
254 | } else { | 250 | } else { |
251 | let url = to_proto::url(&world, file_id)?; | ||
255 | let mut symbol_information = Vec::<SymbolInformation>::new(); | 252 | let mut symbol_information = Vec::<SymbolInformation>::new(); |
256 | for symbol in document_symbols { | 253 | for symbol in document_symbols { |
257 | flatten_document_symbol(&symbol, None, &url, &mut symbol_information); | 254 | flatten_document_symbol(&symbol, None, &url, &mut symbol_information); |
258 | } | 255 | } |
256 | symbol_information.into() | ||
257 | }; | ||
258 | return Ok(Some(res)); | ||
259 | 259 | ||
260 | Ok(Some(symbol_information.into())) | 260 | fn flatten_document_symbol( |
261 | } | 261 | symbol: &DocumentSymbol, |
262 | } | 262 | container_name: Option<String>, |
263 | 263 | url: &Url, | |
264 | fn flatten_document_symbol( | 264 | res: &mut Vec<SymbolInformation>, |
265 | symbol: &DocumentSymbol, | 265 | ) { |
266 | container_name: Option<String>, | 266 | res.push(SymbolInformation { |
267 | url: &Url, | 267 | name: symbol.name.clone(), |
268 | res: &mut Vec<SymbolInformation>, | 268 | kind: symbol.kind, |
269 | ) { | 269 | deprecated: symbol.deprecated, |
270 | res.push(SymbolInformation { | 270 | location: Location::new(url.clone(), symbol.range), |
271 | name: symbol.name.clone(), | 271 | container_name: container_name, |
272 | kind: symbol.kind, | 272 | }); |
273 | deprecated: symbol.deprecated, | ||
274 | location: Location::new(url.clone(), symbol.range), | ||
275 | container_name: container_name, | ||
276 | }); | ||
277 | 273 | ||
278 | for child in symbol.children.iter().flatten() { | 274 | for child in symbol.children.iter().flatten() { |
279 | flatten_document_symbol(child, Some(symbol.name.clone()), url, res); | 275 | flatten_document_symbol(child, Some(symbol.name.clone()), url, res); |
276 | } | ||
280 | } | 277 | } |
281 | } | 278 | } |
282 | 279 | ||
283 | pub fn handle_workspace_symbol( | 280 | pub fn handle_workspace_symbol( |
284 | world: WorldSnapshot, | 281 | world: WorldSnapshot, |
285 | params: req::WorkspaceSymbolParams, | 282 | params: lsp_types::WorkspaceSymbolParams, |
286 | ) -> Result<Option<Vec<SymbolInformation>>> { | 283 | ) -> Result<Option<Vec<SymbolInformation>>> { |
287 | let _p = profile("handle_workspace_symbol"); | 284 | let _p = profile("handle_workspace_symbol"); |
288 | let all_symbols = params.query.contains('#'); | 285 | let all_symbols = params.query.contains('#'); |
@@ -313,8 +310,8 @@ pub fn handle_workspace_symbol( | |||
313 | for nav in world.analysis().symbol_search(query)? { | 310 | for nav in world.analysis().symbol_search(query)? { |
314 | let info = SymbolInformation { | 311 | let info = SymbolInformation { |
315 | name: nav.name().to_string(), | 312 | name: nav.name().to_string(), |
316 | kind: nav.kind().conv(), | 313 | kind: to_proto::symbol_kind(nav.kind()), |
317 | location: nav.try_conv_with(world)?, | 314 | location: to_proto::location(world, nav.file_range())?, |
318 | container_name: nav.container_name().map(|v| v.to_string()), | 315 | container_name: nav.container_name().map(|v| v.to_string()), |
319 | deprecated: None, | 316 | deprecated: None, |
320 | }; | 317 | }; |
@@ -326,63 +323,80 @@ pub fn handle_workspace_symbol( | |||
326 | 323 | ||
327 | pub fn handle_goto_definition( | 324 | pub fn handle_goto_definition( |
328 | world: WorldSnapshot, | 325 | world: WorldSnapshot, |
329 | params: req::GotoDefinitionParams, | 326 | params: lsp_types::GotoDefinitionParams, |
330 | ) -> Result<Option<req::GotoDefinitionResponse>> { | 327 | ) -> Result<Option<lsp_types::GotoDefinitionResponse>> { |
331 | let _p = profile("handle_goto_definition"); | 328 | let _p = profile("handle_goto_definition"); |
332 | let position = params.text_document_position_params.try_conv_with(&world)?; | 329 | let position = from_proto::file_position(&world, params.text_document_position_params)?; |
333 | let nav_info = match world.analysis().goto_definition(position)? { | 330 | let nav_info = match world.analysis().goto_definition(position)? { |
334 | None => return Ok(None), | 331 | None => return Ok(None), |
335 | Some(it) => it, | 332 | Some(it) => it, |
336 | }; | 333 | }; |
337 | let res = (position.file_id, nav_info).try_conv_with(&world)?; | 334 | let res = to_proto::goto_definition_response( |
335 | &world, | ||
336 | FileRange { file_id: position.file_id, range: nav_info.range }, | ||
337 | nav_info.info, | ||
338 | )?; | ||
338 | Ok(Some(res)) | 339 | Ok(Some(res)) |
339 | } | 340 | } |
340 | 341 | ||
341 | pub fn handle_goto_implementation( | 342 | pub fn handle_goto_implementation( |
342 | world: WorldSnapshot, | 343 | world: WorldSnapshot, |
343 | params: req::GotoImplementationParams, | 344 | params: lsp_types::request::GotoImplementationParams, |
344 | ) -> Result<Option<req::GotoImplementationResponse>> { | 345 | ) -> Result<Option<lsp_types::request::GotoImplementationResponse>> { |
345 | let _p = profile("handle_goto_implementation"); | 346 | let _p = profile("handle_goto_implementation"); |
346 | let position = params.text_document_position_params.try_conv_with(&world)?; | 347 | let position = from_proto::file_position(&world, params.text_document_position_params)?; |
347 | let nav_info = match world.analysis().goto_implementation(position)? { | 348 | let nav_info = match world.analysis().goto_implementation(position)? { |
348 | None => return Ok(None), | 349 | None => return Ok(None), |
349 | Some(it) => it, | 350 | Some(it) => it, |
350 | }; | 351 | }; |
351 | let res = (position.file_id, nav_info).try_conv_with(&world)?; | 352 | let res = to_proto::goto_definition_response( |
353 | &world, | ||
354 | FileRange { file_id: position.file_id, range: nav_info.range }, | ||
355 | nav_info.info, | ||
356 | )?; | ||
352 | Ok(Some(res)) | 357 | Ok(Some(res)) |
353 | } | 358 | } |
354 | 359 | ||
355 | pub fn handle_goto_type_definition( | 360 | pub fn handle_goto_type_definition( |
356 | world: WorldSnapshot, | 361 | world: WorldSnapshot, |
357 | params: req::GotoTypeDefinitionParams, | 362 | params: lsp_types::request::GotoTypeDefinitionParams, |
358 | ) -> Result<Option<req::GotoTypeDefinitionResponse>> { | 363 | ) -> Result<Option<lsp_types::request::GotoTypeDefinitionResponse>> { |
359 | let _p = profile("handle_goto_type_definition"); | 364 | let _p = profile("handle_goto_type_definition"); |
360 | let position = params.text_document_position_params.try_conv_with(&world)?; | 365 | let position = from_proto::file_position(&world, params.text_document_position_params)?; |
361 | let nav_info = match world.analysis().goto_type_definition(position)? { | 366 | let nav_info = match world.analysis().goto_type_definition(position)? { |
362 | None => return Ok(None), | 367 | None => return Ok(None), |
363 | Some(it) => it, | 368 | Some(it) => it, |
364 | }; | 369 | }; |
365 | let res = (position.file_id, nav_info).try_conv_with(&world)?; | 370 | let res = to_proto::goto_definition_response( |
371 | &world, | ||
372 | FileRange { file_id: position.file_id, range: nav_info.range }, | ||
373 | nav_info.info, | ||
374 | )?; | ||
366 | Ok(Some(res)) | 375 | Ok(Some(res)) |
367 | } | 376 | } |
368 | 377 | ||
369 | pub fn handle_parent_module( | 378 | pub fn handle_parent_module( |
370 | world: WorldSnapshot, | 379 | world: WorldSnapshot, |
371 | params: req::TextDocumentPositionParams, | 380 | params: lsp_types::TextDocumentPositionParams, |
372 | ) -> Result<Vec<Location>> { | 381 | ) -> Result<Vec<Location>> { |
373 | let _p = profile("handle_parent_module"); | 382 | let _p = profile("handle_parent_module"); |
374 | let position = params.try_conv_with(&world)?; | 383 | let position = from_proto::file_position(&world, params)?; |
375 | world.analysis().parent_module(position)?.iter().try_conv_with_to_vec(&world) | 384 | world |
385 | .analysis() | ||
386 | .parent_module(position)? | ||
387 | .into_iter() | ||
388 | .map(|it| to_proto::location(&world, it.file_range())) | ||
389 | .collect::<Result<Vec<_>>>() | ||
376 | } | 390 | } |
377 | 391 | ||
378 | pub fn handle_runnables( | 392 | pub fn handle_runnables( |
379 | world: WorldSnapshot, | 393 | world: WorldSnapshot, |
380 | params: req::RunnablesParams, | 394 | params: lsp_ext::RunnablesParams, |
381 | ) -> Result<Vec<req::Runnable>> { | 395 | ) -> Result<Vec<lsp_ext::Runnable>> { |
382 | let _p = profile("handle_runnables"); | 396 | let _p = profile("handle_runnables"); |
383 | let file_id = params.text_document.try_conv_with(&world)?; | 397 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
384 | let line_index = world.analysis().file_line_index(file_id)?; | 398 | let line_index = world.analysis().file_line_index(file_id)?; |
385 | let offset = params.position.map(|it| it.conv_with(&line_index)); | 399 | let offset = params.position.map(|it| from_proto::offset(&line_index, it)); |
386 | let mut res = Vec::new(); | 400 | let mut res = Vec::new(); |
387 | let workspace_root = world.workspace_root_for(file_id); | 401 | let workspace_root = world.workspace_root_for(file_id); |
388 | let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; | 402 | let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; |
@@ -408,7 +422,7 @@ pub fn handle_runnables( | |||
408 | match cargo_spec { | 422 | match cargo_spec { |
409 | Some(spec) => { | 423 | Some(spec) => { |
410 | for &cmd in ["check", "test"].iter() { | 424 | for &cmd in ["check", "test"].iter() { |
411 | res.push(req::Runnable { | 425 | res.push(lsp_ext::Runnable { |
412 | range: Default::default(), | 426 | range: Default::default(), |
413 | label: format!("cargo {} -p {}", cmd, spec.package), | 427 | label: format!("cargo {} -p {}", cmd, spec.package), |
414 | bin: "cargo".to_string(), | 428 | bin: "cargo".to_string(), |
@@ -420,7 +434,7 @@ pub fn handle_runnables( | |||
420 | } | 434 | } |
421 | } | 435 | } |
422 | None => { | 436 | None => { |
423 | res.push(req::Runnable { | 437 | res.push(lsp_ext::Runnable { |
424 | range: Default::default(), | 438 | range: Default::default(), |
425 | label: "cargo check --workspace".to_string(), | 439 | label: "cargo check --workspace".to_string(), |
426 | bin: "cargo".to_string(), | 440 | bin: "cargo".to_string(), |
@@ -436,10 +450,10 @@ pub fn handle_runnables( | |||
436 | 450 | ||
437 | pub fn handle_completion( | 451 | pub fn handle_completion( |
438 | world: WorldSnapshot, | 452 | world: WorldSnapshot, |
439 | params: req::CompletionParams, | 453 | params: lsp_types::CompletionParams, |
440 | ) -> Result<Option<req::CompletionResponse>> { | 454 | ) -> Result<Option<lsp_types::CompletionResponse>> { |
441 | let _p = profile("handle_completion"); | 455 | let _p = profile("handle_completion"); |
442 | let position = params.text_document_position.try_conv_with(&world)?; | 456 | let position = from_proto::file_position(&world, params.text_document_position)?; |
443 | let completion_triggered_after_single_colon = { | 457 | let completion_triggered_after_single_colon = { |
444 | let mut res = false; | 458 | let mut res = false; |
445 | if let Some(ctx) = params.context { | 459 | if let Some(ctx) = params.context { |
@@ -468,8 +482,10 @@ pub fn handle_completion( | |||
468 | }; | 482 | }; |
469 | let line_index = world.analysis().file_line_index(position.file_id)?; | 483 | let line_index = world.analysis().file_line_index(position.file_id)?; |
470 | let line_endings = world.file_line_endings(position.file_id); | 484 | let line_endings = world.file_line_endings(position.file_id); |
471 | let items: Vec<CompletionItem> = | 485 | let items: Vec<CompletionItem> = items |
472 | items.into_iter().map(|item| item.conv_with((&line_index, line_endings))).collect(); | 486 | .into_iter() |
487 | .map(|item| to_proto::completion_item(&line_index, line_endings, item)) | ||
488 | .collect(); | ||
473 | 489 | ||
474 | Ok(Some(items.into())) | 490 | Ok(Some(items.into())) |
475 | } | 491 | } |
@@ -479,52 +495,51 @@ pub fn handle_folding_range( | |||
479 | params: FoldingRangeParams, | 495 | params: FoldingRangeParams, |
480 | ) -> Result<Option<Vec<FoldingRange>>> { | 496 | ) -> Result<Option<Vec<FoldingRange>>> { |
481 | let _p = profile("handle_folding_range"); | 497 | let _p = profile("handle_folding_range"); |
482 | let file_id = params.text_document.try_conv_with(&world)?; | 498 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
483 | let folds = world.analysis().folding_ranges(file_id)?; | 499 | let folds = world.analysis().folding_ranges(file_id)?; |
484 | let text = world.analysis().file_text(file_id)?; | 500 | let text = world.analysis().file_text(file_id)?; |
485 | let line_index = world.analysis().file_line_index(file_id)?; | 501 | let line_index = world.analysis().file_line_index(file_id)?; |
486 | let ctx = FoldConvCtx { | 502 | let line_folding_only = world.config.client_caps.line_folding_only; |
487 | text: &text, | 503 | let res = folds |
488 | line_index: &line_index, | 504 | .into_iter() |
489 | line_folding_only: world.config.client_caps.line_folding_only, | 505 | .map(|it| to_proto::folding_range(&*text, &line_index, line_folding_only, it)) |
490 | }; | 506 | .collect(); |
491 | let res = Some(folds.into_iter().map_conv_with(&ctx).collect()); | 507 | Ok(Some(res)) |
492 | Ok(res) | ||
493 | } | 508 | } |
494 | 509 | ||
495 | pub fn handle_signature_help( | 510 | pub fn handle_signature_help( |
496 | world: WorldSnapshot, | 511 | world: WorldSnapshot, |
497 | params: req::SignatureHelpParams, | 512 | params: lsp_types::SignatureHelpParams, |
498 | ) -> Result<Option<req::SignatureHelp>> { | 513 | ) -> Result<Option<lsp_types::SignatureHelp>> { |
499 | let _p = profile("handle_signature_help"); | 514 | let _p = profile("handle_signature_help"); |
500 | let position = params.text_document_position_params.try_conv_with(&world)?; | 515 | let position = from_proto::file_position(&world, params.text_document_position_params)?; |
501 | if let Some(call_info) = world.analysis().call_info(position)? { | 516 | let call_info = match world.analysis().call_info(position)? { |
502 | let concise = !world.config.call_info_full; | 517 | None => return Ok(None), |
503 | let mut active_parameter = call_info.active_parameter.map(|it| it as i64); | 518 | Some(it) => it, |
504 | if concise && call_info.signature.has_self_param { | 519 | }; |
505 | active_parameter = active_parameter.map(|it| it.saturating_sub(1)); | 520 | let concise = !world.config.call_info_full; |
506 | } | 521 | let mut active_parameter = call_info.active_parameter.map(|it| it as i64); |
507 | let sig_info = call_info.signature.conv_with(concise); | 522 | if concise && call_info.signature.has_self_param { |
508 | 523 | active_parameter = active_parameter.map(|it| it.saturating_sub(1)); | |
509 | Ok(Some(req::SignatureHelp { | ||
510 | signatures: vec![sig_info], | ||
511 | active_signature: Some(0), | ||
512 | active_parameter, | ||
513 | })) | ||
514 | } else { | ||
515 | Ok(None) | ||
516 | } | 524 | } |
525 | let sig_info = to_proto::signature_information(call_info.signature, concise); | ||
526 | |||
527 | Ok(Some(lsp_types::SignatureHelp { | ||
528 | signatures: vec![sig_info], | ||
529 | active_signature: Some(0), | ||
530 | active_parameter, | ||
531 | })) | ||
517 | } | 532 | } |
518 | 533 | ||
519 | pub fn handle_hover(world: WorldSnapshot, params: req::HoverParams) -> Result<Option<Hover>> { | 534 | pub fn handle_hover(world: WorldSnapshot, params: lsp_types::HoverParams) -> Result<Option<Hover>> { |
520 | let _p = profile("handle_hover"); | 535 | let _p = profile("handle_hover"); |
521 | let position = params.text_document_position_params.try_conv_with(&world)?; | 536 | let position = from_proto::file_position(&world, params.text_document_position_params)?; |
522 | let info = match world.analysis().hover(position)? { | 537 | let info = match world.analysis().hover(position)? { |
523 | None => return Ok(None), | 538 | None => return Ok(None), |
524 | Some(info) => info, | 539 | Some(info) => info, |
525 | }; | 540 | }; |
526 | let line_index = world.analysis.file_line_index(position.file_id)?; | 541 | let line_index = world.analysis.file_line_index(position.file_id)?; |
527 | let range = info.range.conv_with(&line_index); | 542 | let range = to_proto::range(&line_index, info.range); |
528 | let res = Hover { | 543 | let res = Hover { |
529 | contents: HoverContents::Markup(MarkupContent { | 544 | contents: HoverContents::Markup(MarkupContent { |
530 | kind: MarkupKind::Markdown, | 545 | kind: MarkupKind::Markdown, |
@@ -537,10 +552,10 @@ pub fn handle_hover(world: WorldSnapshot, params: req::HoverParams) -> Result<Op | |||
537 | 552 | ||
538 | pub fn handle_prepare_rename( | 553 | pub fn handle_prepare_rename( |
539 | world: WorldSnapshot, | 554 | world: WorldSnapshot, |
540 | params: req::TextDocumentPositionParams, | 555 | params: lsp_types::TextDocumentPositionParams, |
541 | ) -> Result<Option<PrepareRenameResponse>> { | 556 | ) -> Result<Option<PrepareRenameResponse>> { |
542 | let _p = profile("handle_prepare_rename"); | 557 | let _p = profile("handle_prepare_rename"); |
543 | let position = params.try_conv_with(&world)?; | 558 | let position = from_proto::file_position(&world, params)?; |
544 | 559 | ||
545 | let optional_change = world.analysis().rename(position, "dummy")?; | 560 | let optional_change = world.analysis().rename(position, "dummy")?; |
546 | let range = match optional_change { | 561 | let range = match optional_change { |
@@ -548,15 +563,14 @@ pub fn handle_prepare_rename( | |||
548 | Some(it) => it.range, | 563 | Some(it) => it.range, |
549 | }; | 564 | }; |
550 | 565 | ||
551 | let file_id = params.text_document.try_conv_with(&world)?; | 566 | let line_index = world.analysis().file_line_index(position.file_id)?; |
552 | let line_index = world.analysis().file_line_index(file_id)?; | 567 | let range = to_proto::range(&line_index, range); |
553 | let range = range.conv_with(&line_index); | ||
554 | Ok(Some(PrepareRenameResponse::Range(range))) | 568 | Ok(Some(PrepareRenameResponse::Range(range))) |
555 | } | 569 | } |
556 | 570 | ||
557 | pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Option<WorkspaceEdit>> { | 571 | pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Option<WorkspaceEdit>> { |
558 | let _p = profile("handle_rename"); | 572 | let _p = profile("handle_rename"); |
559 | let position = params.text_document_position.try_conv_with(&world)?; | 573 | let position = from_proto::file_position(&world, params.text_document_position)?; |
560 | 574 | ||
561 | if params.new_name.is_empty() { | 575 | if params.new_name.is_empty() { |
562 | return Err(LspError::new( | 576 | return Err(LspError::new( |
@@ -567,22 +581,21 @@ pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Optio | |||
567 | } | 581 | } |
568 | 582 | ||
569 | let optional_change = world.analysis().rename(position, &*params.new_name)?; | 583 | let optional_change = world.analysis().rename(position, &*params.new_name)?; |
570 | let change = match optional_change { | 584 | let source_change = match optional_change { |
571 | None => return Ok(None), | 585 | None => return Ok(None), |
572 | Some(it) => it.info, | 586 | Some(it) => it.info, |
573 | }; | 587 | }; |
574 | 588 | ||
575 | let source_change_req = change.try_conv_with(&world)?; | 589 | let source_change = to_proto::source_change(&world, source_change)?; |
576 | 590 | Ok(Some(source_change.workspace_edit)) | |
577 | Ok(Some(source_change_req.workspace_edit)) | ||
578 | } | 591 | } |
579 | 592 | ||
580 | pub fn handle_references( | 593 | pub fn handle_references( |
581 | world: WorldSnapshot, | 594 | world: WorldSnapshot, |
582 | params: req::ReferenceParams, | 595 | params: lsp_types::ReferenceParams, |
583 | ) -> Result<Option<Vec<Location>>> { | 596 | ) -> Result<Option<Vec<Location>>> { |
584 | let _p = profile("handle_references"); | 597 | let _p = profile("handle_references"); |
585 | let position = params.text_document_position.try_conv_with(&world)?; | 598 | let position = from_proto::file_position(&world, params.text_document_position)?; |
586 | 599 | ||
587 | let refs = match world.analysis().find_all_refs(position, None)? { | 600 | let refs = match world.analysis().find_all_refs(position, None)? { |
588 | None => return Ok(None), | 601 | None => return Ok(None), |
@@ -591,33 +604,13 @@ pub fn handle_references( | |||
591 | 604 | ||
592 | let locations = if params.context.include_declaration { | 605 | let locations = if params.context.include_declaration { |
593 | refs.into_iter() | 606 | refs.into_iter() |
594 | .filter_map(|reference| { | 607 | .filter_map(|reference| to_proto::location(&world, reference.file_range).ok()) |
595 | let line_index = | ||
596 | world.analysis().file_line_index(reference.file_range.file_id).ok()?; | ||
597 | to_location( | ||
598 | reference.file_range.file_id, | ||
599 | reference.file_range.range, | ||
600 | &world, | ||
601 | &line_index, | ||
602 | ) | ||
603 | .ok() | ||
604 | }) | ||
605 | .collect() | 608 | .collect() |
606 | } else { | 609 | } else { |
607 | // Only iterate over the references if include_declaration was false | 610 | // Only iterate over the references if include_declaration was false |
608 | refs.references() | 611 | refs.references() |
609 | .iter() | 612 | .iter() |
610 | .filter_map(|reference| { | 613 | .filter_map(|reference| to_proto::location(&world, reference.file_range).ok()) |
611 | let line_index = | ||
612 | world.analysis().file_line_index(reference.file_range.file_id).ok()?; | ||
613 | to_location( | ||
614 | reference.file_range.file_id, | ||
615 | reference.file_range.range, | ||
616 | &world, | ||
617 | &line_index, | ||
618 | ) | ||
619 | .ok() | ||
620 | }) | ||
621 | .collect() | 614 | .collect() |
622 | }; | 615 | }; |
623 | 616 | ||
@@ -629,12 +622,12 @@ pub fn handle_formatting( | |||
629 | params: DocumentFormattingParams, | 622 | params: DocumentFormattingParams, |
630 | ) -> Result<Option<Vec<TextEdit>>> { | 623 | ) -> Result<Option<Vec<TextEdit>>> { |
631 | let _p = profile("handle_formatting"); | 624 | let _p = profile("handle_formatting"); |
632 | let file_id = params.text_document.try_conv_with(&world)?; | 625 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
633 | let file = world.analysis().file_text(file_id)?; | 626 | let file = world.analysis().file_text(file_id)?; |
634 | let crate_ids = world.analysis().crate_for(file_id)?; | 627 | let crate_ids = world.analysis().crate_for(file_id)?; |
635 | 628 | ||
636 | let file_line_index = world.analysis().file_line_index(file_id)?; | 629 | let file_line_index = world.analysis().file_line_index(file_id)?; |
637 | let end_position = TextSize::of(file.as_str()).conv_with(&file_line_index); | 630 | let end_position = to_proto::position(&file_line_index, TextSize::of(file.as_str())); |
638 | 631 | ||
639 | let mut rustfmt = match &world.config.rustfmt { | 632 | let mut rustfmt = match &world.config.rustfmt { |
640 | RustfmtConfig::Rustfmt { extra_args } => { | 633 | RustfmtConfig::Rustfmt { extra_args } => { |
@@ -700,33 +693,14 @@ pub fn handle_formatting( | |||
700 | }])) | 693 | }])) |
701 | } | 694 | } |
702 | 695 | ||
703 | fn create_single_code_action(assist: Assist, world: &WorldSnapshot) -> Result<CodeAction> { | ||
704 | let arg = to_value(assist.source_change.try_conv_with(world)?)?; | ||
705 | let title = assist.label; | ||
706 | let command = Command { | ||
707 | title: title.clone(), | ||
708 | command: "rust-analyzer.applySourceChange".to_string(), | ||
709 | arguments: Some(vec![arg]), | ||
710 | }; | ||
711 | |||
712 | Ok(CodeAction { | ||
713 | title, | ||
714 | kind: Some(String::new()), | ||
715 | diagnostics: None, | ||
716 | edit: None, | ||
717 | command: Some(command), | ||
718 | is_preferred: None, | ||
719 | }) | ||
720 | } | ||
721 | |||
722 | pub fn handle_code_action( | 696 | pub fn handle_code_action( |
723 | world: WorldSnapshot, | 697 | world: WorldSnapshot, |
724 | params: req::CodeActionParams, | 698 | params: lsp_types::CodeActionParams, |
725 | ) -> Result<Option<CodeActionResponse>> { | 699 | ) -> Result<Option<CodeActionResponse>> { |
726 | let _p = profile("handle_code_action"); | 700 | let _p = profile("handle_code_action"); |
727 | let file_id = params.text_document.try_conv_with(&world)?; | 701 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
728 | let line_index = world.analysis().file_line_index(file_id)?; | 702 | let line_index = world.analysis().file_line_index(file_id)?; |
729 | let range = params.range.conv_with(&line_index); | 703 | let range = from_proto::text_range(&line_index, params.range); |
730 | 704 | ||
731 | let diagnostics = world.analysis().diagnostics(file_id)?; | 705 | let diagnostics = world.analysis().diagnostics(file_id)?; |
732 | let mut res = CodeActionResponse::default(); | 706 | let mut res = CodeActionResponse::default(); |
@@ -739,7 +713,7 @@ pub fn handle_code_action( | |||
739 | 713 | ||
740 | for source_edit in fixes_from_diagnostics { | 714 | for source_edit in fixes_from_diagnostics { |
741 | let title = source_edit.label.clone(); | 715 | let title = source_edit.label.clone(); |
742 | let edit = source_edit.try_conv_with(&world)?; | 716 | let edit = to_proto::source_change(&world, source_edit)?; |
743 | 717 | ||
744 | let command = Command { | 718 | let command = Command { |
745 | title, | 719 | title, |
@@ -758,7 +732,7 @@ pub fn handle_code_action( | |||
758 | } | 732 | } |
759 | 733 | ||
760 | for fix in world.check_fixes.get(&file_id).into_iter().flatten() { | 734 | for fix in world.check_fixes.get(&file_id).into_iter().flatten() { |
761 | let fix_range = fix.range.conv_with(&line_index); | 735 | let fix_range = from_proto::text_range(&line_index, fix.range); |
762 | if fix_range.intersect(range).is_none() { | 736 | if fix_range.intersect(range).is_none() { |
763 | continue; | 737 | continue; |
764 | } | 738 | } |
@@ -779,21 +753,21 @@ pub fn handle_code_action( | |||
779 | .1 | 753 | .1 |
780 | .push(assist), | 754 | .push(assist), |
781 | None => { | 755 | None => { |
782 | res.push(create_single_code_action(assist, &world)?.into()); | 756 | res.push(to_proto::code_action(&world, assist)?.into()); |
783 | } | 757 | } |
784 | } | 758 | } |
785 | } | 759 | } |
786 | 760 | ||
787 | for (group_label, (idx, assists)) in grouped_assists { | 761 | for (group_label, (idx, assists)) in grouped_assists { |
788 | if assists.len() == 1 { | 762 | if assists.len() == 1 { |
789 | res[idx] = | 763 | res[idx] = to_proto::code_action(&world, assists.into_iter().next().unwrap())?.into(); |
790 | create_single_code_action(assists.into_iter().next().unwrap(), &world)?.into(); | ||
791 | } else { | 764 | } else { |
792 | let title = group_label; | 765 | let title = group_label; |
793 | 766 | ||
794 | let mut arguments = Vec::with_capacity(assists.len()); | 767 | let mut arguments = Vec::with_capacity(assists.len()); |
795 | for assist in assists { | 768 | for assist in assists { |
796 | arguments.push(to_value(assist.source_change.try_conv_with(&world)?)?); | 769 | let source_change = to_proto::source_change(&world, assist.source_change)?; |
770 | arguments.push(to_value(source_change)?); | ||
797 | } | 771 | } |
798 | 772 | ||
799 | let command = Some(Command { | 773 | let command = Some(Command { |
@@ -835,10 +809,10 @@ pub fn handle_code_action( | |||
835 | 809 | ||
836 | pub fn handle_code_lens( | 810 | pub fn handle_code_lens( |
837 | world: WorldSnapshot, | 811 | world: WorldSnapshot, |
838 | params: req::CodeLensParams, | 812 | params: lsp_types::CodeLensParams, |
839 | ) -> Result<Option<Vec<CodeLens>>> { | 813 | ) -> Result<Option<Vec<CodeLens>>> { |
840 | let _p = profile("handle_code_lens"); | 814 | let _p = profile("handle_code_lens"); |
841 | let file_id = params.text_document.try_conv_with(&world)?; | 815 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
842 | let line_index = world.analysis().file_line_index(file_id)?; | 816 | let line_index = world.analysis().file_line_index(file_id)?; |
843 | 817 | ||
844 | let mut lenses: Vec<CodeLens> = Default::default(); | 818 | let mut lenses: Vec<CodeLens> = Default::default(); |
@@ -902,10 +876,10 @@ pub fn handle_code_lens( | |||
902 | _ => false, | 876 | _ => false, |
903 | }) | 877 | }) |
904 | .map(|it| { | 878 | .map(|it| { |
905 | let range = it.node_range.conv_with(&line_index); | 879 | let range = to_proto::range(&line_index, it.node_range); |
906 | let pos = range.start; | 880 | let pos = range.start; |
907 | let lens_params = req::GotoImplementationParams { | 881 | let lens_params = lsp_types::request::GotoImplementationParams { |
908 | text_document_position_params: req::TextDocumentPositionParams::new( | 882 | text_document_position_params: lsp_types::TextDocumentPositionParams::new( |
909 | params.text_document.clone(), | 883 | params.text_document.clone(), |
910 | pos, | 884 | pos, |
911 | ), | 885 | ), |
@@ -926,7 +900,7 @@ pub fn handle_code_lens( | |||
926 | #[derive(Debug, Serialize, Deserialize)] | 900 | #[derive(Debug, Serialize, Deserialize)] |
927 | #[serde(rename_all = "camelCase")] | 901 | #[serde(rename_all = "camelCase")] |
928 | enum CodeLensResolveData { | 902 | enum CodeLensResolveData { |
929 | Impls(req::GotoImplementationParams), | 903 | Impls(lsp_types::request::GotoImplementationParams), |
930 | } | 904 | } |
931 | 905 | ||
932 | pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Result<CodeLens> { | 906 | pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Result<CodeLens> { |
@@ -937,9 +911,9 @@ pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Re | |||
937 | Some(CodeLensResolveData::Impls(lens_params)) => { | 911 | Some(CodeLensResolveData::Impls(lens_params)) => { |
938 | let locations: Vec<Location> = | 912 | let locations: Vec<Location> = |
939 | match handle_goto_implementation(world, lens_params.clone())? { | 913 | match handle_goto_implementation(world, lens_params.clone())? { |
940 | Some(req::GotoDefinitionResponse::Scalar(loc)) => vec![loc], | 914 | Some(lsp_types::GotoDefinitionResponse::Scalar(loc)) => vec![loc], |
941 | Some(req::GotoDefinitionResponse::Array(locs)) => locs, | 915 | Some(lsp_types::GotoDefinitionResponse::Array(locs)) => locs, |
942 | Some(req::GotoDefinitionResponse::Link(links)) => links | 916 | Some(lsp_types::GotoDefinitionResponse::Link(links)) => links |
943 | .into_iter() | 917 | .into_iter() |
944 | .map(|link| Location::new(link.target_uri, link.target_selection_range)) | 918 | .map(|link| Location::new(link.target_uri, link.target_selection_range)) |
945 | .collect(), | 919 | .collect(), |
@@ -976,37 +950,39 @@ pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Re | |||
976 | 950 | ||
977 | pub fn handle_document_highlight( | 951 | pub fn handle_document_highlight( |
978 | world: WorldSnapshot, | 952 | world: WorldSnapshot, |
979 | params: req::DocumentHighlightParams, | 953 | params: lsp_types::DocumentHighlightParams, |
980 | ) -> Result<Option<Vec<DocumentHighlight>>> { | 954 | ) -> Result<Option<Vec<DocumentHighlight>>> { |
981 | let _p = profile("handle_document_highlight"); | 955 | let _p = profile("handle_document_highlight"); |
982 | let file_id = params.text_document_position_params.text_document.try_conv_with(&world)?; | 956 | let position = from_proto::file_position(&world, params.text_document_position_params)?; |
983 | let line_index = world.analysis().file_line_index(file_id)?; | 957 | let line_index = world.analysis().file_line_index(position.file_id)?; |
984 | 958 | ||
985 | let refs = match world.analysis().find_all_refs( | 959 | let refs = match world |
986 | params.text_document_position_params.try_conv_with(&world)?, | 960 | .analysis() |
987 | Some(SearchScope::single_file(file_id)), | 961 | .find_all_refs(position, Some(SearchScope::single_file(position.file_id)))? |
988 | )? { | 962 | { |
989 | None => return Ok(None), | 963 | None => return Ok(None), |
990 | Some(refs) => refs, | 964 | Some(refs) => refs, |
991 | }; | 965 | }; |
992 | 966 | ||
993 | Ok(Some( | 967 | let res = refs |
994 | refs.into_iter() | 968 | .into_iter() |
995 | .filter(|reference| reference.file_range.file_id == file_id) | 969 | .filter(|reference| reference.file_range.file_id == position.file_id) |
996 | .map(|reference| DocumentHighlight { | 970 | .map(|reference| DocumentHighlight { |
997 | range: reference.file_range.range.conv_with(&line_index), | 971 | range: to_proto::range(&line_index, reference.file_range.range), |
998 | kind: reference.access.map(|it| it.conv()), | 972 | kind: reference.access.map(to_proto::document_highlight_kind), |
999 | }) | 973 | }) |
1000 | .collect(), | 974 | .collect(); |
1001 | )) | 975 | Ok(Some(res)) |
1002 | } | 976 | } |
1003 | 977 | ||
1004 | pub fn handle_ssr(world: WorldSnapshot, params: req::SsrParams) -> Result<req::SourceChange> { | 978 | pub fn handle_ssr( |
979 | world: WorldSnapshot, | ||
980 | params: lsp_ext::SsrParams, | ||
981 | ) -> Result<lsp_ext::SourceChange> { | ||
1005 | let _p = profile("handle_ssr"); | 982 | let _p = profile("handle_ssr"); |
1006 | world | 983 | let source_change = |
1007 | .analysis() | 984 | world.analysis().structural_search_replace(¶ms.query, params.parse_only)??; |
1008 | .structural_search_replace(¶ms.query, params.parse_only)?? | 985 | to_proto::source_change(&world, source_change) |
1009 | .try_conv_with(&world) | ||
1010 | } | 986 | } |
1011 | 987 | ||
1012 | pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> { | 988 | pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> { |
@@ -1017,8 +993,8 @@ pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<Dia | |||
1017 | .diagnostics(file_id)? | 993 | .diagnostics(file_id)? |
1018 | .into_iter() | 994 | .into_iter() |
1019 | .map(|d| Diagnostic { | 995 | .map(|d| Diagnostic { |
1020 | range: d.range.conv_with(&line_index), | 996 | range: to_proto::range(&line_index, d.range), |
1021 | severity: Some(d.severity.conv()), | 997 | severity: Some(to_proto::diagnostic_severity(d.severity)), |
1022 | code: None, | 998 | code: None, |
1023 | source: Some("rust-analyzer".to_string()), | 999 | source: Some("rust-analyzer".to_string()), |
1024 | message: d.message, | 1000 | message: d.message, |
@@ -1033,7 +1009,7 @@ fn to_lsp_runnable( | |||
1033 | world: &WorldSnapshot, | 1009 | world: &WorldSnapshot, |
1034 | file_id: FileId, | 1010 | file_id: FileId, |
1035 | runnable: Runnable, | 1011 | runnable: Runnable, |
1036 | ) -> Result<req::Runnable> { | 1012 | ) -> Result<lsp_ext::Runnable> { |
1037 | let spec = CargoTargetSpec::for_file(world, file_id)?; | 1013 | let spec = CargoTargetSpec::for_file(world, file_id)?; |
1038 | let (args, extra_args) = CargoTargetSpec::runnable_args(spec, &runnable.kind)?; | 1014 | let (args, extra_args) = CargoTargetSpec::runnable_args(spec, &runnable.kind)?; |
1039 | let line_index = world.analysis().file_line_index(file_id)?; | 1015 | let line_index = world.analysis().file_line_index(file_id)?; |
@@ -1044,8 +1020,8 @@ fn to_lsp_runnable( | |||
1044 | RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id), | 1020 | RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id), |
1045 | RunnableKind::Bin => "run binary".to_string(), | 1021 | RunnableKind::Bin => "run binary".to_string(), |
1046 | }; | 1022 | }; |
1047 | Ok(req::Runnable { | 1023 | Ok(lsp_ext::Runnable { |
1048 | range: runnable.range.conv_with(&line_index), | 1024 | range: to_proto::range(&line_index, runnable.range), |
1049 | label, | 1025 | label, |
1050 | bin: "cargo".to_string(), | 1026 | bin: "cargo".to_string(), |
1051 | args, | 1027 | args, |
@@ -1064,13 +1040,13 @@ pub fn handle_inlay_hints( | |||
1064 | params: InlayHintsParams, | 1040 | params: InlayHintsParams, |
1065 | ) -> Result<Vec<InlayHint>> { | 1041 | ) -> Result<Vec<InlayHint>> { |
1066 | let _p = profile("handle_inlay_hints"); | 1042 | let _p = profile("handle_inlay_hints"); |
1067 | let file_id = params.text_document.try_conv_with(&world)?; | 1043 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
1068 | let analysis = world.analysis(); | 1044 | let analysis = world.analysis(); |
1069 | let line_index = analysis.file_line_index(file_id)?; | 1045 | let line_index = analysis.file_line_index(file_id)?; |
1070 | Ok(analysis | 1046 | Ok(analysis |
1071 | .inlay_hints(file_id, &world.config.inlay_hints)? | 1047 | .inlay_hints(file_id, &world.config.inlay_hints)? |
1072 | .into_iter() | 1048 | .into_iter() |
1073 | .map_conv_with(&line_index) | 1049 | .map(|it| to_proto::inlay_int(&line_index, it)) |
1074 | .collect()) | 1050 | .collect()) |
1075 | } | 1051 | } |
1076 | 1052 | ||
@@ -1079,21 +1055,19 @@ pub fn handle_call_hierarchy_prepare( | |||
1079 | params: CallHierarchyPrepareParams, | 1055 | params: CallHierarchyPrepareParams, |
1080 | ) -> Result<Option<Vec<CallHierarchyItem>>> { | 1056 | ) -> Result<Option<Vec<CallHierarchyItem>>> { |
1081 | let _p = profile("handle_call_hierarchy_prepare"); | 1057 | let _p = profile("handle_call_hierarchy_prepare"); |
1082 | let position = params.text_document_position_params.try_conv_with(&world)?; | 1058 | let position = from_proto::file_position(&world, params.text_document_position_params)?; |
1083 | let file_id = position.file_id; | ||
1084 | 1059 | ||
1085 | let nav_info = match world.analysis().call_hierarchy(position)? { | 1060 | let nav_info = match world.analysis().call_hierarchy(position)? { |
1086 | None => return Ok(None), | 1061 | None => return Ok(None), |
1087 | Some(it) => it, | 1062 | Some(it) => it, |
1088 | }; | 1063 | }; |
1089 | 1064 | ||
1090 | let line_index = world.analysis().file_line_index(file_id)?; | 1065 | let RangeInfo { range: _, info: navs } = nav_info; |
1091 | let RangeInfo { range, info: navs } = nav_info; | ||
1092 | let res = navs | 1066 | let res = navs |
1093 | .into_iter() | 1067 | .into_iter() |
1094 | .filter(|it| it.kind() == SyntaxKind::FN_DEF) | 1068 | .filter(|it| it.kind() == SyntaxKind::FN_DEF) |
1095 | .filter_map(|it| to_call_hierarchy_item(file_id, range, &world, &line_index, it).ok()) | 1069 | .map(|it| to_proto::call_hierarchy_item(&world, it)) |
1096 | .collect(); | 1070 | .collect::<Result<Vec<_>>>()?; |
1097 | 1071 | ||
1098 | Ok(Some(res)) | 1072 | Ok(Some(res)) |
1099 | } | 1073 | } |
@@ -1106,7 +1080,7 @@ pub fn handle_call_hierarchy_incoming( | |||
1106 | let item = params.item; | 1080 | let item = params.item; |
1107 | 1081 | ||
1108 | let doc = TextDocumentIdentifier::new(item.uri); | 1082 | let doc = TextDocumentIdentifier::new(item.uri); |
1109 | let frange: FileRange = (&doc, item.range).try_conv_with(&world)?; | 1083 | let frange = from_proto::file_range(&world, doc, item.range)?; |
1110 | let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; | 1084 | let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; |
1111 | 1085 | ||
1112 | let call_items = match world.analysis().incoming_calls(fpos)? { | 1086 | let call_items = match world.analysis().incoming_calls(fpos)? { |
@@ -1119,11 +1093,14 @@ pub fn handle_call_hierarchy_incoming( | |||
1119 | for call_item in call_items.into_iter() { | 1093 | for call_item in call_items.into_iter() { |
1120 | let file_id = call_item.target.file_id(); | 1094 | let file_id = call_item.target.file_id(); |
1121 | let line_index = world.analysis().file_line_index(file_id)?; | 1095 | let line_index = world.analysis().file_line_index(file_id)?; |
1122 | let range = call_item.target.range(); | 1096 | let item = to_proto::call_hierarchy_item(&world, call_item.target)?; |
1123 | let item = to_call_hierarchy_item(file_id, range, &world, &line_index, call_item.target)?; | ||
1124 | res.push(CallHierarchyIncomingCall { | 1097 | res.push(CallHierarchyIncomingCall { |
1125 | from: item, | 1098 | from: item, |
1126 | from_ranges: call_item.ranges.iter().map(|it| it.conv_with(&line_index)).collect(), | 1099 | from_ranges: call_item |
1100 | .ranges | ||
1101 | .into_iter() | ||
1102 | .map(|it| to_proto::range(&line_index, it)) | ||
1103 | .collect(), | ||
1127 | }); | 1104 | }); |
1128 | } | 1105 | } |
1129 | 1106 | ||
@@ -1138,7 +1115,7 @@ pub fn handle_call_hierarchy_outgoing( | |||
1138 | let item = params.item; | 1115 | let item = params.item; |
1139 | 1116 | ||
1140 | let doc = TextDocumentIdentifier::new(item.uri); | 1117 | let doc = TextDocumentIdentifier::new(item.uri); |
1141 | let frange: FileRange = (&doc, item.range).try_conv_with(&world)?; | 1118 | let frange = from_proto::file_range(&world, doc, item.range)?; |
1142 | let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; | 1119 | let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; |
1143 | 1120 | ||
1144 | let call_items = match world.analysis().outgoing_calls(fpos)? { | 1121 | let call_items = match world.analysis().outgoing_calls(fpos)? { |
@@ -1151,11 +1128,14 @@ pub fn handle_call_hierarchy_outgoing( | |||
1151 | for call_item in call_items.into_iter() { | 1128 | for call_item in call_items.into_iter() { |
1152 | let file_id = call_item.target.file_id(); | 1129 | let file_id = call_item.target.file_id(); |
1153 | let line_index = world.analysis().file_line_index(file_id)?; | 1130 | let line_index = world.analysis().file_line_index(file_id)?; |
1154 | let range = call_item.target.range(); | 1131 | let item = to_proto::call_hierarchy_item(&world, call_item.target)?; |
1155 | let item = to_call_hierarchy_item(file_id, range, &world, &line_index, call_item.target)?; | ||
1156 | res.push(CallHierarchyOutgoingCall { | 1132 | res.push(CallHierarchyOutgoingCall { |
1157 | to: item, | 1133 | to: item, |
1158 | from_ranges: call_item.ranges.iter().map(|it| it.conv_with(&line_index)).collect(), | 1134 | from_ranges: call_item |
1135 | .ranges | ||
1136 | .into_iter() | ||
1137 | .map(|it| to_proto::range(&line_index, it)) | ||
1138 | .collect(), | ||
1159 | }); | 1139 | }); |
1160 | } | 1140 | } |
1161 | 1141 | ||
@@ -1168,26 +1148,13 @@ pub fn handle_semantic_tokens( | |||
1168 | ) -> Result<Option<SemanticTokensResult>> { | 1148 | ) -> Result<Option<SemanticTokensResult>> { |
1169 | let _p = profile("handle_semantic_tokens"); | 1149 | let _p = profile("handle_semantic_tokens"); |
1170 | 1150 | ||
1171 | let file_id = params.text_document.try_conv_with(&world)?; | 1151 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
1172 | let text = world.analysis().file_text(file_id)?; | 1152 | let text = world.analysis().file_text(file_id)?; |
1173 | let line_index = world.analysis().file_line_index(file_id)?; | 1153 | let line_index = world.analysis().file_line_index(file_id)?; |
1174 | 1154 | ||
1175 | let mut builder = SemanticTokensBuilder::default(); | 1155 | let highlights = world.analysis().highlight(file_id)?; |
1176 | 1156 | let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); | |
1177 | for highlight_range in world.analysis().highlight(file_id)?.into_iter() { | 1157 | Ok(Some(semantic_tokens.into())) |
1178 | let (token_index, modifier_bitset) = highlight_range.highlight.conv(); | ||
1179 | for mut range in line_index.lines(highlight_range.range) { | ||
1180 | if text[range].ends_with('\n') { | ||
1181 | range = TextRange::new(range.start(), range.end() - TextSize::of('\n')); | ||
1182 | } | ||
1183 | let range = range.conv_with(&line_index); | ||
1184 | builder.push(range, token_index, modifier_bitset); | ||
1185 | } | ||
1186 | } | ||
1187 | |||
1188 | let tokens = builder.build(); | ||
1189 | |||
1190 | Ok(Some(tokens.into())) | ||
1191 | } | 1158 | } |
1192 | 1159 | ||
1193 | pub fn handle_semantic_tokens_range( | 1160 | pub fn handle_semantic_tokens_range( |
@@ -1196,17 +1163,11 @@ pub fn handle_semantic_tokens_range( | |||
1196 | ) -> Result<Option<SemanticTokensRangeResult>> { | 1163 | ) -> Result<Option<SemanticTokensRangeResult>> { |
1197 | let _p = profile("handle_semantic_tokens_range"); | 1164 | let _p = profile("handle_semantic_tokens_range"); |
1198 | 1165 | ||
1199 | let frange = (¶ms.text_document, params.range).try_conv_with(&world)?; | 1166 | let frange = from_proto::file_range(&world, params.text_document, params.range)?; |
1167 | let text = world.analysis().file_text(frange.file_id)?; | ||
1200 | let line_index = world.analysis().file_line_index(frange.file_id)?; | 1168 | let line_index = world.analysis().file_line_index(frange.file_id)?; |
1201 | 1169 | ||
1202 | let mut builder = SemanticTokensBuilder::default(); | 1170 | let highlights = world.analysis().highlight_range(frange)?; |
1203 | 1171 | let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); | |
1204 | for highlight_range in world.analysis().highlight_range(frange)?.into_iter() { | 1172 | Ok(Some(semantic_tokens.into())) |
1205 | let (token_type, token_modifiers) = highlight_range.highlight.conv(); | ||
1206 | builder.push(highlight_range.range.conv_with(&line_index), token_type, token_modifiers); | ||
1207 | } | ||
1208 | |||
1209 | let tokens = builder.build(); | ||
1210 | |||
1211 | Ok(Some(tokens.into())) | ||
1212 | } | 1173 | } |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs new file mode 100644 index 000000000..4500d4982 --- /dev/null +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -0,0 +1,592 @@ | |||
1 | //! Conversion of rust-analyzer specific types to lsp_types equivalents. | ||
2 | use ra_db::{FileId, FileRange}; | ||
3 | use ra_ide::{ | ||
4 | translate_offset_with_edit, Assist, CompletionItem, CompletionItemKind, Documentation, | ||
5 | FileSystemEdit, Fold, FoldKind, FunctionSignature, Highlight, HighlightModifier, HighlightTag, | ||
6 | HighlightedRange, InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, | ||
7 | ReferenceAccess, Severity, SourceChange, SourceFileEdit, | ||
8 | }; | ||
9 | use ra_syntax::{SyntaxKind, TextRange, TextSize}; | ||
10 | use ra_text_edit::{Indel, TextEdit}; | ||
11 | use ra_vfs::LineEndings; | ||
12 | |||
13 | use crate::{lsp_ext, semantic_tokens, world::WorldSnapshot, Result}; | ||
14 | |||
15 | pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { | ||
16 | let line_col = line_index.line_col(offset); | ||
17 | let line = u64::from(line_col.line); | ||
18 | let character = u64::from(line_col.col_utf16); | ||
19 | lsp_types::Position::new(line, character) | ||
20 | } | ||
21 | |||
22 | pub(crate) fn range(line_index: &LineIndex, range: TextRange) -> lsp_types::Range { | ||
23 | let start = position(line_index, range.start()); | ||
24 | let end = position(line_index, range.end()); | ||
25 | lsp_types::Range::new(start, end) | ||
26 | } | ||
27 | |||
28 | pub(crate) fn symbol_kind(syntax_kind: SyntaxKind) -> lsp_types::SymbolKind { | ||
29 | match syntax_kind { | ||
30 | SyntaxKind::FN_DEF => lsp_types::SymbolKind::Function, | ||
31 | SyntaxKind::STRUCT_DEF => lsp_types::SymbolKind::Struct, | ||
32 | SyntaxKind::ENUM_DEF => lsp_types::SymbolKind::Enum, | ||
33 | SyntaxKind::ENUM_VARIANT => lsp_types::SymbolKind::EnumMember, | ||
34 | SyntaxKind::TRAIT_DEF => lsp_types::SymbolKind::Interface, | ||
35 | SyntaxKind::MACRO_CALL => lsp_types::SymbolKind::Function, | ||
36 | SyntaxKind::MODULE => lsp_types::SymbolKind::Module, | ||
37 | SyntaxKind::TYPE_ALIAS_DEF => lsp_types::SymbolKind::TypeParameter, | ||
38 | SyntaxKind::RECORD_FIELD_DEF => lsp_types::SymbolKind::Field, | ||
39 | SyntaxKind::STATIC_DEF => lsp_types::SymbolKind::Constant, | ||
40 | SyntaxKind::CONST_DEF => lsp_types::SymbolKind::Constant, | ||
41 | SyntaxKind::IMPL_DEF => lsp_types::SymbolKind::Object, | ||
42 | _ => lsp_types::SymbolKind::Variable, | ||
43 | } | ||
44 | } | ||
45 | |||
46 | pub(crate) fn document_highlight_kind( | ||
47 | reference_access: ReferenceAccess, | ||
48 | ) -> lsp_types::DocumentHighlightKind { | ||
49 | match reference_access { | ||
50 | ReferenceAccess::Read => lsp_types::DocumentHighlightKind::Read, | ||
51 | ReferenceAccess::Write => lsp_types::DocumentHighlightKind::Write, | ||
52 | } | ||
53 | } | ||
54 | |||
55 | pub(crate) fn diagnostic_severity(severity: Severity) -> lsp_types::DiagnosticSeverity { | ||
56 | match severity { | ||
57 | Severity::Error => lsp_types::DiagnosticSeverity::Error, | ||
58 | Severity::WeakWarning => lsp_types::DiagnosticSeverity::Hint, | ||
59 | } | ||
60 | } | ||
61 | |||
62 | pub(crate) fn documentation(documentation: Documentation) -> lsp_types::Documentation { | ||
63 | let value = crate::markdown::format_docs(documentation.as_str()); | ||
64 | let markup_content = lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value }; | ||
65 | lsp_types::Documentation::MarkupContent(markup_content) | ||
66 | } | ||
67 | |||
68 | pub(crate) fn insert_text_format( | ||
69 | insert_text_format: InsertTextFormat, | ||
70 | ) -> lsp_types::InsertTextFormat { | ||
71 | match insert_text_format { | ||
72 | InsertTextFormat::Snippet => lsp_types::InsertTextFormat::Snippet, | ||
73 | InsertTextFormat::PlainText => lsp_types::InsertTextFormat::PlainText, | ||
74 | } | ||
75 | } | ||
76 | |||
77 | pub(crate) fn completion_item_kind( | ||
78 | completion_item_kind: CompletionItemKind, | ||
79 | ) -> lsp_types::CompletionItemKind { | ||
80 | match completion_item_kind { | ||
81 | CompletionItemKind::Keyword => lsp_types::CompletionItemKind::Keyword, | ||
82 | CompletionItemKind::Snippet => lsp_types::CompletionItemKind::Snippet, | ||
83 | CompletionItemKind::Module => lsp_types::CompletionItemKind::Module, | ||
84 | CompletionItemKind::Function => lsp_types::CompletionItemKind::Function, | ||
85 | CompletionItemKind::Struct => lsp_types::CompletionItemKind::Struct, | ||
86 | CompletionItemKind::Enum => lsp_types::CompletionItemKind::Enum, | ||
87 | CompletionItemKind::EnumVariant => lsp_types::CompletionItemKind::EnumMember, | ||
88 | CompletionItemKind::BuiltinType => lsp_types::CompletionItemKind::Struct, | ||
89 | CompletionItemKind::Binding => lsp_types::CompletionItemKind::Variable, | ||
90 | CompletionItemKind::Field => lsp_types::CompletionItemKind::Field, | ||
91 | CompletionItemKind::Trait => lsp_types::CompletionItemKind::Interface, | ||
92 | CompletionItemKind::TypeAlias => lsp_types::CompletionItemKind::Struct, | ||
93 | CompletionItemKind::Const => lsp_types::CompletionItemKind::Constant, | ||
94 | CompletionItemKind::Static => lsp_types::CompletionItemKind::Value, | ||
95 | CompletionItemKind::Method => lsp_types::CompletionItemKind::Method, | ||
96 | CompletionItemKind::TypeParam => lsp_types::CompletionItemKind::TypeParameter, | ||
97 | CompletionItemKind::Macro => lsp_types::CompletionItemKind::Method, | ||
98 | CompletionItemKind::Attribute => lsp_types::CompletionItemKind::EnumMember, | ||
99 | } | ||
100 | } | ||
101 | |||
102 | pub(crate) fn text_edit( | ||
103 | line_index: &LineIndex, | ||
104 | line_endings: LineEndings, | ||
105 | indel: Indel, | ||
106 | ) -> lsp_types::TextEdit { | ||
107 | let range = range(line_index, indel.delete); | ||
108 | let new_text = match line_endings { | ||
109 | LineEndings::Unix => indel.insert, | ||
110 | LineEndings::Dos => indel.insert.replace('\n', "\r\n"), | ||
111 | }; | ||
112 | lsp_types::TextEdit { range, new_text } | ||
113 | } | ||
114 | |||
115 | pub(crate) fn text_edit_vec( | ||
116 | line_index: &LineIndex, | ||
117 | line_endings: LineEndings, | ||
118 | text_edit: TextEdit, | ||
119 | ) -> Vec<lsp_types::TextEdit> { | ||
120 | text_edit | ||
121 | .as_indels() | ||
122 | .iter() | ||
123 | .map(|it| self::text_edit(line_index, line_endings, it.clone())) | ||
124 | .collect() | ||
125 | } | ||
126 | |||
127 | pub(crate) fn completion_item( | ||
128 | line_index: &LineIndex, | ||
129 | line_endings: LineEndings, | ||
130 | completion_item: CompletionItem, | ||
131 | ) -> lsp_types::CompletionItem { | ||
132 | let mut additional_text_edits = Vec::new(); | ||
133 | let mut text_edit = None; | ||
134 | // LSP does not allow arbitrary edits in completion, so we have to do a | ||
135 | // non-trivial mapping here. | ||
136 | let source_range = completion_item.source_range(); | ||
137 | for indel in completion_item.text_edit().as_indels() { | ||
138 | if indel.delete.contains_range(source_range) { | ||
139 | text_edit = Some(if indel.delete == source_range { | ||
140 | self::text_edit(line_index, line_endings, indel.clone()) | ||
141 | } else { | ||
142 | assert!(source_range.end() == indel.delete.end()); | ||
143 | let range1 = TextRange::new(indel.delete.start(), source_range.start()); | ||
144 | let range2 = source_range; | ||
145 | let indel1 = Indel::replace(range1, String::new()); | ||
146 | let indel2 = Indel::replace(range2, indel.insert.clone()); | ||
147 | additional_text_edits.push(self::text_edit(line_index, line_endings, indel1)); | ||
148 | self::text_edit(line_index, line_endings, indel2) | ||
149 | }) | ||
150 | } else { | ||
151 | assert!(source_range.intersect(indel.delete).is_none()); | ||
152 | let text_edit = self::text_edit(line_index, line_endings, indel.clone()); | ||
153 | additional_text_edits.push(text_edit); | ||
154 | } | ||
155 | } | ||
156 | let text_edit = text_edit.unwrap(); | ||
157 | |||
158 | let mut res = lsp_types::CompletionItem { | ||
159 | label: completion_item.label().to_string(), | ||
160 | detail: completion_item.detail().map(|it| it.to_string()), | ||
161 | filter_text: Some(completion_item.lookup().to_string()), | ||
162 | kind: completion_item.kind().map(completion_item_kind), | ||
163 | text_edit: Some(text_edit.into()), | ||
164 | additional_text_edits: Some(additional_text_edits), | ||
165 | documentation: completion_item.documentation().map(documentation), | ||
166 | deprecated: Some(completion_item.deprecated()), | ||
167 | command: if completion_item.trigger_call_info() { | ||
168 | let cmd = lsp_types::Command { | ||
169 | title: "triggerParameterHints".into(), | ||
170 | command: "editor.action.triggerParameterHints".into(), | ||
171 | arguments: None, | ||
172 | }; | ||
173 | Some(cmd) | ||
174 | } else { | ||
175 | None | ||
176 | }, | ||
177 | ..Default::default() | ||
178 | }; | ||
179 | |||
180 | if completion_item.score().is_some() { | ||
181 | res.preselect = Some(true) | ||
182 | } | ||
183 | |||
184 | if completion_item.deprecated() { | ||
185 | res.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated]) | ||
186 | } | ||
187 | |||
188 | res.insert_text_format = Some(insert_text_format(completion_item.insert_text_format())); | ||
189 | |||
190 | res | ||
191 | } | ||
192 | |||
193 | pub(crate) fn signature_information( | ||
194 | signature: FunctionSignature, | ||
195 | concise: bool, | ||
196 | ) -> lsp_types::SignatureInformation { | ||
197 | let (label, documentation, params) = if concise { | ||
198 | let mut params = signature.parameters; | ||
199 | if signature.has_self_param { | ||
200 | params.remove(0); | ||
201 | } | ||
202 | (params.join(", "), None, params) | ||
203 | } else { | ||
204 | (signature.to_string(), signature.doc.map(documentation), signature.parameters) | ||
205 | }; | ||
206 | |||
207 | let parameters: Vec<lsp_types::ParameterInformation> = params | ||
208 | .into_iter() | ||
209 | .map(|param| lsp_types::ParameterInformation { | ||
210 | label: lsp_types::ParameterLabel::Simple(param), | ||
211 | documentation: None, | ||
212 | }) | ||
213 | .collect(); | ||
214 | |||
215 | lsp_types::SignatureInformation { label, documentation, parameters: Some(parameters) } | ||
216 | } | ||
217 | |||
218 | pub(crate) fn inlay_int(line_index: &LineIndex, inlay_hint: InlayHint) -> lsp_ext::InlayHint { | ||
219 | lsp_ext::InlayHint { | ||
220 | label: inlay_hint.label.to_string(), | ||
221 | range: range(line_index, inlay_hint.range), | ||
222 | kind: match inlay_hint.kind { | ||
223 | InlayKind::ParameterHint => lsp_ext::InlayKind::ParameterHint, | ||
224 | InlayKind::TypeHint => lsp_ext::InlayKind::TypeHint, | ||
225 | InlayKind::ChainingHint => lsp_ext::InlayKind::ChainingHint, | ||
226 | }, | ||
227 | } | ||
228 | } | ||
229 | |||
230 | pub(crate) fn semantic_tokens( | ||
231 | text: &str, | ||
232 | line_index: &LineIndex, | ||
233 | highlights: Vec<HighlightedRange>, | ||
234 | ) -> lsp_types::SemanticTokens { | ||
235 | let mut builder = semantic_tokens::SemanticTokensBuilder::default(); | ||
236 | |||
237 | for highlight_range in highlights { | ||
238 | let (type_, mods) = semantic_token_type_and_modifiers(highlight_range.highlight); | ||
239 | let token_index = semantic_tokens::type_index(type_); | ||
240 | let modifier_bitset = mods.0; | ||
241 | |||
242 | for mut text_range in line_index.lines(highlight_range.range) { | ||
243 | if text[text_range].ends_with('\n') { | ||
244 | text_range = | ||
245 | TextRange::new(text_range.start(), text_range.end() - TextSize::of('\n')); | ||
246 | } | ||
247 | let range = range(&line_index, text_range); | ||
248 | builder.push(range, token_index, modifier_bitset); | ||
249 | } | ||
250 | } | ||
251 | |||
252 | builder.build() | ||
253 | } | ||
254 | |||
255 | fn semantic_token_type_and_modifiers( | ||
256 | highlight: Highlight, | ||
257 | ) -> (lsp_types::SemanticTokenType, semantic_tokens::ModifierSet) { | ||
258 | let mut mods = semantic_tokens::ModifierSet::default(); | ||
259 | let type_ = match highlight.tag { | ||
260 | HighlightTag::Struct => lsp_types::SemanticTokenType::STRUCT, | ||
261 | HighlightTag::Enum => lsp_types::SemanticTokenType::ENUM, | ||
262 | HighlightTag::Union => semantic_tokens::UNION, | ||
263 | HighlightTag::TypeAlias => semantic_tokens::TYPE_ALIAS, | ||
264 | HighlightTag::Trait => lsp_types::SemanticTokenType::INTERFACE, | ||
265 | HighlightTag::BuiltinType => semantic_tokens::BUILTIN_TYPE, | ||
266 | HighlightTag::SelfType => lsp_types::SemanticTokenType::TYPE, | ||
267 | HighlightTag::Field => lsp_types::SemanticTokenType::MEMBER, | ||
268 | HighlightTag::Function => lsp_types::SemanticTokenType::FUNCTION, | ||
269 | HighlightTag::Module => lsp_types::SemanticTokenType::NAMESPACE, | ||
270 | HighlightTag::Constant => { | ||
271 | mods |= semantic_tokens::CONSTANT; | ||
272 | mods |= lsp_types::SemanticTokenModifier::STATIC; | ||
273 | lsp_types::SemanticTokenType::VARIABLE | ||
274 | } | ||
275 | HighlightTag::Static => { | ||
276 | mods |= lsp_types::SemanticTokenModifier::STATIC; | ||
277 | lsp_types::SemanticTokenType::VARIABLE | ||
278 | } | ||
279 | HighlightTag::EnumVariant => semantic_tokens::ENUM_MEMBER, | ||
280 | HighlightTag::Macro => lsp_types::SemanticTokenType::MACRO, | ||
281 | HighlightTag::Local => lsp_types::SemanticTokenType::VARIABLE, | ||
282 | HighlightTag::TypeParam => lsp_types::SemanticTokenType::TYPE_PARAMETER, | ||
283 | HighlightTag::Lifetime => semantic_tokens::LIFETIME, | ||
284 | HighlightTag::ByteLiteral | HighlightTag::NumericLiteral => { | ||
285 | lsp_types::SemanticTokenType::NUMBER | ||
286 | } | ||
287 | HighlightTag::CharLiteral | HighlightTag::StringLiteral => { | ||
288 | lsp_types::SemanticTokenType::STRING | ||
289 | } | ||
290 | HighlightTag::Comment => lsp_types::SemanticTokenType::COMMENT, | ||
291 | HighlightTag::Attribute => semantic_tokens::ATTRIBUTE, | ||
292 | HighlightTag::Keyword => lsp_types::SemanticTokenType::KEYWORD, | ||
293 | HighlightTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE, | ||
294 | HighlightTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER, | ||
295 | }; | ||
296 | |||
297 | for modifier in highlight.modifiers.iter() { | ||
298 | let modifier = match modifier { | ||
299 | HighlightModifier::Definition => lsp_types::SemanticTokenModifier::DECLARATION, | ||
300 | HighlightModifier::ControlFlow => semantic_tokens::CONTROL_FLOW, | ||
301 | HighlightModifier::Mutable => semantic_tokens::MUTABLE, | ||
302 | HighlightModifier::Unsafe => semantic_tokens::UNSAFE, | ||
303 | }; | ||
304 | mods |= modifier; | ||
305 | } | ||
306 | |||
307 | (type_, mods) | ||
308 | } | ||
309 | |||
310 | pub(crate) fn folding_range( | ||
311 | text: &str, | ||
312 | line_index: &LineIndex, | ||
313 | line_folding_only: bool, | ||
314 | fold: Fold, | ||
315 | ) -> lsp_types::FoldingRange { | ||
316 | let kind = match fold.kind { | ||
317 | FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment), | ||
318 | FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports), | ||
319 | FoldKind::Mods | FoldKind::Block => None, | ||
320 | }; | ||
321 | |||
322 | let range = range(line_index, fold.range); | ||
323 | |||
324 | if line_folding_only { | ||
325 | // Clients with line_folding_only == true (such as VSCode) will fold the whole end line | ||
326 | // even if it contains text not in the folding range. To prevent that we exclude | ||
327 | // range.end.line from the folding region if there is more text after range.end | ||
328 | // on the same line. | ||
329 | let has_more_text_on_end_line = text[TextRange::new(fold.range.end(), TextSize::of(text))] | ||
330 | .chars() | ||
331 | .take_while(|it| *it != '\n') | ||
332 | .any(|it| !it.is_whitespace()); | ||
333 | |||
334 | let end_line = if has_more_text_on_end_line { | ||
335 | range.end.line.saturating_sub(1) | ||
336 | } else { | ||
337 | range.end.line | ||
338 | }; | ||
339 | |||
340 | lsp_types::FoldingRange { | ||
341 | start_line: range.start.line, | ||
342 | start_character: None, | ||
343 | end_line, | ||
344 | end_character: None, | ||
345 | kind, | ||
346 | } | ||
347 | } else { | ||
348 | lsp_types::FoldingRange { | ||
349 | start_line: range.start.line, | ||
350 | start_character: Some(range.start.character), | ||
351 | end_line: range.end.line, | ||
352 | end_character: Some(range.end.character), | ||
353 | kind, | ||
354 | } | ||
355 | } | ||
356 | } | ||
357 | |||
358 | pub(crate) fn url(world: &WorldSnapshot, file_id: FileId) -> Result<lsp_types::Url> { | ||
359 | world.file_id_to_uri(file_id) | ||
360 | } | ||
361 | |||
362 | pub(crate) fn text_document_identifier( | ||
363 | world: &WorldSnapshot, | ||
364 | file_id: FileId, | ||
365 | ) -> Result<lsp_types::TextDocumentIdentifier> { | ||
366 | let res = lsp_types::TextDocumentIdentifier { uri: url(world, file_id)? }; | ||
367 | Ok(res) | ||
368 | } | ||
369 | |||
370 | pub(crate) fn versioned_text_document_identifier( | ||
371 | world: &WorldSnapshot, | ||
372 | file_id: FileId, | ||
373 | version: Option<i64>, | ||
374 | ) -> Result<lsp_types::VersionedTextDocumentIdentifier> { | ||
375 | let res = lsp_types::VersionedTextDocumentIdentifier { uri: url(world, file_id)?, version }; | ||
376 | Ok(res) | ||
377 | } | ||
378 | |||
379 | pub(crate) fn location(world: &WorldSnapshot, frange: FileRange) -> Result<lsp_types::Location> { | ||
380 | let url = url(world, frange.file_id)?; | ||
381 | let line_index = world.analysis().file_line_index(frange.file_id)?; | ||
382 | let range = range(&line_index, frange.range); | ||
383 | let loc = lsp_types::Location::new(url, range); | ||
384 | Ok(loc) | ||
385 | } | ||
386 | |||
387 | pub(crate) fn location_link( | ||
388 | world: &WorldSnapshot, | ||
389 | src: FileRange, | ||
390 | target: NavigationTarget, | ||
391 | ) -> Result<lsp_types::LocationLink> { | ||
392 | let src_location = location(world, src)?; | ||
393 | let (target_uri, target_range, target_selection_range) = location_info(world, target)?; | ||
394 | let res = lsp_types::LocationLink { | ||
395 | origin_selection_range: Some(src_location.range), | ||
396 | target_uri, | ||
397 | target_range, | ||
398 | target_selection_range, | ||
399 | }; | ||
400 | Ok(res) | ||
401 | } | ||
402 | |||
403 | fn location_info( | ||
404 | world: &WorldSnapshot, | ||
405 | target: NavigationTarget, | ||
406 | ) -> Result<(lsp_types::Url, lsp_types::Range, lsp_types::Range)> { | ||
407 | let line_index = world.analysis().file_line_index(target.file_id())?; | ||
408 | |||
409 | let target_uri = url(world, target.file_id())?; | ||
410 | let target_range = range(&line_index, target.full_range()); | ||
411 | let target_selection_range = | ||
412 | target.focus_range().map(|it| range(&line_index, it)).unwrap_or(target_range); | ||
413 | Ok((target_uri, target_range, target_selection_range)) | ||
414 | } | ||
415 | |||
416 | pub(crate) fn goto_definition_response( | ||
417 | world: &WorldSnapshot, | ||
418 | src: FileRange, | ||
419 | targets: Vec<NavigationTarget>, | ||
420 | ) -> Result<lsp_types::GotoDefinitionResponse> { | ||
421 | if world.config.client_caps.location_link { | ||
422 | let links = targets | ||
423 | .into_iter() | ||
424 | .map(|nav| location_link(world, src, nav)) | ||
425 | .collect::<Result<Vec<_>>>()?; | ||
426 | Ok(links.into()) | ||
427 | } else { | ||
428 | let locations = targets | ||
429 | .into_iter() | ||
430 | .map(|nav| { | ||
431 | location( | ||
432 | world, | ||
433 | FileRange { | ||
434 | file_id: nav.file_id(), | ||
435 | range: nav.focus_range().unwrap_or(nav.range()), | ||
436 | }, | ||
437 | ) | ||
438 | }) | ||
439 | .collect::<Result<Vec<_>>>()?; | ||
440 | Ok(locations.into()) | ||
441 | } | ||
442 | } | ||
443 | |||
444 | pub(crate) fn text_document_edit( | ||
445 | world: &WorldSnapshot, | ||
446 | source_file_edit: SourceFileEdit, | ||
447 | ) -> Result<lsp_types::TextDocumentEdit> { | ||
448 | let text_document = versioned_text_document_identifier(world, source_file_edit.file_id, None)?; | ||
449 | let line_index = world.analysis().file_line_index(source_file_edit.file_id)?; | ||
450 | let line_endings = world.file_line_endings(source_file_edit.file_id); | ||
451 | let edits = source_file_edit | ||
452 | .edit | ||
453 | .as_indels() | ||
454 | .iter() | ||
455 | .map(|it| text_edit(&line_index, line_endings, it.clone())) | ||
456 | .collect(); | ||
457 | Ok(lsp_types::TextDocumentEdit { text_document, edits }) | ||
458 | } | ||
459 | |||
460 | pub(crate) fn resource_op( | ||
461 | world: &WorldSnapshot, | ||
462 | file_system_edit: FileSystemEdit, | ||
463 | ) -> Result<lsp_types::ResourceOp> { | ||
464 | let res = match file_system_edit { | ||
465 | FileSystemEdit::CreateFile { source_root, path } => { | ||
466 | let uri = world.path_to_uri(source_root, &path)?; | ||
467 | lsp_types::ResourceOp::Create(lsp_types::CreateFile { uri, options: None }) | ||
468 | } | ||
469 | FileSystemEdit::MoveFile { src, dst_source_root, dst_path } => { | ||
470 | let old_uri = world.file_id_to_uri(src)?; | ||
471 | let new_uri = world.path_to_uri(dst_source_root, &dst_path)?; | ||
472 | lsp_types::ResourceOp::Rename(lsp_types::RenameFile { old_uri, new_uri, options: None }) | ||
473 | } | ||
474 | }; | ||
475 | Ok(res) | ||
476 | } | ||
477 | |||
478 | pub(crate) fn source_change( | ||
479 | world: &WorldSnapshot, | ||
480 | source_change: SourceChange, | ||
481 | ) -> Result<lsp_ext::SourceChange> { | ||
482 | let cursor_position = match source_change.cursor_position { | ||
483 | None => None, | ||
484 | Some(pos) => { | ||
485 | let line_index = world.analysis().file_line_index(pos.file_id)?; | ||
486 | let edit = source_change | ||
487 | .source_file_edits | ||
488 | .iter() | ||
489 | .find(|it| it.file_id == pos.file_id) | ||
490 | .map(|it| &it.edit); | ||
491 | let line_col = match edit { | ||
492 | Some(edit) => translate_offset_with_edit(&*line_index, pos.offset, edit), | ||
493 | None => line_index.line_col(pos.offset), | ||
494 | }; | ||
495 | let position = | ||
496 | lsp_types::Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16)); | ||
497 | Some(lsp_types::TextDocumentPositionParams { | ||
498 | text_document: text_document_identifier(world, pos.file_id)?, | ||
499 | position, | ||
500 | }) | ||
501 | } | ||
502 | }; | ||
503 | let mut document_changes: Vec<lsp_types::DocumentChangeOperation> = Vec::new(); | ||
504 | for op in source_change.file_system_edits { | ||
505 | let op = resource_op(&world, op)?; | ||
506 | document_changes.push(lsp_types::DocumentChangeOperation::Op(op)); | ||
507 | } | ||
508 | for edit in source_change.source_file_edits { | ||
509 | let edit = text_document_edit(&world, edit)?; | ||
510 | document_changes.push(lsp_types::DocumentChangeOperation::Edit(edit)); | ||
511 | } | ||
512 | let workspace_edit = lsp_types::WorkspaceEdit { | ||
513 | changes: None, | ||
514 | document_changes: Some(lsp_types::DocumentChanges::Operations(document_changes)), | ||
515 | }; | ||
516 | Ok(lsp_ext::SourceChange { label: source_change.label, workspace_edit, cursor_position }) | ||
517 | } | ||
518 | |||
519 | pub fn call_hierarchy_item( | ||
520 | world: &WorldSnapshot, | ||
521 | target: NavigationTarget, | ||
522 | ) -> Result<lsp_types::CallHierarchyItem> { | ||
523 | let name = target.name().to_string(); | ||
524 | let detail = target.description().map(|it| it.to_string()); | ||
525 | let kind = symbol_kind(target.kind()); | ||
526 | let (uri, range, selection_range) = location_info(world, target)?; | ||
527 | Ok(lsp_types::CallHierarchyItem { name, kind, tags: None, detail, uri, range, selection_range }) | ||
528 | } | ||
529 | |||
530 | #[cfg(test)] | ||
531 | mod tests { | ||
532 | use test_utils::extract_ranges; | ||
533 | |||
534 | use super::*; | ||
535 | |||
536 | #[test] | ||
537 | fn conv_fold_line_folding_only_fixup() { | ||
538 | let text = r#"<fold>mod a; | ||
539 | mod b; | ||
540 | mod c;</fold> | ||
541 | |||
542 | fn main() <fold>{ | ||
543 | if cond <fold>{ | ||
544 | a::do_a(); | ||
545 | }</fold> else <fold>{ | ||
546 | b::do_b(); | ||
547 | }</fold> | ||
548 | }</fold>"#; | ||
549 | |||
550 | let (ranges, text) = extract_ranges(text, "fold"); | ||
551 | assert_eq!(ranges.len(), 4); | ||
552 | let folds = vec![ | ||
553 | Fold { range: ranges[0], kind: FoldKind::Mods }, | ||
554 | Fold { range: ranges[1], kind: FoldKind::Block }, | ||
555 | Fold { range: ranges[2], kind: FoldKind::Block }, | ||
556 | Fold { range: ranges[3], kind: FoldKind::Block }, | ||
557 | ]; | ||
558 | |||
559 | let line_index = LineIndex::new(&text); | ||
560 | let converted: Vec<lsp_types::FoldingRange> = | ||
561 | folds.into_iter().map(|it| folding_range(&text, &line_index, true, it)).collect(); | ||
562 | |||
563 | let expected_lines = [(0, 2), (4, 10), (5, 6), (7, 9)]; | ||
564 | assert_eq!(converted.len(), expected_lines.len()); | ||
565 | for (folding_range, (start_line, end_line)) in converted.iter().zip(expected_lines.iter()) { | ||
566 | assert_eq!(folding_range.start_line, *start_line); | ||
567 | assert_eq!(folding_range.start_character, None); | ||
568 | assert_eq!(folding_range.end_line, *end_line); | ||
569 | assert_eq!(folding_range.end_character, None); | ||
570 | } | ||
571 | } | ||
572 | } | ||
573 | |||
574 | pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_types::CodeAction> { | ||
575 | let source_change = source_change(&world, assist.source_change)?; | ||
576 | let arg = serde_json::to_value(source_change)?; | ||
577 | let title = assist.label; | ||
578 | let command = lsp_types::Command { | ||
579 | title: title.clone(), | ||
580 | command: "rust-analyzer.applySourceChange".to_string(), | ||
581 | arguments: Some(vec![arg]), | ||
582 | }; | ||
583 | |||
584 | Ok(lsp_types::CodeAction { | ||
585 | title, | ||
586 | kind: Some(String::new()), | ||
587 | diagnostics: None, | ||
588 | edit: None, | ||
589 | command: Some(command), | ||
590 | is_preferred: None, | ||
591 | }) | ||
592 | } | ||
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index e459e3a3c..5011cc273 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs | |||
@@ -3,15 +3,16 @@ mod support; | |||
3 | use std::{collections::HashMap, path::PathBuf, time::Instant}; | 3 | use std::{collections::HashMap, path::PathBuf, time::Instant}; |
4 | 4 | ||
5 | use lsp_types::{ | 5 | use lsp_types::{ |
6 | CodeActionContext, DidOpenTextDocumentParams, DocumentFormattingParams, FormattingOptions, | 6 | notification::DidOpenTextDocument, |
7 | GotoDefinitionParams, HoverParams, PartialResultParams, Position, Range, TextDocumentItem, | 7 | request::{ |
8 | TextDocumentPositionParams, WorkDoneProgressParams, | 8 | CodeActionRequest, Completion, Formatting, GotoDefinition, GotoTypeDefinition, HoverRequest, |
9 | }; | 9 | }, |
10 | use rust_analyzer::req::{ | 10 | CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams, |
11 | CodeActionParams, CodeActionRequest, Completion, CompletionParams, DidOpenTextDocument, | 11 | DocumentFormattingParams, FormattingOptions, GotoDefinitionParams, HoverParams, |
12 | Formatting, GotoDefinition, GotoTypeDefinition, HoverRequest, OnEnter, Runnables, | 12 | PartialResultParams, Position, Range, TextDocumentItem, TextDocumentPositionParams, |
13 | RunnablesParams, | 13 | WorkDoneProgressParams, |
14 | }; | 14 | }; |
15 | use rust_analyzer::lsp_ext::{OnEnter, Runnables, RunnablesParams}; | ||
15 | use serde_json::json; | 16 | use serde_json::json; |
16 | use tempfile::TempDir; | 17 | use tempfile::TempDir; |
17 | use test_utils::skip_slow_tests; | 18 | use test_utils::skip_slow_tests; |
diff --git a/crates/rust-analyzer/tests/heavy_tests/support.rs b/crates/rust-analyzer/tests/heavy_tests/support.rs index 8d47ee4f6..8756ad4a3 100644 --- a/crates/rust-analyzer/tests/heavy_tests/support.rs +++ b/crates/rust-analyzer/tests/heavy_tests/support.rs | |||
@@ -13,15 +13,15 @@ use lsp_types::{ | |||
13 | request::Shutdown, | 13 | request::Shutdown, |
14 | DidOpenTextDocumentParams, TextDocumentIdentifier, TextDocumentItem, Url, WorkDoneProgress, | 14 | DidOpenTextDocumentParams, TextDocumentIdentifier, TextDocumentItem, Url, WorkDoneProgress, |
15 | }; | 15 | }; |
16 | use lsp_types::{ProgressParams, ProgressParamsValue}; | ||
16 | use serde::Serialize; | 17 | use serde::Serialize; |
17 | use serde_json::{to_string_pretty, Value}; | 18 | use serde_json::{to_string_pretty, Value}; |
18 | use tempfile::TempDir; | 19 | use tempfile::TempDir; |
19 | use test_utils::{find_mismatch, parse_fixture}; | 20 | use test_utils::{find_mismatch, parse_fixture}; |
20 | 21 | ||
21 | use req::{ProgressParams, ProgressParamsValue}; | ||
22 | use rust_analyzer::{ | 22 | use rust_analyzer::{ |
23 | config::{ClientCapsConfig, Config}, | 23 | config::{ClientCapsConfig, Config}, |
24 | main_loop, req, | 24 | main_loop, |
25 | }; | 25 | }; |
26 | 26 | ||
27 | pub struct Project<'a> { | 27 | pub struct Project<'a> { |
@@ -206,7 +206,7 @@ impl Server { | |||
206 | Message::Notification(n) if n.method == "$/progress" => { | 206 | Message::Notification(n) if n.method == "$/progress" => { |
207 | match n.clone().extract::<ProgressParams>("$/progress").unwrap() { | 207 | match n.clone().extract::<ProgressParams>("$/progress").unwrap() { |
208 | ProgressParams { | 208 | ProgressParams { |
209 | token: req::ProgressToken::String(ref token), | 209 | token: lsp_types::ProgressToken::String(ref token), |
210 | value: ProgressParamsValue::WorkDone(WorkDoneProgress::End(_)), | 210 | value: ProgressParamsValue::WorkDone(WorkDoneProgress::End(_)), |
211 | } if token == "rustAnalyzer/startup" => true, | 211 | } if token == "rustAnalyzer/startup" => true, |
212 | _ => false, | 212 | _ => false, |
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index b13e13af2..b1e3c328f 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs | |||
@@ -270,7 +270,7 @@ fn parse_fixture_checks_further_indented_metadata() { | |||
270 | } | 270 | } |
271 | 271 | ||
272 | #[test] | 272 | #[test] |
273 | fn parse_fixture_can_handle_unindented_first_line() { | 273 | fn parse_fixture_can_handle_dedented_first_line() { |
274 | let fixture = "//- /lib.rs | 274 | let fixture = "//- /lib.rs |
275 | mod foo; | 275 | mod foo; |
276 | //- /foo.rs | 276 | //- /foo.rs |
diff --git a/editors/code/src/commands/syntax_tree.ts b/editors/code/src/commands/syntax_tree.ts index cfcf47b2f..b80a18a47 100644 --- a/editors/code/src/commands/syntax_tree.ts +++ b/editors/code/src/commands/syntax_tree.ts | |||
@@ -225,7 +225,7 @@ class AstInspector implements vscode.HoverProvider, vscode.DefinitionProvider, D | |||
225 | return doc.positionAt(targetOffset); | 225 | return doc.positionAt(targetOffset); |
226 | } | 226 | } |
227 | 227 | ||
228 | // Shitty workaround for crlf line endings | 228 | // Dirty workaround for crlf line endings |
229 | // We are still in this prehistoric era of carriage returns here... | 229 | // We are still in this prehistoric era of carriage returns here... |
230 | 230 | ||
231 | let line = 0; | 231 | let line = 0; |