diff options
Diffstat (limited to 'helix-core/src/syntax.rs')
-rw-r--r-- | helix-core/src/syntax.rs | 90 |
1 files changed, 49 insertions, 41 deletions
diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index 441802a5..18504c21 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>>, #[serde(skip_serializing_if = "Option::is_none")] pub debugger: Option<DebugAdapterConfig>, } @@ -160,6 +162,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") @@ -208,13 +236,14 @@ 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() { None } else { - let language = get_language(&crate::RUNTIME_DIR, &self.language_id).ok()?; + let language = get_language(&crate::RUNTIME_DIR, &self.language_id) + .map_err(|e| log::info!("{}", e)) + .ok()?; let config = HighlightConfiguration::new( language, &highlights_query, @@ -258,6 +287,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 } @@ -451,7 +492,7 @@ impl Syntax { /// Iterate over the highlighted regions for a given slice of source code. pub fn highlight_iter<'a>( - &self, + &'a self, source: RopeSlice<'a>, range: Option<std::ops::Range<usize>>, cancellation_flag: Option<&'a AtomicUsize>, @@ -466,11 +507,10 @@ impl Syntax { let highlighter = &mut ts_parser.borrow_mut(); highlighter.cursors.pop().unwrap_or_else(QueryCursor::new) }); - let tree_ref = unsafe { mem::transmute::<_, &'static Tree>(self.tree()) }; + let tree_ref = self.tree(); let cursor_ref = unsafe { mem::transmute::<_, &'static mut QueryCursor>(&mut cursor) }; - let query_ref = unsafe { mem::transmute::<_, &'static Query>(&self.config.query) }; - let config_ref = - unsafe { mem::transmute::<_, &'static HighlightConfiguration>(self.config.as_ref()) }; + let query_ref = &self.config.query; + let config_ref = self.config.as_ref(); // if reusing cursors & no range this resets to whole range cursor_ref.set_byte_range(range.clone().unwrap_or(0..usize::MAX)); @@ -582,39 +622,7 @@ impl LanguageLayer { self.tree.as_ref(), ) .ok_or(Error::Cancelled)?; - // unsafe { syntax.parser.set_cancellation_flag(None) }; - // let mut cursor = syntax.cursors.pop().unwrap_or_else(QueryCursor::new); - - // Process combined injections. (ERB, EJS, etc https://github.com/tree-sitter/tree-sitter/pull/526) - // if let Some(combined_injections_query) = &config.combined_injections_query { - // let mut injections_by_pattern_index = - // vec![(None, Vec::new(), false); combined_injections_query.pattern_count()]; - // let matches = - // cursor.matches(combined_injections_query, tree.root_node(), RopeProvider(source)); - // for mat in matches { - // let entry = &mut injections_by_pattern_index[mat.pattern_index]; - // let (language_name, content_node, include_children) = - // injection_for_match(config, combined_injections_query, &mat, source); - // if language_name.is_some() { - // entry.0 = language_name; - // } - // if let Some(content_node) = content_node { - // entry.1.push(content_node); - // } - // entry.2 = include_children; - // } - // for (lang_name, content_nodes, includes_children) in injections_by_pattern_index { - // if let (Some(lang_name), false) = (lang_name, content_nodes.is_empty()) { - // if let Some(next_config) = (injection_callback)(lang_name) { - // let ranges = - // Self::intersect_ranges(&ranges, &content_nodes, includes_children); - // if !ranges.is_empty() { - // queue.push((next_config, depth + 1, ranges)); - // } - // } - // } - // } - // } + self.tree = Some(tree) } Ok(()) |