From 80ac5c569114e9f99a37119d97a6050b438f573a Mon Sep 17 00:00:00 2001 From: Akshay Date: Sun, 14 Jul 2024 19:31:42 +0100 Subject: init --- src/app.rs | 259 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 src/app.rs (limited to 'src/app.rs') diff --git a/src/app.rs b/src/app.rs new file mode 100644 index 0000000..285034b --- /dev/null +++ b/src/app.rs @@ -0,0 +1,259 @@ +use crate::config::Config; + +use std::{ + collections::HashMap, + fmt::Write, + path::{Path, PathBuf}, +}; + +use console::{style, Style, Term}; +use tree_sitter::{Node, Parser, Query, QueryCursor, Range, Tree}; + +pub struct App { + config: Config, + language: tree_sitter::Language, + path: PathBuf, + query: Option, + query_path: Option, + src: Vec, + tree: Tree, +} + +impl App { + pub fn new<'a, P: AsRef>( + src: &'a [u8], + path: P, + query_path: Option

, + language: tree_sitter::Language, + ) -> Self { + let path = path.as_ref().to_owned(); + + let mut parser = Parser::new(); + parser.set_language(&language).unwrap(); + + let tree = parser.parse(&src, None).unwrap(); + let query_path = query_path.map(|q| q.as_ref().to_owned()); + let query = query_path.as_ref().map(|p| { + let query_src = std::fs::read_to_string(&p).expect("unable to read query"); + Query::new(&language, &query_src).expect("query parse error") + }); + + Self { + config: Default::default(), + path, + query, + query_path, + src: src.to_owned(), + tree, + language, + } + } + + pub fn draw(&self) { + let term = Term::stdout(); + term.clear_screen().unwrap(); + let mut done = false; + let mut depth = 0; + let mut in_capture: Option = None; + let mut cursor = self.tree.walk(); + + let capture_names = self + .query + .as_ref() + .map(|q| q.capture_names()) + .unwrap_or_default(); + let capture_map = self + .query + .as_ref() + .map(|query| { + QueryCursor::new() + .matches(&query, self.tree.root_node(), self.src.as_slice()) + .flat_map(|match_| match_.captures) + .fold( + HashMap::new(), + |mut map: HashMap>, capture| { + map.entry(capture.node) + .and_modify(|idxs| idxs.push(capture.index)) + .or_insert_with(|| vec![capture.index]); + map + }, + ) + }) + .unwrap_or_default(); + + while !done { + let node = cursor.node(); + let mut tree_string = String::new(); + in_capture = match in_capture { + Some(range) + if !contains(&range, &node.range()) && capture_map.contains_key(&node) => + { + Some(node.range()) + } + Some(range) if !contains(&range, &node.range()) => None, + None if capture_map.contains_key(&node) => Some(node.range()), + i => i, + }; + + write!( + tree_string, + "{}", + (if in_capture.is_some() { + Style::new().on_yellow().on_bright() + } else { + Style::new() + }) + .bright() + .black() + .apply_to( + format!("{}{}", "|", " ".repeat(self.config.indent_level)) + .repeat(depth as usize) + ) + ) + .unwrap(); + + if self.config.show_field_name { + if let Some(f) = cursor.field_name() { + write!( + tree_string, + "{} ", + if in_capture.is_some() { + Style::new().on_yellow().on_bright() + } else { + Style::new() + } + .yellow() + .apply_to(f) + ) + .unwrap() + } + } + + write!( + tree_string, + "{} ", + if node.is_error() { + Style::new().red() + } else if in_capture.is_some() { + Style::new().on_yellow().on_bright() + } else { + Style::new() + } + .apply_to(node.kind()), + ) + .unwrap(); + + if let Some(idxs) = capture_map.get(&node) { + for index in idxs { + write!( + tree_string, + "@{} ", + style(capture_names[*index as usize]).magenta() + ) + .unwrap(); + } + } + + if self.config.show_ranges { + let range = node.range(); + write!( + tree_string, + " {}", + style(format!("{:?}..{:?}", range.start_byte, range.end_byte,)) + .bright() + .black() + ) + .unwrap(); + } + + if self.config.show_src { + write!( + tree_string, + " {:.?}", + style(node.utf8_text(&self.src).unwrap()).cyan() + ) + .unwrap(); + } + + term.write_line(&tree_string).unwrap(); + term.clear_to_end_of_screen().unwrap(); + + if cursor.goto_first_child() { + depth += 1; + continue; + } + if cursor.goto_next_sibling() { + continue; + } + + loop { + if !cursor.goto_parent() { + done = true; + break; + } else { + depth -= 1; + } + + if cursor.goto_next_sibling() { + break; + } + } + } + + // see https://github.com/console-rs/console/issues/36#issuecomment-624731432 + // for the reasoning behing this hackjob + + term.write_line("\n(>) increase indent").unwrap(); + term.clear_to_end_of_screen().unwrap(); + + term.write_line("(<) decrease indent ").unwrap(); + term.clear_to_end_of_screen().unwrap(); + + term.write_line("(n) toggle ranges").unwrap(); + term.clear_to_end_of_screen().unwrap(); + + term.write_line("(s) toggle source text").unwrap(); + term.clear_to_end_of_screen().unwrap(); + + term.write_line("(r) reload from disk").unwrap(); + term.clear_to_end_of_screen().unwrap(); + + term.write_line("(C-c) quit").unwrap(); + term.clear_to_end_of_screen().unwrap(); + } + + pub fn increase_indent(&mut self) { + self.config.indent_level = self.config.indent_level.saturating_add(1); + } + + pub fn decrease_indent(&mut self) { + self.config.indent_level = self.config.indent_level.saturating_sub(1); + } + + pub fn toggle_ranges(&mut self) { + self.config.show_ranges = !self.config.show_ranges; + } + + pub fn toggle_source(&mut self) { + self.config.show_src = !self.config.show_src; + } + + pub fn reload(&mut self) { + let src = std::fs::read_to_string(&self.path).unwrap(); + let new = Self::new( + src.as_bytes(), + &self.path, + self.query_path.as_ref(), + self.language.clone(), + ); + *self = Self { + config: self.config, + ..new + }; + } +} + +// does a encompass b +fn contains(a: &Range, b: &Range) -> bool { + a.start_byte <= b.start_byte && a.end_byte >= b.end_byte +} -- cgit v1.2.3