on master help sign in
draft: tree-sitter highlighting
25b3f98Blaž Hrastnik
Cargo.lock+13
@@ -393,8 +393,11 @@ dependencies = [
393 "argh",
393 "argh",
394 "crossterm",
394 "crossterm",
395 "helix-core",
395 "helix-core",
396+ "helix-syntax",
396 "num_cpus",
397 "num_cpus",
397 "smol",
398 "smol",
399+ "tree-sitter",
400+ "tree-sitter-highlight",
398 "tui",
401 "tui",
399]
402]
400
403
@@ -716,6 +719,16 @@ dependencies = [
716 "regex",
719 "regex",
717]
720]
718
721
722+[[package]]
723+name = "tree-sitter-highlight"
724+version = "0.2.0"
725+source = "registry+https://github.com/rust-lang/crates.io-index"
726+checksum = "c6e3c800ac0db1562a045a4893cbbd7c484eb93426cae5632f9e5d24dd588cd1"
727+dependencies = [
728+ "regex",
729+ "tree-sitter",
730+]
731+
719[[package]]
732[[package]]
720name = "tui"
733name = "tui"
721version = "0.10.0"
734version = "0.10.0"
helix-core/src/state.rs+11
@@ -81,6 +81,17 @@ impl State {
81 // foldable
81 // foldable
82 // changeFilter/transactionFilter
82 // changeFilter/transactionFilter
83
83
84+ // TODO: move that accepts a boundary matcher fn/list, we keep incrementing until we hit
85+ // a boundary
86+
87+ // TODO: edits, does each keypress trigger a full command? I guess it's adding to the same
88+ // transaction
89+ // There should be three pieces of the state: current transaction, the original doc, "preview"
90+ // of the new state.
91+ // 1. apply the newly generated keypress as a transaction
92+ // 2. compose onto a ongoing transaction
93+ // 3. on insert mode leave, that transaction gets stored into undo history
94+
84 pub fn move_pos(
95 pub fn move_pos(
85 &self,
96 &self,
86 pos: usize,
97 pos: usize,
helix-term/Cargo.toml+2
@@ -24,3 +24,5 @@ crossterm = { version = "0.17", features = ["event-stream"] }
24smol = "1"
24smol = "1"
25num_cpus = "1.13.0"
25num_cpus = "1.13.0"
26tui = { version = "0.10.0", default-features = false, features = ["crossterm"] }
26tui = { version = "0.10.0", default-features = false, features = ["crossterm"] }
27+tree-sitter = "0.16.1"
28+tree-sitter-highlight = "0.2.0"
helix-term/src/editor.rs+115
@@ -216,3 +216,118 @@ impl Editor {
216 Ok(())
216 Ok(())
217 }
217 }
218}
218}
219+
220+// TODO: language configs:
221+// tabSize, fileExtension etc, mapping to tree sitter parser
222+// themes:
223+// map tree sitter highlights to color values
224+//
225+// TODO: expand highlight thing so we're able to render only viewport range
226+// TODO: async: maybe pre-cache scopes as empty so we render all graphemes initially as regular
227+////text until calc finishes
228+// TODO: scope matching: biggest union match? [string] & [html, string], [string, html] & [ string, html]
229+// can do this by sorting our theme matches based on array len (longest first) then stopping at the
230+// first rule that matches (rule.all(|scope| scopes.contains(scope)))
231+//
232+// let visual_x = 0;
233+// let line = ?;
234+// for span in spans {
235+// start(scope) => scopes.push(scope)
236+// span =>
237+// let text = rope.slice(span.start..span.end);
238+// let style = calculate_style(scopes);
239+// for each grapheme in text.graphemes() {
240+// // if newline += lines, continue
241+//
242+// if state.selection.ranges().any(|range| range.contains(char_index)) {
243+// if exactly on cursor {
244+// }
245+// if on primary cursor? {
246+// }
247+// modify style temporarily
248+// }
249+//
250+// // if in bounds
251+//
252+// // if tab, draw tab width
253+// // draw(visual_x, line, grapheme, style)
254+// // increment visual_x by grapheme_width(grapheme)
255+// // increment char_index by grapheme.len_chars()
256+// }
257+// end => scopes.pop()
258+// }
259+#[test]
260+fn test_parser() {
261+ use tree_sitter_highlight::{HighlightConfiguration, HighlightEvent, Highlighter};
262+
263+ let source_code = include_str!("./main.rs");
264+
265+ let highlight_names: Vec<String> = [
266+ "attribute",
267+ "constant",
268+ "function.builtin",
269+ "function",
270+ "keyword",
271+ "operator",
272+ "property",
273+ "punctuation",
274+ "punctuation.bracket",
275+ "punctuation.delimiter",
276+ "string",
277+ "string.special",
278+ "tag",
279+ "type",
280+ "type.builtin",
281+ "variable",
282+ "variable.builtin",
283+ "variable.parameter",
284+ ]
285+ .iter()
286+ .cloned()
287+ .map(String::from)
288+ .collect();
289+
290+ let language = helix_syntax::get_language(&helix_syntax::LANG::Rust);
291+ // let mut parser = tree_sitter::Parser::new();
292+ // parser.set_language(language).unwrap();
293+ // let tree = parser.parse(source_code, None).unwrap();
294+
295+ let mut highlighter = Highlighter::new();
296+
297+ let mut config = HighlightConfiguration::new(
298+ language,
299+ &std::fs::read_to_string(
300+ "../helix-syntax/languages/tree-sitter-rust/queries/highlights.scm",
301+ )
302+ .unwrap(),
303+ &std::fs::read_to_string(
304+ "../helix-syntax/languages/tree-sitter-rust/queries/injections.scm",
305+ )
306+ .unwrap(),
307+ "", // locals.scm
308+ )
309+ .unwrap();
310+
311+ config.configure(&highlight_names);
312+
313+ let highlights = highlighter
314+ .highlight(&config, source_code.as_bytes(), None, |_| None)
315+ .unwrap();
316+
317+ for event in highlights {
318+ match event.unwrap() {
319+ HighlightEvent::Source { start, end } => {
320+ eprintln!("source: {}-{}", start, end);
321+ // iterate over range char by char
322+ }
323+ HighlightEvent::HighlightStart(s) => {
324+ eprintln!("highlight style started: {:?}", highlight_names[s.0]);
325+ // store/push highlight styles
326+ }
327+ HighlightEvent::HighlightEnd => {
328+ eprintln!("highlight style ended");
329+ // pop highlight styles
330+ }
331+ }
332+ }
333+}