Skip to content

Commit

Permalink
Start implementing context tree-sitter queries
Browse files Browse the repository at this point in the history
First iteration of working ts context grammar
  • Loading branch information
SoraTenshi committed Mar 10, 2023
1 parent 0fb8a87 commit 49e657d
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 13 deletions.
25 changes: 22 additions & 3 deletions helix-core/src/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)]
Expand Down Expand Up @@ -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>),
Expand Down Expand Up @@ -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
}
Expand Down
4 changes: 4 additions & 0 deletions helix-term/src/health.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub enum TsFeature {
Highlight,
TextObject,
AutoIndent,
Context
}

impl TsFeature {
Expand All @@ -24,6 +25,7 @@ impl TsFeature {
Self::Highlight => "highlights.scm",
Self::TextObject => "textobjects.scm",
Self::AutoIndent => "indents.scm",
Self::Context => "context.scm",
}
}

Expand All @@ -32,6 +34,7 @@ impl TsFeature {
Self::Highlight => "Syntax Highlighting",
Self::TextObject => "Treesitter Textobjects",
Self::AutoIndent => "Auto Indent",
Self::Context => "Sticky Context",
}
}

Expand All @@ -40,6 +43,7 @@ impl TsFeature {
Self::Highlight => "Highlight",
Self::TextObject => "Textobject",
Self::AutoIndent => "Indent",
Self::Context => "Context",
}
}
}
Expand Down
37 changes: 28 additions & 9 deletions helix-term/src/ui/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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"),
Expand Down Expand Up @@ -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()
Expand All @@ -855,15 +855,30 @@ 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 {
context.pop();
}
}

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,
Expand Down Expand Up @@ -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,
Expand Down
1 change: 0 additions & 1 deletion languages.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
'(' = ')'
Expand Down
29 changes: 29 additions & 0 deletions runtime/queries/rust/context.scm
Original file line number Diff line number Diff line change
@@ -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)
1 change: 1 addition & 0 deletions xtask/src/querycheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub fn query_check() -> Result<(), DynError> {
"injections.scm",
"textobjects.scm",
"indents.scm",
"context.scm",
];

for language in lang_config().language {
Expand Down

0 comments on commit 49e657d

Please sign in to comment.