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