Detailed changes
@@ -2253,7 +2253,7 @@ impl BufferSnapshot {
}
pub fn outline(&self, theme: Option<&SyntaxTheme>) -> Option<Outline<Anchor>> {
- self.outline_items_containing(0..self.len(), theme)
+ self.outline_items_containing(0..self.len(), true, theme)
.map(Outline::new)
}
@@ -2265,6 +2265,7 @@ impl BufferSnapshot {
let position = position.to_offset(self);
let mut items = self.outline_items_containing(
position.saturating_sub(1)..self.len().min(position + 1),
+ false,
theme,
)?;
let mut prev_depth = None;
@@ -2279,6 +2280,7 @@ impl BufferSnapshot {
fn outline_items_containing(
&self,
range: Range<usize>,
+ include_extra_context: bool,
theme: Option<&SyntaxTheme>,
) -> Option<Vec<OutlineItem<Anchor>>> {
let mut matches = self.syntax.matches(range.clone(), &self.text, |grammar| {
@@ -2313,7 +2315,10 @@ impl BufferSnapshot {
let node_is_name;
if capture.index == config.name_capture_ix {
node_is_name = true;
- } else if Some(capture.index) == config.context_capture_ix {
+ } else if Some(capture.index) == config.context_capture_ix
+ || (Some(capture.index) == config.extra_context_capture_ix
+ && include_extra_context)
+ {
node_is_name = false;
} else {
continue;
@@ -2340,10 +2345,12 @@ impl BufferSnapshot {
buffer_ranges.first().unwrap().0.start..buffer_ranges.last().unwrap().0.end,
true,
);
+ let mut last_buffer_range_end = 0;
for (buffer_range, is_name) in buffer_ranges {
- if !text.is_empty() {
+ if !text.is_empty() && buffer_range.start > last_buffer_range_end {
text.push(' ');
}
+ last_buffer_range_end = buffer_range.end;
if is_name {
let mut start = text.len();
let end = start + buffer_range.len();
@@ -592,6 +592,52 @@ async fn test_outline_nodes_with_newlines(cx: &mut gpui::TestAppContext) {
);
}
+#[gpui::test]
+async fn test_outline_with_extra_context(cx: &mut gpui::TestAppContext) {
+ let language = javascript_lang()
+ .with_outline_query(
+ r#"
+ (function_declaration
+ "function" @context
+ name: (_) @name
+ parameters: (formal_parameters
+ "(" @context.extra
+ ")" @context.extra)) @item
+ "#,
+ )
+ .unwrap();
+
+ let text = r#"
+ function a() {}
+ function b(c) {}
+ "#
+ .unindent();
+
+ let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(language), cx));
+ let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot());
+
+ // extra context nodes are included in the outline.
+ let outline = snapshot.outline(None).unwrap();
+ assert_eq!(
+ outline
+ .items
+ .iter()
+ .map(|item| (item.text.as_str(), item.depth))
+ .collect::<Vec<_>>(),
+ &[("function a()", 0), ("function b( )", 0),]
+ );
+
+ // extra context nodes do not appear in breadcrumbs.
+ let symbols = snapshot.symbols_containing(3, None).unwrap();
+ assert_eq!(
+ symbols
+ .iter()
+ .map(|item| (item.text.as_str(), item.depth))
+ .collect::<Vec<_>>(),
+ &[("function a", 0)]
+ );
+}
+
#[gpui::test]
async fn test_symbols_containing(cx: &mut gpui::TestAppContext) {
let text = r#"
@@ -455,6 +455,7 @@ struct OutlineConfig {
item_capture_ix: u32,
name_capture_ix: u32,
context_capture_ix: Option<u32>,
+ extra_context_capture_ix: Option<u32>,
}
struct InjectionConfig {
@@ -1091,12 +1092,14 @@ impl Language {
let mut item_capture_ix = None;
let mut name_capture_ix = None;
let mut context_capture_ix = None;
+ let mut extra_context_capture_ix = None;
get_capture_indices(
&query,
&mut [
("item", &mut item_capture_ix),
("name", &mut name_capture_ix),
("context", &mut context_capture_ix),
+ ("context.extra", &mut extra_context_capture_ix),
],
);
if let Some((item_capture_ix, name_capture_ix)) = item_capture_ix.zip(name_capture_ix) {
@@ -1105,6 +1108,7 @@ impl Language {
item_capture_ix,
name_capture_ix,
context_capture_ix,
+ extra_context_capture_ix,
});
}
Ok(self)
@@ -1,6 +1,4 @@
-[
- (call)
-] @indent
+(call) @indent
(_ "[" "]" @end) @indent
(_ "{" "}" @end) @indent
@@ -8,9 +8,19 @@
(arguments
[
(identifier) @name
- (call target: (identifier) @name)
+ (call
+ target: (identifier) @name
+ (arguments
+ "(" @context.extra
+ _* @context.extra
+ ")" @context.extra))
(binary_operator
- left: (call target: (identifier) @name)
+ left: (call
+ target: (identifier) @name
+ (arguments
+ "(" @context.extra
+ _* @context.extra
+ ")" @context.extra))
operator: "when")
])
(#match? @context "^(def|defp|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp)$")) @item
@@ -327,10 +327,10 @@ mod tests {
.map(|item| (item.text.as_str(), item.depth))
.collect::<Vec<_>>(),
&[
- ("function a ( )", 0),
- ("async function a2 ( )", 1),
+ ("function a()", 0),
+ ("async function a2()", 1),
("let b", 0),
- ("function getB ( )", 0),
+ ("function getB()", 0),
("const d", 0),
]
);