Start implementing context tree-sitter queries

First iteration of working ts context grammar

fmt
pull/6118/merge^2
SoraTenshi 1 year ago
parent 0fb8a87f7d
commit 7021fc42ee

@ -119,6 +119,10 @@ pub struct LanguageConfiguration {
#[serde(skip_serializing_if = "Option::is_none")]
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,
/// etc. Defaults to true. Optionally, this can be a list of 2-tuples
/// to specify a list of characters to pair. This overrides the
@ -127,9 +131,6 @@ pub struct LanguageConfiguration {
pub auto_pairs: Option<AutoPairs>,
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)]
@ -336,6 +337,15 @@ pub struct TextObjectQuery {
pub query: Query,
}
#[derive(Debug)]
pub struct ContextQuery {
pub query: Query,
}
impl ContextQuery {
// pub fn is_node(&self, node: &Node) -> bool {}
}
#[derive(Debug)]
pub enum CapturedNode<'a> {
Single(Node<'a>),
@ -528,6 +538,15 @@ impl LanguageConfiguration {
.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 {
&self.scope
}

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

@ -15,8 +15,9 @@ use helix_core::{
ensure_grapheme_boundary_next_byte, next_grapheme_boundary, prev_grapheme_boundary,
},
movement::Direction,
syntax::{self, HighlightEvent},
syntax::{self, HighlightEvent, RopeProvider},
text_annotations::TextAnnotations,
tree_sitter::QueryCursor,
unicode::width::UnicodeWidthStr,
visual_offset_from_block, Position, Range, Selection, Transaction,
};
@ -456,7 +457,6 @@ impl EditorView {
let base_primary_cursor_scope = theme
.find_scope_index("ui.cursor.primary")
.unwrap_or(base_cursor_scope);
let cursor_scope = match mode {
Mode::Insert => theme.find_scope_index_exact("ui.cursor.insert"),
Mode::Select => theme.find_scope_index_exact("ui.cursor.select"),
@ -843,7 +843,7 @@ impl EditorView {
let context_nodes = doc
.language_config()
.and_then(|lc| lc.sticky_context_nodes.as_ref());
.and_then(|lang| lang.context_query())?;
let mut parent = tree
.root_node()
@ -855,7 +855,6 @@ impl EditorView {
while let Some(node) = parent {
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 let Some(prev_line) = context.last() {
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 {
visual_line: 0, // with sorting it will be done
line_nr: line,
@ -903,19 +918,23 @@ impl EditorView {
.collect();
if config.sticky_context.indicator {
let mut str = String::new();
let message = "┤Sticky Context├";
let side_placeholder = (viewport.width as usize)
.saturating_div(2)
.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 {
str.push_str(message);
}
str.push_str(&"─".repeat(side_placeholder));
str.extend(std::iter::repeat("─").take(side_placeholder));
context.push(StickyNode {
visual_line: context.len() as u16,

@ -11,7 +11,6 @@ auto-format = true
comment-token = "//"
language-server = { command = "rust-analyzer" }
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]
'(' = ')'

@ -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",
"textobjects.scm",
"indents.scm",
"context.scm",
];
for language in lang_config().language {

Loading…
Cancel
Save