summaryrefslogtreecommitdiff
path: root/cli/lsp/tsc.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/lsp/tsc.rs')
-rw-r--r--cli/lsp/tsc.rs86
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>),