Start implementing context tree-sitter queries

First iteration of working ts context grammar

fmt
SoraTenshi 1 year ago
parent 0fb8a87f7d
commit 7021fc42ee

@ -119,6 +119,10 @@ pub struct LanguageConfiguration {
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub debugger: Option<DebugAdapterConfig>, pub debugger: Option<DebugAdapterConfig>,
/// The Grammar query for Sticky Context
#[serde(skip)]
pub(crate) context_query: OnceCell<Option<ContextQuery>>,
/// Automatic insertion of pairs to parentheses, brackets, /// Automatic insertion of pairs to parentheses, brackets,
/// etc. Defaults to true. Optionally, this can be a list of 2-tuples /// etc. Defaults to true. Optionally, this can be a list of 2-tuples
/// to specify a list of characters to pair. This overrides the /// to specify a list of characters to pair. This overrides the
@ -127,9 +131,6 @@ pub struct LanguageConfiguration {
pub auto_pairs: Option<AutoPairs>, pub auto_pairs: Option<AutoPairs>,
pub rulers: Option<Vec<u16>>, // if set, override editor's rulers pub rulers: Option<Vec<u16>>, // if set, override editor's rulers
/// List of tree-sitter nodes that should be displayed in the sticky context.
pub sticky_context_nodes: Option<Vec<String>>,
} }
#[derive(Debug, PartialEq, Eq, Hash)] #[derive(Debug, PartialEq, Eq, Hash)]
@ -336,6 +337,15 @@ pub struct TextObjectQuery {
pub query: Query, pub query: Query,
} }
#[derive(Debug)]
pub struct ContextQuery {
pub query: Query,
}
impl ContextQuery {
// pub fn is_node(&self, node: &Node) -> bool {}
}
#[derive(Debug)] #[derive(Debug)]
pub enum CapturedNode<'a> { pub enum CapturedNode<'a> {
Single(Node<'a>), Single(Node<'a>),
@ -528,6 +538,15 @@ impl LanguageConfiguration {
.as_ref() .as_ref()
} }
pub fn context_query(&self) -> Option<&ContextQuery> {
self.context_query
.get_or_init(|| {
self.load_query("context.scm")
.map(|query| ContextQuery { query })
})
.as_ref()
}
pub fn scope(&self) -> &str { pub fn scope(&self) -> &str {
&self.scope &self.scope
} }

@ -12,6 +12,7 @@ pub enum TsFeature {
Highlight, Highlight,
TextObject, TextObject,
AutoIndent, AutoIndent,
Context,
} }
impl TsFeature { impl TsFeature {
@ -24,6 +25,7 @@ impl TsFeature {
Self::Highlight => "highlights.scm", Self::Highlight => "highlights.scm",
Self::TextObject => "textobjects.scm", Self::TextObject => "textobjects.scm",
Self::AutoIndent => "indents.scm", Self::AutoIndent => "indents.scm",
Self::Context => "context.scm",
} }
} }
@ -32,6 +34,7 @@ impl TsFeature {
Self::Highlight => "Syntax Highlighting", Self::Highlight => "Syntax Highlighting",
Self::TextObject => "Treesitter Textobjects", Self::TextObject => "Treesitter Textobjects",
Self::AutoIndent => "Auto Indent", Self::AutoIndent => "Auto Indent",
Self::Context => "Sticky Context",
} }
} }
@ -40,6 +43,7 @@ impl TsFeature {
Self::Highlight => "Highlight", Self::Highlight => "Highlight",
Self::TextObject => "Textobject", Self::TextObject => "Textobject",
Self::AutoIndent => "Indent", Self::AutoIndent => "Indent",
Self::Context => "Context",
} }
} }
} }

@ -15,8 +15,9 @@ use helix_core::{
ensure_grapheme_boundary_next_byte, next_grapheme_boundary, prev_grapheme_boundary, ensure_grapheme_boundary_next_byte, next_grapheme_boundary, prev_grapheme_boundary,
}, },
movement::Direction, movement::Direction,
syntax::{self, HighlightEvent}, syntax::{self, HighlightEvent, RopeProvider},
text_annotations::TextAnnotations, text_annotations::TextAnnotations,
tree_sitter::QueryCursor,
unicode::width::UnicodeWidthStr, unicode::width::UnicodeWidthStr,
visual_offset_from_block, Position, Range, Selection, Transaction, visual_offset_from_block, Position, Range, Selection, Transaction,
}; };
@ -456,7 +457,6 @@ impl EditorView {
let base_primary_cursor_scope = theme let base_primary_cursor_scope = theme
.find_scope_index("ui.cursor.primary") .find_scope_index("ui.cursor.primary")
.unwrap_or(base_cursor_scope); .unwrap_or(base_cursor_scope);
let cursor_scope = match mode { let cursor_scope = match mode {
Mode::Insert => theme.find_scope_index_exact("ui.cursor.insert"), Mode::Insert => theme.find_scope_index_exact("ui.cursor.insert"),
Mode::Select => theme.find_scope_index_exact("ui.cursor.select"), Mode::Select => theme.find_scope_index_exact("ui.cursor.select"),
@ -843,7 +843,7 @@ impl EditorView {
let context_nodes = doc let context_nodes = doc
.language_config() .language_config()
.and_then(|lc| lc.sticky_context_nodes.as_ref()); .and_then(|lang| lang.context_query())?;
let mut parent = tree let mut parent = tree
.root_node() .root_node()
@ -855,7 +855,6 @@ impl EditorView {
while let Some(node) = parent { while let Some(node) = parent {
let line = text.char_to_line(node.start_byte()); let line = text.char_to_line(node.start_byte());
// if parent of previous node is still on the same line, use the parent node // if parent of previous node is still on the same line, use the parent node
if let Some(prev_line) = context.last() { if let Some(prev_line) = context.last() {
if prev_line.line_nr == line { if prev_line.line_nr == line {
@ -863,7 +862,23 @@ impl EditorView {
} }
} }
if context_nodes.map_or(true, |nodes| nodes.iter().any(|n| n == node.kind())) { let mut cursor = QueryCursor::new();
let query = &context_nodes.query;
let query_nodes = cursor.matches(query, node, RopeProvider(text));
let query_ranges = query_nodes
.flat_map(|qnode| {
qnode
.captures
.iter()
.map(|capture| capture.node.start_byte()..capture.node.end_byte())
})
.collect::<Vec<std::ops::Range<usize>>>();
if query_ranges
.iter()
.any(|query_range| query_range.contains(&node.start_byte()))
{
context.push(StickyNode { context.push(StickyNode {
visual_line: 0, // with sorting it will be done visual_line: 0, // with sorting it will be done
line_nr: line, line_nr: line,
@ -903,19 +918,23 @@ impl EditorView {
.collect(); .collect();
if config.sticky_context.indicator { if config.sticky_context.indicator {
let mut str = String::new();
let message = "┤Sticky Context├"; let message = "┤Sticky Context├";
let side_placeholder = (viewport.width as usize) let side_placeholder = (viewport.width as usize)
.saturating_div(2) .saturating_div(2)
.saturating_sub(message.len() - 1); .saturating_sub(message.len() - 1);
str.push_str(&"─".repeat(side_placeholder)); let added_length = if side_placeholder > 1 {
message.len()
} else {
0
};
let mut str = String::with_capacity("─".len() * side_placeholder * 2 + added_length);
str.extend(std::iter::repeat("─").take(side_placeholder));
if side_placeholder > 1 { if side_placeholder > 1 {
str.push_str(message); str.push_str(message);
} }
str.extend(std::iter::repeat("─").take(side_placeholder));
str.push_str(&"─".repeat(side_placeholder));
context.push(StickyNode { context.push(StickyNode {
visual_line: context.len() as u16, visual_line: context.len() as u16,

@ -11,7 +11,6 @@ auto-format = true
comment-token = "//" comment-token = "//"
language-server = { command = "rust-analyzer" } language-server = { command = "rust-analyzer" }
indent = { tab-width = 4, unit = " " } indent = { tab-width = 4, unit = " " }
sticky-context-nodes = ["impl_item", "function_item", "struct_item", "enum_item", "match_expression", "match_arm", "let_declaration"]
[language.auto-pairs] [language.auto-pairs]
'(' = ')' '(' = ')'

@ -0,0 +1,29 @@
; Credits to: nvim-treesitter/nvim-treesitter-context
(for_expression
body: (_ (_) @context.end)
) @context
(if_expression
consequence: (_ (_) @context.end)
) @context
(function_item
body: (_ (_) @context.end)
) @context
(impl_item
type: (_) @context.final
) @context
(struct_item
body: (_ (_) @context.end)
) @context
([
(mod_item)
(enum_item)
(closure_expression)
(expression_statement)
(loop_expression)
(match_expression)
] @context)

@ -11,6 +11,7 @@ pub fn query_check() -> Result<(), DynError> {
"injections.scm", "injections.scm",
"textobjects.scm", "textobjects.scm",
"indents.scm", "indents.scm",
"context.scm",
]; ];
for language in lang_config().language { for language in lang_config().language {

Loading…
Cancel
Save