diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2021-05-22 00:18:19 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2021-05-22 00:18:19 +0100 |
commit | ae24651e445444d4ed4275a717ac10980f2957a4 (patch) | |
tree | 9ae9115957ab35a3bc7d2d446bce72031328af1a | |
parent | 5b6c0c1af290996a407fb4be51e852317dfab7c2 (diff) | |
parent | 463ecefc64a48d80b2c4591fd4a1b82ae62b2897 (diff) |
Merge #8916
8916: ItemTree pretty-printing r=jonas-schievink a=jonas-schievink
This adds a printer for `ItemTree` contents, and a few tests to ensure that `ItemTree` lowering works like we expect it to. It also adds a new "Debug ItemTree" command that can be used to see the `ItemTree` of the currently open file. The pretty-printed output is usually close enough to Rust syntax that we can even use Rust syntax highlighting.
This is similar to the old `ItemTree` tests we had, but produces significantly more readable output, so these should actually carry their weight.
Co-authored-by: Jonas Schievink <[email protected]>
-rw-r--r-- | crates/hir_def/src/attr.rs | 11 | ||||
-rw-r--r-- | crates/hir_def/src/item_tree.rs | 11 | ||||
-rw-r--r-- | crates/hir_def/src/item_tree/pretty.rs | 525 | ||||
-rw-r--r-- | crates/hir_def/src/item_tree/tests.rs | 244 | ||||
-rw-r--r-- | crates/hir_def/src/path.rs | 9 | ||||
-rw-r--r-- | crates/ide/src/lib.rs | 5 | ||||
-rw-r--r-- | crates/ide/src/view_item_tree.rs | 16 | ||||
-rw-r--r-- | crates/rust-analyzer/src/handlers.rs | 10 | ||||
-rw-r--r-- | crates/rust-analyzer/src/lsp_ext.rs | 14 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop.rs | 1 | ||||
-rw-r--r-- | docs/dev/lsp-extensions.md | 20 | ||||
-rw-r--r-- | editors/code/package.json | 5 | ||||
-rw-r--r-- | editors/code/src/commands.ts | 50 | ||||
-rw-r--r-- | editors/code/src/lsp_ext.ts | 6 | ||||
-rw-r--r-- | editors/code/src/main.ts | 1 |
15 files changed, 925 insertions, 3 deletions
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index aadd4e44a..89a1ea770 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | use std::{ | 3 | use std::{ |
4 | convert::{TryFrom, TryInto}, | 4 | convert::{TryFrom, TryInto}, |
5 | ops, | 5 | fmt, ops, |
6 | sync::Arc, | 6 | sync::Arc, |
7 | }; | 7 | }; |
8 | 8 | ||
@@ -648,6 +648,15 @@ pub enum AttrInput { | |||
648 | TokenTree(Subtree), | 648 | TokenTree(Subtree), |
649 | } | 649 | } |
650 | 650 | ||
651 | impl fmt::Display for AttrInput { | ||
652 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
653 | match self { | ||
654 | AttrInput::Literal(lit) => write!(f, " = \"{}\"", lit.escape_debug()), | ||
655 | AttrInput::TokenTree(subtree) => subtree.fmt(f), | ||
656 | } | ||
657 | } | ||
658 | } | ||
659 | |||
651 | impl Attr { | 660 | impl Attr { |
652 | fn from_src( | 661 | fn from_src( |
653 | db: &dyn DefDatabase, | 662 | db: &dyn DefDatabase, |
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index 7440e7d29..528270d49 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs | |||
@@ -1,6 +1,9 @@ | |||
1 | //! A simplified AST that only contains items. | 1 | //! A simplified AST that only contains items. |
2 | 2 | ||
3 | mod lower; | 3 | mod lower; |
4 | mod pretty; | ||
5 | #[cfg(test)] | ||
6 | mod tests; | ||
4 | 7 | ||
5 | use std::{ | 8 | use std::{ |
6 | any::type_name, | 9 | any::type_name, |
@@ -205,6 +208,10 @@ impl ItemTree { | |||
205 | } | 208 | } |
206 | } | 209 | } |
207 | 210 | ||
211 | pub fn pretty_print(&self) -> String { | ||
212 | pretty::print_item_tree(self) | ||
213 | } | ||
214 | |||
208 | fn data(&self) -> &ItemTreeData { | 215 | fn data(&self) -> &ItemTreeData { |
209 | self.data.as_ref().expect("attempted to access data of empty ItemTree") | 216 | self.data.as_ref().expect("attempted to access data of empty ItemTree") |
210 | } | 217 | } |
@@ -776,6 +783,10 @@ impl<T> IdRange<T> { | |||
776 | fn new(range: Range<Idx<T>>) -> Self { | 783 | fn new(range: Range<Idx<T>>) -> Self { |
777 | Self { range: range.start.into_raw().into()..range.end.into_raw().into(), _p: PhantomData } | 784 | Self { range: range.start.into_raw().into()..range.end.into_raw().into(), _p: PhantomData } |
778 | } | 785 | } |
786 | |||
787 | fn is_empty(&self) -> bool { | ||
788 | self.range.is_empty() | ||
789 | } | ||
779 | } | 790 | } |
780 | 791 | ||
781 | impl<T> Iterator for IdRange<T> { | 792 | impl<T> Iterator for IdRange<T> { |
diff --git a/crates/hir_def/src/item_tree/pretty.rs b/crates/hir_def/src/item_tree/pretty.rs new file mode 100644 index 000000000..5ec02d1be --- /dev/null +++ b/crates/hir_def/src/item_tree/pretty.rs | |||
@@ -0,0 +1,525 @@ | |||
1 | //! `ItemTree` debug printer. | ||
2 | |||
3 | use std::fmt::{self, Write}; | ||
4 | |||
5 | use crate::{attr::RawAttrs, visibility::RawVisibility}; | ||
6 | |||
7 | use super::*; | ||
8 | |||
9 | pub(super) fn print_item_tree(tree: &ItemTree) -> String { | ||
10 | let mut p = Printer { tree, buf: String::new(), indent_level: 0, needs_indent: true }; | ||
11 | |||
12 | if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) { | ||
13 | p.print_attrs(attrs, true); | ||
14 | } | ||
15 | p.blank(); | ||
16 | |||
17 | for item in tree.top_level_items() { | ||
18 | p.print_mod_item(*item); | ||
19 | } | ||
20 | |||
21 | let mut s = p.buf.trim_end_matches('\n').to_string(); | ||
22 | s.push('\n'); | ||
23 | s | ||
24 | } | ||
25 | |||
26 | macro_rules! w { | ||
27 | ($dst:expr, $($arg:tt)*) => { | ||
28 | drop(write!($dst, $($arg)*)) | ||
29 | }; | ||
30 | } | ||
31 | |||
32 | macro_rules! wln { | ||
33 | ($dst:expr) => { | ||
34 | drop(writeln!($dst)) | ||
35 | }; | ||
36 | ($dst:expr, $($arg:tt)*) => { | ||
37 | drop(writeln!($dst, $($arg)*)) | ||
38 | }; | ||
39 | } | ||
40 | |||
41 | struct Printer<'a> { | ||
42 | tree: &'a ItemTree, | ||
43 | buf: String, | ||
44 | indent_level: usize, | ||
45 | needs_indent: bool, | ||
46 | } | ||
47 | |||
48 | impl<'a> Printer<'a> { | ||
49 | fn indented(&mut self, f: impl FnOnce(&mut Self)) { | ||
50 | self.indent_level += 1; | ||
51 | wln!(self); | ||
52 | f(self); | ||
53 | self.indent_level -= 1; | ||
54 | self.buf = self.buf.trim_end_matches('\n').to_string(); | ||
55 | } | ||
56 | |||
57 | /// Ensures that a blank line is output before the next text. | ||
58 | fn blank(&mut self) { | ||
59 | let mut iter = self.buf.chars().rev().fuse(); | ||
60 | match (iter.next(), iter.next()) { | ||
61 | (Some('\n'), Some('\n')) | (Some('\n'), None) | (None, None) => {} | ||
62 | (Some('\n'), Some(_)) => { | ||
63 | self.buf.push('\n'); | ||
64 | } | ||
65 | (Some(_), _) => { | ||
66 | self.buf.push('\n'); | ||
67 | self.buf.push('\n'); | ||
68 | } | ||
69 | (None, Some(_)) => unreachable!(), | ||
70 | } | ||
71 | } | ||
72 | |||
73 | fn print_attrs(&mut self, attrs: &RawAttrs, inner: bool) { | ||
74 | let inner = if inner { "!" } else { "" }; | ||
75 | for attr in &**attrs { | ||
76 | wln!( | ||
77 | self, | ||
78 | "#{}[{}{}] // {:?}", | ||
79 | inner, | ||
80 | attr.path, | ||
81 | attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(), | ||
82 | attr.id, | ||
83 | ); | ||
84 | } | ||
85 | } | ||
86 | |||
87 | fn print_attrs_of(&mut self, of: impl Into<AttrOwner>) { | ||
88 | if let Some(attrs) = self.tree.attrs.get(&of.into()) { | ||
89 | self.print_attrs(attrs, false); | ||
90 | } | ||
91 | } | ||
92 | |||
93 | fn print_visibility(&mut self, vis: RawVisibilityId) { | ||
94 | match &self.tree[vis] { | ||
95 | RawVisibility::Module(path) => w!(self, "pub({}) ", path), | ||
96 | RawVisibility::Public => w!(self, "pub "), | ||
97 | }; | ||
98 | } | ||
99 | |||
100 | fn print_fields(&mut self, fields: &Fields) { | ||
101 | match fields { | ||
102 | Fields::Record(fields) => { | ||
103 | w!(self, " {{"); | ||
104 | self.indented(|this| { | ||
105 | for field in fields.clone() { | ||
106 | let Field { visibility, name, type_ref } = &this.tree[field]; | ||
107 | this.print_attrs_of(field); | ||
108 | this.print_visibility(*visibility); | ||
109 | w!(this, "{}: ", name); | ||
110 | this.print_type_ref(type_ref); | ||
111 | wln!(this, ","); | ||
112 | } | ||
113 | }); | ||
114 | w!(self, "}}"); | ||
115 | } | ||
116 | Fields::Tuple(fields) => { | ||
117 | w!(self, "("); | ||
118 | self.indented(|this| { | ||
119 | for field in fields.clone() { | ||
120 | let Field { visibility, name, type_ref } = &this.tree[field]; | ||
121 | this.print_attrs_of(field); | ||
122 | this.print_visibility(*visibility); | ||
123 | w!(this, "{}: ", name); | ||
124 | this.print_type_ref(type_ref); | ||
125 | wln!(this, ","); | ||
126 | } | ||
127 | }); | ||
128 | w!(self, ")"); | ||
129 | } | ||
130 | Fields::Unit => {} | ||
131 | } | ||
132 | } | ||
133 | |||
134 | fn print_mod_item(&mut self, item: ModItem) { | ||
135 | self.print_attrs_of(item); | ||
136 | |||
137 | match item { | ||
138 | ModItem::Import(it) => { | ||
139 | let Import { visibility, path, is_glob, alias, ast_id: _, index } = &self.tree[it]; | ||
140 | self.print_visibility(*visibility); | ||
141 | w!(self, "use {}", path); | ||
142 | if *is_glob { | ||
143 | w!(self, "::*"); | ||
144 | } | ||
145 | if let Some(alias) = alias { | ||
146 | w!(self, " as {}", alias); | ||
147 | } | ||
148 | wln!(self, "; // {}", index); | ||
149 | } | ||
150 | ModItem::ExternCrate(it) => { | ||
151 | let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it]; | ||
152 | self.print_visibility(*visibility); | ||
153 | w!(self, "extern crate {}", name); | ||
154 | if let Some(alias) = alias { | ||
155 | w!(self, " as {}", alias); | ||
156 | } | ||
157 | wln!(self, ";"); | ||
158 | } | ||
159 | ModItem::ExternBlock(it) => { | ||
160 | let ExternBlock { abi, ast_id: _, children } = &self.tree[it]; | ||
161 | w!(self, "extern "); | ||
162 | if let Some(abi) = abi { | ||
163 | w!(self, "\"{}\" ", abi); | ||
164 | } | ||
165 | w!(self, "{{"); | ||
166 | self.indented(|this| { | ||
167 | for child in &**children { | ||
168 | this.print_mod_item(*child); | ||
169 | } | ||
170 | }); | ||
171 | wln!(self, "}}"); | ||
172 | } | ||
173 | ModItem::Function(it) => { | ||
174 | let Function { | ||
175 | name, | ||
176 | visibility, | ||
177 | generic_params: _, // FIXME print these somehow | ||
178 | abi, | ||
179 | params, | ||
180 | ret_type, | ||
181 | ast_id: _, | ||
182 | flags, | ||
183 | } = &self.tree[it]; | ||
184 | if flags.bits != 0 { | ||
185 | wln!(self, "// flags = 0x{:X}", flags.bits); | ||
186 | } | ||
187 | self.print_visibility(*visibility); | ||
188 | if let Some(abi) = abi { | ||
189 | w!(self, "extern \"{}\" ", abi); | ||
190 | } | ||
191 | w!(self, "fn {}(", name); | ||
192 | if !params.is_empty() { | ||
193 | self.indented(|this| { | ||
194 | for param in params.clone() { | ||
195 | this.print_attrs_of(param); | ||
196 | match &this.tree[param] { | ||
197 | Param::Normal(ty) => { | ||
198 | w!(this, "_: "); | ||
199 | this.print_type_ref(ty); | ||
200 | wln!(this, ","); | ||
201 | } | ||
202 | Param::Varargs => { | ||
203 | wln!(this, "..."); | ||
204 | } | ||
205 | }; | ||
206 | } | ||
207 | }); | ||
208 | } | ||
209 | w!(self, ") -> "); | ||
210 | self.print_type_ref(ret_type); | ||
211 | wln!(self, ";"); | ||
212 | } | ||
213 | ModItem::Struct(it) => { | ||
214 | let Struct { visibility, name, fields, generic_params: _, ast_id: _ } = | ||
215 | &self.tree[it]; | ||
216 | self.print_visibility(*visibility); | ||
217 | w!(self, "struct {}", name); | ||
218 | self.print_fields(fields); | ||
219 | if matches!(fields, Fields::Record(_)) { | ||
220 | wln!(self); | ||
221 | } else { | ||
222 | wln!(self, ";"); | ||
223 | } | ||
224 | } | ||
225 | ModItem::Union(it) => { | ||
226 | let Union { name, visibility, fields, generic_params: _, ast_id: _ } = | ||
227 | &self.tree[it]; | ||
228 | self.print_visibility(*visibility); | ||
229 | w!(self, "union {}", name); | ||
230 | self.print_fields(fields); | ||
231 | if matches!(fields, Fields::Record(_)) { | ||
232 | wln!(self); | ||
233 | } else { | ||
234 | wln!(self, ";"); | ||
235 | } | ||
236 | } | ||
237 | ModItem::Enum(it) => { | ||
238 | let Enum { name, visibility, variants, generic_params: _, ast_id: _ } = | ||
239 | &self.tree[it]; | ||
240 | self.print_visibility(*visibility); | ||
241 | w!(self, "enum {} {{", name); | ||
242 | self.indented(|this| { | ||
243 | for variant in variants.clone() { | ||
244 | let Variant { name, fields } = &this.tree[variant]; | ||
245 | this.print_attrs_of(variant); | ||
246 | w!(this, "{}", name); | ||
247 | this.print_fields(fields); | ||
248 | wln!(this, ","); | ||
249 | } | ||
250 | }); | ||
251 | wln!(self, "}}"); | ||
252 | } | ||
253 | ModItem::Const(it) => { | ||
254 | let Const { name, visibility, type_ref, ast_id: _ } = &self.tree[it]; | ||
255 | self.print_visibility(*visibility); | ||
256 | w!(self, "const "); | ||
257 | match name { | ||
258 | Some(name) => w!(self, "{}", name), | ||
259 | None => w!(self, "_"), | ||
260 | } | ||
261 | w!(self, ": "); | ||
262 | self.print_type_ref(type_ref); | ||
263 | wln!(self, " = _;"); | ||
264 | } | ||
265 | ModItem::Static(it) => { | ||
266 | let Static { name, visibility, mutable, is_extern, type_ref, ast_id: _ } = | ||
267 | &self.tree[it]; | ||
268 | self.print_visibility(*visibility); | ||
269 | w!(self, "static "); | ||
270 | if *mutable { | ||
271 | w!(self, "mut "); | ||
272 | } | ||
273 | w!(self, "{}: ", name); | ||
274 | self.print_type_ref(type_ref); | ||
275 | w!(self, " = _;"); | ||
276 | if *is_extern { | ||
277 | w!(self, " // extern"); | ||
278 | } | ||
279 | wln!(self); | ||
280 | } | ||
281 | ModItem::Trait(it) => { | ||
282 | let Trait { | ||
283 | name, | ||
284 | visibility, | ||
285 | is_auto, | ||
286 | is_unsafe, | ||
287 | bounds, | ||
288 | items, | ||
289 | generic_params: _, | ||
290 | ast_id: _, | ||
291 | } = &self.tree[it]; | ||
292 | self.print_visibility(*visibility); | ||
293 | if *is_unsafe { | ||
294 | w!(self, "unsafe "); | ||
295 | } | ||
296 | if *is_auto { | ||
297 | w!(self, "auto "); | ||
298 | } | ||
299 | w!(self, "trait {}", name); | ||
300 | if !bounds.is_empty() { | ||
301 | w!(self, ": "); | ||
302 | self.print_type_bounds(bounds); | ||
303 | } | ||
304 | w!(self, " {{"); | ||
305 | self.indented(|this| { | ||
306 | for item in &**items { | ||
307 | this.print_mod_item((*item).into()); | ||
308 | } | ||
309 | }); | ||
310 | wln!(self, "}}"); | ||
311 | } | ||
312 | ModItem::Impl(it) => { | ||
313 | let Impl { | ||
314 | target_trait, | ||
315 | self_ty, | ||
316 | is_negative, | ||
317 | items, | ||
318 | generic_params: _, | ||
319 | ast_id: _, | ||
320 | } = &self.tree[it]; | ||
321 | w!(self, "impl "); | ||
322 | if *is_negative { | ||
323 | w!(self, "!"); | ||
324 | } | ||
325 | if let Some(tr) = target_trait { | ||
326 | self.print_path(&tr.path); | ||
327 | w!(self, " for "); | ||
328 | } | ||
329 | self.print_type_ref(self_ty); | ||
330 | w!(self, " {{"); | ||
331 | self.indented(|this| { | ||
332 | for item in &**items { | ||
333 | this.print_mod_item((*item).into()); | ||
334 | } | ||
335 | }); | ||
336 | wln!(self, "}}"); | ||
337 | } | ||
338 | ModItem::TypeAlias(it) => { | ||
339 | let TypeAlias { | ||
340 | name, | ||
341 | visibility, | ||
342 | bounds, | ||
343 | type_ref, | ||
344 | is_extern, | ||
345 | generic_params: _, | ||
346 | ast_id: _, | ||
347 | } = &self.tree[it]; | ||
348 | self.print_visibility(*visibility); | ||
349 | w!(self, "type {}", name); | ||
350 | if !bounds.is_empty() { | ||
351 | w!(self, ": "); | ||
352 | self.print_type_bounds(bounds); | ||
353 | } | ||
354 | if let Some(ty) = type_ref { | ||
355 | w!(self, " = "); | ||
356 | self.print_type_ref(ty); | ||
357 | } | ||
358 | w!(self, ";"); | ||
359 | if *is_extern { | ||
360 | w!(self, " // extern"); | ||
361 | } | ||
362 | wln!(self); | ||
363 | } | ||
364 | ModItem::Mod(it) => { | ||
365 | let Mod { name, visibility, kind, ast_id: _ } = &self.tree[it]; | ||
366 | self.print_visibility(*visibility); | ||
367 | w!(self, "mod {}", name); | ||
368 | match kind { | ||
369 | ModKind::Inline { items } => { | ||
370 | w!(self, " {{"); | ||
371 | self.indented(|this| { | ||
372 | for item in &**items { | ||
373 | this.print_mod_item((*item).into()); | ||
374 | } | ||
375 | }); | ||
376 | wln!(self, "}}"); | ||
377 | } | ||
378 | ModKind::Outline {} => { | ||
379 | wln!(self, ";"); | ||
380 | } | ||
381 | } | ||
382 | } | ||
383 | ModItem::MacroCall(it) => { | ||
384 | let MacroCall { path, ast_id: _, fragment: _ } = &self.tree[it]; | ||
385 | wln!(self, "{}!(...);", path); | ||
386 | } | ||
387 | ModItem::MacroRules(it) => { | ||
388 | let MacroRules { name, ast_id: _ } = &self.tree[it]; | ||
389 | wln!(self, "macro_rules! {} {{ ... }}", name); | ||
390 | } | ||
391 | ModItem::MacroDef(it) => { | ||
392 | let MacroDef { name, visibility, ast_id: _ } = &self.tree[it]; | ||
393 | self.print_visibility(*visibility); | ||
394 | wln!(self, "macro {} {{ ... }}", name); | ||
395 | } | ||
396 | } | ||
397 | |||
398 | self.blank(); | ||
399 | } | ||
400 | |||
401 | fn print_type_ref(&mut self, type_ref: &TypeRef) { | ||
402 | // FIXME: deduplicate with `HirDisplay` impl | ||
403 | match type_ref { | ||
404 | TypeRef::Never => w!(self, "!"), | ||
405 | TypeRef::Placeholder => w!(self, "_"), | ||
406 | TypeRef::Tuple(fields) => { | ||
407 | w!(self, "("); | ||
408 | for (i, field) in fields.iter().enumerate() { | ||
409 | if i != 0 { | ||
410 | w!(self, ", "); | ||
411 | } | ||
412 | self.print_type_ref(field); | ||
413 | } | ||
414 | w!(self, ")"); | ||
415 | } | ||
416 | TypeRef::Path(path) => self.print_path(path), | ||
417 | TypeRef::RawPtr(pointee, mtbl) => { | ||
418 | let mtbl = match mtbl { | ||
419 | Mutability::Shared => "*const", | ||
420 | Mutability::Mut => "*mut", | ||
421 | }; | ||
422 | w!(self, "{} ", mtbl); | ||
423 | self.print_type_ref(pointee); | ||
424 | } | ||
425 | TypeRef::Reference(pointee, lt, mtbl) => { | ||
426 | let mtbl = match mtbl { | ||
427 | Mutability::Shared => "", | ||
428 | Mutability::Mut => "mut ", | ||
429 | }; | ||
430 | w!(self, "&"); | ||
431 | if let Some(lt) = lt { | ||
432 | w!(self, "{} ", lt.name); | ||
433 | } | ||
434 | w!(self, "{}", mtbl); | ||
435 | self.print_type_ref(pointee); | ||
436 | } | ||
437 | TypeRef::Array(elem, len) => { | ||
438 | w!(self, "["); | ||
439 | self.print_type_ref(elem); | ||
440 | w!(self, "; {}]", len); | ||
441 | } | ||
442 | TypeRef::Slice(elem) => { | ||
443 | w!(self, "["); | ||
444 | self.print_type_ref(elem); | ||
445 | w!(self, "]"); | ||
446 | } | ||
447 | TypeRef::Fn(args_and_ret, varargs) => { | ||
448 | let (ret, args) = | ||
449 | args_and_ret.split_last().expect("TypeRef::Fn is missing return type"); | ||
450 | w!(self, "fn("); | ||
451 | for (i, arg) in args.iter().enumerate() { | ||
452 | if i != 0 { | ||
453 | w!(self, ", "); | ||
454 | } | ||
455 | self.print_type_ref(arg); | ||
456 | } | ||
457 | if *varargs { | ||
458 | if !args.is_empty() { | ||
459 | w!(self, ", "); | ||
460 | } | ||
461 | w!(self, "..."); | ||
462 | } | ||
463 | w!(self, ") -> "); | ||
464 | self.print_type_ref(ret); | ||
465 | } | ||
466 | TypeRef::Macro(_ast_id) => { | ||
467 | w!(self, "<macro>"); | ||
468 | } | ||
469 | TypeRef::Error => drop(write!(self, "{{unknown}}")), | ||
470 | TypeRef::ImplTrait(bounds) => { | ||
471 | w!(self, "impl "); | ||
472 | self.print_type_bounds(bounds); | ||
473 | } | ||
474 | TypeRef::DynTrait(bounds) => { | ||
475 | w!(self, "dyn "); | ||
476 | self.print_type_bounds(bounds); | ||
477 | } | ||
478 | } | ||
479 | } | ||
480 | |||
481 | fn print_type_bounds(&mut self, bounds: &[TypeBound]) { | ||
482 | for (i, bound) in bounds.iter().enumerate() { | ||
483 | if i != 0 { | ||
484 | w!(self, " + "); | ||
485 | } | ||
486 | |||
487 | match bound { | ||
488 | TypeBound::Path(path) => self.print_path(path), | ||
489 | TypeBound::Lifetime(lt) => w!(self, "{}", lt.name), | ||
490 | TypeBound::Error => w!(self, "{{unknown}}"), | ||
491 | } | ||
492 | } | ||
493 | } | ||
494 | |||
495 | fn print_path(&mut self, path: &Path) { | ||
496 | if path.type_anchor().is_none() | ||
497 | && path.segments().iter().all(|seg| seg.args_and_bindings.is_none()) | ||
498 | { | ||
499 | w!(self, "{}", path.mod_path()); | ||
500 | } else { | ||
501 | // too complicated, just use `Debug` | ||
502 | w!(self, "{:?}", path); | ||
503 | } | ||
504 | } | ||
505 | } | ||
506 | |||
507 | impl<'a> Write for Printer<'a> { | ||
508 | fn write_str(&mut self, s: &str) -> fmt::Result { | ||
509 | for line in s.split_inclusive('\n') { | ||
510 | if self.needs_indent { | ||
511 | match self.buf.chars().last() { | ||
512 | Some('\n') | None => {} | ||
513 | _ => self.buf.push('\n'), | ||
514 | } | ||
515 | self.buf.push_str(&" ".repeat(self.indent_level)); | ||
516 | self.needs_indent = false; | ||
517 | } | ||
518 | |||
519 | self.buf.push_str(line); | ||
520 | self.needs_indent = line.ends_with('\n'); | ||
521 | } | ||
522 | |||
523 | Ok(()) | ||
524 | } | ||
525 | } | ||
diff --git a/crates/hir_def/src/item_tree/tests.rs b/crates/hir_def/src/item_tree/tests.rs new file mode 100644 index 000000000..100ae9b97 --- /dev/null +++ b/crates/hir_def/src/item_tree/tests.rs | |||
@@ -0,0 +1,244 @@ | |||
1 | use base_db::fixture::WithFixture; | ||
2 | use expect_test::{expect, Expect}; | ||
3 | |||
4 | use crate::{db::DefDatabase, test_db::TestDB}; | ||
5 | |||
6 | fn check(ra_fixture: &str, expect: Expect) { | ||
7 | let (db, file_id) = TestDB::with_single_file(ra_fixture); | ||
8 | let item_tree = db.file_item_tree(file_id.into()); | ||
9 | let pretty = item_tree.pretty_print(); | ||
10 | expect.assert_eq(&pretty); | ||
11 | } | ||
12 | |||
13 | #[test] | ||
14 | fn imports() { | ||
15 | check( | ||
16 | r#" | ||
17 | //! file comment | ||
18 | #![no_std] | ||
19 | //! another file comment | ||
20 | |||
21 | extern crate self as renamed; | ||
22 | pub(super) extern crate bli; | ||
23 | |||
24 | pub use crate::path::{nested, items as renamed, Trait as _}; | ||
25 | use globs::*; | ||
26 | |||
27 | /// docs on import | ||
28 | use crate::{A, B}; | ||
29 | "#, | ||
30 | expect![[r##" | ||
31 | #![doc = " file comment"] // AttrId { is_doc_comment: true, ast_index: 0 } | ||
32 | #![no_std] // AttrId { is_doc_comment: false, ast_index: 0 } | ||
33 | #![doc = " another file comment"] // AttrId { is_doc_comment: true, ast_index: 1 } | ||
34 | |||
35 | pub(self) extern crate self as renamed; | ||
36 | |||
37 | pub(super) extern crate bli; | ||
38 | |||
39 | pub use crate::path::nested; // 0 | ||
40 | |||
41 | pub use crate::path::items as renamed; // 1 | ||
42 | |||
43 | pub use crate::path::Trait as _; // 2 | ||
44 | |||
45 | pub(self) use globs::*; // 0 | ||
46 | |||
47 | #[doc = " docs on import"] // AttrId { is_doc_comment: true, ast_index: 0 } | ||
48 | pub(self) use crate::A; // 0 | ||
49 | |||
50 | #[doc = " docs on import"] // AttrId { is_doc_comment: true, ast_index: 0 } | ||
51 | pub(self) use crate::B; // 1 | ||
52 | "##]], | ||
53 | ); | ||
54 | } | ||
55 | |||
56 | #[test] | ||
57 | fn extern_blocks() { | ||
58 | check( | ||
59 | r#" | ||
60 | #[on_extern_block] | ||
61 | extern "C" { | ||
62 | #[on_extern_type] | ||
63 | type ExType; | ||
64 | |||
65 | #[on_extern_static] | ||
66 | static EX_STATIC: u8; | ||
67 | |||
68 | #[on_extern_fn] | ||
69 | fn ex_fn(); | ||
70 | } | ||
71 | "#, | ||
72 | expect![[r##" | ||
73 | #[on_extern_block] // AttrId { is_doc_comment: false, ast_index: 0 } | ||
74 | extern "C" { | ||
75 | #[on_extern_type] // AttrId { is_doc_comment: false, ast_index: 0 } | ||
76 | pub(self) type ExType; // extern | ||
77 | |||
78 | #[on_extern_static] // AttrId { is_doc_comment: false, ast_index: 0 } | ||
79 | pub(self) static EX_STATIC: u8 = _; // extern | ||
80 | |||
81 | #[on_extern_fn] // AttrId { is_doc_comment: false, ast_index: 0 } | ||
82 | // flags = 0x60 | ||
83 | pub(self) fn ex_fn() -> (); | ||
84 | } | ||
85 | "##]], | ||
86 | ); | ||
87 | } | ||
88 | |||
89 | #[test] | ||
90 | fn adts() { | ||
91 | check( | ||
92 | r#" | ||
93 | struct Unit; | ||
94 | |||
95 | #[derive(Debug)] | ||
96 | struct Struct { | ||
97 | /// fld docs | ||
98 | fld: (), | ||
99 | } | ||
100 | |||
101 | struct Tuple(#[attr] u8); | ||
102 | |||
103 | union Ize { | ||
104 | a: (), | ||
105 | b: (), | ||
106 | } | ||
107 | |||
108 | enum E { | ||
109 | /// comment on Unit | ||
110 | Unit, | ||
111 | /// comment on Tuple | ||
112 | Tuple(u8), | ||
113 | Struct { | ||
114 | /// comment on a: u8 | ||
115 | a: u8, | ||
116 | } | ||
117 | } | ||
118 | "#, | ||
119 | expect![[r##" | ||
120 | pub(self) struct Unit; | ||
121 | |||
122 | #[derive(Debug)] // AttrId { is_doc_comment: false, ast_index: 0 } | ||
123 | pub(self) struct Struct { | ||
124 | #[doc = " fld docs"] // AttrId { is_doc_comment: true, ast_index: 0 } | ||
125 | pub(self) fld: (), | ||
126 | } | ||
127 | |||
128 | pub(self) struct Tuple( | ||
129 | #[attr] // AttrId { is_doc_comment: false, ast_index: 0 } | ||
130 | pub(self) 0: u8, | ||
131 | ); | ||
132 | |||
133 | pub(self) union Ize { | ||
134 | pub(self) a: (), | ||
135 | pub(self) b: (), | ||
136 | } | ||
137 | |||
138 | pub(self) enum E { | ||
139 | #[doc = " comment on Unit"] // AttrId { is_doc_comment: true, ast_index: 0 } | ||
140 | Unit, | ||
141 | #[doc = " comment on Tuple"] // AttrId { is_doc_comment: true, ast_index: 0 } | ||
142 | Tuple( | ||
143 | pub(self) 0: u8, | ||
144 | ), | ||
145 | Struct { | ||
146 | #[doc = " comment on a: u8"] // AttrId { is_doc_comment: true, ast_index: 0 } | ||
147 | pub(self) a: u8, | ||
148 | }, | ||
149 | } | ||
150 | "##]], | ||
151 | ); | ||
152 | } | ||
153 | |||
154 | #[test] | ||
155 | fn misc() { | ||
156 | check( | ||
157 | r#" | ||
158 | pub static mut ST: () = (); | ||
159 | |||
160 | const _: Anon = (); | ||
161 | |||
162 | #[attr] | ||
163 | fn f(#[attr] arg: u8, _: ()) { | ||
164 | #![inner_attr_in_fn] | ||
165 | } | ||
166 | |||
167 | trait Tr: SuperTrait + 'lifetime { | ||
168 | type Assoc: AssocBound = Default; | ||
169 | fn method(&self); | ||
170 | } | ||
171 | "#, | ||
172 | expect![[r##" | ||
173 | pub static mut ST: () = _; | ||
174 | |||
175 | pub(self) const _: Anon = _; | ||
176 | |||
177 | #[attr] // AttrId { is_doc_comment: false, ast_index: 0 } | ||
178 | #[inner_attr_in_fn] // AttrId { is_doc_comment: false, ast_index: 1 } | ||
179 | // flags = 0x2 | ||
180 | pub(self) fn f( | ||
181 | #[attr] // AttrId { is_doc_comment: false, ast_index: 0 } | ||
182 | _: u8, | ||
183 | _: (), | ||
184 | ) -> (); | ||
185 | |||
186 | pub(self) trait Tr: SuperTrait + 'lifetime { | ||
187 | pub(self) type Assoc: AssocBound = Default; | ||
188 | |||
189 | // flags = 0x1 | ||
190 | pub(self) fn method( | ||
191 | _: &Self, | ||
192 | ) -> (); | ||
193 | } | ||
194 | "##]], | ||
195 | ); | ||
196 | } | ||
197 | |||
198 | #[test] | ||
199 | fn modules() { | ||
200 | check( | ||
201 | r#" | ||
202 | /// outer | ||
203 | mod inline { | ||
204 | //! inner | ||
205 | |||
206 | use super::*; | ||
207 | |||
208 | fn fn_in_module() {} | ||
209 | } | ||
210 | "#, | ||
211 | expect![[r##" | ||
212 | #[doc = " outer"] // AttrId { is_doc_comment: true, ast_index: 0 } | ||
213 | #[doc = " inner"] // AttrId { is_doc_comment: true, ast_index: 1 } | ||
214 | pub(self) mod inline { | ||
215 | pub(self) use super::*; // 0 | ||
216 | |||
217 | // flags = 0x2 | ||
218 | pub(self) fn fn_in_module() -> (); | ||
219 | } | ||
220 | "##]], | ||
221 | ); | ||
222 | } | ||
223 | |||
224 | #[test] | ||
225 | fn macros() { | ||
226 | check( | ||
227 | r#" | ||
228 | macro_rules! m { | ||
229 | () => {}; | ||
230 | } | ||
231 | |||
232 | pub macro m2() {} | ||
233 | |||
234 | m!(); | ||
235 | "#, | ||
236 | expect![[r#" | ||
237 | macro_rules! m { ... } | ||
238 | |||
239 | pub macro m2 { ... } | ||
240 | |||
241 | m!(...); | ||
242 | "#]], | ||
243 | ); | ||
244 | } | ||
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs index a43441b1c..9b8873fd2 100644 --- a/crates/hir_def/src/path.rs +++ b/crates/hir_def/src/path.rs | |||
@@ -46,6 +46,15 @@ pub enum ImportAlias { | |||
46 | Alias(Name), | 46 | Alias(Name), |
47 | } | 47 | } |
48 | 48 | ||
49 | impl Display for ImportAlias { | ||
50 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
51 | match self { | ||
52 | ImportAlias::Underscore => f.write_str("_"), | ||
53 | ImportAlias::Alias(name) => f.write_str(&name.to_string()), | ||
54 | } | ||
55 | } | ||
56 | } | ||
57 | |||
49 | impl ModPath { | 58 | impl ModPath { |
50 | pub fn from_src(db: &dyn DefDatabase, path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> { | 59 | pub fn from_src(db: &dyn DefDatabase, path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> { |
51 | let ctx = LowerCtx::with_hygiene(db, hygiene); | 60 | let ctx = LowerCtx::with_hygiene(db, hygiene); |
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index f4b90db3a..ff2a54117 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -50,6 +50,7 @@ mod typing; | |||
50 | mod markdown_remove; | 50 | mod markdown_remove; |
51 | mod doc_links; | 51 | mod doc_links; |
52 | mod view_crate_graph; | 52 | mod view_crate_graph; |
53 | mod view_item_tree; | ||
53 | 54 | ||
54 | use std::sync::Arc; | 55 | use std::sync::Arc; |
55 | 56 | ||
@@ -288,6 +289,10 @@ impl Analysis { | |||
288 | self.with_db(|db| view_hir::view_hir(&db, position)) | 289 | self.with_db(|db| view_hir::view_hir(&db, position)) |
289 | } | 290 | } |
290 | 291 | ||
292 | pub fn view_item_tree(&self, file_id: FileId) -> Cancelable<String> { | ||
293 | self.with_db(|db| view_item_tree::view_item_tree(&db, file_id)) | ||
294 | } | ||
295 | |||
291 | /// Renders the crate graph to GraphViz "dot" syntax. | 296 | /// Renders the crate graph to GraphViz "dot" syntax. |
292 | pub fn view_crate_graph(&self) -> Cancelable<Result<String, String>> { | 297 | pub fn view_crate_graph(&self) -> Cancelable<Result<String, String>> { |
293 | self.with_db(|db| view_crate_graph::view_crate_graph(&db)) | 298 | self.with_db(|db| view_crate_graph::view_crate_graph(&db)) |
diff --git a/crates/ide/src/view_item_tree.rs b/crates/ide/src/view_item_tree.rs new file mode 100644 index 000000000..3dc03085d --- /dev/null +++ b/crates/ide/src/view_item_tree.rs | |||
@@ -0,0 +1,16 @@ | |||
1 | use hir::db::DefDatabase; | ||
2 | use ide_db::base_db::FileId; | ||
3 | use ide_db::RootDatabase; | ||
4 | |||
5 | // Feature: Debug ItemTree | ||
6 | // | ||
7 | // Displays the ItemTree of the currently open file, for debugging. | ||
8 | // | ||
9 | // |=== | ||
10 | // | Editor | Action Name | ||
11 | // | ||
12 | // | VS Code | **Rust Analyzer: Debug ItemTree** | ||
13 | // |=== | ||
14 | pub(crate) fn view_item_tree(db: &RootDatabase, file_id: FileId) -> String { | ||
15 | db.file_item_tree(file_id.into()).pretty_print() | ||
16 | } | ||
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 51041d7a0..aa12fd94b 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -117,6 +117,16 @@ pub(crate) fn handle_view_hir( | |||
117 | Ok(res) | 117 | Ok(res) |
118 | } | 118 | } |
119 | 119 | ||
120 | pub(crate) fn handle_view_item_tree( | ||
121 | snap: GlobalStateSnapshot, | ||
122 | params: lsp_ext::ViewItemTreeParams, | ||
123 | ) -> Result<String> { | ||
124 | let _p = profile::span("handle_view_item_tree"); | ||
125 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; | ||
126 | let res = snap.analysis.view_item_tree(file_id)?; | ||
127 | Ok(res) | ||
128 | } | ||
129 | |||
120 | pub(crate) fn handle_view_crate_graph(snap: GlobalStateSnapshot, (): ()) -> Result<String> { | 130 | pub(crate) fn handle_view_crate_graph(snap: GlobalStateSnapshot, (): ()) -> Result<String> { |
121 | let _p = profile::span("handle_view_crate_graph"); | 131 | let _p = profile::span("handle_view_crate_graph"); |
122 | let dot = snap.analysis.view_crate_graph()??; | 132 | let dot = snap.analysis.view_crate_graph()??; |
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 34b53a7a8..905048793 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs | |||
@@ -70,6 +70,20 @@ impl Request for ViewCrateGraph { | |||
70 | const METHOD: &'static str = "rust-analyzer/viewCrateGraph"; | 70 | const METHOD: &'static str = "rust-analyzer/viewCrateGraph"; |
71 | } | 71 | } |
72 | 72 | ||
73 | #[derive(Deserialize, Serialize, Debug)] | ||
74 | #[serde(rename_all = "camelCase")] | ||
75 | pub struct ViewItemTreeParams { | ||
76 | pub text_document: TextDocumentIdentifier, | ||
77 | } | ||
78 | |||
79 | pub enum ViewItemTree {} | ||
80 | |||
81 | impl Request for ViewItemTree { | ||
82 | type Params = ViewItemTreeParams; | ||
83 | type Result = String; | ||
84 | const METHOD: &'static str = "rust-analyzer/viewItemTree"; | ||
85 | } | ||
86 | |||
73 | pub enum ExpandMacro {} | 87 | pub enum ExpandMacro {} |
74 | 88 | ||
75 | impl Request for ExpandMacro { | 89 | impl Request for ExpandMacro { |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 4e0791611..f837b89dd 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -514,6 +514,7 @@ impl GlobalState { | |||
514 | .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree) | 514 | .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree) |
515 | .on::<lsp_ext::ViewHir>(handlers::handle_view_hir) | 515 | .on::<lsp_ext::ViewHir>(handlers::handle_view_hir) |
516 | .on::<lsp_ext::ViewCrateGraph>(handlers::handle_view_crate_graph) | 516 | .on::<lsp_ext::ViewCrateGraph>(handlers::handle_view_crate_graph) |
517 | .on::<lsp_ext::ViewItemTree>(handlers::handle_view_item_tree) | ||
517 | .on::<lsp_ext::ExpandMacro>(handlers::handle_expand_macro) | 518 | .on::<lsp_ext::ExpandMacro>(handlers::handle_expand_macro) |
518 | .on::<lsp_ext::ParentModule>(handlers::handle_parent_module) | 519 | .on::<lsp_ext::ParentModule>(handlers::handle_parent_module) |
519 | .on::<lsp_ext::Runnables>(handlers::handle_runnables) | 520 | .on::<lsp_ext::Runnables>(handlers::handle_runnables) |
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index 3c4eacfeb..fbe2ce1c9 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md | |||
@@ -1,5 +1,5 @@ | |||
1 | <!--- | 1 | <!--- |
2 | lsp_ext.rs hash: 10a8988e6893e6b2 | 2 | lsp_ext.rs hash: 49f253e4a9307d4f |
3 | 3 | ||
4 | If you need to change the above hash to make the test pass, please check if you | 4 | If you need to change the above hash to make the test pass, please check if you |
5 | need to adjust this doc as well and ping this issue: | 5 | need to adjust this doc as well and ping this issue: |
@@ -464,7 +464,7 @@ Clients are discouraged from but are allowed to use the `health` status to decid | |||
464 | **Request:** | 464 | **Request:** |
465 | 465 | ||
466 | ```typescript | 466 | ```typescript |
467 | interface SyntaxTeeParams { | 467 | interface SyntaxTreeParams { |
468 | textDocument: TextDocumentIdentifier, | 468 | textDocument: TextDocumentIdentifier, |
469 | range?: Range, | 469 | range?: Range, |
470 | } | 470 | } |
@@ -486,6 +486,22 @@ Primarily for debugging, but very useful for all people working on rust-analyzer | |||
486 | Returns a textual representation of the HIR of the function containing the cursor. | 486 | Returns a textual representation of the HIR of the function containing the cursor. |
487 | For debugging or when working on rust-analyzer itself. | 487 | For debugging or when working on rust-analyzer itself. |
488 | 488 | ||
489 | ## View ItemTree | ||
490 | |||
491 | **Method:** `rust-analyzer/viewItemTree` | ||
492 | |||
493 | **Request:** | ||
494 | |||
495 | ```typescript | ||
496 | interface ViewItemTreeParams { | ||
497 | textDocument: TextDocumentIdentifier, | ||
498 | } | ||
499 | ``` | ||
500 | |||
501 | **Response:** `string` | ||
502 | |||
503 | Returns a textual representation of the `ItemTree` of the currently open file, for debugging. | ||
504 | |||
489 | ## View Crate Graph | 505 | ## View Crate Graph |
490 | 506 | ||
491 | **Method:** `rust-analyzer/viewCrateGraph` | 507 | **Method:** `rust-analyzer/viewCrateGraph` |
diff --git a/editors/code/package.json b/editors/code/package.json index 1743b374c..17d9281ff 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -110,6 +110,11 @@ | |||
110 | "category": "Rust Analyzer" | 110 | "category": "Rust Analyzer" |
111 | }, | 111 | }, |
112 | { | 112 | { |
113 | "command": "rust-analyzer.viewItemTree", | ||
114 | "title": "Debug ItemTree", | ||
115 | "category": "Rust Analyzer" | ||
116 | }, | ||
117 | { | ||
113 | "command": "rust-analyzer.viewCrateGraph", | 118 | "command": "rust-analyzer.viewCrateGraph", |
114 | "title": "View Crate Graph", | 119 | "title": "View Crate Graph", |
115 | "category": "Rust Analyzer" | 120 | "category": "Rust Analyzer" |
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 8ab259af2..0fdb9fe05 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts | |||
@@ -429,6 +429,56 @@ export function viewHir(ctx: Ctx): Cmd { | |||
429 | }; | 429 | }; |
430 | } | 430 | } |
431 | 431 | ||
432 | export function viewItemTree(ctx: Ctx): Cmd { | ||
433 | const tdcp = new class implements vscode.TextDocumentContentProvider { | ||
434 | readonly uri = vscode.Uri.parse('rust-analyzer://viewItemTree/itemtree.rs'); | ||
435 | readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>(); | ||
436 | constructor() { | ||
437 | vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions); | ||
438 | vscode.window.onDidChangeActiveTextEditor(this.onDidChangeActiveTextEditor, this, ctx.subscriptions); | ||
439 | } | ||
440 | |||
441 | private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) { | ||
442 | if (isRustDocument(event.document)) { | ||
443 | // We need to order this after language server updates, but there's no API for that. | ||
444 | // Hence, good old sleep(). | ||
445 | void sleep(10).then(() => this.eventEmitter.fire(this.uri)); | ||
446 | } | ||
447 | } | ||
448 | private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) { | ||
449 | if (editor && isRustEditor(editor)) { | ||
450 | this.eventEmitter.fire(this.uri); | ||
451 | } | ||
452 | } | ||
453 | |||
454 | provideTextDocumentContent(_uri: vscode.Uri, ct: vscode.CancellationToken): vscode.ProviderResult<string> { | ||
455 | const rustEditor = ctx.activeRustEditor; | ||
456 | const client = ctx.client; | ||
457 | if (!rustEditor || !client) return ''; | ||
458 | |||
459 | const params = { | ||
460 | textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(rustEditor.document), | ||
461 | }; | ||
462 | return client.sendRequest(ra.viewItemTree, params, ct); | ||
463 | } | ||
464 | |||
465 | get onDidChange(): vscode.Event<vscode.Uri> { | ||
466 | return this.eventEmitter.event; | ||
467 | } | ||
468 | }; | ||
469 | |||
470 | ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider('rust-analyzer', tdcp)); | ||
471 | |||
472 | return async () => { | ||
473 | const document = await vscode.workspace.openTextDocument(tdcp.uri); | ||
474 | tdcp.eventEmitter.fire(tdcp.uri); | ||
475 | void await vscode.window.showTextDocument(document, { | ||
476 | viewColumn: vscode.ViewColumn.Two, | ||
477 | preserveFocus: true | ||
478 | }); | ||
479 | }; | ||
480 | } | ||
481 | |||
432 | export function viewCrateGraph(ctx: Ctx): Cmd { | 482 | export function viewCrateGraph(ctx: Ctx): Cmd { |
433 | return async () => { | 483 | return async () => { |
434 | const panel = vscode.window.createWebviewPanel("rust-analyzer.crate-graph", "rust-analyzer crate graph", vscode.ViewColumn.Two); | 484 | const panel = vscode.window.createWebviewPanel("rust-analyzer.crate-graph", "rust-analyzer crate graph", vscode.ViewColumn.Two); |
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index aa745a65c..6d5c2ea72 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts | |||
@@ -27,6 +27,12 @@ export const syntaxTree = new lc.RequestType<SyntaxTreeParams, string, void>("ru | |||
27 | 27 | ||
28 | export const viewHir = new lc.RequestType<lc.TextDocumentPositionParams, string, void>("rust-analyzer/viewHir"); | 28 | export const viewHir = new lc.RequestType<lc.TextDocumentPositionParams, string, void>("rust-analyzer/viewHir"); |
29 | 29 | ||
30 | export interface ViewItemTreeParams { | ||
31 | textDocument: lc.TextDocumentIdentifier; | ||
32 | } | ||
33 | |||
34 | export const viewItemTree = new lc.RequestType<ViewItemTreeParams, string, void>("rust-analyzer/viewItemTree"); | ||
35 | |||
30 | export const viewCrateGraph = new lc.RequestType0<string, void>("rust-analyzer/viewCrateGraph"); | 36 | export const viewCrateGraph = new lc.RequestType0<string, void>("rust-analyzer/viewCrateGraph"); |
31 | 37 | ||
32 | export interface ExpandMacroParams { | 38 | export interface ExpandMacroParams { |
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 516322d03..92c797d47 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts | |||
@@ -106,6 +106,7 @@ async function tryActivate(context: vscode.ExtensionContext) { | |||
106 | ctx.registerCommand('parentModule', commands.parentModule); | 106 | ctx.registerCommand('parentModule', commands.parentModule); |
107 | ctx.registerCommand('syntaxTree', commands.syntaxTree); | 107 | ctx.registerCommand('syntaxTree', commands.syntaxTree); |
108 | ctx.registerCommand('viewHir', commands.viewHir); | 108 | ctx.registerCommand('viewHir', commands.viewHir); |
109 | ctx.registerCommand('viewItemTree', commands.viewItemTree); | ||
109 | ctx.registerCommand('viewCrateGraph', commands.viewCrateGraph); | 110 | ctx.registerCommand('viewCrateGraph', commands.viewCrateGraph); |
110 | ctx.registerCommand('expandMacro', commands.expandMacro); | 111 | ctx.registerCommand('expandMacro', commands.expandMacro); |
111 | ctx.registerCommand('run', commands.run); | 112 | ctx.registerCommand('run', commands.run); |