From b959162ceb41d891c8b5fad6145ca5d1a4964a54 Mon Sep 17 00:00:00 2001 From: Luke Halasy Date: Sun, 10 Sep 2023 08:57:44 -0400 Subject: [PATCH] Add tree-sitter-highlight-name command (#8170) * adds treesitter-highlight-name command * commit documentation changes * moves the get_highlight_name function into core/syntax * rename get_highlight_name function to get_highlight_for_node_at_position * addresses pr comments: moves fn into helper fn, simplifies a lot * commit updated documentation changes * changes scope method to return &str so that callers can decide whether or not to own --- book/src/generated/typable-cmd.md | 1 + helix-term/src/commands/typed.rs | 85 +++++++++++++++++++++++++++++++ helix-view/src/theme.rs | 5 ++ 3 files changed, 91 insertions(+) diff --git a/book/src/generated/typable-cmd.md b/book/src/generated/typable-cmd.md index 4a6e697a..4b737893 100644 --- a/book/src/generated/typable-cmd.md +++ b/book/src/generated/typable-cmd.md @@ -55,6 +55,7 @@ | `:lsp-restart` | Restarts the language servers used by the current doc | | `:lsp-stop` | Stops the language servers that are used by the current doc | | `:tree-sitter-scopes` | Display tree sitter scopes, primarily for theming and development. | +| `:tree-sitter-highlight-name` | Display name of tree-sitter highlight scope under the cursor. | | `:debug-start`, `:dbg` | Start a debug session from a given template with given parameters. | | `:debug-remote`, `:dbg-tcp` | Connect to a debug adapter by TCP address and start a debugging session from a given template with given parameters. | | `:debug-eval` | Evaluate expression in current debug context. | diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index ef539180..4237b6f6 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -1527,6 +1527,84 @@ fn tree_sitter_scopes( Ok(()) } +fn tree_sitter_highlight_name( + cx: &mut compositor::Context, + _args: &[Cow], + event: PromptEvent, +) -> anyhow::Result<()> { + fn find_highlight_at_cursor( + cx: &mut compositor::Context<'_>, + ) -> Option { + use helix_core::syntax::HighlightEvent; + + let (view, doc) = current!(cx.editor); + let syntax = doc.syntax()?; + let text = doc.text().slice(..); + let cursor = doc.selection(view.id).primary().cursor(text); + let byte = text.char_to_byte(cursor); + let node = syntax + .tree() + .root_node() + .descendant_for_byte_range(byte, byte)?; + // Query the same range as the one used in syntax highlighting. + let range = { + // Calculate viewport byte ranges: + let row = text.char_to_line(view.offset.anchor.min(text.len_chars())); + // Saturating subs to make it inclusive zero indexing. + let last_line = text.len_lines().saturating_sub(1); + let height = view.inner_area(doc).height; + let last_visible_line = (row + height as usize).saturating_sub(1).min(last_line); + let start = text.line_to_byte(row.min(last_line)); + let end = text.line_to_byte(last_visible_line + 1); + + start..end + }; + + let mut highlight = None; + + for event in syntax.highlight_iter(text, Some(range), None) { + match event.unwrap() { + HighlightEvent::Source { start, end } + if start == node.start_byte() && end == node.end_byte() => + { + return highlight; + } + HighlightEvent::HighlightStart(hl) => { + highlight = Some(hl); + } + _ => (), + } + } + + None + } + + if event != PromptEvent::Validate { + return Ok(()); + } + + let Some(highlight) = find_highlight_at_cursor(cx) else { + return Ok(()); + }; + + let content = cx.editor.theme.scope(highlight.0).to_string(); + + let callback = async move { + let call: job::Callback = Callback::EditorCompositor(Box::new( + move |editor: &mut Editor, compositor: &mut Compositor| { + let content = ui::Markdown::new(content, editor.syn_loader.clone()); + let popup = Popup::new("hover", content).auto_close(true); + compositor.replace_or_push("hover", popup); + }, + )); + Ok(call) + }; + + cx.jobs.callback(callback); + + Ok(()) +} + fn vsplit( cx: &mut compositor::Context, args: &[Cow], @@ -2703,6 +2781,13 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ fun: tree_sitter_scopes, signature: CommandSignature::none(), }, + TypableCommand { + name: "tree-sitter-highlight-name", + aliases: &[], + doc: "Display name of tree-sitter highlight scope under the cursor.", + fun: tree_sitter_highlight_name, + signature: CommandSignature::none(), + }, TypableCommand { name: "debug-start", aliases: &["dbg"], diff --git a/helix-view/src/theme.rs b/helix-view/src/theme.rs index a288ae9a..4acc5664 100644 --- a/helix-view/src/theme.rs +++ b/helix-view/src/theme.rs @@ -297,6 +297,11 @@ impl Theme { self.highlights[index] } + #[inline] + pub fn scope(&self, index: usize) -> &str { + &self.scopes[index] + } + pub fn name(&self) -> &str { &self.name }