@@ -49,7 +49,7 @@ pub struct Configuration {
4949#[serde(rename_all = "kebab-case")]
5050pub struct LanguageConfiguration {
5151 #[serde(rename = "name")]
52− pub(crate) language_id: String,
52+ pub language_id: String,
5353 pub scope: String, // source.rust
5454 pub file_types: Vec<String>, // filename ends_with? <Gemfile, rb, etc>
5555 pub roots: Vec<String>, // these indicate project roots <.git, Cargo.toml>
@@ -76,6 +76,8 @@ pub struct LanguageConfiguration {
7676
7777 #[serde(skip)]
7878 pub(crate) indent_query: OnceCell<Option<IndentQuery>>,
79+ #[serde(skip)]
80+ pub(crate) textobject_query: OnceCell<Option<TextObjectQuery>>,
7981}
8082
8183#[derive(Debug, Serialize, Deserialize)]
@@ -105,6 +107,32 @@ pub struct IndentQuery {
105107 pub outdent: HashSet<String>,
106108}
107109
110+#[derive(Debug)]
111+pub struct TextObjectQuery {
112+ pub query: Query,
113+}
114+
115+impl TextObjectQuery {
116+ /// Run the query on the given node and return sub nodes which match given
117+ /// capture ("function.inside", "class.around", etc).
118+ pub fn capture_nodes<'a>(
119+ &'a self,
120+ capture_name: &str,
121+ node: Node<'a>,
122+ slice: RopeSlice<'a>,
123+ cursor: &'a mut QueryCursor,
124+ ) -> Option<impl Iterator<Item = Node<'a>>> {
125+ let capture_idx = self.query.capture_index_for_name(capture_name)?;
126+ let captures = cursor.captures(&self.query, node, RopeProvider(slice));
127+
128+ captures
129+ .filter_map(move |(mat, idx)| {
130+ (mat.captures[idx].index == capture_idx).then(|| mat.captures[idx].node)
131+ })
132+ .into()
133+ }
134+}
135+
108136fn load_runtime_file(language: &str, filename: &str) -> Result<String, std::io::Error> {
109137 let path = crate::RUNTIME_DIR
110138 .join("queries")
@@ -153,7 +181,6 @@ impl LanguageConfiguration {
153181 // highlights_query += "\n(ERROR) @error";
154182
155183 let injections_query = read_query(&language, "injections.scm");
156−
157184 let locals_query = read_query(&language, "locals.scm");
158185
159186 if highlights_query.is_empty() {
@@ -203,6 +230,18 @@ impl LanguageConfiguration {
203230 .as_ref()
204231 }
205232
233+ pub fn textobject_query(&self) -> Option<&TextObjectQuery> {
234+ self.textobject_query
235+ .get_or_init(|| -> Option<TextObjectQuery> {
236+ let lang_name = self.language_id.to_ascii_lowercase();
237+ let query_text = read_query(&lang_name, "textobjects.scm");
238+ let lang = self.highlight_config.get()?.as_ref()?.language;
239+ let query = Query::new(lang, &query_text).ok()?;
240+ Some(TextObjectQuery { query })
241+ })
242+ .as_ref()
243+ }
244+
206245 pub fn scope(&self) -> &str {
207246 &self.scope
208247 }