From 6d23cfa8ea342f3a1cdf9e6f8d9592ae00076ac7 Mon Sep 17 00:00:00 2001 From: Ruslan Prokopchuk Date: Wed, 2 Jan 2019 21:01:45 +1100 Subject: [PATCH] minor cleanup --- src/config.rs | 58 +++++++ src/ffi.rs | 87 ++++++++++ src/kakoune.rs | 89 ++++++++++ src/log.rs | 19 +++ src/main.rs | 429 ++++++++----------------------------------------- src/tree.rs | 61 +++++++ 6 files changed, 384 insertions(+), 359 deletions(-) create mode 100644 src/config.rs create mode 100644 src/ffi.rs create mode 100644 src/kakoune.rs create mode 100644 src/log.rs create mode 100644 src/tree.rs diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..9a13130 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,58 @@ +use serde::Deserialize; +use std::collections::HashMap; +use toml; +use tree_sitter::Node; + +#[derive(Deserialize, Default)] +pub struct Config { + #[serde(default)] + filetype: HashMap, +} + +#[derive(Clone, Deserialize, Default)] +pub struct FiletypeConfig { + blacklist: Option>, + whitelist: Option>, + #[serde(default)] + group: HashMap>, +} + +impl Config { + pub fn load>(path: P) -> Option { + let config = std::fs::read_to_string(path).ok()?; + let mut config: Config = toml::from_str(&config).ok()?; + if config.filetype.get("default").is_none() { + config + .filetype + .insert("default".to_owned(), FiletypeConfig::default()); + } + Some(config) + } + + pub fn get_filetype_config<'a>(&'a self, filetype: &str) -> &'a FiletypeConfig { + self.filetype + .get(filetype) + .or_else(|| self.filetype.get("default")) + .unwrap() + } +} + +impl FiletypeConfig { + pub fn is_node_visible(&self, node: Node) -> bool { + let kind = node.kind(); + match &self.whitelist { + Some(whitelist) => whitelist.iter().any(|x| x == kind), + None => match &self.blacklist { + Some(blacklist) => !blacklist.iter().any(|x| x == kind), + None => true, + }, + } + } + + pub fn resolve_alias<'a>(&'a self, kind: &str) -> Vec { + self.group + .get(kind) + .cloned() + .unwrap_or_else(|| vec![kind.to_string()]) + } +} diff --git a/src/ffi.rs b/src/ffi.rs new file mode 100644 index 0000000..1d7d08a --- /dev/null +++ b/src/ffi.rs @@ -0,0 +1,87 @@ +use tree_sitter::Language; + +extern "C" { + #[cfg(feature = "bash")] + fn tree_sitter_bash() -> Language; + #[cfg(feature = "c")] + fn tree_sitter_c() -> Language; + #[cfg(feature = "c_sharp")] + fn tree_sitter_c_sharp() -> Language; + #[cfg(feature = "cpp")] + fn tree_sitter_cpp() -> Language; + #[cfg(feature = "css")] + fn tree_sitter_css() -> Language; + #[cfg(feature = "go")] + fn tree_sitter_go() -> Language; + #[cfg(feature = "haskell")] + fn tree_sitter_haskell() -> Language; + #[cfg(feature = "html")] + fn tree_sitter_html() -> Language; + #[cfg(feature = "java")] + fn tree_sitter_java() -> Language; + #[cfg(feature = "javascript")] + fn tree_sitter_javascript() -> Language; + #[cfg(feature = "json")] + fn tree_sitter_json() -> Language; + #[cfg(feature = "julia")] + fn tree_sitter_julia() -> Language; + #[cfg(feature = "ocaml")] + fn tree_sitter_ocaml() -> Language; + #[cfg(feature = "php")] + fn tree_sitter_php() -> Language; + #[cfg(feature = "python")] + fn tree_sitter_python() -> Language; + #[cfg(feature = "ruby")] + fn tree_sitter_ruby() -> Language; + #[cfg(feature = "rust")] + fn tree_sitter_rust() -> Language; + #[cfg(feature = "scala")] + fn tree_sitter_scala() -> Language; + #[cfg(feature = "typescript")] + fn tree_sitter_typescript() -> Language; +} + +pub fn filetype_to_language(filetype: &str) -> Option { + let sitter = match filetype { + #[cfg(feature = "bash")] + "sh" => tree_sitter_bash, + #[cfg(feature = "c")] + "c" => tree_sitter_c, + #[cfg(feature = "c_sharp")] + "c_sharp" => tree_sitter_c_sharp, + #[cfg(feature = "cpp")] + "cpp" => tree_sitter_cpp, + #[cfg(feature = "css")] + "css" => tree_sitter_css, + #[cfg(feature = "go")] + "go" => tree_sitter_go, + #[cfg(feature = "haskell")] + "haskell" => tree_sitter_haskell, + #[cfg(feature = "html")] + "html" => tree_sitter_html, + #[cfg(feature = "java")] + "java" => tree_sitter_java, + #[cfg(feature = "javascript")] + "javascript" => tree_sitter_javascript, + #[cfg(feature = "json")] + "json" => tree_sitter_json, + #[cfg(feature = "julia")] + "julia" => tree_sitter_julia, + #[cfg(feature = "ocaml")] + "ocaml" => tree_sitter_ocaml, + #[cfg(feature = "php")] + "php" => tree_sitter_php, + #[cfg(feature = "python")] + "python" => tree_sitter_python, + #[cfg(feature = "ruby")] + "ruby" => tree_sitter_ruby, + #[cfg(feature = "rust")] + "rust" => tree_sitter_rust, + #[cfg(feature = "scala")] + "scala" => tree_sitter_scala, + #[cfg(feature = "typescript")] + "typescript" => tree_sitter_typescript, + _ => return None, + }; + Some(unsafe { sitter() }) +} diff --git a/src/kakoune.rs b/src/kakoune.rs new file mode 100644 index 0000000..1bc881b --- /dev/null +++ b/src/kakoune.rs @@ -0,0 +1,89 @@ +use itertools::Itertools; +use tree_sitter::{Point, Range}; + +pub fn select_ranges(buffer: &[String], ranges: &[Range]) -> String { + if ranges.is_empty() { + "fail no selections remaining".into() + } else { + format!("select {}", ranges_to_selections_desc(&buffer, &ranges)) + } +} + +pub fn ranges_to_selections_desc(buffer: &[String], ranges: &[Range]) -> String { + ranges + .iter() + .map(|range| { + let mut end_row = range.end_point.row; + let mut end_column = range.end_point.column; + if end_column > 0 { + end_column -= 1; + } else { + end_row -= 1; + end_column = 1_000_000; + } + format!( + "{},{}", + point_to_kak_coords(buffer, range.start_point), + point_to_kak_coords(buffer, Point::new(end_row, end_column)) + ) + }) + .join(" ") +} + +pub fn selections_desc_to_ranges(buffer: &[String], selections_desc: &str) -> Vec { + selections_desc + .split_whitespace() + .map(|selection_desc| selection_desc_to_range(buffer, selection_desc)) + .collect() +} + +fn selection_desc_to_range(buffer: &[String], selection_desc: &str) -> Range { + let mut range = selection_desc.split(','); + let start = range.next().unwrap(); + let end = range.next().unwrap(); + let (start_byte, start_point) = kak_coords_to_byte_and_point(buffer, start); + let (end_byte, end_point) = kak_coords_to_byte_and_point(buffer, end); + let reverse = start_byte > end_byte; + if reverse { + Range { + start_byte: end_byte, + end_byte: start_byte, + start_point: end_point, + end_point: start_point, + } + } else { + Range { + start_byte, + end_byte, + start_point, + end_point, + } + } +} + +fn point_to_kak_coords(buffer: &[String], p: Point) -> String { + let offset = buffer[p.row] + .char_indices() + .enumerate() + .find_map(|(column, (offset, _))| { + if column == p.column { + Some(offset) + } else { + None + } + }) + .unwrap_or_else(|| buffer[p.row].len()); + format!("{}.{}", p.row + 1, offset + 1) +} + +fn kak_coords_to_byte_and_point(buffer: &[String], coords: &str) -> (usize, Point) { + let mut coords = coords.split('.'); + let row = coords.next().unwrap().parse::().unwrap() - 1; + let offset = coords.next().unwrap().parse::().unwrap() - 1; + let byte = buffer[..row].iter().fold(0, |offset, c| offset + c.len()) + offset; + let column = buffer[row] + .char_indices() + .position(|(i, _)| i == offset) + .unwrap(); + (byte, Point::new(row, column)) +} diff --git a/src/log.rs b/src/log.rs new file mode 100644 index 0000000..cb87199 --- /dev/null +++ b/src/log.rs @@ -0,0 +1,19 @@ +use sloggers::terminal::{Destination, TerminalLoggerBuilder}; +use sloggers::types::Severity; +use sloggers::Build; + +pub fn init_global_logger(verbosity: u8) { + let level = match verbosity { + 0 => Severity::Error, + 1 => Severity::Warning, + 2 => Severity::Info, + 3 => Severity::Debug, + _ => Severity::Trace, + }; + + let mut builder = TerminalLoggerBuilder::new(); + builder.level(level); + builder.destination(Destination::Stderr); + let logger = builder.build().unwrap(); + let _guard = slog_scope::set_global_logger(logger); +} diff --git a/src/main.rs b/src/main.rs index 0ed3eb3..8a1117a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,53 +1,23 @@ +use crate::config::{Config, FiletypeConfig}; use clap::{crate_version, App, Arg}; -use itertools::Itertools; use serde::Deserialize; -use sloggers::terminal::{Destination, TerminalLoggerBuilder}; -use sloggers::types::Severity; -use sloggers::Build; -use std::collections::HashMap; use std::io::Read; use toml; -use tree_sitter::{Language, Node, Parser, Point, Range}; +use tree_sitter::{Node, Parser, Range}; -extern "C" { - #[cfg(feature = "bash")] - fn tree_sitter_bash() -> Language; - #[cfg(feature = "c")] - fn tree_sitter_c() -> Language; - #[cfg(feature = "c_sharp")] - fn tree_sitter_c_sharp() -> Language; - #[cfg(feature = "cpp")] - fn tree_sitter_cpp() -> Language; - #[cfg(feature = "css")] - fn tree_sitter_css() -> Language; - #[cfg(feature = "go")] - fn tree_sitter_go() -> Language; - #[cfg(feature = "haskell")] - fn tree_sitter_haskell() -> Language; - #[cfg(feature = "html")] - fn tree_sitter_html() -> Language; - #[cfg(feature = "java")] - fn tree_sitter_java() -> Language; - #[cfg(feature = "javascript")] - fn tree_sitter_javascript() -> Language; - #[cfg(feature = "json")] - fn tree_sitter_json() -> Language; - #[cfg(feature = "julia")] - fn tree_sitter_julia() -> Language; - #[cfg(feature = "ocaml")] - fn tree_sitter_ocaml() -> Language; - #[cfg(feature = "php")] - fn tree_sitter_php() -> Language; - #[cfg(feature = "python")] - fn tree_sitter_python() -> Language; - #[cfg(feature = "ruby")] - fn tree_sitter_ruby() -> Language; - #[cfg(feature = "rust")] - fn tree_sitter_rust() -> Language; - #[cfg(feature = "scala")] - fn tree_sitter_scala() -> Language; - #[cfg(feature = "typescript")] - fn tree_sitter_typescript() -> Language; +mod config; +mod ffi; +mod kakoune; +mod log; +mod tree; + +#[derive(Deserialize)] +struct Request { + op: Op, + param: String, + filetype: String, + selections_desc: String, + content: String, } #[derive(Deserialize)] @@ -62,31 +32,32 @@ enum Op { SelectPrevNode, } -#[derive(Deserialize)] -struct Request { - op: Op, - param: String, - filetype: String, - selections_desc: String, - content: String, -} +fn main() { + let matches = cli(); -#[derive(Deserialize)] -struct FiletypeConfig { - blacklist: Option>, - whitelist: Option>, - #[serde(default)] - group: HashMap>, -} + let verbosity = matches.occurrences_of("v") as u8; + log::init_global_logger(verbosity); + + if let Some(filetype) = matches.value_of("do-you-understand") { + let language = ffi::filetype_to_language(filetype); + std::process::exit(if language.is_some() { 0 } else { 1 }); + } + + let config = if let Some(config_path) = matches.value_of("config") { + Config::load(config_path).unwrap() + } else { + Config::default() + }; -#[derive(Deserialize, Default)] -struct Config { - #[serde(default)] - filetype: HashMap, + let mut request = String::new(); + std::io::stdin().read_to_string(&mut request).unwrap(); + let request: Request = toml::from_str(&request).unwrap(); + let response = handle_request(&config, &request); + println!("{}", response); } -fn main() { - let matches = App::new("kak-tree") +fn cli() -> clap::ArgMatches<'static> { + App::new("kak-tree") .version(crate_version!()) .author("Ruslan Prokopchuk ") .about("Structural selections for Kakoune") @@ -111,46 +82,12 @@ fn main() { .multiple(true) .help("Sets the level of verbosity"), ) - .get_matches(); - - if let Some(filetype) = matches.value_of("do-you-understand") { - let language = filetype_to_language(filetype); - std::process::exit(if language.is_some() { 0 } else { 1 }); - } - - let config = if let Some(config_path) = matches.value_of("config") { - let config = std::fs::read_to_string(config_path).unwrap(); - toml::from_str(&config).unwrap() - } else { - Config::default() - }; - - let verbosity = matches.occurrences_of("v") as u8; - - let level = match verbosity { - 0 => Severity::Error, - 1 => Severity::Warning, - 2 => Severity::Info, - 3 => Severity::Debug, - _ => Severity::Trace, - }; - - let mut builder = TerminalLoggerBuilder::new(); - builder.level(level); - builder.destination(Destination::Stderr); - let logger = builder.build().unwrap(); - let _guard = slog_scope::set_global_logger(logger); - - let mut request = String::new(); - std::io::stdin().read_to_string(&mut request).unwrap(); - let request: Request = toml::from_str(&request).unwrap(); - let response = handle_request(&config, &request); - println!("{}", response); + .get_matches() } fn handle_request(config: &Config, request: &Request) -> String { let mut parser = Parser::new(); - let language = filetype_to_language(&request.filetype).unwrap(); + let language = ffi::filetype_to_language(&request.filetype).unwrap(); parser.set_language(language).unwrap(); let tree = parser.parse_str(&request.content, None).unwrap(); let buffer = request @@ -158,21 +95,26 @@ fn handle_request(config: &Config, request: &Request) -> String { .split('\n') .map(|s| format!("{}\n", s)) .collect::>(); - let ranges = selections_desc_to_ranges(&buffer, &request.selections_desc); + let ranges = kakoune::selections_desc_to_ranges(&buffer, &request.selections_desc); let mut new_ranges = Vec::new(); - let filetype_config = config.filetype.get(&request.filetype); + let filetype_config = config.get_filetype_config(&request.filetype); match &request.op { Op::SelectParentNode => { for range in &ranges { - let node = find_range_strict_superset_deepest_node(tree.root_node(), range); + let mut node = tree::shrink_to_range(tree.root_node(), range); + if node.range().start_byte == range.start_byte + && node.range().end_byte >= range.end_byte - 1 + { + node = node.parent().unwrap_or(node) + } let node = traverse_up_to_node_which_matters(filetype_config, node); new_ranges.push(node.range()); } - select_ranges(&buffer, &new_ranges) + kakoune::select_ranges(&buffer, &new_ranges) } Op::SelectNextNode => { for range in &ranges { - let node = find_range_superset_deepest_node(tree.root_node(), range); + let node = tree::shrink_to_range(tree.root_node(), range); let node = traverse_up_to_node_which_matters(filetype_config, node); if let Some(node) = node.next_named_sibling() { new_ranges.push(node.range()); @@ -180,11 +122,11 @@ fn handle_request(config: &Config, request: &Request) -> String { new_ranges.push(node.range()); } } - select_ranges(&buffer, &new_ranges) + kakoune::select_ranges(&buffer, &new_ranges) } Op::SelectPrevNode => { for range in &ranges { - let node = find_range_superset_deepest_node(tree.root_node(), range); + let node = tree::shrink_to_range(tree.root_node(), range); let node = traverse_up_to_node_which_matters(filetype_config, node); if let Some(node) = node.prev_named_sibling() { new_ranges.push(node.range()); @@ -192,50 +134,50 @@ fn handle_request(config: &Config, request: &Request) -> String { new_ranges.push(node.range()); } } - select_ranges(&buffer, &new_ranges) + kakoune::select_ranges(&buffer, &new_ranges) } Op::SelectFirstChild => { 'outer: for range in &ranges { - let node = find_range_superset_deepest_node(tree.root_node(), range); + let node = tree::shrink_to_range(tree.root_node(), range); let node = traverse_up_to_node_which_matters(filetype_config, node); - for child in named_children(&node) { - if node_matters(filetype_config, child.kind()) { + for child in tree::named_children(&node) { + if filetype_config.is_node_visible(child) { new_ranges.push(child.range()); continue 'outer; } } new_ranges.push(node.range()); } - select_ranges(&buffer, &new_ranges) + kakoune::select_ranges(&buffer, &new_ranges) } Op::SelectChildren => { for range in &ranges { - let node = find_range_superset_deepest_node(tree.root_node(), range); - for child in named_children(&node) { - if node_matters(filetype_config, child.kind()) { + let node = tree::shrink_to_range(tree.root_node(), range); + for child in tree::named_children(&node) { + if filetype_config.is_node_visible(child) { new_ranges.push(child.range()); } } } - select_ranges(&buffer, &new_ranges) + kakoune::select_ranges(&buffer, &new_ranges) } Op::NodeSExp => { - let node = find_range_superset_deepest_node(tree.root_node(), &ranges[0]); + let node = tree::shrink_to_range(tree.root_node(), &ranges[0]); format!("info '{}'", node.to_sexp()) } Op::SelectKind => { - let kinds = resolve_kind(filetype_config, &request.param); + let kinds = filetype_config.resolve_alias(&request.param); for range in &ranges { - for node in find_nodes_in_range(tree.root_node(), range) { + for node in tree::nodes_in_range(tree.root_node(), range) { select_nodes(&node, &kinds, &mut new_ranges); } } - select_ranges(&buffer, &new_ranges) + kakoune::select_ranges(&buffer, &new_ranges) } Op::SelectParentKind => { - let kinds = resolve_kind(filetype_config, &request.param); + let kinds = filetype_config.resolve_alias(&request.param); for range in &ranges { - let mut cursor = Some(find_range_superset_deepest_node(tree.root_node(), range)); + let mut cursor = Some(tree::shrink_to_range(tree.root_node(), range)); while let Some(node) = cursor { if kinds.iter().any(|kind| kind == node.kind()) { new_ranges.push(node.range()); @@ -244,22 +186,16 @@ fn handle_request(config: &Config, request: &Request) -> String { cursor = node.parent(); } } - select_ranges(&buffer, &new_ranges) + kakoune::select_ranges(&buffer, &new_ranges) } } } -fn resolve_kind(filetype_config: Option<&FiletypeConfig>, kind: &str) -> Vec { - filetype_config - .and_then(|config| config.group.get(kind).cloned()) - .unwrap_or_else(|| vec![kind.to_string()]) -} - fn select_nodes(node: &Node, kinds: &[String], new_ranges: &mut Vec) { if kinds.iter().any(|kind| kind == node.kind()) { new_ranges.push(node.range()); } else { - for child in named_children(&node) { + for child in tree::named_children(&node) { if kinds.iter().any(|kind| kind == child.kind()) { new_ranges.push(child.range()); } else { @@ -269,238 +205,13 @@ fn select_nodes(node: &Node, kinds: &[String], new_ranges: &mut Vec) { } } -fn named_children<'a>(node: &'a Node) -> impl Iterator> { - (0..node.child_count()).map(move |i| node.child(i).unwrap()) -} - -fn select_ranges(buffer: &[String], ranges: &[Range]) -> String { - if ranges.is_empty() { - "fail no selections remaining".into() - } else { - format!("select {}", ranges_to_selections_desc(&buffer, &ranges)) - } -} - -fn node_matters(filetype_config: Option<&FiletypeConfig>, kind: &str) -> bool { - match filetype_config { - Some(config) => match &config.whitelist { - Some(whitelist) => whitelist.iter().any(|s| s == kind), - None => match &config.blacklist { - Some(blacklist) => !blacklist.iter().any(|s| s == kind), - None => true, - }, - }, - None => true, - } -} - fn traverse_up_to_node_which_matters<'a>( - filetype_config: Option<&FiletypeConfig>, + filetype_config: &FiletypeConfig, current_node: Node<'a>, ) -> Node<'a> { let mut node = current_node; - while !(node.is_named() && node_matters(filetype_config, node.kind())) - && node.parent().is_some() - { + while !(node.is_named() && filetype_config.is_node_visible(node)) && node.parent().is_some() { node = node.parent().unwrap(); } node } - -fn find_range_strict_superset_deepest_node<'a>(root_node: Node<'a>, range: &Range) -> Node<'a> { - let mut node = root_node; - 'outer: loop { - let parent = node; - for child in parent.children() { - if child.range().start_byte <= range.start_byte - && range.end_byte < child.range().end_byte - && !(child.range().start_byte == range.start_byte - && range.end_byte == child.range().end_byte - 1) - { - node = child; - continue 'outer; - } - } - return node; - } -} - -fn find_range_superset_deepest_node<'a>(root_node: Node<'a>, range: &Range) -> Node<'a> { - let mut node = root_node; - 'outer: loop { - let parent = node; - for child in parent.children() { - if child.range().start_byte <= range.start_byte - && range.end_byte <= child.range().end_byte - { - node = child; - continue 'outer; - } - } - return node; - } -} - -fn find_nodes_in_range<'a>(root_node: Node<'a>, range: &Range) -> Vec> { - let mut nodes = Vec::new(); - let node = find_range_superset_deepest_node(root_node, range); - if node.range().start_byte >= range.start_byte && range.end_byte >= node.range().end_byte { - nodes.push(node); - return nodes; - } - for child in node.children() { - if child.range().start_byte <= range.start_byte && range.end_byte >= child.range().end_byte - { - nodes.extend(find_nodes_in_range( - child, - &Range { - start_byte: range.start_byte, - start_point: range.start_point, - end_byte: child.range().end_byte, - end_point: child.range().end_point, - }, - )); - } else if child.range().start_byte >= range.start_byte - && range.end_byte <= child.range().end_byte - { - nodes.extend(find_nodes_in_range( - child, - &Range { - start_byte: child.range().start_byte, - start_point: child.range().start_point, - end_byte: range.end_byte, - end_point: range.end_point, - }, - )); - } else if child.range().start_byte >= range.start_byte - && range.end_byte >= child.range().end_byte - { - nodes.push(child); - } - } - nodes -} - -fn ranges_to_selections_desc(buffer: &[String], ranges: &[Range]) -> String { - ranges - .iter() - .map(|range| { - let mut end_row = range.end_point.row; - let mut end_column = range.end_point.column; - if end_column > 0 { - end_column -= 1; - } else { - end_row -= 1; - end_column = 1_000_000; - } - format!( - "{},{}", - point_to_kak_coords(buffer, range.start_point), - point_to_kak_coords(buffer, Point::new(end_row, end_column)) - ) - }) - .join(" ") -} - -fn selections_desc_to_ranges(buffer: &[String], selections_desc: &str) -> Vec { - selections_desc - .split_whitespace() - .map(|selection_desc| selection_desc_to_range(buffer, selection_desc)) - .collect() -} - -fn selection_desc_to_range(buffer: &[String], selection_desc: &str) -> Range { - let mut range = selection_desc.split(','); - let start = range.next().unwrap(); - let end = range.next().unwrap(); - let (start_byte, start_point) = kak_coords_to_byte_and_point(buffer, start); - let (end_byte, end_point) = kak_coords_to_byte_and_point(buffer, end); - let reverse = start_byte > end_byte; - if reverse { - Range { - start_byte: end_byte, - end_byte: start_byte, - start_point: end_point, - end_point: start_point, - } - } else { - Range { - start_byte, - end_byte, - start_point, - end_point, - } - } -} - -fn point_to_kak_coords(buffer: &[String], p: Point) -> String { - let offset = buffer[p.row] - .char_indices() - .enumerate() - .find_map(|(column, (offset, _))| { - if column == p.column { - Some(offset) - } else { - None - } - }) - .unwrap_or_else(|| buffer[p.row].len()); - format!("{}.{}", p.row + 1, offset + 1) -} - -fn kak_coords_to_byte_and_point(buffer: &[String], coords: &str) -> (usize, Point) { - let mut coords = coords.split('.'); - let row = coords.next().unwrap().parse::().unwrap() - 1; - let offset = coords.next().unwrap().parse::().unwrap() - 1; - let byte = buffer[..row].iter().fold(0, |offset, c| offset + c.len()) + offset; - let column = buffer[row] - .char_indices() - .position(|(i, _)| i == offset) - .unwrap(); - (byte, Point::new(row, column)) -} - -fn filetype_to_language(filetype: &str) -> Option { - let sitter = match filetype { - #[cfg(feature = "bash")] - "sh" => tree_sitter_bash, - #[cfg(feature = "c")] - "c" => tree_sitter_c, - #[cfg(feature = "c_sharp")] - "c_sharp" => tree_sitter_c_sharp, - #[cfg(feature = "cpp")] - "cpp" => tree_sitter_cpp, - #[cfg(feature = "css")] - "css" => tree_sitter_css, - #[cfg(feature = "go")] - "go" => tree_sitter_go, - #[cfg(feature = "haskell")] - "haskell" => tree_sitter_haskell, - #[cfg(feature = "html")] - "html" => tree_sitter_html, - #[cfg(feature = "java")] - "java" => tree_sitter_java, - #[cfg(feature = "javascript")] - "javascript" => tree_sitter_javascript, - #[cfg(feature = "json")] - "json" => tree_sitter_json, - #[cfg(feature = "julia")] - "julia" => tree_sitter_julia, - #[cfg(feature = "ocaml")] - "ocaml" => tree_sitter_ocaml, - #[cfg(feature = "php")] - "php" => tree_sitter_php, - #[cfg(feature = "python")] - "python" => tree_sitter_python, - #[cfg(feature = "ruby")] - "ruby" => tree_sitter_ruby, - #[cfg(feature = "rust")] - "rust" => tree_sitter_rust, - #[cfg(feature = "scala")] - "scala" => tree_sitter_scala, - #[cfg(feature = "typescript")] - "typescript" => tree_sitter_typescript, - _ => return None, - }; - Some(unsafe { sitter() }) -} diff --git a/src/tree.rs b/src/tree.rs new file mode 100644 index 0000000..0dd7dc1 --- /dev/null +++ b/src/tree.rs @@ -0,0 +1,61 @@ +use tree_sitter::{Node, Range}; + +pub fn named_children<'a>(node: &'a Node) -> impl Iterator> { + (0..node.child_count()).map(move |i| node.child(i).unwrap()) +} + +pub fn shrink_to_range<'a>(root_node: Node<'a>, range: &Range) -> Node<'a> { + let mut node = root_node; + 'outer: loop { + let parent = node; + for child in parent.children() { + if child.range().start_byte <= range.start_byte + && range.end_byte <= child.range().end_byte + { + node = child; + continue 'outer; + } + } + return node; + } +} + +pub fn nodes_in_range<'a>(root_node: Node<'a>, range: &Range) -> Vec> { + let mut nodes = Vec::new(); + let node = shrink_to_range(root_node, range); + if node.range().start_byte >= range.start_byte && range.end_byte >= node.range().end_byte { + nodes.push(node); + return nodes; + } + for child in node.children() { + if child.range().start_byte <= range.start_byte && range.end_byte >= child.range().end_byte + { + nodes.extend(nodes_in_range( + child, + &Range { + start_byte: range.start_byte, + start_point: range.start_point, + end_byte: child.range().end_byte, + end_point: child.range().end_point, + }, + )); + } else if child.range().start_byte >= range.start_byte + && range.end_byte <= child.range().end_byte + { + nodes.extend(nodes_in_range( + child, + &Range { + start_byte: child.range().start_byte, + start_point: child.range().start_point, + end_byte: range.end_byte, + end_point: range.end_point, + }, + )); + } else if child.range().start_byte >= range.start_byte + && range.end_byte >= child.range().end_byte + { + nodes.push(child); + } + } + nodes +}