diff options
Diffstat (limited to 'crates')
30 files changed, 642 insertions, 112 deletions
diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs index 04e2be390..0132565e4 100644 --- a/crates/base_db/src/fixture.rs +++ b/crates/base_db/src/fixture.rs | |||
@@ -35,7 +35,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { | |||
35 | fn with_position(ra_fixture: &str) -> (Self, FilePosition) { | 35 | fn with_position(ra_fixture: &str) -> (Self, FilePosition) { |
36 | let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); | 36 | let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); |
37 | let offset = match range_or_offset { | 37 | let offset = match range_or_offset { |
38 | RangeOrOffset::Range(_) => panic!(), | 38 | RangeOrOffset::Range(_) => panic!("Expected a cursor position, got a range instead"), |
39 | RangeOrOffset::Offset(it) => it, | 39 | RangeOrOffset::Offset(it) => it, |
40 | }; | 40 | }; |
41 | (db, FilePosition { file_id, offset }) | 41 | (db, FilePosition { file_id, offset }) |
@@ -45,7 +45,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { | |||
45 | let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); | 45 | let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); |
46 | let range = match range_or_offset { | 46 | let range = match range_or_offset { |
47 | RangeOrOffset::Range(it) => it, | 47 | RangeOrOffset::Range(it) => it, |
48 | RangeOrOffset::Offset(_) => panic!(), | 48 | RangeOrOffset::Offset(_) => panic!("Expected a cursor range, got a position instead"), |
49 | }; | 49 | }; |
50 | (db, FileRange { file_id, range }) | 50 | (db, FileRange { file_id, range }) |
51 | } | 51 | } |
@@ -54,7 +54,9 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { | |||
54 | let fixture = ChangeFixture::parse(ra_fixture); | 54 | let fixture = ChangeFixture::parse(ra_fixture); |
55 | let mut db = Self::default(); | 55 | let mut db = Self::default(); |
56 | fixture.change.apply(&mut db); | 56 | fixture.change.apply(&mut db); |
57 | let (file_id, range_or_offset) = fixture.file_position.unwrap(); | 57 | let (file_id, range_or_offset) = fixture |
58 | .file_position | ||
59 | .expect("Could not find file position in fixture. Did you forget to add an `$0`?"); | ||
58 | (db, file_id, range_or_offset) | 60 | (db, file_id, range_or_offset) |
59 | } | 61 | } |
60 | 62 | ||
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs index 059724daa..25694f037 100644 --- a/crates/hir_def/src/lib.rs +++ b/crates/hir_def/src/lib.rs | |||
@@ -108,6 +108,18 @@ impl ModuleId { | |||
108 | pub fn containing_module(&self, db: &dyn db::DefDatabase) -> Option<ModuleId> { | 108 | pub fn containing_module(&self, db: &dyn db::DefDatabase) -> Option<ModuleId> { |
109 | self.def_map(db).containing_module(self.local_id) | 109 | self.def_map(db).containing_module(self.local_id) |
110 | } | 110 | } |
111 | |||
112 | /// Returns `true` if this module represents a block expression. | ||
113 | /// | ||
114 | /// Returns `false` if this module is a submodule *inside* a block expression | ||
115 | /// (eg. `m` in `{ mod m {} }`). | ||
116 | pub fn is_block_root(&self, db: &dyn db::DefDatabase) -> bool { | ||
117 | if self.block.is_none() { | ||
118 | return false; | ||
119 | } | ||
120 | |||
121 | self.def_map(db)[self.local_id].parent.is_none() | ||
122 | } | ||
111 | } | 123 | } |
112 | 124 | ||
113 | /// An ID of a module, **local** to a specific crate | 125 | /// An ID of a module, **local** to a specific crate |
diff --git a/crates/hir_def/src/visibility.rs b/crates/hir_def/src/visibility.rs index 9908cd926..d4b7c9970 100644 --- a/crates/hir_def/src/visibility.rs +++ b/crates/hir_def/src/visibility.rs | |||
@@ -123,11 +123,19 @@ impl Visibility { | |||
123 | def_map: &DefMap, | 123 | def_map: &DefMap, |
124 | mut from_module: crate::LocalModuleId, | 124 | mut from_module: crate::LocalModuleId, |
125 | ) -> bool { | 125 | ) -> bool { |
126 | let to_module = match self { | 126 | let mut to_module = match self { |
127 | Visibility::Module(m) => m, | 127 | Visibility::Module(m) => m, |
128 | Visibility::Public => return true, | 128 | Visibility::Public => return true, |
129 | }; | 129 | }; |
130 | 130 | ||
131 | // `to_module` might be the root module of a block expression. Those have the same | ||
132 | // visibility as the containing module (even though no items are directly nameable from | ||
133 | // there, getting this right is important for method resolution). | ||
134 | // In that case, we adjust the visibility of `to_module` to point to the containing module. | ||
135 | if to_module.is_block_root(db) { | ||
136 | to_module = to_module.containing_module(db).unwrap(); | ||
137 | } | ||
138 | |||
131 | // from_module needs to be a descendant of to_module | 139 | // from_module needs to be a descendant of to_module |
132 | let mut def_map = def_map; | 140 | let mut def_map = def_map; |
133 | let mut parent_arc; | 141 | let mut parent_arc; |
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs index 80365fc16..179de61f9 100644 --- a/crates/hir_expand/src/builtin_macro.rs +++ b/crates/hir_expand/src/builtin_macro.rs | |||
@@ -110,6 +110,7 @@ register_builtin! { | |||
110 | (format_args_nl, FormatArgsNl) => format_args_expand, | 110 | (format_args_nl, FormatArgsNl) => format_args_expand, |
111 | (llvm_asm, LlvmAsm) => asm_expand, | 111 | (llvm_asm, LlvmAsm) => asm_expand, |
112 | (asm, Asm) => asm_expand, | 112 | (asm, Asm) => asm_expand, |
113 | (global_asm, GlobalAsm) => global_asm_expand, | ||
113 | (cfg, Cfg) => cfg_expand, | 114 | (cfg, Cfg) => cfg_expand, |
114 | (core_panic, CorePanic) => panic_expand, | 115 | (core_panic, CorePanic) => panic_expand, |
115 | (std_panic, StdPanic) => panic_expand, | 116 | (std_panic, StdPanic) => panic_expand, |
@@ -274,6 +275,15 @@ fn asm_expand( | |||
274 | ExpandResult::ok(expanded) | 275 | ExpandResult::ok(expanded) |
275 | } | 276 | } |
276 | 277 | ||
278 | fn global_asm_expand( | ||
279 | _db: &dyn AstDatabase, | ||
280 | _id: LazyMacroId, | ||
281 | _tt: &tt::Subtree, | ||
282 | ) -> ExpandResult<tt::Subtree> { | ||
283 | // Expand to nothing (at item-level) | ||
284 | ExpandResult::ok(quote! {}) | ||
285 | } | ||
286 | |||
277 | fn cfg_expand( | 287 | fn cfg_expand( |
278 | db: &dyn AstDatabase, | 288 | db: &dyn AstDatabase, |
279 | id: LazyMacroId, | 289 | id: LazyMacroId, |
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs index a0f8766b0..bcfd3e524 100644 --- a/crates/hir_expand/src/name.rs +++ b/crates/hir_expand/src/name.rs | |||
@@ -221,6 +221,7 @@ pub mod known { | |||
221 | option_env, | 221 | option_env, |
222 | llvm_asm, | 222 | llvm_asm, |
223 | asm, | 223 | asm, |
224 | global_asm, | ||
224 | // Builtin derives | 225 | // Builtin derives |
225 | Copy, | 226 | Copy, |
226 | Clone, | 227 | Clone, |
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs index 3693e3284..48bbcfd9f 100644 --- a/crates/hir_ty/src/method_resolution.rs +++ b/crates/hir_ty/src/method_resolution.rs | |||
@@ -246,29 +246,39 @@ pub struct InherentImpls { | |||
246 | 246 | ||
247 | impl InherentImpls { | 247 | impl InherentImpls { |
248 | pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> { | 248 | pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> { |
249 | let mut map: FxHashMap<_, Vec<_>> = FxHashMap::default(); | 249 | let mut impls = Self { map: FxHashMap::default() }; |
250 | 250 | ||
251 | let crate_def_map = db.crate_def_map(krate); | 251 | let crate_def_map = db.crate_def_map(krate); |
252 | for (_module_id, module_data) in crate_def_map.modules() { | 252 | collect_def_map(db, &crate_def_map, &mut impls); |
253 | for impl_id in module_data.scope.impls() { | 253 | |
254 | let data = db.impl_data(impl_id); | 254 | return Arc::new(impls); |
255 | if data.target_trait.is_some() { | 255 | |
256 | continue; | 256 | fn collect_def_map(db: &dyn HirDatabase, def_map: &DefMap, impls: &mut InherentImpls) { |
257 | for (_module_id, module_data) in def_map.modules() { | ||
258 | for impl_id in module_data.scope.impls() { | ||
259 | let data = db.impl_data(impl_id); | ||
260 | if data.target_trait.is_some() { | ||
261 | continue; | ||
262 | } | ||
263 | |||
264 | let self_ty = db.impl_self_ty(impl_id); | ||
265 | let fp = TyFingerprint::for_inherent_impl(self_ty.skip_binders()); | ||
266 | if let Some(fp) = fp { | ||
267 | impls.map.entry(fp).or_default().push(impl_id); | ||
268 | } | ||
269 | // `fp` should only be `None` in error cases (either erroneous code or incomplete name resolution) | ||
257 | } | 270 | } |
258 | 271 | ||
259 | let self_ty = db.impl_self_ty(impl_id); | 272 | // To better support custom derives, collect impls in all unnamed const items. |
260 | let fp = TyFingerprint::for_inherent_impl(self_ty.skip_binders()); | 273 | // const _: () = { ... }; |
261 | if let Some(fp) = fp { | 274 | for konst in module_data.scope.unnamed_consts() { |
262 | map.entry(fp).or_default().push(impl_id); | 275 | let body = db.body(konst.into()); |
276 | for (_, block_def_map) in body.blocks(db.upcast()) { | ||
277 | collect_def_map(db, &block_def_map, impls); | ||
278 | } | ||
263 | } | 279 | } |
264 | // `fp` should only be `None` in error cases (either erroneous code or incomplete name resolution) | ||
265 | } | 280 | } |
266 | } | 281 | } |
267 | |||
268 | // NOTE: We're not collecting inherent impls from unnamed consts here, we intentionally only | ||
269 | // support trait impls there. | ||
270 | |||
271 | Arc::new(Self { map }) | ||
272 | } | 282 | } |
273 | 283 | ||
274 | pub fn for_self_ty(&self, self_ty: &Ty) -> &[ImplId] { | 284 | pub fn for_self_ty(&self, self_ty: &Ty) -> &[ImplId] { |
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs index 4b2c82b41..a4c132bc5 100644 --- a/crates/hir_ty/src/tests/method_resolution.rs +++ b/crates/hir_ty/src/tests/method_resolution.rs | |||
@@ -1294,7 +1294,7 @@ mod b { | |||
1294 | } | 1294 | } |
1295 | 1295 | ||
1296 | #[test] | 1296 | #[test] |
1297 | fn impl_in_unnamed_const() { | 1297 | fn trait_impl_in_unnamed_const() { |
1298 | check_types( | 1298 | check_types( |
1299 | r#" | 1299 | r#" |
1300 | struct S; | 1300 | struct S; |
@@ -1314,3 +1314,38 @@ fn f() { | |||
1314 | "#, | 1314 | "#, |
1315 | ); | 1315 | ); |
1316 | } | 1316 | } |
1317 | |||
1318 | #[test] | ||
1319 | fn inherent_impl_in_unnamed_const() { | ||
1320 | check_types( | ||
1321 | r#" | ||
1322 | struct S; | ||
1323 | |||
1324 | const _: () = { | ||
1325 | impl S { | ||
1326 | fn method(&self) -> u16 { 0 } | ||
1327 | |||
1328 | pub(super) fn super_method(&self) -> u16 { 0 } | ||
1329 | |||
1330 | pub(crate) fn crate_method(&self) -> u16 { 0 } | ||
1331 | |||
1332 | pub fn pub_method(&self) -> u16 { 0 } | ||
1333 | } | ||
1334 | }; | ||
1335 | |||
1336 | fn f() { | ||
1337 | S.method(); | ||
1338 | //^^^^^^^^^^ u16 | ||
1339 | |||
1340 | S.super_method(); | ||
1341 | //^^^^^^^^^^^^^^^^ u16 | ||
1342 | |||
1343 | S.crate_method(); | ||
1344 | //^^^^^^^^^^^^^^^^ u16 | ||
1345 | |||
1346 | S.pub_method(); | ||
1347 | //^^^^^^^^^^^^^^ u16 | ||
1348 | } | ||
1349 | "#, | ||
1350 | ); | ||
1351 | } | ||
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index b24c664ba..99e45633e 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -244,6 +244,12 @@ impl Analysis { | |||
244 | self.with_db(|db| db.parse(file_id).tree()) | 244 | self.with_db(|db| db.parse(file_id).tree()) |
245 | } | 245 | } |
246 | 246 | ||
247 | /// Returns true if this file belongs to an immutable library. | ||
248 | pub fn is_library_file(&self, file_id: FileId) -> Cancelable<bool> { | ||
249 | use ide_db::base_db::SourceDatabaseExt; | ||
250 | self.with_db(|db| db.source_root(db.file_source_root(file_id)).is_library) | ||
251 | } | ||
252 | |||
247 | /// Gets the file's `LineIndex`: data structure to convert between absolute | 253 | /// Gets the file's `LineIndex`: data structure to convert between absolute |
248 | /// offsets and line/column representation. | 254 | /// offsets and line/column representation. |
249 | pub fn file_line_index(&self, file_id: FileId) -> Cancelable<Arc<LineIndex>> { | 255 | pub fn file_line_index(&self, file_id: FileId) -> Cancelable<Arc<LineIndex>> { |
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index 04fafd244..bc221d599 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs | |||
@@ -4,7 +4,7 @@ use std::mem; | |||
4 | 4 | ||
5 | use either::Either; | 5 | use either::Either; |
6 | use hir::{InFile, Semantics}; | 6 | use hir::{InFile, Semantics}; |
7 | use ide_db::{call_info::ActiveParameter, SymbolKind}; | 7 | use ide_db::{call_info::ActiveParameter, helpers::rust_doc::is_rust_fence, SymbolKind}; |
8 | use syntax::{ | 8 | use syntax::{ |
9 | ast::{self, AstNode}, | 9 | ast::{self, AstNode}, |
10 | AstToken, NodeOrToken, SyntaxNode, SyntaxToken, TextRange, TextSize, | 10 | AstToken, NodeOrToken, SyntaxNode, SyntaxToken, TextRange, TextSize, |
@@ -78,17 +78,6 @@ pub(super) fn ra_fixture( | |||
78 | } | 78 | } |
79 | 79 | ||
80 | const RUSTDOC_FENCE: &'static str = "```"; | 80 | const RUSTDOC_FENCE: &'static str = "```"; |
81 | const RUSTDOC_FENCE_TOKENS: &[&'static str] = &[ | ||
82 | "", | ||
83 | "rust", | ||
84 | "should_panic", | ||
85 | "ignore", | ||
86 | "no_run", | ||
87 | "compile_fail", | ||
88 | "edition2015", | ||
89 | "edition2018", | ||
90 | "edition2021", | ||
91 | ]; | ||
92 | 81 | ||
93 | /// Injection of syntax highlighting of doctests. | 82 | /// Injection of syntax highlighting of doctests. |
94 | pub(super) fn doc_comment( | 83 | pub(super) fn doc_comment( |
@@ -174,8 +163,7 @@ pub(super) fn doc_comment( | |||
174 | is_codeblock = !is_codeblock; | 163 | is_codeblock = !is_codeblock; |
175 | // Check whether code is rust by inspecting fence guards | 164 | // Check whether code is rust by inspecting fence guards |
176 | let guards = &line[idx + RUSTDOC_FENCE.len()..]; | 165 | let guards = &line[idx + RUSTDOC_FENCE.len()..]; |
177 | let is_rust = | 166 | let is_rust = is_rust_fence(guards); |
178 | guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim())); | ||
179 | is_doctest = is_codeblock && is_rust; | 167 | is_doctest = is_codeblock && is_rust; |
180 | continue; | 168 | continue; |
181 | } | 169 | } |
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 933cfa6f3..17cc6334b 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs | |||
@@ -307,7 +307,7 @@ fn benchmark_syntax_highlighting_parser() { | |||
307 | .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function)) | 307 | .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function)) |
308 | .count() | 308 | .count() |
309 | }; | 309 | }; |
310 | assert_eq!(hash, 1629); | 310 | assert_eq!(hash, 1632); |
311 | } | 311 | } |
312 | 312 | ||
313 | #[test] | 313 | #[test] |
diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs index 80bd1b7e8..a30c4d04e 100644 --- a/crates/ide_assists/src/handlers/fill_match_arms.rs +++ b/crates/ide_assists/src/handlers/fill_match_arms.rs | |||
@@ -1,5 +1,6 @@ | |||
1 | use std::iter; | 1 | use std::iter; |
2 | 2 | ||
3 | use either::Either; | ||
3 | use hir::{Adt, HasSource, ModuleDef, Semantics}; | 4 | use hir::{Adt, HasSource, ModuleDef, Semantics}; |
4 | use ide_db::helpers::{mod_path_to_ast, FamousDefs}; | 5 | use ide_db::helpers::{mod_path_to_ast, FamousDefs}; |
5 | use ide_db::RootDatabase; | 6 | use ide_db::RootDatabase; |
@@ -7,7 +8,7 @@ use itertools::Itertools; | |||
7 | use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; | 8 | use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; |
8 | 9 | ||
9 | use crate::{ | 10 | use crate::{ |
10 | utils::{does_pat_match_variant, render_snippet, Cursor}, | 11 | utils::{self, render_snippet, Cursor}, |
11 | AssistContext, AssistId, AssistKind, Assists, | 12 | AssistContext, AssistId, AssistKind, Assists, |
12 | }; | 13 | }; |
13 | 14 | ||
@@ -48,6 +49,18 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
48 | } | 49 | } |
49 | } | 50 | } |
50 | 51 | ||
52 | let top_lvl_pats: Vec<_> = arms | ||
53 | .iter() | ||
54 | .filter_map(ast::MatchArm::pat) | ||
55 | .flat_map(|pat| match pat { | ||
56 | // Special casee OrPat as separate top-level pats | ||
57 | Pat::OrPat(or_pat) => Either::Left(or_pat.pats()), | ||
58 | _ => Either::Right(iter::once(pat)), | ||
59 | }) | ||
60 | // Exclude top level wildcards so that they are expanded by this assist, retains status quo in #8129. | ||
61 | .filter(|pat| !matches!(pat, Pat::WildcardPat(_))) | ||
62 | .collect(); | ||
63 | |||
51 | let module = ctx.sema.scope(expr.syntax()).module()?; | 64 | let module = ctx.sema.scope(expr.syntax()).module()?; |
52 | 65 | ||
53 | let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) { | 66 | let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) { |
@@ -56,7 +69,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
56 | let mut variants = variants | 69 | let mut variants = variants |
57 | .into_iter() | 70 | .into_iter() |
58 | .filter_map(|variant| build_pat(ctx.db(), module, variant)) | 71 | .filter_map(|variant| build_pat(ctx.db(), module, variant)) |
59 | .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) | 72 | .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) |
60 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) | 73 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) |
61 | .collect::<Vec<_>>(); | 74 | .collect::<Vec<_>>(); |
62 | if Some(enum_def) == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option() { | 75 | if Some(enum_def) == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option() { |
@@ -66,11 +79,6 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
66 | } | 79 | } |
67 | variants | 80 | variants |
68 | } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) { | 81 | } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) { |
69 | // Partial fill not currently supported for tuple of enums. | ||
70 | if !arms.is_empty() { | ||
71 | return None; | ||
72 | } | ||
73 | |||
74 | // When calculating the match arms for a tuple of enums, we want | 82 | // When calculating the match arms for a tuple of enums, we want |
75 | // to create a match arm for each possible combination of enum | 83 | // to create a match arm for each possible combination of enum |
76 | // values. The `multi_cartesian_product` method transforms | 84 | // values. The `multi_cartesian_product` method transforms |
@@ -85,7 +93,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
85 | variants.into_iter().filter_map(|variant| build_pat(ctx.db(), module, variant)); | 93 | variants.into_iter().filter_map(|variant| build_pat(ctx.db(), module, variant)); |
86 | ast::Pat::from(make::tuple_pat(patterns)) | 94 | ast::Pat::from(make::tuple_pat(patterns)) |
87 | }) | 95 | }) |
88 | .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) | 96 | .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) |
89 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) | 97 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) |
90 | .collect() | 98 | .collect() |
91 | } else { | 99 | } else { |
@@ -128,16 +136,19 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
128 | ) | 136 | ) |
129 | } | 137 | } |
130 | 138 | ||
131 | fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool { | 139 | fn is_variant_missing(existing_pats: &[Pat], var: &Pat) -> bool { |
132 | existing_arms.iter().filter_map(|arm| arm.pat()).all(|pat| { | 140 | !existing_pats.iter().any(|pat| does_pat_match_variant(pat, var)) |
133 | // Special casee OrPat as separate top-level pats | 141 | } |
134 | let top_level_pats: Vec<Pat> = match pat { | ||
135 | Pat::OrPat(pats) => pats.pats().collect::<Vec<_>>(), | ||
136 | _ => vec![pat], | ||
137 | }; | ||
138 | 142 | ||
139 | !top_level_pats.iter().any(|pat| does_pat_match_variant(pat, var)) | 143 | // Fixme: this is still somewhat limited, use hir_ty::diagnostics::match_check? |
140 | }) | 144 | fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool { |
145 | match (pat, var) { | ||
146 | (Pat::WildcardPat(_), _) => true, | ||
147 | (Pat::TuplePat(tpat), Pat::TuplePat(tvar)) => { | ||
148 | tpat.fields().zip(tvar.fields()).all(|(p, v)| does_pat_match_variant(&p, &v)) | ||
149 | } | ||
150 | _ => utils::does_pat_match_variant(pat, var), | ||
151 | } | ||
141 | } | 152 | } |
142 | 153 | ||
143 | fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> { | 154 | fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> { |
@@ -467,20 +478,81 @@ fn main() { | |||
467 | 478 | ||
468 | #[test] | 479 | #[test] |
469 | fn fill_match_arms_tuple_of_enum_partial() { | 480 | fn fill_match_arms_tuple_of_enum_partial() { |
470 | check_assist_not_applicable( | 481 | check_assist( |
471 | fill_match_arms, | 482 | fill_match_arms, |
472 | r#" | 483 | r#" |
473 | enum A { One, Two } | 484 | enum A { One, Two } |
474 | enum B { One, Two } | 485 | enum B { One, Two } |
475 | 486 | ||
476 | fn main() { | 487 | fn main() { |
477 | let a = A::One; | 488 | let a = A::One; |
478 | let b = B::One; | 489 | let b = B::One; |
479 | match (a$0, b) { | 490 | match (a$0, b) { |
480 | (A::Two, B::One) => {} | 491 | (A::Two, B::One) => {} |
481 | } | 492 | } |
482 | } | 493 | } |
483 | "#, | 494 | "#, |
495 | r#" | ||
496 | enum A { One, Two } | ||
497 | enum B { One, Two } | ||
498 | |||
499 | fn main() { | ||
500 | let a = A::One; | ||
501 | let b = B::One; | ||
502 | match (a, b) { | ||
503 | (A::Two, B::One) => {} | ||
504 | $0(A::One, B::One) => {} | ||
505 | (A::One, B::Two) => {} | ||
506 | (A::Two, B::Two) => {} | ||
507 | } | ||
508 | } | ||
509 | "#, | ||
510 | ); | ||
511 | } | ||
512 | |||
513 | #[test] | ||
514 | fn fill_match_arms_tuple_of_enum_partial_with_wildcards() { | ||
515 | let ra_fixture = r#" | ||
516 | fn main() { | ||
517 | let a = Some(1); | ||
518 | let b = Some(()); | ||
519 | match (a$0, b) { | ||
520 | (Some(_), _) => {} | ||
521 | (None, Some(_)) => {} | ||
522 | } | ||
523 | } | ||
524 | "#; | ||
525 | check_assist( | ||
526 | fill_match_arms, | ||
527 | &format!("//- /main.rs crate:main deps:core{}{}", ra_fixture, FamousDefs::FIXTURE), | ||
528 | r#" | ||
529 | fn main() { | ||
530 | let a = Some(1); | ||
531 | let b = Some(()); | ||
532 | match (a, b) { | ||
533 | (Some(_), _) => {} | ||
534 | (None, Some(_)) => {} | ||
535 | $0(None, None) => {} | ||
536 | } | ||
537 | } | ||
538 | "#, | ||
539 | ); | ||
540 | } | ||
541 | |||
542 | #[test] | ||
543 | fn fill_match_arms_partial_with_deep_pattern() { | ||
544 | // Fixme: cannot handle deep patterns | ||
545 | let ra_fixture = r#" | ||
546 | fn main() { | ||
547 | match $0Some(true) { | ||
548 | Some(true) => {} | ||
549 | None => {} | ||
550 | } | ||
551 | } | ||
552 | "#; | ||
553 | check_assist_not_applicable( | ||
554 | fill_match_arms, | ||
555 | &format!("//- /main.rs crate:main deps:core{}{}", ra_fixture, FamousDefs::FIXTURE), | ||
484 | ); | 556 | ); |
485 | } | 557 | } |
486 | 558 | ||
diff --git a/crates/ide_assists/src/handlers/generate_deref.rs b/crates/ide_assists/src/handlers/generate_deref.rs new file mode 100644 index 000000000..4998ff7a4 --- /dev/null +++ b/crates/ide_assists/src/handlers/generate_deref.rs | |||
@@ -0,0 +1,227 @@ | |||
1 | use std::fmt::Display; | ||
2 | |||
3 | use ide_db::{helpers::FamousDefs, RootDatabase}; | ||
4 | use syntax::{ | ||
5 | ast::{self, NameOwner}, | ||
6 | AstNode, SyntaxNode, | ||
7 | }; | ||
8 | |||
9 | use crate::{ | ||
10 | assist_context::{AssistBuilder, AssistContext, Assists}, | ||
11 | utils::generate_trait_impl_text, | ||
12 | AssistId, AssistKind, | ||
13 | }; | ||
14 | |||
15 | // Assist: generate_deref | ||
16 | // | ||
17 | // Generate `Deref` impl using the given struct field. | ||
18 | // | ||
19 | // ``` | ||
20 | // struct A; | ||
21 | // struct B { | ||
22 | // $0a: A | ||
23 | // } | ||
24 | // ``` | ||
25 | // -> | ||
26 | // ``` | ||
27 | // struct A; | ||
28 | // struct B { | ||
29 | // a: A | ||
30 | // } | ||
31 | // | ||
32 | // impl std::ops::Deref for B { | ||
33 | // type Target = A; | ||
34 | // | ||
35 | // fn deref(&self) -> &Self::Target { | ||
36 | // &self.a | ||
37 | // } | ||
38 | // } | ||
39 | // ``` | ||
40 | pub(crate) fn generate_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
41 | generate_record_deref(acc, ctx).or_else(|| generate_tuple_deref(acc, ctx)) | ||
42 | } | ||
43 | |||
44 | fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
45 | let strukt = ctx.find_node_at_offset::<ast::Struct>()?; | ||
46 | let field = ctx.find_node_at_offset::<ast::RecordField>()?; | ||
47 | |||
48 | if existing_deref_impl(&ctx.sema, &strukt).is_some() { | ||
49 | cov_mark::hit!(test_add_record_deref_impl_already_exists); | ||
50 | return None; | ||
51 | } | ||
52 | |||
53 | let field_type = field.ty()?; | ||
54 | let field_name = field.name()?; | ||
55 | let target = field.syntax().text_range(); | ||
56 | acc.add( | ||
57 | AssistId("generate_deref", AssistKind::Generate), | ||
58 | format!("Generate `Deref` impl using `{}`", field_name), | ||
59 | target, | ||
60 | |edit| generate_edit(edit, strukt, field_type.syntax(), field_name.syntax()), | ||
61 | ) | ||
62 | } | ||
63 | |||
64 | fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
65 | let strukt = ctx.find_node_at_offset::<ast::Struct>()?; | ||
66 | let field = ctx.find_node_at_offset::<ast::TupleField>()?; | ||
67 | let field_list = ctx.find_node_at_offset::<ast::TupleFieldList>()?; | ||
68 | let field_list_index = | ||
69 | field_list.syntax().children().into_iter().position(|s| &s == field.syntax())?; | ||
70 | |||
71 | if existing_deref_impl(&ctx.sema, &strukt).is_some() { | ||
72 | cov_mark::hit!(test_add_field_deref_impl_already_exists); | ||
73 | return None; | ||
74 | } | ||
75 | |||
76 | let field_type = field.ty()?; | ||
77 | let target = field.syntax().text_range(); | ||
78 | acc.add( | ||
79 | AssistId("generate_deref", AssistKind::Generate), | ||
80 | format!("Generate `Deref` impl using `{}`", field.syntax()), | ||
81 | target, | ||
82 | |edit| generate_edit(edit, strukt, field_type.syntax(), field_list_index), | ||
83 | ) | ||
84 | } | ||
85 | |||
86 | fn generate_edit( | ||
87 | edit: &mut AssistBuilder, | ||
88 | strukt: ast::Struct, | ||
89 | field_type_syntax: &SyntaxNode, | ||
90 | field_name: impl Display, | ||
91 | ) { | ||
92 | let start_offset = strukt.syntax().text_range().end(); | ||
93 | let impl_code = format!( | ||
94 | r#" type Target = {0}; | ||
95 | |||
96 | fn deref(&self) -> &Self::Target {{ | ||
97 | &self.{1} | ||
98 | }}"#, | ||
99 | field_type_syntax, field_name | ||
100 | ); | ||
101 | let strukt_adt = ast::Adt::Struct(strukt); | ||
102 | let deref_impl = generate_trait_impl_text(&strukt_adt, "std::ops::Deref", &impl_code); | ||
103 | edit.insert(start_offset, deref_impl); | ||
104 | } | ||
105 | |||
106 | fn existing_deref_impl( | ||
107 | sema: &'_ hir::Semantics<'_, RootDatabase>, | ||
108 | strukt: &ast::Struct, | ||
109 | ) -> Option<()> { | ||
110 | let strukt = sema.to_def(strukt)?; | ||
111 | let krate = strukt.module(sema.db).krate(); | ||
112 | |||
113 | let deref_trait = FamousDefs(sema, Some(krate)).core_ops_Deref()?; | ||
114 | let strukt_type = strukt.ty(sema.db); | ||
115 | |||
116 | if strukt_type.impls_trait(sema.db, deref_trait, &[]) { | ||
117 | Some(()) | ||
118 | } else { | ||
119 | None | ||
120 | } | ||
121 | } | ||
122 | |||
123 | #[cfg(test)] | ||
124 | mod tests { | ||
125 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
126 | |||
127 | use super::*; | ||
128 | |||
129 | #[test] | ||
130 | fn test_generate_record_deref() { | ||
131 | check_assist( | ||
132 | generate_deref, | ||
133 | r#"struct A { } | ||
134 | struct B { $0a: A }"#, | ||
135 | r#"struct A { } | ||
136 | struct B { a: A } | ||
137 | |||
138 | impl std::ops::Deref for B { | ||
139 | type Target = A; | ||
140 | |||
141 | fn deref(&self) -> &Self::Target { | ||
142 | &self.a | ||
143 | } | ||
144 | }"#, | ||
145 | ); | ||
146 | } | ||
147 | |||
148 | #[test] | ||
149 | fn test_generate_field_deref_idx_0() { | ||
150 | check_assist( | ||
151 | generate_deref, | ||
152 | r#"struct A { } | ||
153 | struct B($0A);"#, | ||
154 | r#"struct A { } | ||
155 | struct B(A); | ||
156 | |||
157 | impl std::ops::Deref for B { | ||
158 | type Target = A; | ||
159 | |||
160 | fn deref(&self) -> &Self::Target { | ||
161 | &self.0 | ||
162 | } | ||
163 | }"#, | ||
164 | ); | ||
165 | } | ||
166 | #[test] | ||
167 | fn test_generate_field_deref_idx_1() { | ||
168 | check_assist( | ||
169 | generate_deref, | ||
170 | r#"struct A { } | ||
171 | struct B(u8, $0A);"#, | ||
172 | r#"struct A { } | ||
173 | struct B(u8, A); | ||
174 | |||
175 | impl std::ops::Deref for B { | ||
176 | type Target = A; | ||
177 | |||
178 | fn deref(&self) -> &Self::Target { | ||
179 | &self.1 | ||
180 | } | ||
181 | }"#, | ||
182 | ); | ||
183 | } | ||
184 | |||
185 | fn check_not_applicable(ra_fixture: &str) { | ||
186 | let fixture = format!( | ||
187 | "//- /main.rs crate:main deps:core,std\n{}\n{}", | ||
188 | ra_fixture, | ||
189 | FamousDefs::FIXTURE | ||
190 | ); | ||
191 | check_assist_not_applicable(generate_deref, &fixture) | ||
192 | } | ||
193 | |||
194 | #[test] | ||
195 | fn test_generate_record_deref_not_applicable_if_already_impl() { | ||
196 | cov_mark::check!(test_add_record_deref_impl_already_exists); | ||
197 | check_not_applicable( | ||
198 | r#"struct A { } | ||
199 | struct B { $0a: A } | ||
200 | |||
201 | impl std::ops::Deref for B { | ||
202 | type Target = A; | ||
203 | |||
204 | fn deref(&self) -> &Self::Target { | ||
205 | &self.a | ||
206 | } | ||
207 | }"#, | ||
208 | ) | ||
209 | } | ||
210 | |||
211 | #[test] | ||
212 | fn test_generate_field_deref_not_applicable_if_already_impl() { | ||
213 | cov_mark::check!(test_add_field_deref_impl_already_exists); | ||
214 | check_not_applicable( | ||
215 | r#"struct A { } | ||
216 | struct B($0A) | ||
217 | |||
218 | impl std::ops::Deref for B { | ||
219 | type Target = A; | ||
220 | |||
221 | fn deref(&self) -> &Self::Target { | ||
222 | &self.0 | ||
223 | } | ||
224 | }"#, | ||
225 | ) | ||
226 | } | ||
227 | } | ||
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs index 3694f468f..8996c1b61 100644 --- a/crates/ide_assists/src/lib.rs +++ b/crates/ide_assists/src/lib.rs | |||
@@ -134,6 +134,7 @@ mod handlers { | |||
134 | mod generate_default_from_enum_variant; | 134 | mod generate_default_from_enum_variant; |
135 | mod generate_default_from_new; | 135 | mod generate_default_from_new; |
136 | mod generate_is_empty_from_len; | 136 | mod generate_is_empty_from_len; |
137 | mod generate_deref; | ||
137 | mod generate_derive; | 138 | mod generate_derive; |
138 | mod generate_enum_is_method; | 139 | mod generate_enum_is_method; |
139 | mod generate_enum_projection_method; | 140 | mod generate_enum_projection_method; |
@@ -201,6 +202,7 @@ mod handlers { | |||
201 | generate_default_from_enum_variant::generate_default_from_enum_variant, | 202 | generate_default_from_enum_variant::generate_default_from_enum_variant, |
202 | generate_default_from_new::generate_default_from_new, | 203 | generate_default_from_new::generate_default_from_new, |
203 | generate_is_empty_from_len::generate_is_empty_from_len, | 204 | generate_is_empty_from_len::generate_is_empty_from_len, |
205 | generate_deref::generate_deref, | ||
204 | generate_derive::generate_derive, | 206 | generate_derive::generate_derive, |
205 | generate_enum_is_method::generate_enum_is_method, | 207 | generate_enum_is_method::generate_enum_is_method, |
206 | generate_enum_projection_method::generate_enum_as_method, | 208 | generate_enum_projection_method::generate_enum_as_method, |
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs index a7a923beb..49533e7d2 100644 --- a/crates/ide_assists/src/tests.rs +++ b/crates/ide_assists/src/tests.rs | |||
@@ -84,7 +84,8 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) { | |||
84 | }); | 84 | }); |
85 | 85 | ||
86 | let actual = { | 86 | let actual = { |
87 | let source_change = assist.source_change.unwrap(); | 87 | let source_change = |
88 | assist.source_change.expect("Assist did not contain any source changes"); | ||
88 | let mut actual = before; | 89 | let mut actual = before; |
89 | if let Some(source_file_edit) = source_change.get_source_edit(file_id) { | 90 | if let Some(source_file_edit) = source_change.get_source_edit(file_id) { |
90 | source_file_edit.apply(&mut actual); | 91 | source_file_edit.apply(&mut actual); |
@@ -121,7 +122,8 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label: | |||
121 | 122 | ||
122 | match (assist, expected) { | 123 | match (assist, expected) { |
123 | (Some(assist), ExpectedResult::After(after)) => { | 124 | (Some(assist), ExpectedResult::After(after)) => { |
124 | let source_change = assist.source_change.unwrap(); | 125 | let source_change = |
126 | assist.source_change.expect("Assist did not contain any source changes"); | ||
125 | assert!(!source_change.source_file_edits.is_empty()); | 127 | assert!(!source_change.source_file_edits.is_empty()); |
126 | let skip_header = source_change.source_file_edits.len() == 1 | 128 | let skip_header = source_change.source_file_edits.len() == 1 |
127 | && source_change.file_system_edits.len() == 0; | 129 | && source_change.file_system_edits.len() == 0; |
@@ -191,6 +193,7 @@ fn assist_order_field_struct() { | |||
191 | let mut assists = assists.iter(); | 193 | let mut assists = assists.iter(); |
192 | 194 | ||
193 | assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)"); | 195 | assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)"); |
196 | assert_eq!(assists.next().expect("expected assist").label, "Generate `Deref` impl using `bar`"); | ||
194 | assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method"); | 197 | assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method"); |
195 | assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method"); | 198 | assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method"); |
196 | assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method"); | 199 | assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method"); |
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs index 27a22ca10..41559b43a 100644 --- a/crates/ide_assists/src/tests/generated.rs +++ b/crates/ide_assists/src/tests/generated.rs | |||
@@ -552,6 +552,33 @@ impl Default for Example { | |||
552 | } | 552 | } |
553 | 553 | ||
554 | #[test] | 554 | #[test] |
555 | fn doctest_generate_deref() { | ||
556 | check_doc_test( | ||
557 | "generate_deref", | ||
558 | r#####" | ||
559 | struct A; | ||
560 | struct B { | ||
561 | $0a: A | ||
562 | } | ||
563 | "#####, | ||
564 | r#####" | ||
565 | struct A; | ||
566 | struct B { | ||
567 | a: A | ||
568 | } | ||
569 | |||
570 | impl std::ops::Deref for B { | ||
571 | type Target = A; | ||
572 | |||
573 | fn deref(&self) -> &Self::Target { | ||
574 | &self.a | ||
575 | } | ||
576 | } | ||
577 | "#####, | ||
578 | ) | ||
579 | } | ||
580 | |||
581 | #[test] | ||
555 | fn doctest_generate_derive() { | 582 | fn doctest_generate_derive() { |
556 | check_doc_test( | 583 | check_doc_test( |
557 | "generate_derive", | 584 | "generate_derive", |
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs index 66798ea3a..720de0d1f 100644 --- a/crates/ide_db/src/helpers.rs +++ b/crates/ide_db/src/helpers.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | //! A module with ide helpers for high-level ide features. | 1 | //! A module with ide helpers for high-level ide features. |
2 | pub mod insert_use; | 2 | pub mod insert_use; |
3 | pub mod import_assets; | 3 | pub mod import_assets; |
4 | pub mod rust_doc; | ||
4 | 5 | ||
5 | use std::collections::VecDeque; | 6 | use std::collections::VecDeque; |
6 | 7 | ||
@@ -113,6 +114,10 @@ impl FamousDefs<'_, '_> { | |||
113 | self.find_module("core:iter") | 114 | self.find_module("core:iter") |
114 | } | 115 | } |
115 | 116 | ||
117 | pub fn core_ops_Deref(&self) -> Option<Trait> { | ||
118 | self.find_trait("core:ops:Deref") | ||
119 | } | ||
120 | |||
116 | fn find_trait(&self, path: &str) -> Option<Trait> { | 121 | fn find_trait(&self, path: &str) -> Option<Trait> { |
117 | match self.find_def(path)? { | 122 | match self.find_def(path)? { |
118 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it), | 123 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it), |
diff --git a/crates/ide_db/src/helpers/famous_defs_fixture.rs b/crates/ide_db/src/helpers/famous_defs_fixture.rs index 4d79e064e..29ae12dcf 100644 --- a/crates/ide_db/src/helpers/famous_defs_fixture.rs +++ b/crates/ide_db/src/helpers/famous_defs_fixture.rs | |||
@@ -112,6 +112,12 @@ pub mod ops { | |||
112 | type Output; | 112 | type Output; |
113 | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; | 113 | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; |
114 | } | 114 | } |
115 | |||
116 | #[lang = "deref"] | ||
117 | pub trait Deref { | ||
118 | type Target: ?Sized; | ||
119 | fn deref(&self) -> &Self::Target; | ||
120 | } | ||
115 | } | 121 | } |
116 | 122 | ||
117 | pub mod option { | 123 | pub mod option { |
@@ -141,3 +147,5 @@ mod return_keyword {} | |||
141 | 147 | ||
142 | /// Docs for prim_str | 148 | /// Docs for prim_str |
143 | mod prim_str {} | 149 | mod prim_str {} |
150 | |||
151 | pub use core::ops; \ No newline at end of file | ||
diff --git a/crates/ide_db/src/helpers/rust_doc.rs b/crates/ide_db/src/helpers/rust_doc.rs new file mode 100644 index 000000000..e27e23867 --- /dev/null +++ b/crates/ide_db/src/helpers/rust_doc.rs | |||
@@ -0,0 +1,34 @@ | |||
1 | //! Rustdoc specific doc comment handling | ||
2 | |||
3 | // stripped down version of https://github.com/rust-lang/rust/blob/392ba2ba1a7d6c542d2459fb8133bebf62a4a423/src/librustdoc/html/markdown.rs#L810-L933 | ||
4 | pub fn is_rust_fence(s: &str) -> bool { | ||
5 | let mut seen_rust_tags = false; | ||
6 | let mut seen_other_tags = false; | ||
7 | |||
8 | let tokens = s | ||
9 | .trim() | ||
10 | .split(|c| c == ',' || c == ' ' || c == '\t') | ||
11 | .map(str::trim) | ||
12 | .filter(|t| !t.is_empty()); | ||
13 | |||
14 | for token in tokens { | ||
15 | match token { | ||
16 | "should_panic" | "no_run" | "ignore" | "allow_fail" => { | ||
17 | seen_rust_tags = !seen_other_tags | ||
18 | } | ||
19 | "rust" => seen_rust_tags = true, | ||
20 | "test_harness" | "compile_fail" => seen_rust_tags = !seen_other_tags || seen_rust_tags, | ||
21 | x if x.starts_with("edition") => {} | ||
22 | x if x.starts_with('E') && x.len() == 5 => { | ||
23 | if x[1..].parse::<u32>().is_ok() { | ||
24 | seen_rust_tags = !seen_other_tags || seen_rust_tags; | ||
25 | } else { | ||
26 | seen_other_tags = true; | ||
27 | } | ||
28 | } | ||
29 | _ => seen_other_tags = true, | ||
30 | } | ||
31 | } | ||
32 | |||
33 | !seen_other_tags || seen_rust_tags | ||
34 | } | ||
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs index 9ba98f7fb..a7c8c13c6 100644 --- a/crates/mbe/src/syntax_bridge.rs +++ b/crates/mbe/src/syntax_bridge.rs | |||
@@ -213,7 +213,7 @@ fn doc_comment_text(comment: &ast::Comment) -> SmolStr { | |||
213 | 213 | ||
214 | // Quote the string | 214 | // Quote the string |
215 | // Note that `tt::Literal` expect an escaped string | 215 | // Note that `tt::Literal` expect an escaped string |
216 | let text = format!("{:?}", text.escape_debug().to_string()); | 216 | let text = format!("\"{}\"", text.escape_debug()); |
217 | text.into() | 217 | text.into() |
218 | } | 218 | } |
219 | 219 | ||
diff --git a/crates/mbe/src/tests/expand.rs b/crates/mbe/src/tests/expand.rs index 146b236e2..3a1d840ea 100644 --- a/crates/mbe/src/tests/expand.rs +++ b/crates/mbe/src/tests/expand.rs | |||
@@ -936,7 +936,7 @@ fn test_meta_doc_comments() { | |||
936 | MultiLines Doc | 936 | MultiLines Doc |
937 | */ | 937 | */ |
938 | }"#, | 938 | }"#, |
939 | "# [doc = \" Single Line Doc 1\"] # [doc = \"\\\\n MultiLines Doc\\\\n \"] fn bar () {}", | 939 | "# [doc = \" Single Line Doc 1\"] # [doc = \"\\n MultiLines Doc\\n \"] fn bar () {}", |
940 | ); | 940 | ); |
941 | } | 941 | } |
942 | 942 | ||
@@ -977,7 +977,27 @@ fn test_meta_doc_comments_non_latin() { | |||
977 | 莊生曉夢迷蝴蝶,望帝春心託杜鵑。 | 977 | 莊生曉夢迷蝴蝶,望帝春心託杜鵑。 |
978 | */ | 978 | */ |
979 | }"#, | 979 | }"#, |
980 | "# [doc = \" 錦瑟無端五十弦,一弦一柱思華年。\"] # [doc = \"\\\\n 莊生曉夢迷蝴蝶,望帝春心託杜鵑。\\\\n \"] fn bar () {}", | 980 | "# [doc = \" 錦瑟無端五十弦,一弦一柱思華年。\"] # [doc = \"\\n 莊生曉夢迷蝴蝶,望帝春心託杜鵑。\\n \"] fn bar () {}", |
981 | ); | ||
982 | } | ||
983 | |||
984 | #[test] | ||
985 | fn test_meta_doc_comments_escaped_characters() { | ||
986 | parse_macro( | ||
987 | r#" | ||
988 | macro_rules! foo { | ||
989 | ($(#[$ i:meta])+) => ( | ||
990 | $(#[$ i])+ | ||
991 | fn bar() {} | ||
992 | ) | ||
993 | } | ||
994 | "#, | ||
995 | ) | ||
996 | .assert_expand_items( | ||
997 | r#"foo! { | ||
998 | /// \ " ' | ||
999 | }"#, | ||
1000 | r#"# [doc = " \\ \" \'"] fn bar () {}"#, | ||
981 | ); | 1001 | ); |
982 | } | 1002 | } |
983 | 1003 | ||
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 1edaa394a..7ddea22c8 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -400,6 +400,17 @@ impl Config { | |||
400 | pub fn will_rename(&self) -> bool { | 400 | pub fn will_rename(&self) -> bool { |
401 | try_or!(self.caps.workspace.as_ref()?.file_operations.as_ref()?.will_rename?, false) | 401 | try_or!(self.caps.workspace.as_ref()?.file_operations.as_ref()?.will_rename?, false) |
402 | } | 402 | } |
403 | pub fn change_annotation_support(&self) -> bool { | ||
404 | try_!(self | ||
405 | .caps | ||
406 | .workspace | ||
407 | .as_ref()? | ||
408 | .workspace_edit | ||
409 | .as_ref()? | ||
410 | .change_annotation_support | ||
411 | .as_ref()?) | ||
412 | .is_some() | ||
413 | } | ||
403 | pub fn code_action_resolve(&self) -> bool { | 414 | pub fn code_action_resolve(&self) -> bool { |
404 | try_or!( | 415 | try_or!( |
405 | self.caps | 416 | self.caps |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt index 23ec2efba..227d96d51 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt | |||
@@ -326,6 +326,7 @@ | |||
326 | }, | 326 | }, |
327 | ), | 327 | ), |
328 | document_changes: None, | 328 | document_changes: None, |
329 | change_annotations: None, | ||
329 | }, | 330 | }, |
330 | ), | 331 | ), |
331 | is_preferred: Some( | 332 | is_preferred: Some( |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt index b6acb5f42..f8adfad3b 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt | |||
@@ -179,6 +179,7 @@ | |||
179 | }, | 179 | }, |
180 | ), | 180 | ), |
181 | document_changes: None, | 181 | document_changes: None, |
182 | change_annotations: None, | ||
182 | }, | 183 | }, |
183 | ), | 184 | ), |
184 | is_preferred: Some( | 185 | is_preferred: Some( |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt index d765257c4..5a70d2ed7 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt | |||
@@ -179,6 +179,7 @@ | |||
179 | }, | 179 | }, |
180 | ), | 180 | ), |
181 | document_changes: None, | 181 | document_changes: None, |
182 | change_annotations: None, | ||
182 | }, | 183 | }, |
183 | ), | 184 | ), |
184 | is_preferred: Some( | 185 | is_preferred: Some( |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt index 6b0d94878..04ca0c9c2 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt | |||
@@ -179,6 +179,7 @@ | |||
179 | }, | 179 | }, |
180 | ), | 180 | ), |
181 | document_changes: None, | 181 | document_changes: None, |
182 | change_annotations: None, | ||
182 | }, | 183 | }, |
183 | ), | 184 | ), |
184 | is_preferred: Some( | 185 | is_preferred: Some( |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt index a0cfb8d33..57d2f1ae3 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt | |||
@@ -339,6 +339,7 @@ | |||
339 | }, | 339 | }, |
340 | ), | 340 | ), |
341 | document_changes: None, | 341 | document_changes: None, |
342 | change_annotations: None, | ||
342 | }, | 343 | }, |
343 | ), | 344 | ), |
344 | is_preferred: Some( | 345 | is_preferred: Some( |
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index e2f319f6b..ca18997e4 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs | |||
@@ -136,6 +136,7 @@ fn map_rust_child_diagnostic( | |||
136 | // FIXME: there's no good reason to use edit_map here.... | 136 | // FIXME: there's no good reason to use edit_map here.... |
137 | changes: Some(edit_map), | 137 | changes: Some(edit_map), |
138 | document_changes: None, | 138 | document_changes: None, |
139 | change_annotations: None, | ||
139 | }), | 140 | }), |
140 | is_preferred: Some(true), | 141 | is_preferred: Some(true), |
141 | data: None, | 142 | data: None, |
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index d648cda32..b8835a534 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs | |||
@@ -312,6 +312,9 @@ pub struct SnippetWorkspaceEdit { | |||
312 | pub changes: Option<HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>>>, | 312 | pub changes: Option<HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>>>, |
313 | #[serde(skip_serializing_if = "Option::is_none")] | 313 | #[serde(skip_serializing_if = "Option::is_none")] |
314 | pub document_changes: Option<Vec<SnippetDocumentChangeOperation>>, | 314 | pub document_changes: Option<Vec<SnippetDocumentChangeOperation>>, |
315 | #[serde(skip_serializing_if = "Option::is_none")] | ||
316 | pub change_annotations: | ||
317 | Option<HashMap<lsp_types::ChangeAnnotationIdentifier, lsp_types::ChangeAnnotation>>, | ||
315 | } | 318 | } |
316 | 319 | ||
317 | #[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)] | 320 | #[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)] |
@@ -335,6 +338,9 @@ pub struct SnippetTextEdit { | |||
335 | pub new_text: String, | 338 | pub new_text: String, |
336 | #[serde(skip_serializing_if = "Option::is_none")] | 339 | #[serde(skip_serializing_if = "Option::is_none")] |
337 | pub insert_text_format: Option<lsp_types::InsertTextFormat>, | 340 | pub insert_text_format: Option<lsp_types::InsertTextFormat>, |
341 | /// The annotation id if this is an annotated | ||
342 | #[serde(skip_serializing_if = "Option::is_none")] | ||
343 | pub annotation_id: Option<lsp_types::ChangeAnnotationIdentifier>, | ||
338 | } | 344 | } |
339 | 345 | ||
340 | pub enum HoverRequest {} | 346 | pub enum HoverRequest {} |
diff --git a/crates/rust-analyzer/src/markdown.rs b/crates/rust-analyzer/src/markdown.rs index 865eaae9b..35eaffba8 100644 --- a/crates/rust-analyzer/src/markdown.rs +++ b/crates/rust-analyzer/src/markdown.rs | |||
@@ -1,17 +1,7 @@ | |||
1 | //! Transforms markdown | 1 | //! Transforms markdown |
2 | use ide_db::helpers::rust_doc::is_rust_fence; | ||
2 | 3 | ||
3 | const RUSTDOC_FENCE: &str = "```"; | 4 | const RUSTDOC_FENCE: &str = "```"; |
4 | const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUST_SPECIFIC: &[&str] = &[ | ||
5 | "", | ||
6 | "rust", | ||
7 | "should_panic", | ||
8 | "ignore", | ||
9 | "no_run", | ||
10 | "compile_fail", | ||
11 | "edition2015", | ||
12 | "edition2018", | ||
13 | "edition2021", | ||
14 | ]; | ||
15 | 5 | ||
16 | pub(crate) fn format_docs(src: &str) -> String { | 6 | pub(crate) fn format_docs(src: &str) -> String { |
17 | let mut processed_lines = Vec::new(); | 7 | let mut processed_lines = Vec::new(); |
@@ -27,9 +17,7 @@ pub(crate) fn format_docs(src: &str) -> String { | |||
27 | in_code_block ^= true; | 17 | in_code_block ^= true; |
28 | 18 | ||
29 | if in_code_block { | 19 | if in_code_block { |
30 | is_rust = header | 20 | is_rust = is_rust_fence(header); |
31 | .split(',') | ||
32 | .all(|sub| RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUST_SPECIFIC.contains(&sub.trim())); | ||
33 | 21 | ||
34 | if is_rust { | 22 | if is_rust { |
35 | line = "```rust"; | 23 | line = "```rust"; |
@@ -82,6 +70,12 @@ mod tests { | |||
82 | } | 70 | } |
83 | 71 | ||
84 | #[test] | 72 | #[test] |
73 | fn test_format_docs_handles_error_codes() { | ||
74 | let comment = "```compile_fail,E0641\nlet b = 0 as *const _;\n```"; | ||
75 | assert_eq!(format_docs(comment), "```rust\nlet b = 0 as *const _;\n```"); | ||
76 | } | ||
77 | |||
78 | #[test] | ||
85 | fn test_format_docs_skips_comments_in_rust_block() { | 79 | fn test_format_docs_skips_comments_in_rust_block() { |
86 | let comment = | 80 | let comment = |
87 | "```rust\n # skip1\n# skip2\n#stay1\nstay2\n#\n #\n # \n #\tskip3\n\t#\t\n```"; | 81 | "```rust\n # skip1\n# skip2\n#stay1\nstay2\n#\n #\n # \n #\tskip3\n\t#\t\n```"; |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 1a1f65f3b..fe4d0733d 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -1,15 +1,16 @@ | |||
1 | //! Conversion of rust-analyzer specific types to lsp_types equivalents. | 1 | //! Conversion of rust-analyzer specific types to lsp_types equivalents. |
2 | use std::{ | 2 | use std::{ |
3 | iter::once, | ||
3 | path::{self, Path}, | 4 | path::{self, Path}, |
4 | sync::atomic::{AtomicU32, Ordering}, | 5 | sync::atomic::{AtomicU32, Ordering}, |
5 | }; | 6 | }; |
6 | 7 | ||
7 | use ide::{ | 8 | use ide::{ |
8 | Annotation, AnnotationKind, Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, | 9 | Annotation, AnnotationKind, Assist, AssistKind, CallInfo, Cancelable, CompletionItem, |
9 | CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind, | 10 | CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, |
10 | Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, InlayHint, InlayKind, | 11 | Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, InlayHint, |
11 | InsertTextFormat, Markup, NavigationTarget, ReferenceAccess, RenameError, Runnable, Severity, | 12 | InlayKind, InsertTextFormat, Markup, NavigationTarget, ReferenceAccess, RenameError, Runnable, |
12 | SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize, | 13 | Severity, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize, |
13 | }; | 14 | }; |
14 | use itertools::Itertools; | 15 | use itertools::Itertools; |
15 | use serde_json::to_value; | 16 | use serde_json::to_value; |
@@ -174,6 +175,7 @@ pub(crate) fn snippet_text_edit( | |||
174 | range: text_edit.range, | 175 | range: text_edit.range, |
175 | new_text: text_edit.new_text, | 176 | new_text: text_edit.new_text, |
176 | insert_text_format, | 177 | insert_text_format, |
178 | annotation_id: None, | ||
177 | } | 179 | } |
178 | } | 180 | } |
179 | 181 | ||
@@ -688,6 +690,10 @@ pub(crate) fn goto_definition_response( | |||
688 | } | 690 | } |
689 | } | 691 | } |
690 | 692 | ||
693 | fn outside_workspace_annotation_id() -> String { | ||
694 | String::from("OutsideWorkspace") | ||
695 | } | ||
696 | |||
691 | pub(crate) fn snippet_text_document_edit( | 697 | pub(crate) fn snippet_text_document_edit( |
692 | snap: &GlobalStateSnapshot, | 698 | snap: &GlobalStateSnapshot, |
693 | is_snippet: bool, | 699 | is_snippet: bool, |
@@ -696,14 +702,21 @@ pub(crate) fn snippet_text_document_edit( | |||
696 | ) -> Result<lsp_ext::SnippetTextDocumentEdit> { | 702 | ) -> Result<lsp_ext::SnippetTextDocumentEdit> { |
697 | let text_document = optional_versioned_text_document_identifier(snap, file_id); | 703 | let text_document = optional_versioned_text_document_identifier(snap, file_id); |
698 | let line_index = snap.file_line_index(file_id)?; | 704 | let line_index = snap.file_line_index(file_id)?; |
699 | let edits = edit.into_iter().map(|it| snippet_text_edit(&line_index, is_snippet, it)).collect(); | 705 | let mut edits: Vec<_> = |
706 | edit.into_iter().map(|it| snippet_text_edit(&line_index, is_snippet, it)).collect(); | ||
707 | |||
708 | if snap.analysis.is_library_file(file_id)? && snap.config.change_annotation_support() { | ||
709 | for edit in &mut edits { | ||
710 | edit.annotation_id = Some(outside_workspace_annotation_id()) | ||
711 | } | ||
712 | } | ||
700 | Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits }) | 713 | Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits }) |
701 | } | 714 | } |
702 | 715 | ||
703 | pub(crate) fn snippet_text_document_ops( | 716 | pub(crate) fn snippet_text_document_ops( |
704 | snap: &GlobalStateSnapshot, | 717 | snap: &GlobalStateSnapshot, |
705 | file_system_edit: FileSystemEdit, | 718 | file_system_edit: FileSystemEdit, |
706 | ) -> Vec<lsp_ext::SnippetDocumentChangeOperation> { | 719 | ) -> Cancelable<Vec<lsp_ext::SnippetDocumentChangeOperation>> { |
707 | let mut ops = Vec::new(); | 720 | let mut ops = Vec::new(); |
708 | match file_system_edit { | 721 | match file_system_edit { |
709 | FileSystemEdit::CreateFile { dst, initial_contents } => { | 722 | FileSystemEdit::CreateFile { dst, initial_contents } => { |
@@ -721,6 +734,7 @@ pub(crate) fn snippet_text_document_ops( | |||
721 | range: lsp_types::Range::default(), | 734 | range: lsp_types::Range::default(), |
722 | new_text: initial_contents, | 735 | new_text: initial_contents, |
723 | insert_text_format: Some(lsp_types::InsertTextFormat::PlainText), | 736 | insert_text_format: Some(lsp_types::InsertTextFormat::PlainText), |
737 | annotation_id: None, | ||
724 | }; | 738 | }; |
725 | let edit_file = | 739 | let edit_file = |
726 | lsp_ext::SnippetTextDocumentEdit { text_document, edits: vec![text_edit] }; | 740 | lsp_ext::SnippetTextDocumentEdit { text_document, edits: vec![text_edit] }; |
@@ -730,16 +744,19 @@ pub(crate) fn snippet_text_document_ops( | |||
730 | FileSystemEdit::MoveFile { src, dst } => { | 744 | FileSystemEdit::MoveFile { src, dst } => { |
731 | let old_uri = snap.file_id_to_url(src); | 745 | let old_uri = snap.file_id_to_url(src); |
732 | let new_uri = snap.anchored_path(&dst); | 746 | let new_uri = snap.anchored_path(&dst); |
733 | let rename_file = lsp_types::ResourceOp::Rename(lsp_types::RenameFile { | 747 | let mut rename_file = |
734 | old_uri, | 748 | lsp_types::RenameFile { old_uri, new_uri, options: None, annotation_id: None }; |
735 | new_uri, | 749 | if snap.analysis.is_library_file(src) == Ok(true) |
736 | options: None, | 750 | && snap.config.change_annotation_support() |
737 | annotation_id: None, | 751 | { |
738 | }); | 752 | rename_file.annotation_id = Some(outside_workspace_annotation_id()) |
739 | ops.push(lsp_ext::SnippetDocumentChangeOperation::Op(rename_file)) | 753 | } |
754 | ops.push(lsp_ext::SnippetDocumentChangeOperation::Op(lsp_types::ResourceOp::Rename( | ||
755 | rename_file, | ||
756 | ))) | ||
740 | } | 757 | } |
741 | } | 758 | } |
742 | ops | 759 | Ok(ops) |
743 | } | 760 | } |
744 | 761 | ||
745 | pub(crate) fn snippet_workspace_edit( | 762 | pub(crate) fn snippet_workspace_edit( |
@@ -747,16 +764,35 @@ pub(crate) fn snippet_workspace_edit( | |||
747 | source_change: SourceChange, | 764 | source_change: SourceChange, |
748 | ) -> Result<lsp_ext::SnippetWorkspaceEdit> { | 765 | ) -> Result<lsp_ext::SnippetWorkspaceEdit> { |
749 | let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new(); | 766 | let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new(); |
767 | |||
750 | for op in source_change.file_system_edits { | 768 | for op in source_change.file_system_edits { |
751 | let ops = snippet_text_document_ops(snap, op); | 769 | let ops = snippet_text_document_ops(snap, op)?; |
752 | document_changes.extend_from_slice(&ops); | 770 | document_changes.extend_from_slice(&ops); |
753 | } | 771 | } |
754 | for (file_id, edit) in source_change.source_file_edits { | 772 | for (file_id, edit) in source_change.source_file_edits { |
755 | let edit = snippet_text_document_edit(&snap, source_change.is_snippet, file_id, edit)?; | 773 | let edit = snippet_text_document_edit(&snap, source_change.is_snippet, file_id, edit)?; |
756 | document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit)); | 774 | document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit)); |
757 | } | 775 | } |
758 | let workspace_edit = | 776 | let mut workspace_edit = lsp_ext::SnippetWorkspaceEdit { |
759 | lsp_ext::SnippetWorkspaceEdit { changes: None, document_changes: Some(document_changes) }; | 777 | changes: None, |
778 | document_changes: Some(document_changes), | ||
779 | change_annotations: None, | ||
780 | }; | ||
781 | if snap.config.change_annotation_support() { | ||
782 | workspace_edit.change_annotations = Some( | ||
783 | once(( | ||
784 | outside_workspace_annotation_id(), | ||
785 | lsp_types::ChangeAnnotation { | ||
786 | label: String::from("Edit outside of the workspace"), | ||
787 | needs_confirmation: Some(true), | ||
788 | description: Some(String::from( | ||
789 | "This edit lies outside of the workspace and may affect dependencies", | ||
790 | )), | ||
791 | }, | ||
792 | )) | ||
793 | .collect(), | ||
794 | ) | ||
795 | } | ||
760 | Ok(workspace_edit) | 796 | Ok(workspace_edit) |
761 | } | 797 | } |
762 | 798 | ||
@@ -784,16 +820,7 @@ impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit { | |||
784 | lsp_types::DocumentChangeOperation::Edit( | 820 | lsp_types::DocumentChangeOperation::Edit( |
785 | lsp_types::TextDocumentEdit { | 821 | lsp_types::TextDocumentEdit { |
786 | text_document: edit.text_document, | 822 | text_document: edit.text_document, |
787 | edits: edit | 823 | edits: edit.edits.into_iter().map(From::from).collect(), |
788 | .edits | ||
789 | .into_iter() | ||
790 | .map(|edit| { | ||
791 | lsp_types::OneOf::Left(lsp_types::TextEdit { | ||
792 | range: edit.range, | ||
793 | new_text: edit.new_text, | ||
794 | }) | ||
795 | }) | ||
796 | .collect(), | ||
797 | }, | 824 | }, |
798 | ) | 825 | ) |
799 | } | 826 | } |
@@ -801,7 +828,23 @@ impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit { | |||
801 | .collect(), | 828 | .collect(), |
802 | ) | 829 | ) |
803 | }), | 830 | }), |
804 | change_annotations: None, | 831 | change_annotations: snippet_workspace_edit.change_annotations, |
832 | } | ||
833 | } | ||
834 | } | ||
835 | |||
836 | impl From<lsp_ext::SnippetTextEdit> | ||
837 | for lsp_types::OneOf<lsp_types::TextEdit, lsp_types::AnnotatedTextEdit> | ||
838 | { | ||
839 | fn from( | ||
840 | lsp_ext::SnippetTextEdit { annotation_id, insert_text_format:_, new_text, range }: lsp_ext::SnippetTextEdit, | ||
841 | ) -> Self { | ||
842 | match annotation_id { | ||
843 | Some(annotation_id) => lsp_types::OneOf::Right(lsp_types::AnnotatedTextEdit { | ||
844 | text_edit: lsp_types::TextEdit { range, new_text }, | ||
845 | annotation_id, | ||
846 | }), | ||
847 | None => lsp_types::OneOf::Left(lsp_types::TextEdit { range, new_text }), | ||
805 | } | 848 | } |
806 | } | 849 | } |
807 | } | 850 | } |