Add textobjects queries (#20924)

Co-Authored-By: Max <max@zed.dev>

Release Notes:

- vim: Added motions `[[`, `[]`, `]]`, `][` for navigating by section,
`[m`, `]m`, `[M`, `]M` for navigating by method, and `[*`, `]*`, `[/`,
`]/` for comments. These currently only work for languages built in to
Zed, as they are powered by new tree-sitter queries.
- vim: Added new text objects: `ic`, `ac` for inside/around classes,
`if`,`af` for functions/methods, and `g c` for comments. These currently
only work for languages built in to Zed, as they are powered by new
tree-sitter queries.

---------

Co-authored-by: Max <max@zed.dev>
This commit is contained in:
Conrad Irwin 2024-12-03 09:37:01 -08:00 committed by GitHub
parent c443307c19
commit 75c9dc179b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 1205 additions and 26 deletions

View file

@ -78,7 +78,7 @@ pub use language_registry::{
};
pub use lsp::LanguageServerId;
pub use outline::*;
pub use syntax_map::{OwnedSyntaxLayer, SyntaxLayer};
pub use syntax_map::{OwnedSyntaxLayer, SyntaxLayer, TreeSitterOptions};
pub use text::{AnchorRangeExt, LineEnding};
pub use tree_sitter::{Node, Parser, Tree, TreeCursor};
@ -848,6 +848,7 @@ pub struct Grammar {
pub(crate) runnable_config: Option<RunnableConfig>,
pub(crate) indents_config: Option<IndentConfig>,
pub outline_config: Option<OutlineConfig>,
pub text_object_config: Option<TextObjectConfig>,
pub embedding_config: Option<EmbeddingConfig>,
pub(crate) injection_config: Option<InjectionConfig>,
pub(crate) override_config: Option<OverrideConfig>,
@ -873,6 +874,44 @@ pub struct OutlineConfig {
pub annotation_capture_ix: Option<u32>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum TextObject {
InsideFunction,
AroundFunction,
InsideClass,
AroundClass,
InsideComment,
AroundComment,
}
impl TextObject {
pub fn from_capture_name(name: &str) -> Option<TextObject> {
match name {
"function.inside" => Some(TextObject::InsideFunction),
"function.around" => Some(TextObject::AroundFunction),
"class.inside" => Some(TextObject::InsideClass),
"class.around" => Some(TextObject::AroundClass),
"comment.inside" => Some(TextObject::InsideComment),
"comment.around" => Some(TextObject::AroundComment),
_ => None,
}
}
pub fn around(&self) -> Option<Self> {
match self {
TextObject::InsideFunction => Some(TextObject::AroundFunction),
TextObject::InsideClass => Some(TextObject::AroundClass),
TextObject::InsideComment => Some(TextObject::AroundComment),
_ => None,
}
}
}
pub struct TextObjectConfig {
pub query: Query,
pub text_objects_by_capture_ix: Vec<(u32, TextObject)>,
}
#[derive(Debug)]
pub struct EmbeddingConfig {
pub query: Query,
@ -950,6 +989,7 @@ impl Language {
highlights_query: None,
brackets_config: None,
outline_config: None,
text_object_config: None,
embedding_config: None,
indents_config: None,
injection_config: None,
@ -1020,7 +1060,12 @@ impl Language {
if let Some(query) = queries.runnables {
self = self
.with_runnable_query(query.as_ref())
.context("Error loading tests query")?;
.context("Error loading runnables query")?;
}
if let Some(query) = queries.text_objects {
self = self
.with_text_object_query(query.as_ref())
.context("Error loading textobject query")?;
}
Ok(self)
}
@ -1097,6 +1142,26 @@ impl Language {
Ok(self)
}
pub fn with_text_object_query(mut self, source: &str) -> Result<Self> {
let grammar = self
.grammar_mut()
.ok_or_else(|| anyhow!("cannot mutate grammar"))?;
let query = Query::new(&grammar.ts_language, source)?;
let mut text_objects_by_capture_ix = Vec::new();
for (ix, name) in query.capture_names().iter().enumerate() {
if let Some(text_object) = TextObject::from_capture_name(name) {
text_objects_by_capture_ix.push((ix as u32, text_object));
}
}
grammar.text_object_config = Some(TextObjectConfig {
query,
text_objects_by_capture_ix,
});
Ok(self)
}
pub fn with_embedding_query(mut self, source: &str) -> Result<Self> {
let grammar = self
.grammar_mut()