Detailed changes
@@ -57,6 +57,6 @@ util = { path = "../util", features = ["test-support"] }
ctor = "0.1"
env_logger = "0.8"
rand = "0.8.3"
-tree-sitter-json = "0.19.0"
-tree-sitter-rust = "0.20.0"
+tree-sitter-json = "*"
+tree-sitter-rust = "*"
unindent = "0.1.7"
@@ -1730,7 +1730,7 @@ impl BufferSnapshot {
.and_then(|language| language.grammar.as_ref())?;
let mut cursor = QueryCursorHandle::new();
- cursor.set_byte_range(range);
+ cursor.set_byte_range(range.clone());
let matches = cursor.matches(
&grammar.outline_query,
tree.root_node(),
@@ -1750,7 +1750,10 @@ impl BufferSnapshot {
let items = matches
.filter_map(|mat| {
let item_node = mat.nodes_for_capture_index(item_capture_ix).next()?;
- let range = item_node.start_byte()..item_node.end_byte();
+ let item_range = item_node.start_byte()..item_node.end_byte();
+ if item_range.end < range.start || item_range.start > range.end {
+ return None;
+ }
let mut text = String::new();
let mut name_ranges = Vec::new();
let mut highlight_ranges = Vec::new();
@@ -1808,15 +1811,15 @@ impl BufferSnapshot {
}
while stack.last().map_or(false, |prev_range| {
- !prev_range.contains(&range.start) || !prev_range.contains(&range.end)
+ !prev_range.contains(&item_range.start) || !prev_range.contains(&item_range.end)
}) {
stack.pop();
}
- stack.push(range.clone());
+ stack.push(item_range.clone());
Some(OutlineItem {
depth: stack.len() - 1,
- range: self.anchor_after(range.start)..self.anchor_before(range.end),
+ range: self.anchor_after(item_range.start)..self.anchor_before(item_range.end),
text,
highlight_ranges,
name_ranges,
@@ -63,7 +63,7 @@ pub fn build_language_registry(login_shell_env_loaded: Task<()>) -> LanguageRegi
languages
}
-fn language(
+pub(crate) fn language(
name: &str,
grammar: tree_sitter::Language,
lsp_adapter: Option<Arc<dyn LspAdapter>>,
@@ -144,3 +144,54 @@ impl LspAdapter for TypeScriptLspAdapter {
}))
}
}
+
+#[cfg(test)]
+mod tests {
+ use std::sync::Arc;
+
+ use gpui::MutableAppContext;
+ use unindent::Unindent;
+
+ #[gpui::test]
+ fn test_outline(cx: &mut MutableAppContext) {
+ let language = crate::languages::language(
+ "typescript",
+ tree_sitter_typescript::language_typescript(),
+ None,
+ );
+
+ let text = r#"
+ function a() {
+ // local variables are omitted
+ let a1 = 1;
+ // all functions are included
+ async function a2() {}
+ }
+ // top-level variables are included
+ let b: C
+ function getB() {}
+ // exported variables are included
+ export const d = e;
+ "#
+ .unindent();
+
+ let buffer = cx.add_model(|cx| {
+ language::Buffer::new(0, text, cx).with_language(Arc::new(language), cx)
+ });
+ let outline = buffer.read(cx).snapshot().outline(None).unwrap();
+ assert_eq!(
+ outline
+ .items
+ .iter()
+ .map(|item| (item.text.as_str(), item.depth))
+ .collect::<Vec<_>>(),
+ &[
+ ("function a ( )", 0),
+ ("async function a2 ( )", 1),
+ ("let b", 0),
+ ("function getB ( )", 0),
+ ("const d", 0),
+ ]
+ );
+ }
+}
@@ -6,6 +6,10 @@
"enum" @context
name: (_) @name) @item
+(type_alias_declaration
+ "type" @context
+ name: (_) @name) @item
+
(function_declaration
"async"? @context
"function" @context
@@ -18,6 +22,12 @@
"interface" @context
name: (_) @name) @item
+(export_statement
+ (lexical_declaration
+ ["let" "const"] @context
+ (variable_declarator
+ name: (_) @name) @item))
+
(program
(lexical_declaration
["let" "const"] @context