Skip to content
This repository has been archived by the owner on Nov 14, 2024. It is now read-only.

Commit

Permalink
First shot at text-objects #27
Browse files Browse the repository at this point in the history
  • Loading branch information
hadronized committed Feb 27, 2024
1 parent ba9403e commit 1350fa1
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 3 deletions.
16 changes: 15 additions & 1 deletion kak-tree-sitter/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ use std::{io, path::PathBuf};

use log::SetLoggerError;
use thiserror::Error;
use tree_sitter::LanguageError;
use tree_sitter::{LanguageError, QueryError};

use crate::text_objects;

#[derive(Debug, Error)]
pub enum OhNo {
Expand Down Expand Up @@ -77,4 +79,16 @@ pub enum OhNo {
#[from]
err: LanguageError,
},

#[error("query error: {err}")]
QueryError {
#[from]
err: QueryError,
},

#[error("text-objects not supported")]
UnsupportedTextObjects,

#[error("no such {ty} text-object query")]
UnknownTextObjectQuery { ty: text_objects::Type },
}
10 changes: 10 additions & 0 deletions kak-tree-sitter/src/languages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::{collections::HashMap, path::Path};

use kak_tree_sitter_config::{Config, LanguagesConfig};
use libloading::Symbol;
use tree_sitter::Query;
use tree_sitter_highlight::HighlightConfiguration;

use crate::{error::OhNo, queries::Queries};
Expand All @@ -15,6 +16,8 @@ pub struct Language {
pub hl_names: Vec<String>,
// whether we should remove the default highlighter when highlighting a buffer with this language
pub remove_default_highlighter: bool,
// query to use for text objects, if supported by the language
pub textobject_query: Option<Query>,

// NOTE: we need to keep that alive *probably*; better be safe than sorry
ts_lang: tree_sitter::Language,
Expand Down Expand Up @@ -96,10 +99,17 @@ impl Languages {

let remove_default_highlighter = lang_config.remove_default_highlighter.to_bool();

let textobject_query = queries
.text_objects
.as_deref()
.map(|q| Query::new(ts_lang, q).map(Some))
.unwrap_or_else(|| Ok(None))?;

let lang = Language {
hl_config,
hl_names,
remove_default_highlighter,
textobject_query,
ts_lang,
_ts_lib: ts_lib,
};
Expand Down
1 change: 1 addition & 0 deletions kak-tree-sitter/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ mod request;
mod response;
mod server;
mod session;
mod text_objects;
mod tree_sitter_state;

use clap::Parser;
Expand Down
2 changes: 1 addition & 1 deletion kak-tree-sitter/src/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ impl Queries {
let highlights = fs::read_to_string(dir.join("highlights.scm")).ok();
let injections = fs::read_to_string(dir.join("injections.scm")).ok();
let locals = fs::read_to_string(dir.join("locals.scm")).ok();
let text_objects = fs::read_to_string(dir.join("text_objects.scm")).ok();
let text_objects = fs::read_to_string(dir.join("textobjects.scm")).ok();

Queries {
highlights,
Expand Down
51 changes: 51 additions & 0 deletions kak-tree-sitter/src/text_objects.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//! Text-object support.

use std::fmt::Display;

/// A text-object type.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Type {
pub pattern: Pattern,
pub level: Level,
}

impl Display for Type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_query_name())
}
}

impl Type {
/// Return the query name representation of this text-object type.
pub fn as_query_name(&self) -> &'static str {
match (self.pattern, self.level) {
(Pattern::Function, Level::Inside) => "function.inside",
(Pattern::Function, Level::Around) => "function.around",
(Pattern::Parameter, Level::Inside) => "parameter.inside",
(Pattern::Parameter, Level::Around) => "parameter.around",
(Pattern::Class, Level::Inside) => "class.inside",
(Pattern::Class, Level::Around) => "class.around",
(Pattern::Comment, Level::Inside) => "comment.inside",
(Pattern::Comment, Level::Around) => "comment.around",
(Pattern::Test, Level::Inside) => "test.inside",
(Pattern::Test, Level::Around) => "test.around",
}
}
}

/// Text-object flavor as currently supported.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Pattern {
Function,
Parameter,
Class,
Comment,
Test,
}

/// Level of the text-object.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Level {
Inside,
Around,
}
41 changes: 40 additions & 1 deletion kak-tree-sitter/src/tree_sitter_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use tree_sitter::{Parser, Query, QueryCursor};

use crate::{error::OhNo, highlighting::KakHighlightRange, languages::Language};
use crate::{error::OhNo, highlighting::KakHighlightRange, languages::Language, text_objects};

/// State around a tree.
///
Expand Down Expand Up @@ -41,6 +41,15 @@ impl TreeState {
injection_callback: impl FnMut(&str) -> Option<&'a tree_sitter_highlight::HighlightConfiguration>
+ 'a,
) -> Result<Vec<KakHighlightRange>, OhNo> {
self.text_objects(
lang,
buf,
text_objects::Type {
pattern: text_objects::Pattern::Function,
level: text_objects::Level::Inside,
},
)?;

let events = self
.highlighter
.highlight(&lang.hl_config, buf.as_bytes(), None, injection_callback)
Expand All @@ -55,6 +64,36 @@ impl TreeState {
))
}

/// Get the text-objects for the given type.
pub fn text_objects(
&self,
lang: &Language,
buf: &str,
ty: text_objects::Type,
) -> Result<(), OhNo> {
// first, check whether the language supports text-objects, and also check whether it has the text-object type in
// its capture names
let query = lang
.textobject_query
.as_ref()
.ok_or_else(|| OhNo::UnsupportedTextObjects)?;
let capture_index = query
.capture_index_for_name(ty.as_query_name())
.ok_or_else(|| OhNo::UnknownTextObjectQuery { ty })?;

// run the query via a query cursor
let mut cursor = QueryCursor::new();
let mut captures = cursor
.captures(query, self.tree.root_node(), buf.as_bytes())
.flat_map(|(cm, _)| cm.captures.iter())
.filter(|cq| cq.index == capture_index)
.collect::<Vec<_>>();

log::info!("text_objects: {captures:#?}");

Ok(())
}

pub fn query(&self, query: &Query, code: &str) {
let mut cursor = QueryCursor::new();
let captures = cursor.captures(query, self.tree.root_node(), code.as_bytes());
Expand Down

0 comments on commit 1350fa1

Please sign in to comment.