From 25b3f98e3d200ae7f0f08b10be50552359502494 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= <blaz@mxxn.io>
Date: Wed, 9 Sep 2020 15:48:25 +0900
Subject: [PATCH] draft: tree-sitter highlighting

---
 Cargo.lock               |  13 +++++
 helix-core/src/state.rs  |  11 ++++
 helix-term/Cargo.toml    |   2 +
 helix-term/src/editor.rs | 115 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 141 insertions(+)

diff --git a/Cargo.lock b/Cargo.lock
index 0385b9d3..6334ee8b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -393,8 +393,11 @@ dependencies = [
  "argh",
  "crossterm",
  "helix-core",
+ "helix-syntax",
  "num_cpus",
  "smol",
+ "tree-sitter",
+ "tree-sitter-highlight",
  "tui",
 ]
 
@@ -716,6 +719,16 @@ dependencies = [
  "regex",
 ]
 
+[[package]]
+name = "tree-sitter-highlight"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6e3c800ac0db1562a045a4893cbbd7c484eb93426cae5632f9e5d24dd588cd1"
+dependencies = [
+ "regex",
+ "tree-sitter",
+]
+
 [[package]]
 name = "tui"
 version = "0.10.0"
diff --git a/helix-core/src/state.rs b/helix-core/src/state.rs
index 67f23009..5f941fb5 100644
--- a/helix-core/src/state.rs
+++ b/helix-core/src/state.rs
@@ -81,6 +81,17 @@ impl State {
     // foldable
     // changeFilter/transactionFilter
 
+    // TODO: move that accepts a boundary matcher fn/list, we keep incrementing until we hit
+    // a boundary
+
+    // TODO: edits, does each keypress trigger a full command? I guess it's adding to the same
+    // transaction
+    // There should be three pieces of the state: current transaction, the original doc, "preview"
+    // of the new state.
+    // 1. apply the newly generated keypress as a transaction
+    // 2. compose onto a ongoing transaction
+    // 3. on insert mode leave, that transaction gets stored into undo history
+
     pub fn move_pos(
         &self,
         pos: usize,
diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml
index ce3add5c..dcb01c1d 100644
--- a/helix-term/Cargo.toml
+++ b/helix-term/Cargo.toml
@@ -24,3 +24,5 @@ crossterm = { version = "0.17", features = ["event-stream"] }
 smol = "1"
 num_cpus = "1.13.0"
 tui = { version = "0.10.0", default-features = false, features = ["crossterm"] }
+tree-sitter = "0.16.1"
+tree-sitter-highlight = "0.2.0"
diff --git a/helix-term/src/editor.rs b/helix-term/src/editor.rs
index e019b5ba..1b58c46d 100644
--- a/helix-term/src/editor.rs
+++ b/helix-term/src/editor.rs
@@ -216,3 +216,118 @@ impl Editor {
         Ok(())
     }
 }
+
+// TODO: language configs:
+// tabSize, fileExtension etc, mapping to tree sitter parser
+// themes:
+// map tree sitter highlights to color values
+//
+// TODO: expand highlight thing so we're able to render only viewport range
+// TODO: async: maybe pre-cache scopes as empty so we render all graphemes initially as regular
+////text until calc finishes
+// TODO: scope matching: biggest union match? [string] & [html, string], [string, html] & [ string, html]
+// can do this by sorting our theme matches based on array len (longest first) then stopping at the
+// first rule that matches (rule.all(|scope| scopes.contains(scope)))
+//
+// let visual_x = 0;
+// let line = ?;
+// for span in spans {
+// start(scope) => scopes.push(scope)
+//  span =>
+//      let text = rope.slice(span.start..span.end);
+//      let style = calculate_style(scopes);
+//      for each grapheme in text.graphemes() {
+//          // if newline += lines, continue
+//
+//          if state.selection.ranges().any(|range| range.contains(char_index)) {
+//              if exactly on cursor {
+//              }
+//              if on primary cursor? {
+//              }
+//              modify style temporarily
+//          }
+//
+//          // if in bounds
+//
+//          // if tab, draw tab width
+//          // draw(visual_x, line, grapheme, style)
+//          // increment visual_x by grapheme_width(grapheme)
+//          // increment char_index by grapheme.len_chars()
+//      }
+//  end => scopes.pop()
+// }
+#[test]
+fn test_parser() {
+    use tree_sitter_highlight::{HighlightConfiguration, HighlightEvent, Highlighter};
+
+    let source_code = include_str!("./main.rs");
+
+    let highlight_names: Vec<String> = [
+        "attribute",
+        "constant",
+        "function.builtin",
+        "function",
+        "keyword",
+        "operator",
+        "property",
+        "punctuation",
+        "punctuation.bracket",
+        "punctuation.delimiter",
+        "string",
+        "string.special",
+        "tag",
+        "type",
+        "type.builtin",
+        "variable",
+        "variable.builtin",
+        "variable.parameter",
+    ]
+    .iter()
+    .cloned()
+    .map(String::from)
+    .collect();
+
+    let language = helix_syntax::get_language(&helix_syntax::LANG::Rust);
+    // let mut parser = tree_sitter::Parser::new();
+    // parser.set_language(language).unwrap();
+    // let tree = parser.parse(source_code, None).unwrap();
+
+    let mut highlighter = Highlighter::new();
+
+    let mut config = HighlightConfiguration::new(
+        language,
+        &std::fs::read_to_string(
+            "../helix-syntax/languages/tree-sitter-rust/queries/highlights.scm",
+        )
+        .unwrap(),
+        &std::fs::read_to_string(
+            "../helix-syntax/languages/tree-sitter-rust/queries/injections.scm",
+        )
+        .unwrap(),
+        "", // locals.scm
+    )
+    .unwrap();
+
+    config.configure(&highlight_names);
+
+    let highlights = highlighter
+        .highlight(&config, source_code.as_bytes(), None, |_| None)
+        .unwrap();
+
+    for event in highlights {
+        match event.unwrap() {
+            HighlightEvent::Source { start, end } => {
+                eprintln!("source: {}-{}", start, end);
+                // iterate over range char by char
+            }
+            HighlightEvent::HighlightStart(s) => {
+                eprintln!("highlight style started: {:?}", highlight_names[s.0]);
+                // store/push highlight styles
+            }
+            HighlightEvent::HighlightEnd => {
+                eprintln!("highlight style ended");
+                // pop highlight styles
+            }
+        }
+    }
+}