diff options
-rw-r--r-- | crates/ra_hir/src/source_analyzer.rs | 16 | ||||
-rw-r--r-- | crates/ra_hir_def/src/body.rs | 54 | ||||
-rw-r--r-- | crates/ra_hir_def/src/lib.rs | 63 | ||||
-rw-r--r-- | crates/ra_hir_def/src/nameres/collector.rs | 77 | ||||
-rw-r--r-- | crates/ra_ide/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_ide/src/runnables.rs | 109 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/cargo_target_spec.rs | 16 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/handlers.rs | 4 | ||||
-rw-r--r-- | crates/ra_lsp_server/tests/heavy_tests/main.rs | 2 | ||||
-rw-r--r-- | docs/dev/debugging.md | 64 | ||||
-rw-r--r-- | editors/code/src/config.ts | 1 | ||||
-rw-r--r-- | editors/code/src/ctx.ts | 12 | ||||
-rw-r--r-- | editors/code/src/inlay_hints.ts | 49 |
13 files changed, 304 insertions, 165 deletions
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs index bb9a35c5d..94d5b4cfd 100644 --- a/crates/ra_hir/src/source_analyzer.rs +++ b/crates/ra_hir/src/source_analyzer.rs | |||
@@ -15,11 +15,9 @@ use hir_def::{ | |||
15 | }, | 15 | }, |
16 | expr::{ExprId, PatId}, | 16 | expr::{ExprId, PatId}, |
17 | resolver::{self, resolver_for_scope, Resolver, TypeNs, ValueNs}, | 17 | resolver::{self, resolver_for_scope, Resolver, TypeNs, ValueNs}, |
18 | DefWithBodyId, TraitId, | 18 | AsMacroCall, DefWithBodyId, TraitId, |
19 | }; | ||
20 | use hir_expand::{ | ||
21 | hygiene::Hygiene, name::AsName, AstId, HirFileId, InFile, MacroCallId, MacroCallKind, | ||
22 | }; | 19 | }; |
20 | use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile, MacroCallId}; | ||
23 | use hir_ty::{InEnvironment, InferenceResult, TraitEnvironment}; | 21 | use hir_ty::{InEnvironment, InferenceResult, TraitEnvironment}; |
24 | use ra_syntax::{ | 22 | use ra_syntax::{ |
25 | ast::{self, AstNode}, | 23 | ast::{self, AstNode}, |
@@ -363,12 +361,10 @@ impl SourceAnalyzer { | |||
363 | db: &impl HirDatabase, | 361 | db: &impl HirDatabase, |
364 | macro_call: InFile<&ast::MacroCall>, | 362 | macro_call: InFile<&ast::MacroCall>, |
365 | ) -> Option<Expansion> { | 363 | ) -> Option<Expansion> { |
366 | let def = self.resolve_macro_call(db, macro_call)?.id; | 364 | let macro_call_id = macro_call.as_call_id(db, |path| { |
367 | let ast_id = AstId::new( | 365 | self.resolver.resolve_path_as_macro(db, &path).map(|it| it.into()) |
368 | macro_call.file_id, | 366 | })?; |
369 | db.ast_id_map(macro_call.file_id).ast_id(macro_call.value), | 367 | Some(Expansion { macro_call_id }) |
370 | ); | ||
371 | Some(Expansion { macro_call_id: def.as_call_id(db, MacroCallKind::FnLike(ast_id)) }) | ||
372 | } | 368 | } |
373 | } | 369 | } |
374 | 370 | ||
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs index 142c52d35..010d35e55 100644 --- a/crates/ra_hir_def/src/body.rs +++ b/crates/ra_hir_def/src/body.rs | |||
@@ -7,9 +7,7 @@ use std::{mem, ops::Index, sync::Arc}; | |||
7 | 7 | ||
8 | use drop_bomb::DropBomb; | 8 | use drop_bomb::DropBomb; |
9 | use either::Either; | 9 | use either::Either; |
10 | use hir_expand::{ | 10 | use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile, MacroDefId}; |
11 | ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile, MacroCallKind, MacroDefId, | ||
12 | }; | ||
13 | use ra_arena::{map::ArenaMap, Arena}; | 11 | use ra_arena::{map::ArenaMap, Arena}; |
14 | use ra_prof::profile; | 12 | use ra_prof::profile; |
15 | use ra_syntax::{ast, AstNode, AstPtr}; | 13 | use ra_syntax::{ast, AstNode, AstPtr}; |
@@ -23,7 +21,7 @@ use crate::{ | |||
23 | nameres::CrateDefMap, | 21 | nameres::CrateDefMap, |
24 | path::{ModPath, Path}, | 22 | path::{ModPath, Path}, |
25 | src::HasSource, | 23 | src::HasSource, |
26 | DefWithBodyId, HasModule, Lookup, ModuleId, | 24 | AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId, |
27 | }; | 25 | }; |
28 | 26 | ||
29 | pub(crate) struct Expander { | 27 | pub(crate) struct Expander { |
@@ -51,30 +49,26 @@ impl Expander { | |||
51 | db: &DB, | 49 | db: &DB, |
52 | macro_call: ast::MacroCall, | 50 | macro_call: ast::MacroCall, |
53 | ) -> Option<(Mark, T)> { | 51 | ) -> Option<(Mark, T)> { |
54 | let ast_id = AstId::new( | 52 | let macro_call = InFile::new(self.current_file_id, ¯o_call); |
55 | self.current_file_id, | 53 | |
56 | db.ast_id_map(self.current_file_id).ast_id(¯o_call), | 54 | if let Some(call_id) = |
57 | ); | 55 | macro_call.as_call_id(db, |path| self.resolve_path_as_macro(db, &path)) |
58 | 56 | { | |
59 | if let Some(path) = macro_call.path().and_then(|path| self.parse_mod_path(path)) { | 57 | let file_id = call_id.as_file(); |
60 | if let Some(def) = self.resolve_path_as_macro(db, &path) { | 58 | if let Some(node) = db.parse_or_expand(file_id) { |
61 | let call_id = def.as_call_id(db, MacroCallKind::FnLike(ast_id)); | 59 | if let Some(expr) = T::cast(node) { |
62 | let file_id = call_id.as_file(); | 60 | log::debug!("macro expansion {:#?}", expr.syntax()); |
63 | if let Some(node) = db.parse_or_expand(file_id) { | 61 | |
64 | if let Some(expr) = T::cast(node) { | 62 | let mark = Mark { |
65 | log::debug!("macro expansion {:#?}", expr.syntax()); | 63 | file_id: self.current_file_id, |
66 | 64 | ast_id_map: mem::take(&mut self.ast_id_map), | |
67 | let mark = Mark { | 65 | bomb: DropBomb::new("expansion mark dropped"), |
68 | file_id: self.current_file_id, | 66 | }; |
69 | ast_id_map: mem::take(&mut self.ast_id_map), | 67 | self.hygiene = Hygiene::new(db, file_id); |
70 | bomb: DropBomb::new("expansion mark dropped"), | 68 | self.current_file_id = file_id; |
71 | }; | 69 | self.ast_id_map = db.ast_id_map(file_id); |
72 | self.hygiene = Hygiene::new(db, file_id); | 70 | |
73 | self.current_file_id = file_id; | 71 | return Some((mark, expr)); |
74 | self.ast_id_map = db.ast_id_map(file_id); | ||
75 | |||
76 | return Some((mark, expr)); | ||
77 | } | ||
78 | } | 72 | } |
79 | } | 73 | } |
80 | } | 74 | } |
@@ -99,10 +93,6 @@ impl Expander { | |||
99 | Path::from_src(path, &self.hygiene) | 93 | Path::from_src(path, &self.hygiene) |
100 | } | 94 | } |
101 | 95 | ||
102 | fn parse_mod_path(&mut self, path: ast::Path) -> Option<ModPath> { | ||
103 | ModPath::from_src(path, &self.hygiene) | ||
104 | } | ||
105 | |||
106 | fn resolve_path_as_macro(&self, db: &impl DefDatabase, path: &ModPath) -> Option<MacroDefId> { | 96 | fn resolve_path_as_macro(&self, db: &impl DefDatabase, path: &ModPath) -> Option<MacroDefId> { |
107 | self.crate_def_map | 97 | self.crate_def_map |
108 | .resolve_path(db, self.module.local_id, path, BuiltinShadowMode::Other) | 98 | .resolve_path(db, self.module.local_id, path, BuiltinShadowMode::Other) |
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs index feb3a300d..aa0b558b8 100644 --- a/crates/ra_hir_def/src/lib.rs +++ b/crates/ra_hir_def/src/lib.rs | |||
@@ -46,7 +46,10 @@ mod marks; | |||
46 | 46 | ||
47 | use std::hash::Hash; | 47 | use std::hash::Hash; |
48 | 48 | ||
49 | use hir_expand::{ast_id_map::FileAstId, AstId, HirFileId, InFile, MacroDefId}; | 49 | use hir_expand::{ |
50 | ast_id_map::FileAstId, db::AstDatabase, hygiene::Hygiene, AstId, HirFileId, InFile, | ||
51 | MacroCallId, MacroCallKind, MacroDefId, | ||
52 | }; | ||
50 | use ra_arena::{impl_arena_id, RawId}; | 53 | use ra_arena::{impl_arena_id, RawId}; |
51 | use ra_db::{impl_intern_key, salsa, CrateId}; | 54 | use ra_db::{impl_intern_key, salsa, CrateId}; |
52 | use ra_syntax::{ast, AstNode}; | 55 | use ra_syntax::{ast, AstNode}; |
@@ -413,3 +416,61 @@ impl HasModule for StaticLoc { | |||
413 | self.container.module(db) | 416 | self.container.module(db) |
414 | } | 417 | } |
415 | } | 418 | } |
419 | |||
420 | /// A helper trait for converting to MacroCallId | ||
421 | pub trait AsMacroCall { | ||
422 | fn as_call_id( | ||
423 | &self, | ||
424 | db: &(impl db::DefDatabase + AstDatabase), | ||
425 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | ||
426 | ) -> Option<MacroCallId>; | ||
427 | } | ||
428 | |||
429 | impl AsMacroCall for InFile<&ast::MacroCall> { | ||
430 | fn as_call_id( | ||
431 | &self, | ||
432 | db: &(impl db::DefDatabase + AstDatabase), | ||
433 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | ||
434 | ) -> Option<MacroCallId> { | ||
435 | let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value)); | ||
436 | let h = Hygiene::new(db, self.file_id); | ||
437 | let path = path::ModPath::from_src(self.value.path()?, &h)?; | ||
438 | |||
439 | AstIdWithPath::new(ast_id.file_id, ast_id.value, path).as_call_id(db, resolver) | ||
440 | } | ||
441 | } | ||
442 | |||
443 | /// Helper wrapper for `AstId` with `ModPath` | ||
444 | #[derive(Clone, Debug, Eq, PartialEq)] | ||
445 | struct AstIdWithPath<T: ast::AstNode> { | ||
446 | pub ast_id: AstId<T>, | ||
447 | pub path: path::ModPath, | ||
448 | } | ||
449 | |||
450 | impl<T: ast::AstNode> AstIdWithPath<T> { | ||
451 | pub fn new(file_id: HirFileId, ast_id: FileAstId<T>, path: path::ModPath) -> AstIdWithPath<T> { | ||
452 | AstIdWithPath { ast_id: AstId::new(file_id, ast_id), path } | ||
453 | } | ||
454 | } | ||
455 | |||
456 | impl AsMacroCall for AstIdWithPath<ast::MacroCall> { | ||
457 | fn as_call_id( | ||
458 | &self, | ||
459 | db: &impl AstDatabase, | ||
460 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | ||
461 | ) -> Option<MacroCallId> { | ||
462 | let def = resolver(self.path.clone())?; | ||
463 | Some(def.as_call_id(db, MacroCallKind::FnLike(self.ast_id.clone()))) | ||
464 | } | ||
465 | } | ||
466 | |||
467 | impl AsMacroCall for AstIdWithPath<ast::ModuleItem> { | ||
468 | fn as_call_id( | ||
469 | &self, | ||
470 | db: &impl AstDatabase, | ||
471 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | ||
472 | ) -> Option<MacroCallId> { | ||
473 | let def = resolver(self.path.clone())?; | ||
474 | Some(def.as_call_id(db, MacroCallKind::Attr(self.ast_id.clone()))) | ||
475 | } | ||
476 | } | ||
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs index b1f3f525d..51c65a5d7 100644 --- a/crates/ra_hir_def/src/nameres/collector.rs +++ b/crates/ra_hir_def/src/nameres/collector.rs | |||
@@ -7,7 +7,7 @@ use hir_expand::{ | |||
7 | builtin_derive::find_builtin_derive, | 7 | builtin_derive::find_builtin_derive, |
8 | builtin_macro::find_builtin_macro, | 8 | builtin_macro::find_builtin_macro, |
9 | name::{name, AsName, Name}, | 9 | name::{name, AsName, Name}, |
10 | HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, | 10 | HirFileId, MacroCallId, MacroDefId, MacroDefKind, |
11 | }; | 11 | }; |
12 | use ra_cfg::CfgOptions; | 12 | use ra_cfg::CfgOptions; |
13 | use ra_db::{CrateId, FileId}; | 13 | use ra_db::{CrateId, FileId}; |
@@ -25,8 +25,9 @@ use crate::{ | |||
25 | path::{ImportAlias, ModPath, PathKind}, | 25 | path::{ImportAlias, ModPath, PathKind}, |
26 | per_ns::PerNs, | 26 | per_ns::PerNs, |
27 | visibility::Visibility, | 27 | visibility::Visibility, |
28 | AdtId, AstId, ConstLoc, ContainerId, EnumLoc, EnumVariantId, FunctionLoc, ImplLoc, Intern, | 28 | AdtId, AsMacroCall, AstId, AstIdWithPath, ConstLoc, ContainerId, EnumLoc, EnumVariantId, |
29 | LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, | 29 | FunctionLoc, ImplLoc, Intern, LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc, |
30 | TraitLoc, TypeAliasLoc, UnionLoc, | ||
30 | }; | 31 | }; |
31 | 32 | ||
32 | pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> CrateDefMap { | 33 | pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> CrateDefMap { |
@@ -99,11 +100,16 @@ struct ImportDirective { | |||
99 | #[derive(Clone, Debug, Eq, PartialEq)] | 100 | #[derive(Clone, Debug, Eq, PartialEq)] |
100 | struct MacroDirective { | 101 | struct MacroDirective { |
101 | module_id: LocalModuleId, | 102 | module_id: LocalModuleId, |
102 | ast_id: AstId<ast::MacroCall>, | 103 | ast_id: AstIdWithPath<ast::MacroCall>, |
103 | path: ModPath, | ||
104 | legacy: Option<MacroCallId>, | 104 | legacy: Option<MacroCallId>, |
105 | } | 105 | } |
106 | 106 | ||
107 | #[derive(Clone, Debug, Eq, PartialEq)] | ||
108 | struct DeriveDirective { | ||
109 | module_id: LocalModuleId, | ||
110 | ast_id: AstIdWithPath<ast::ModuleItem>, | ||
111 | } | ||
112 | |||
107 | /// Walks the tree of module recursively | 113 | /// Walks the tree of module recursively |
108 | struct DefCollector<'a, DB> { | 114 | struct DefCollector<'a, DB> { |
109 | db: &'a DB, | 115 | db: &'a DB, |
@@ -112,7 +118,7 @@ struct DefCollector<'a, DB> { | |||
112 | unresolved_imports: Vec<ImportDirective>, | 118 | unresolved_imports: Vec<ImportDirective>, |
113 | resolved_imports: Vec<ImportDirective>, | 119 | resolved_imports: Vec<ImportDirective>, |
114 | unexpanded_macros: Vec<MacroDirective>, | 120 | unexpanded_macros: Vec<MacroDirective>, |
115 | unexpanded_attribute_macros: Vec<(LocalModuleId, AstId<ast::ModuleItem>, ModPath)>, | 121 | unexpanded_attribute_macros: Vec<DeriveDirective>, |
116 | mod_dirs: FxHashMap<LocalModuleId, ModDir>, | 122 | mod_dirs: FxHashMap<LocalModuleId, ModDir>, |
117 | cfg_options: &'a CfgOptions, | 123 | cfg_options: &'a CfgOptions, |
118 | } | 124 | } |
@@ -515,16 +521,16 @@ where | |||
515 | return false; | 521 | return false; |
516 | } | 522 | } |
517 | 523 | ||
518 | let resolved_res = self.def_map.resolve_path_fp_with_macro( | 524 | if let Some(call_id) = directive.ast_id.as_call_id(self.db, |path| { |
519 | self.db, | 525 | let resolved_res = self.def_map.resolve_path_fp_with_macro( |
520 | ResolveMode::Other, | 526 | self.db, |
521 | directive.module_id, | 527 | ResolveMode::Other, |
522 | &directive.path, | 528 | directive.module_id, |
523 | BuiltinShadowMode::Module, | 529 | &path, |
524 | ); | 530 | BuiltinShadowMode::Module, |
525 | 531 | ); | |
526 | if let Some(def) = resolved_res.resolved_def.take_macros() { | 532 | resolved_res.resolved_def.take_macros() |
527 | let call_id = def.as_call_id(self.db, MacroCallKind::FnLike(directive.ast_id)); | 533 | }) { |
528 | resolved.push((directive.module_id, call_id)); | 534 | resolved.push((directive.module_id, call_id)); |
529 | res = ReachedFixedPoint::No; | 535 | res = ReachedFixedPoint::No; |
530 | return false; | 536 | return false; |
@@ -532,12 +538,11 @@ where | |||
532 | 538 | ||
533 | true | 539 | true |
534 | }); | 540 | }); |
535 | attribute_macros.retain(|(module_id, ast_id, path)| { | 541 | attribute_macros.retain(|directive| { |
536 | let resolved_res = self.resolve_attribute_macro(path); | 542 | if let Some(call_id) = |
537 | 543 | directive.ast_id.as_call_id(self.db, |path| self.resolve_attribute_macro(&path)) | |
538 | if let Some(def) = resolved_res { | 544 | { |
539 | let call_id = def.as_call_id(self.db, MacroCallKind::Attr(*ast_id)); | 545 | resolved.push((directive.module_id, call_id)); |
540 | resolved.push((*module_id, call_id)); | ||
541 | res = ReachedFixedPoint::No; | 546 | res = ReachedFixedPoint::No; |
542 | return false; | 547 | return false; |
543 | } | 548 | } |
@@ -833,20 +838,22 @@ where | |||
833 | }; | 838 | }; |
834 | let path = ModPath::from_tt_ident(ident); | 839 | let path = ModPath::from_tt_ident(ident); |
835 | 840 | ||
836 | let ast_id = AstId::new(self.file_id, def.kind.ast_id()); | 841 | let ast_id = AstIdWithPath::new(self.file_id, def.kind.ast_id(), path); |
837 | self.def_collector.unexpanded_attribute_macros.push((self.module_id, ast_id, path)); | 842 | self.def_collector |
843 | .unexpanded_attribute_macros | ||
844 | .push(DeriveDirective { module_id: self.module_id, ast_id }); | ||
838 | } | 845 | } |
839 | } | 846 | } |
840 | } | 847 | } |
841 | 848 | ||
842 | fn collect_macro(&mut self, mac: &raw::MacroData) { | 849 | fn collect_macro(&mut self, mac: &raw::MacroData) { |
843 | let ast_id = AstId::new(self.file_id, mac.ast_id); | 850 | let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone()); |
844 | 851 | ||
845 | // Case 0: builtin macros | 852 | // Case 0: builtin macros |
846 | if mac.builtin { | 853 | if mac.builtin { |
847 | if let Some(name) = &mac.name { | 854 | if let Some(name) = &mac.name { |
848 | let krate = self.def_collector.def_map.krate; | 855 | let krate = self.def_collector.def_map.krate; |
849 | if let Some(macro_id) = find_builtin_macro(name, krate, ast_id) { | 856 | if let Some(macro_id) = find_builtin_macro(name, krate, ast_id.ast_id) { |
850 | self.def_collector.define_macro( | 857 | self.def_collector.define_macro( |
851 | self.module_id, | 858 | self.module_id, |
852 | name.clone(), | 859 | name.clone(), |
@@ -862,7 +869,7 @@ where | |||
862 | if is_macro_rules(&mac.path) { | 869 | if is_macro_rules(&mac.path) { |
863 | if let Some(name) = &mac.name { | 870 | if let Some(name) = &mac.name { |
864 | let macro_id = MacroDefId { | 871 | let macro_id = MacroDefId { |
865 | ast_id: Some(ast_id), | 872 | ast_id: Some(ast_id.ast_id), |
866 | krate: Some(self.def_collector.def_map.krate), | 873 | krate: Some(self.def_collector.def_map.krate), |
867 | kind: MacroDefKind::Declarative, | 874 | kind: MacroDefKind::Declarative, |
868 | }; | 875 | }; |
@@ -872,15 +879,13 @@ where | |||
872 | } | 879 | } |
873 | 880 | ||
874 | // Case 2: try to resolve in legacy scope and expand macro_rules | 881 | // Case 2: try to resolve in legacy scope and expand macro_rules |
875 | if let Some(macro_def) = mac.path.as_ident().and_then(|name| { | 882 | if let Some(macro_call_id) = ast_id.as_call_id(self.def_collector.db, |path| { |
876 | self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) | 883 | path.as_ident().and_then(|name| { |
884 | self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) | ||
885 | }) | ||
877 | }) { | 886 | }) { |
878 | let macro_call_id = | ||
879 | macro_def.as_call_id(self.def_collector.db, MacroCallKind::FnLike(ast_id)); | ||
880 | |||
881 | self.def_collector.unexpanded_macros.push(MacroDirective { | 887 | self.def_collector.unexpanded_macros.push(MacroDirective { |
882 | module_id: self.module_id, | 888 | module_id: self.module_id, |
883 | path: mac.path.clone(), | ||
884 | ast_id, | 889 | ast_id, |
885 | legacy: Some(macro_call_id), | 890 | legacy: Some(macro_call_id), |
886 | }); | 891 | }); |
@@ -890,14 +895,12 @@ where | |||
890 | 895 | ||
891 | // Case 3: resolve in module scope, expand during name resolution. | 896 | // Case 3: resolve in module scope, expand during name resolution. |
892 | // We rewrite simple path `macro_name` to `self::macro_name` to force resolve in module scope only. | 897 | // We rewrite simple path `macro_name` to `self::macro_name` to force resolve in module scope only. |
893 | let mut path = mac.path.clone(); | 898 | if ast_id.path.is_ident() { |
894 | if path.is_ident() { | 899 | ast_id.path.kind = PathKind::Super(0); |
895 | path.kind = PathKind::Super(0); | ||
896 | } | 900 | } |
897 | 901 | ||
898 | self.def_collector.unexpanded_macros.push(MacroDirective { | 902 | self.def_collector.unexpanded_macros.push(MacroDirective { |
899 | module_id: self.module_id, | 903 | module_id: self.module_id, |
900 | path, | ||
901 | ast_id, | 904 | ast_id, |
902 | legacy: None, | 905 | legacy: None, |
903 | }); | 906 | }); |
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 689921f3f..9d66c365b 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs | |||
@@ -71,7 +71,7 @@ pub use crate::{ | |||
71 | references::{ | 71 | references::{ |
72 | Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult, SearchScope, | 72 | Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult, SearchScope, |
73 | }, | 73 | }, |
74 | runnables::{Runnable, RunnableKind}, | 74 | runnables::{Runnable, RunnableKind, TestId}, |
75 | source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, | 75 | source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, |
76 | syntax_highlighting::HighlightedRange, | 76 | syntax_highlighting::HighlightedRange, |
77 | }; | 77 | }; |
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index b6b0c70f9..be2a67d0a 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::InFile; | 3 | use hir::{InFile, SourceBinder}; |
4 | use itertools::Itertools; | 4 | use itertools::Itertools; |
5 | use ra_db::SourceDatabase; | 5 | use ra_db::SourceDatabase; |
6 | use ra_ide_db::RootDatabase; | 6 | use ra_ide_db::RootDatabase; |
@@ -10,6 +10,7 @@ use ra_syntax::{ | |||
10 | }; | 10 | }; |
11 | 11 | ||
12 | use crate::FileId; | 12 | use crate::FileId; |
13 | use std::fmt::Display; | ||
13 | 14 | ||
14 | #[derive(Debug)] | 15 | #[derive(Debug)] |
15 | pub struct Runnable { | 16 | pub struct Runnable { |
@@ -18,38 +19,84 @@ pub struct Runnable { | |||
18 | } | 19 | } |
19 | 20 | ||
20 | #[derive(Debug)] | 21 | #[derive(Debug)] |
22 | pub enum TestId { | ||
23 | Name(String), | ||
24 | Path(String), | ||
25 | } | ||
26 | |||
27 | impl Display for TestId { | ||
28 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | ||
29 | match self { | ||
30 | TestId::Name(name) => write!(f, "{}", name), | ||
31 | TestId::Path(path) => write!(f, "{}", path), | ||
32 | } | ||
33 | } | ||
34 | } | ||
35 | |||
36 | #[derive(Debug)] | ||
21 | pub enum RunnableKind { | 37 | pub enum RunnableKind { |
22 | Test { name: String }, | 38 | Test { test_id: TestId }, |
23 | TestMod { path: String }, | 39 | TestMod { path: String }, |
24 | Bench { name: String }, | 40 | Bench { test_id: TestId }, |
25 | Bin, | 41 | Bin, |
26 | } | 42 | } |
27 | 43 | ||
28 | pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { | 44 | pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { |
29 | let parse = db.parse(file_id); | 45 | let parse = db.parse(file_id); |
30 | parse.tree().syntax().descendants().filter_map(|i| runnable(db, file_id, i)).collect() | 46 | let mut sb = SourceBinder::new(db); |
47 | parse.tree().syntax().descendants().filter_map(|i| runnable(db, &mut sb, file_id, i)).collect() | ||
31 | } | 48 | } |
32 | 49 | ||
33 | fn runnable(db: &RootDatabase, file_id: FileId, item: SyntaxNode) -> Option<Runnable> { | 50 | fn runnable( |
51 | db: &RootDatabase, | ||
52 | source_binder: &mut SourceBinder<RootDatabase>, | ||
53 | file_id: FileId, | ||
54 | item: SyntaxNode, | ||
55 | ) -> Option<Runnable> { | ||
34 | match_ast! { | 56 | match_ast! { |
35 | match item { | 57 | match item { |
36 | ast::FnDef(it) => { runnable_fn(it) }, | 58 | ast::FnDef(it) => { runnable_fn(db, source_binder, file_id, it) }, |
37 | ast::Module(it) => { runnable_mod(db, file_id, it) }, | 59 | ast::Module(it) => { runnable_mod(db, source_binder, file_id, it) }, |
38 | _ => { None }, | 60 | _ => { None }, |
39 | } | 61 | } |
40 | } | 62 | } |
41 | } | 63 | } |
42 | 64 | ||
43 | fn runnable_fn(fn_def: ast::FnDef) -> Option<Runnable> { | 65 | fn runnable_fn( |
44 | let name = fn_def.name()?.text().clone(); | 66 | db: &RootDatabase, |
45 | let kind = if name == "main" { | 67 | source_binder: &mut SourceBinder<RootDatabase>, |
68 | file_id: FileId, | ||
69 | fn_def: ast::FnDef, | ||
70 | ) -> Option<Runnable> { | ||
71 | let name_string = fn_def.name()?.text().to_string(); | ||
72 | |||
73 | let kind = if name_string == "main" { | ||
46 | RunnableKind::Bin | 74 | RunnableKind::Bin |
47 | } else if has_test_related_attribute(&fn_def) { | ||
48 | RunnableKind::Test { name: name.to_string() } | ||
49 | } else if fn_def.has_atom_attr("bench") { | ||
50 | RunnableKind::Bench { name: name.to_string() } | ||
51 | } else { | 75 | } else { |
52 | return None; | 76 | let test_id = if let Some(module) = source_binder |
77 | .to_def(InFile::new(file_id.into(), fn_def.clone())) | ||
78 | .map(|def| def.module(db)) | ||
79 | { | ||
80 | let path = module | ||
81 | .path_to_root(db) | ||
82 | .into_iter() | ||
83 | .rev() | ||
84 | .filter_map(|it| it.name(db)) | ||
85 | .map(|name| name.to_string()) | ||
86 | .chain(std::iter::once(name_string)) | ||
87 | .join("::"); | ||
88 | TestId::Path(path) | ||
89 | } else { | ||
90 | TestId::Name(name_string) | ||
91 | }; | ||
92 | |||
93 | if has_test_related_attribute(&fn_def) { | ||
94 | RunnableKind::Test { test_id } | ||
95 | } else if fn_def.has_atom_attr("bench") { | ||
96 | RunnableKind::Bench { test_id } | ||
97 | } else { | ||
98 | return None; | ||
99 | } | ||
53 | }; | 100 | }; |
54 | Some(Runnable { range: fn_def.syntax().text_range(), kind }) | 101 | Some(Runnable { range: fn_def.syntax().text_range(), kind }) |
55 | } | 102 | } |
@@ -68,7 +115,12 @@ fn has_test_related_attribute(fn_def: &ast::FnDef) -> bool { | |||
68 | .any(|attribute_text| attribute_text.contains("test")) | 115 | .any(|attribute_text| attribute_text.contains("test")) |
69 | } | 116 | } |
70 | 117 | ||
71 | fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Option<Runnable> { | 118 | fn runnable_mod( |
119 | db: &RootDatabase, | ||
120 | source_binder: &mut SourceBinder<RootDatabase>, | ||
121 | file_id: FileId, | ||
122 | module: ast::Module, | ||
123 | ) -> Option<Runnable> { | ||
72 | let has_test_function = module | 124 | let has_test_function = module |
73 | .item_list()? | 125 | .item_list()? |
74 | .items() | 126 | .items() |
@@ -76,13 +128,12 @@ fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Opti | |||
76 | ast::ModuleItem::FnDef(it) => Some(it), | 128 | ast::ModuleItem::FnDef(it) => Some(it), |
77 | _ => None, | 129 | _ => None, |
78 | }) | 130 | }) |
79 | .any(|f| f.has_atom_attr("test")); | 131 | .any(|f| has_test_related_attribute(&f)); |
80 | if !has_test_function { | 132 | if !has_test_function { |
81 | return None; | 133 | return None; |
82 | } | 134 | } |
83 | let range = module.syntax().text_range(); | 135 | let range = module.syntax().text_range(); |
84 | let mut sb = hir::SourceBinder::new(db); | 136 | let module = source_binder.to_def(InFile::new(file_id.into(), module))?; |
85 | let module = sb.to_def(InFile::new(file_id.into(), module))?; | ||
86 | 137 | ||
87 | let path = module.path_to_root(db).into_iter().rev().filter_map(|it| it.name(db)).join("::"); | 138 | let path = module.path_to_root(db).into_iter().rev().filter_map(|it| it.name(db)).join("::"); |
88 | Some(Runnable { range, kind: RunnableKind::TestMod { path } }) | 139 | Some(Runnable { range, kind: RunnableKind::TestMod { path } }) |
@@ -121,13 +172,17 @@ mod tests { | |||
121 | Runnable { | 172 | Runnable { |
122 | range: [22; 46), | 173 | range: [22; 46), |
123 | kind: Test { | 174 | kind: Test { |
124 | name: "test_foo", | 175 | test_id: Path( |
176 | "test_foo", | ||
177 | ), | ||
125 | }, | 178 | }, |
126 | }, | 179 | }, |
127 | Runnable { | 180 | Runnable { |
128 | range: [47; 81), | 181 | range: [47; 81), |
129 | kind: Test { | 182 | kind: Test { |
130 | name: "test_foo", | 183 | test_id: Path( |
184 | "test_foo", | ||
185 | ), | ||
131 | }, | 186 | }, |
132 | }, | 187 | }, |
133 | ] | 188 | ] |
@@ -160,7 +215,9 @@ mod tests { | |||
160 | Runnable { | 215 | Runnable { |
161 | range: [28; 57), | 216 | range: [28; 57), |
162 | kind: Test { | 217 | kind: Test { |
163 | name: "test_foo1", | 218 | test_id: Path( |
219 | "test_mod::test_foo1", | ||
220 | ), | ||
164 | }, | 221 | }, |
165 | }, | 222 | }, |
166 | ] | 223 | ] |
@@ -195,7 +252,9 @@ mod tests { | |||
195 | Runnable { | 252 | Runnable { |
196 | range: [46; 79), | 253 | range: [46; 79), |
197 | kind: Test { | 254 | kind: Test { |
198 | name: "test_foo1", | 255 | test_id: Path( |
256 | "foo::test_mod::test_foo1", | ||
257 | ), | ||
199 | }, | 258 | }, |
200 | }, | 259 | }, |
201 | ] | 260 | ] |
@@ -232,7 +291,9 @@ mod tests { | |||
232 | Runnable { | 291 | Runnable { |
233 | range: [68; 105), | 292 | range: [68; 105), |
234 | kind: Test { | 293 | kind: Test { |
235 | name: "test_foo1", | 294 | test_id: Path( |
295 | "foo::bar::test_mod::test_foo1", | ||
296 | ), | ||
236 | }, | 297 | }, |
237 | }, | 298 | }, |
238 | ] | 299 | ] |
diff --git a/crates/ra_lsp_server/src/cargo_target_spec.rs b/crates/ra_lsp_server/src/cargo_target_spec.rs index 594caffe2..5fd1e7b6b 100644 --- a/crates/ra_lsp_server/src/cargo_target_spec.rs +++ b/crates/ra_lsp_server/src/cargo_target_spec.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use ra_ide::{FileId, RunnableKind}; | 3 | use ra_ide::{FileId, RunnableKind, TestId}; |
4 | use ra_project_model::{self, ProjectWorkspace, TargetKind}; | 4 | use ra_project_model::{self, ProjectWorkspace, TargetKind}; |
5 | 5 | ||
6 | use crate::{world::WorldSnapshot, Result}; | 6 | use crate::{world::WorldSnapshot, Result}; |
@@ -13,13 +13,16 @@ pub(crate) fn runnable_args( | |||
13 | let spec = CargoTargetSpec::for_file(world, file_id)?; | 13 | let spec = CargoTargetSpec::for_file(world, file_id)?; |
14 | let mut res = Vec::new(); | 14 | let mut res = Vec::new(); |
15 | match kind { | 15 | match kind { |
16 | RunnableKind::Test { name } => { | 16 | RunnableKind::Test { test_id } => { |
17 | res.push("test".to_string()); | 17 | res.push("test".to_string()); |
18 | if let Some(spec) = spec { | 18 | if let Some(spec) = spec { |
19 | spec.push_to(&mut res); | 19 | spec.push_to(&mut res); |
20 | } | 20 | } |
21 | res.push("--".to_string()); | 21 | res.push("--".to_string()); |
22 | res.push(name.to_string()); | 22 | res.push(test_id.to_string()); |
23 | if let TestId::Path(_) = test_id { | ||
24 | res.push("--exact".to_string()); | ||
25 | } | ||
23 | res.push("--nocapture".to_string()); | 26 | res.push("--nocapture".to_string()); |
24 | } | 27 | } |
25 | RunnableKind::TestMod { path } => { | 28 | RunnableKind::TestMod { path } => { |
@@ -31,13 +34,16 @@ pub(crate) fn runnable_args( | |||
31 | res.push(path.to_string()); | 34 | res.push(path.to_string()); |
32 | res.push("--nocapture".to_string()); | 35 | res.push("--nocapture".to_string()); |
33 | } | 36 | } |
34 | RunnableKind::Bench { name } => { | 37 | RunnableKind::Bench { test_id } => { |
35 | res.push("bench".to_string()); | 38 | res.push("bench".to_string()); |
36 | if let Some(spec) = spec { | 39 | if let Some(spec) = spec { |
37 | spec.push_to(&mut res); | 40 | spec.push_to(&mut res); |
38 | } | 41 | } |
39 | res.push("--".to_string()); | 42 | res.push("--".to_string()); |
40 | res.push(name.to_string()); | 43 | res.push(test_id.to_string()); |
44 | if let TestId::Path(_) = test_id { | ||
45 | res.push("--exact".to_string()); | ||
46 | } | ||
41 | res.push("--nocapture".to_string()); | 47 | res.push("--nocapture".to_string()); |
42 | } | 48 | } |
43 | RunnableKind::Bin => { | 49 | RunnableKind::Bin => { |
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 1da2d1814..3893430c0 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -919,9 +919,9 @@ fn to_lsp_runnable( | |||
919 | let args = runnable_args(world, file_id, &runnable.kind)?; | 919 | let args = runnable_args(world, file_id, &runnable.kind)?; |
920 | let line_index = world.analysis().file_line_index(file_id)?; | 920 | let line_index = world.analysis().file_line_index(file_id)?; |
921 | let label = match &runnable.kind { | 921 | let label = match &runnable.kind { |
922 | RunnableKind::Test { name } => format!("test {}", name), | 922 | RunnableKind::Test { test_id } => format!("test {}", test_id), |
923 | RunnableKind::TestMod { path } => format!("test-mod {}", path), | 923 | RunnableKind::TestMod { path } => format!("test-mod {}", path), |
924 | RunnableKind::Bench { name } => format!("bench {}", name), | 924 | RunnableKind::Bench { test_id } => format!("bench {}", test_id), |
925 | RunnableKind::Bin => "run binary".to_string(), | 925 | RunnableKind::Bin => "run binary".to_string(), |
926 | }; | 926 | }; |
927 | Ok(req::Runnable { | 927 | Ok(req::Runnable { |
diff --git a/crates/ra_lsp_server/tests/heavy_tests/main.rs b/crates/ra_lsp_server/tests/heavy_tests/main.rs index dff63a12d..9ca31cbcc 100644 --- a/crates/ra_lsp_server/tests/heavy_tests/main.rs +++ b/crates/ra_lsp_server/tests/heavy_tests/main.rs | |||
@@ -147,7 +147,7 @@ fn main() {} | |||
147 | }, | 147 | }, |
148 | json!([ | 148 | json!([ |
149 | { | 149 | { |
150 | "args": [ "test", "--package", "foo", "--test", "spam", "--", "test_eggs", "--nocapture" ], | 150 | "args": [ "test", "--package", "foo", "--test", "spam", "--", "test_eggs", "--exact", "--nocapture" ], |
151 | "bin": "cargo", | 151 | "bin": "cargo", |
152 | "env": { "RUST_BACKTRACE": "short" }, | 152 | "env": { "RUST_BACKTRACE": "short" }, |
153 | "label": "test test_eggs", | 153 | "label": "test test_eggs", |
diff --git a/docs/dev/debugging.md b/docs/dev/debugging.md index 1ccf4dca2..e6b082156 100644 --- a/docs/dev/debugging.md +++ b/docs/dev/debugging.md | |||
@@ -1,44 +1,66 @@ | |||
1 | # Debugging vs Code plugin and the Language Server | 1 | # Debugging VSCode plugin and the language server |
2 | 2 | ||
3 | **NOTE:** the information here is mostly obsolete | 3 | ## Prerequisites |
4 | 4 | ||
5 | Install [LLDB](https://lldb.llvm.org/) and the [LLDB Extension](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb). | 5 | - Install [LLDB](https://lldb.llvm.org/) and the [LLDB Extension](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb). |
6 | - Open the root folder in VSCode. Here you can access the preconfigured debug setups. | ||
6 | 7 | ||
7 | Checkout rust rust-analyzer and open it in vscode. | 8 | <img height=150px src="https://user-images.githubusercontent.com/36276403/74611090-92ec5380-5101-11ea-8a41-598f51f3f3e3.png" alt="Debug options view"> |
9 | |||
10 | - Install all TypeScript dependencies | ||
11 | ```bash | ||
12 | cd editors/code | ||
13 | npm install | ||
14 | ``` | ||
15 | |||
16 | ## Common knowledge | ||
17 | |||
18 | * All debug configurations open a new `[Extension Development Host]` VSCode instance | ||
19 | where **only** the `rust-analyzer` extension being debugged is enabled. | ||
20 | * To activate the extension you need to open any Rust project folder in `[Extension Development Host]`. | ||
8 | 21 | ||
9 | ``` | ||
10 | $ git clone https://github.com/rust-analyzer/rust-analyzer.git --depth 1 | ||
11 | $ cd rust-analyzer | ||
12 | $ code . | ||
13 | ``` | ||
14 | 22 | ||
15 | - To attach to the `lsp server` in linux you'll have to run: | 23 | ## Debug TypeScript VSCode extension |
16 | 24 | ||
17 | `echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope` | 25 | - `Run Extension` - runs the extension with the globally installed `ra_lsp_server` binary. |
26 | - `Run Extension (Dev Server)` - runs extension with the locally built LSP server (`target/debug/ra_lsp_server`). | ||
27 | |||
28 | TypeScript debugging is configured to watch your source edits and recompile. | ||
29 | To apply changes to an already running debug process press <kbd>Ctrl+Shift+P</kbd> and run the following command in your `[Extension Development Host]` | ||
30 | |||
31 | ``` | ||
32 | > Developer: Reload Window | ||
33 | ``` | ||
18 | 34 | ||
19 | This enables ptrace on non forked processes | 35 | ## Debug Rust LSP server |
20 | 36 | ||
21 | - Ensure the dependencies for the extension are installed, run the `npm: install - editors/code` task in vscode. | 37 | - When attaching a debugger to an already running `rust-analyzer` server on Linux you might need to enable `ptrace` for unrelated processes by running: |
22 | 38 | ||
23 | - Launch the `Debug Extension`, this will build the extension and the `lsp server`. | 39 | ``` |
40 | echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope | ||
41 | ``` | ||
24 | 42 | ||
25 | - A new instance of vscode with `[Extension Development Host]` in the title. | ||
26 | 43 | ||
27 | Don't worry about disabling `rls` all other extensions will be disabled but this one. | 44 | - By default, the LSP server is built without debug information. To enable it, you'll need to change `Cargo.toml`: |
45 | ```toml | ||
46 | [profile.dev] | ||
47 | debug = 2 | ||
48 | ``` | ||
28 | 49 | ||
29 | - In the new vscode instance open a rust project, and navigate to a rust file | 50 | - Select `Run Extension (Dev Server)` to run your locally built `target/debug/ra_lsp_server`. |
30 | 51 | ||
31 | - In the original vscode start an additional debug session (the three periods in the launch) and select `Debug Lsp Server`. | 52 | - In the original VSCode window once again select the `Attach To Server` debug configuration. |
32 | 53 | ||
33 | - A list of running processes should appear select the `ra_lsp_server` from this repo. | 54 | - A list of running processes should appear. Select the `ra_lsp_server` from this repo. |
34 | 55 | ||
35 | - Navigate to `crates/ra_lsp_server/src/main_loop.rs` and add a breakpoint to the `on_task` function. | 56 | - Navigate to `crates/ra_lsp_server/src/main_loop.rs` and add a breakpoint to the `on_task` function. |
36 | 57 | ||
37 | - Go back to the `[Extension Development Host]` instance and hover over a rust variable and your breakpoint should hit. | 58 | - Go back to the `[Extension Development Host]` instance and hover over a Rust variable and your breakpoint should hit. |
38 | 59 | ||
39 | ## Demo | 60 | ## Demo |
40 | 61 | ||
41 | ![demonstration of debugging](https://user-images.githubusercontent.com/1711539/51384036-254fab80-1b2c-11e9-824d-95f9a6e9cf4f.gif) | 62 | - [Debugging TypeScript VScode extension](https://www.youtube.com/watch?v=T-hvpK6s4wM). |
63 | - [Debugging Rust LSP server](https://www.youtube.com/watch?v=EaNb5rg4E0M). | ||
42 | 64 | ||
43 | ## Troubleshooting | 65 | ## Troubleshooting |
44 | 66 | ||
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 5fffd1fa7..c3fa788c7 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts | |||
@@ -44,7 +44,6 @@ export class Config { | |||
44 | this.refreshConfig(); | 44 | this.refreshConfig(); |
45 | } | 45 | } |
46 | 46 | ||
47 | |||
48 | private refreshConfig() { | 47 | private refreshConfig() { |
49 | this.cfg = vscode.workspace.getConfiguration(Config.rootSection); | 48 | this.cfg = vscode.workspace.getConfiguration(Config.rootSection); |
50 | console.log("Using configuration:", this.cfg); | 49 | console.log("Using configuration:", this.cfg); |
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index 9fcf2ec38..ff6245f78 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts | |||
@@ -91,15 +91,11 @@ export async function sendRequestWithRetry<R>( | |||
91 | for (const delay of [2, 4, 6, 8, 10, null]) { | 91 | for (const delay of [2, 4, 6, 8, 10, null]) { |
92 | try { | 92 | try { |
93 | return await (token ? client.sendRequest(method, param, token) : client.sendRequest(method, param)); | 93 | return await (token ? client.sendRequest(method, param, token) : client.sendRequest(method, param)); |
94 | } catch (e) { | 94 | } catch (err) { |
95 | if ( | 95 | if (delay === null || err.code !== lc.ErrorCodes.ContentModified) { |
96 | e.code === lc.ErrorCodes.ContentModified && | 96 | throw err; |
97 | delay !== null | ||
98 | ) { | ||
99 | await sleep(10 * (1 << delay)); | ||
100 | continue; | ||
101 | } | 97 | } |
102 | throw e; | 98 | await sleep(10 * (1 << delay)); |
103 | } | 99 | } |
104 | } | 100 | } |
105 | throw 'unreachable'; | 101 | throw 'unreachable'; |
diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts index c317a9213..3896878cd 100644 --- a/editors/code/src/inlay_hints.ts +++ b/editors/code/src/inlay_hints.ts | |||
@@ -27,7 +27,9 @@ export function activateInlayHints(ctx: Ctx) { | |||
27 | ctx.subscriptions | 27 | ctx.subscriptions |
28 | ); | 28 | ); |
29 | 29 | ||
30 | ctx.onDidRestart(_ => hintsUpdater.setEnabled(ctx.config.displayInlayHints)); | 30 | // We pass async function though it will not be awaited when called, |
31 | // thus Promise rejections won't be handled, but this should never throw in fact... | ||
32 | ctx.onDidRestart(async _ => hintsUpdater.setEnabled(ctx.config.displayInlayHints)); | ||
31 | } | 33 | } |
32 | 34 | ||
33 | interface InlayHintsParams { | 35 | interface InlayHintsParams { |
@@ -36,7 +38,7 @@ interface InlayHintsParams { | |||
36 | 38 | ||
37 | interface InlayHint { | 39 | interface InlayHint { |
38 | range: vscode.Range; | 40 | range: vscode.Range; |
39 | kind: string; | 41 | kind: "TypeHint" | "ParameterHint"; |
40 | label: string; | 42 | label: string; |
41 | } | 43 | } |
42 | 44 | ||
@@ -53,7 +55,7 @@ const parameterHintDecorationType = vscode.window.createTextEditorDecorationType | |||
53 | }); | 55 | }); |
54 | 56 | ||
55 | class HintsUpdater { | 57 | class HintsUpdater { |
56 | private pending: Map<string, vscode.CancellationTokenSource> = new Map(); | 58 | private pending = new Map<string, vscode.CancellationTokenSource>(); |
57 | private ctx: Ctx; | 59 | private ctx: Ctx; |
58 | private enabled: boolean; | 60 | private enabled: boolean; |
59 | 61 | ||
@@ -62,30 +64,36 @@ class HintsUpdater { | |||
62 | this.enabled = ctx.config.displayInlayHints; | 64 | this.enabled = ctx.config.displayInlayHints; |
63 | } | 65 | } |
64 | 66 | ||
65 | async setEnabled(enabled: boolean) { | 67 | async setEnabled(enabled: boolean): Promise<void> { |
66 | if (this.enabled == enabled) return; | 68 | if (this.enabled == enabled) return; |
67 | this.enabled = enabled; | 69 | this.enabled = enabled; |
68 | 70 | ||
69 | if (this.enabled) { | 71 | if (this.enabled) { |
70 | await this.refresh(); | 72 | return await this.refresh(); |
71 | } else { | ||
72 | this.allEditors.forEach(it => { | ||
73 | this.setTypeDecorations(it, []); | ||
74 | this.setParameterDecorations(it, []); | ||
75 | }); | ||
76 | } | 73 | } |
74 | this.allEditors.forEach(it => { | ||
75 | this.setTypeDecorations(it, []); | ||
76 | this.setParameterDecorations(it, []); | ||
77 | }); | ||
77 | } | 78 | } |
78 | 79 | ||
79 | async refresh() { | 80 | async refresh() { |
80 | if (!this.enabled) return; | 81 | if (!this.enabled) return; |
81 | const promises = this.allEditors.map(it => this.refreshEditor(it)); | 82 | await Promise.all(this.allEditors.map(it => this.refreshEditor(it))); |
82 | await Promise.all(promises); | 83 | } |
84 | |||
85 | private get allEditors(): vscode.TextEditor[] { | ||
86 | return vscode.window.visibleTextEditors.filter( | ||
87 | editor => editor.document.languageId === 'rust', | ||
88 | ); | ||
83 | } | 89 | } |
84 | 90 | ||
85 | private async refreshEditor(editor: vscode.TextEditor): Promise<void> { | 91 | private async refreshEditor(editor: vscode.TextEditor): Promise<void> { |
86 | const newHints = await this.queryHints(editor.document.uri.toString()); | 92 | const newHints = await this.queryHints(editor.document.uri.toString()); |
87 | if (newHints == null) return; | 93 | if (newHints == null) return; |
88 | const newTypeDecorations = newHints.filter(hint => hint.kind === 'TypeHint') | 94 | |
95 | const newTypeDecorations = newHints | ||
96 | .filter(hint => hint.kind === 'TypeHint') | ||
89 | .map(hint => ({ | 97 | .map(hint => ({ |
90 | range: hint.range, | 98 | range: hint.range, |
91 | renderOptions: { | 99 | renderOptions: { |
@@ -96,7 +104,8 @@ class HintsUpdater { | |||
96 | })); | 104 | })); |
97 | this.setTypeDecorations(editor, newTypeDecorations); | 105 | this.setTypeDecorations(editor, newTypeDecorations); |
98 | 106 | ||
99 | const newParameterDecorations = newHints.filter(hint => hint.kind === 'ParameterHint') | 107 | const newParameterDecorations = newHints |
108 | .filter(hint => hint.kind === 'ParameterHint') | ||
100 | .map(hint => ({ | 109 | .map(hint => ({ |
101 | range: hint.range, | 110 | range: hint.range, |
102 | renderOptions: { | 111 | renderOptions: { |
@@ -108,12 +117,6 @@ class HintsUpdater { | |||
108 | this.setParameterDecorations(editor, newParameterDecorations); | 117 | this.setParameterDecorations(editor, newParameterDecorations); |
109 | } | 118 | } |
110 | 119 | ||
111 | private get allEditors(): vscode.TextEditor[] { | ||
112 | return vscode.window.visibleTextEditors.filter( | ||
113 | editor => editor.document.languageId === 'rust', | ||
114 | ); | ||
115 | } | ||
116 | |||
117 | private setTypeDecorations( | 120 | private setTypeDecorations( |
118 | editor: vscode.TextEditor, | 121 | editor: vscode.TextEditor, |
119 | decorations: vscode.DecorationOptions[], | 122 | decorations: vscode.DecorationOptions[], |
@@ -137,12 +140,14 @@ class HintsUpdater { | |||
137 | private async queryHints(documentUri: string): Promise<InlayHint[] | null> { | 140 | private async queryHints(documentUri: string): Promise<InlayHint[] | null> { |
138 | const client = this.ctx.client; | 141 | const client = this.ctx.client; |
139 | if (!client) return null; | 142 | if (!client) return null; |
143 | |||
140 | const request: InlayHintsParams = { | 144 | const request: InlayHintsParams = { |
141 | textDocument: { uri: documentUri }, | 145 | textDocument: { uri: documentUri }, |
142 | }; | 146 | }; |
143 | const tokenSource = new vscode.CancellationTokenSource(); | 147 | const tokenSource = new vscode.CancellationTokenSource(); |
144 | const prev = this.pending.get(documentUri); | 148 | const prevHintsRequest = this.pending.get(documentUri); |
145 | if (prev) prev.cancel(); | 149 | prevHintsRequest?.cancel(); |
150 | |||
146 | this.pending.set(documentUri, tokenSource); | 151 | this.pending.set(documentUri, tokenSource); |
147 | try { | 152 | try { |
148 | return await sendRequestWithRetry<InlayHint[] | null>( | 153 | return await sendRequestWithRetry<InlayHint[] | null>( |