aboutsummaryrefslogtreecommitdiff
path: root/src/app.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/app.rs')
-rw-r--r--src/app.rs259
1 files changed, 259 insertions, 0 deletions
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 @@
1use crate::config::Config;
2
3use std::{
4 collections::HashMap,
5 fmt::Write,
6 path::{Path, PathBuf},
7};
8
9use console::{style, Style, Term};
10use tree_sitter::{Node, Parser, Query, QueryCursor, Range, Tree};
11
12pub struct App {
13 config: Config,
14 language: tree_sitter::Language,
15 path: PathBuf,
16 query: Option<Query>,
17 query_path: Option<PathBuf>,
18 src: Vec<u8>,
19 tree: Tree,
20}
21
22impl App {
23 pub fn new<'a, P: AsRef<Path>>(
24 src: &'a [u8],
25 path: P,
26 query_path: Option<P>,
27 language: tree_sitter::Language,
28 ) -> Self {
29 let path = path.as_ref().to_owned();
30
31 let mut parser = Parser::new();
32 parser.set_language(&language).unwrap();
33
34 let tree = parser.parse(&src, None).unwrap();
35 let query_path = query_path.map(|q| q.as_ref().to_owned());
36 let query = query_path.as_ref().map(|p| {
37 let query_src = std::fs::read_to_string(&p).expect("unable to read query");
38 Query::new(&language, &query_src).expect("query parse error")
39 });
40
41 Self {
42 config: Default::default(),
43 path,
44 query,
45 query_path,
46 src: src.to_owned(),
47 tree,
48 language,
49 }
50 }
51
52 pub fn draw(&self) {
53 let term = Term::stdout();
54 term.clear_screen().unwrap();
55 let mut done = false;
56 let mut depth = 0;
57 let mut in_capture: Option<Range> = None;
58 let mut cursor = self.tree.walk();
59
60 let capture_names = self
61 .query
62 .as_ref()
63 .map(|q| q.capture_names())
64 .unwrap_or_default();
65 let capture_map = self
66 .query
67 .as_ref()
68 .map(|query| {
69 QueryCursor::new()
70 .matches(&query, self.tree.root_node(), self.src.as_slice())
71 .flat_map(|match_| match_.captures)
72 .fold(
73 HashMap::new(),
74 |mut map: HashMap<Node, Vec<u32>>, capture| {
75 map.entry(capture.node)
76 .and_modify(|idxs| idxs.push(capture.index))
77 .or_insert_with(|| vec![capture.index]);
78 map
79 },
80 )
81 })
82 .unwrap_or_default();
83
84 while !done {
85 let node = cursor.node();
86 let mut tree_string = String::new();
87 in_capture = match in_capture {
88 Some(range)
89 if !contains(&range, &node.range()) && capture_map.contains_key(&node) =>
90 {
91 Some(node.range())
92 }
93 Some(range) if !contains(&range, &node.range()) => None,
94 None if capture_map.contains_key(&node) => Some(node.range()),
95 i => i,
96 };
97
98 write!(
99 tree_string,
100 "{}",
101 (if in_capture.is_some() {
102 Style::new().on_yellow().on_bright()
103 } else {
104 Style::new()
105 })
106 .bright()
107 .black()
108 .apply_to(
109 format!("{}{}", "|", " ".repeat(self.config.indent_level))
110 .repeat(depth as usize)
111 )
112 )
113 .unwrap();
114
115 if self.config.show_field_name {
116 if let Some(f) = cursor.field_name() {
117 write!(
118 tree_string,
119 "{} ",
120 if in_capture.is_some() {
121 Style::new().on_yellow().on_bright()
122 } else {
123 Style::new()
124 }
125 .yellow()
126 .apply_to(f)
127 )
128 .unwrap()
129 }
130 }
131
132 write!(
133 tree_string,
134 "{} ",
135 if node.is_error() {
136 Style::new().red()
137 } else if in_capture.is_some() {
138 Style::new().on_yellow().on_bright()
139 } else {
140 Style::new()
141 }
142 .apply_to(node.kind()),
143 )
144 .unwrap();
145
146 if let Some(idxs) = capture_map.get(&node) {
147 for index in idxs {
148 write!(
149 tree_string,
150 "@{} ",
151 style(capture_names[*index as usize]).magenta()
152 )
153 .unwrap();
154 }
155 }
156
157 if self.config.show_ranges {
158 let range = node.range();
159 write!(
160 tree_string,
161 " {}",
162 style(format!("{:?}..{:?}", range.start_byte, range.end_byte,))
163 .bright()
164 .black()
165 )
166 .unwrap();
167 }
168
169 if self.config.show_src {
170 write!(
171 tree_string,
172 " {:.?}",
173 style(node.utf8_text(&self.src).unwrap()).cyan()
174 )
175 .unwrap();
176 }
177
178 term.write_line(&tree_string).unwrap();
179 term.clear_to_end_of_screen().unwrap();
180
181 if cursor.goto_first_child() {
182 depth += 1;
183 continue;
184 }
185 if cursor.goto_next_sibling() {
186 continue;
187 }
188
189 loop {
190 if !cursor.goto_parent() {
191 done = true;
192 break;
193 } else {
194 depth -= 1;
195 }
196
197 if cursor.goto_next_sibling() {
198 break;
199 }
200 }
201 }
202
203 // see https://github.com/console-rs/console/issues/36#issuecomment-624731432
204 // for the reasoning behing this hackjob
205
206 term.write_line("\n(>) increase indent").unwrap();
207 term.clear_to_end_of_screen().unwrap();
208
209 term.write_line("(<) decrease indent ").unwrap();
210 term.clear_to_end_of_screen().unwrap();
211
212 term.write_line("(n) toggle ranges").unwrap();
213 term.clear_to_end_of_screen().unwrap();
214
215 term.write_line("(s) toggle source text").unwrap();
216 term.clear_to_end_of_screen().unwrap();
217
218 term.write_line("(r) reload from disk").unwrap();
219 term.clear_to_end_of_screen().unwrap();
220
221 term.write_line("(C-c) quit").unwrap();
222 term.clear_to_end_of_screen().unwrap();
223 }
224
225 pub fn increase_indent(&mut self) {
226 self.config.indent_level = self.config.indent_level.saturating_add(1);
227 }
228
229 pub fn decrease_indent(&mut self) {
230 self.config.indent_level = self.config.indent_level.saturating_sub(1);
231 }
232
233 pub fn toggle_ranges(&mut self) {
234 self.config.show_ranges = !self.config.show_ranges;
235 }
236
237 pub fn toggle_source(&mut self) {
238 self.config.show_src = !self.config.show_src;
239 }
240
241 pub fn reload(&mut self) {
242 let src = std::fs::read_to_string(&self.path).unwrap();
243 let new = Self::new(
244 src.as_bytes(),
245 &self.path,
246 self.query_path.as_ref(),
247 self.language.clone(),
248 );
249 *self = Self {
250 config: self.config,
251 ..new
252 };
253 }
254}
255
256// does a encompass b
257fn contains(a: &Range, b: &Range) -> bool {
258 a.start_byte <= b.start_byte && a.end_byte >= b.end_byte
259}