diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 7acb36a92f..e09ee48da6 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -2253,7 +2253,7 @@ impl BufferSnapshot { } pub fn outline(&self, theme: Option<&SyntaxTheme>) -> Option> { - 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, + include_extra_context: bool, theme: Option<&SyntaxTheme>, ) -> Option>> { 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(); diff --git a/crates/language/src/buffer_tests.rs b/crates/language/src/buffer_tests.rs index be573aa895..9f44de40ac 100644 --- a/crates/language/src/buffer_tests.rs +++ b/crates/language/src/buffer_tests.rs @@ -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::>(), + &[("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::>(), + &[("function a", 0)] + ); +} + #[gpui::test] async fn test_symbols_containing(cx: &mut gpui::TestAppContext) { let text = r#" diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 32d74c9aef..0ff1d973d3 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -455,6 +455,7 @@ struct OutlineConfig { item_capture_ix: u32, name_capture_ix: u32, context_capture_ix: Option, + extra_context_capture_ix: Option, } 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) diff --git a/crates/zed/src/languages/elixir/indents.scm b/crates/zed/src/languages/elixir/indents.scm index e4139841fc..ab6fc4da67 100644 --- a/crates/zed/src/languages/elixir/indents.scm +++ b/crates/zed/src/languages/elixir/indents.scm @@ -1,6 +1,4 @@ -[ - (call) -] @indent +(call) @indent (_ "[" "]" @end) @indent (_ "{" "}" @end) @indent diff --git a/crates/zed/src/languages/elixir/outline.scm b/crates/zed/src/languages/elixir/outline.scm index 985c8ffdca..a3311fb6d4 100644 --- a/crates/zed/src/languages/elixir/outline.scm +++ b/crates/zed/src/languages/elixir/outline.scm @@ -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 diff --git a/crates/zed/src/languages/typescript.rs b/crates/zed/src/languages/typescript.rs index a25d7d02ea..7d2d580857 100644 --- a/crates/zed/src/languages/typescript.rs +++ b/crates/zed/src/languages/typescript.rs @@ -327,10 +327,10 @@ mod tests { .map(|item| (item.text.as_str(), item.depth)) .collect::>(), &[ - ("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), ] );