diff options
Diffstat (limited to 'cli/lsp/tsc.rs')
-rw-r--r-- | cli/lsp/tsc.rs | 86 |
1 files changed, 85 insertions, 1 deletions
diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index 38f291d03..fef605397 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -41,7 +41,7 @@ use std::collections::HashSet; use std::thread; use std::{borrow::Cow, cmp}; use std::{collections::HashMap, path::Path}; -use text_size::TextSize; +use text_size::{TextRange, TextSize}; use tokio::sync::mpsc; use tokio::sync::oneshot; @@ -621,6 +621,90 @@ impl NavigationTree { } } + pub fn collect_document_symbols( + &self, + line_index: &LineIndex, + document_symbols: &mut Vec<lsp::DocumentSymbol>, + ) -> bool { + let mut should_include = self.should_include_entry(); + if !should_include + && self.child_items.as_ref().map_or(true, |v| v.is_empty()) + { + return false; + } + + let children = self + .child_items + .as_ref() + .map_or(&[] as &[NavigationTree], |v| v.as_slice()); + for span in self.spans.iter() { + let range = TextRange::at(span.start.into(), span.length.into()); + let mut symbol_children = Vec::<lsp::DocumentSymbol>::new(); + for child in children.iter() { + let should_traverse_child = child + .spans + .iter() + .map(|child_span| { + TextRange::at(child_span.start.into(), child_span.length.into()) + }) + .any(|child_range| range.intersect(child_range).is_some()); + if should_traverse_child { + let included_child = + child.collect_document_symbols(line_index, &mut symbol_children); + should_include = should_include || included_child; + } + } + + if should_include { + let mut selection_span = span; + if let Some(name_span) = self.name_span.as_ref() { + let name_range = + TextRange::at(name_span.start.into(), name_span.length.into()); + if range.contains_range(name_range) { + selection_span = name_span; + } + } + + let mut tags: Option<Vec<lsp::SymbolTag>> = None; + let kind_modifiers = parse_kind_modifier(&self.kind_modifiers); + if kind_modifiers.contains("deprecated") { + tags = Some(vec![lsp::SymbolTag::Deprecated]); + } + + let children = if !symbol_children.is_empty() { + Some(symbol_children) + } else { + None + }; + + // The field `deprecated` is deprecated but DocumentSymbol does not have + // a default, therefore we have to supply the deprecated deprecated + // field. It is like a bad version of Inception. + #[allow(deprecated)] + document_symbols.push(lsp::DocumentSymbol { + name: self.text.clone(), + kind: self.kind.clone().into(), + range: span.to_range(line_index), + selection_range: selection_span.to_range(line_index), + tags, + children, + detail: None, + deprecated: None, + }) + } + } + + should_include + } + + fn should_include_entry(&self) -> bool { + if let ScriptElementKind::Alias = self.kind { + return false; + } + + !self.text.is_empty() && self.text != "<function>" && self.text != "<class>" + } + pub fn walk<F>(&self, callback: &F) where F: Fn(&NavigationTree, Option<&NavigationTree>), |