aboutsummaryrefslogtreecommitdiff
path: root/helix-core/src/syntax.rs
diff options
context:
space:
mode:
authorGokul Soumya2021-10-23 02:41:19 +0000
committerGitHub2021-10-23 02:41:19 +0000
commit4ee92cad19cc94f0751f91fa9391d1899353d740 (patch)
tree794be048905f5d5026ba1968dc0152d12473c024 /helix-core/src/syntax.rs
parentc5298caa752dee136ab1a21dae27a702a00d8eea (diff)
Add treesitter textobjects (#728)
* Add treesitter textobject queries Only for Go, Python and Rust for now. * Add tree-sitter textobjects Only has functions and class objects as of now. * Fix tests * Add docs for tree-sitter textobjects * Add guide for creating new textobject queries * Add parameter textobject Only parameter.inside is implemented now, parameter.around will probably require custom predicates akin to nvim' `make-range` since we want to select a trailing comma too (a comma will be an anonymous node and matching against them doesn't work similar to named nodes) * Simplify TextObject cell init
Diffstat (limited to 'helix-core/src/syntax.rs')
-rw-r--r--helix-core/src/syntax.rs43
1 files changed, 41 insertions, 2 deletions
diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs
index 0929e38f..f4b4535b 100644
--- a/helix-core/src/syntax.rs
+++ b/helix-core/src/syntax.rs
@@ -49,7 +49,7 @@ pub struct Configuration {
#[serde(rename_all = "kebab-case")]
pub struct LanguageConfiguration {
#[serde(rename = "name")]
- pub(crate) language_id: String,
+ pub language_id: String,
pub scope: String, // source.rust
pub file_types: Vec<String>, // filename ends_with? <Gemfile, rb, etc>
pub roots: Vec<String>, // these indicate project roots <.git, Cargo.toml>
@@ -76,6 +76,8 @@ pub struct LanguageConfiguration {
#[serde(skip)]
pub(crate) indent_query: OnceCell<Option<IndentQuery>>,
+ #[serde(skip)]
+ pub(crate) textobject_query: OnceCell<Option<TextObjectQuery>>,
}
#[derive(Debug, Serialize, Deserialize)]
@@ -105,6 +107,32 @@ pub struct IndentQuery {
pub outdent: HashSet<String>,
}
+#[derive(Debug)]
+pub struct TextObjectQuery {
+ pub query: Query,
+}
+
+impl TextObjectQuery {
+ /// Run the query on the given node and return sub nodes which match given
+ /// capture ("function.inside", "class.around", etc).
+ pub fn capture_nodes<'a>(
+ &'a self,
+ capture_name: &str,
+ node: Node<'a>,
+ slice: RopeSlice<'a>,
+ cursor: &'a mut QueryCursor,
+ ) -> Option<impl Iterator<Item = Node<'a>>> {
+ let capture_idx = self.query.capture_index_for_name(capture_name)?;
+ let captures = cursor.captures(&self.query, node, RopeProvider(slice));
+
+ captures
+ .filter_map(move |(mat, idx)| {
+ (mat.captures[idx].index == capture_idx).then(|| mat.captures[idx].node)
+ })
+ .into()
+ }
+}
+
fn load_runtime_file(language: &str, filename: &str) -> Result<String, std::io::Error> {
let path = crate::RUNTIME_DIR
.join("queries")
@@ -153,7 +181,6 @@ impl LanguageConfiguration {
// highlights_query += "\n(ERROR) @error";
let injections_query = read_query(&language, "injections.scm");
-
let locals_query = read_query(&language, "locals.scm");
if highlights_query.is_empty() {
@@ -203,6 +230,18 @@ impl LanguageConfiguration {
.as_ref()
}
+ pub fn textobject_query(&self) -> Option<&TextObjectQuery> {
+ self.textobject_query
+ .get_or_init(|| -> Option<TextObjectQuery> {
+ let lang_name = self.language_id.to_ascii_lowercase();
+ let query_text = read_query(&lang_name, "textobjects.scm");
+ let lang = self.highlight_config.get()?.as_ref()?.language;
+ let query = Query::new(lang, &query_text).ok()?;
+ Some(TextObjectQuery { query })
+ })
+ .as_ref()
+ }
+
pub fn scope(&self) -> &str {
&self.scope
}