summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app.rs1
-rw-r--r--src/widgets/luaeditor/buffer.rs24
-rw-r--r--src/widgets/luaeditor/mod.rs62
-rw-r--r--src/widgets/luaeditor/theme.rs43
-rw-r--r--src/widgets/luaeditor/treesitter.rs75
-rw-r--r--src/widgets/sheetview.rs1
6 files changed, 178 insertions, 28 deletions
diff --git a/src/app.rs b/src/app.rs
index 3544fdf..71e6a02 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -8,7 +8,6 @@ use ratatui::{
widgets::Block,
};
-#[derive(Debug)]
pub struct App<'a> {
exit: bool,
view: SheetView<'a>,
diff --git a/src/widgets/luaeditor/buffer.rs b/src/widgets/luaeditor/buffer.rs
index 6a954ed..905950e 100644
--- a/src/widgets/luaeditor/buffer.rs
+++ b/src/widgets/luaeditor/buffer.rs
@@ -32,16 +32,22 @@ impl Buffer {
fn end_balance(&self, level: usize) -> isize {
let str = "\t".repeat(level);
- let start_count = self.lines.iter().filter(|s| {
- s.starts_with(&(str.clone() + "if "))
- || s.starts_with(&(str.clone() + "for "))
- || s.starts_with(&(str.clone() + "while "))
- || (s.starts_with(&str) && s.contains("function(") && s.ends_with(")"))
- }).count();
+ let start_count = self
+ .lines
+ .iter()
+ .filter(|s| {
+ s.starts_with(&(str.clone() + "if "))
+ || s.starts_with(&(str.clone() + "for "))
+ || s.starts_with(&(str.clone() + "while "))
+ || (s.starts_with(&str) && s.contains("function(") && s.ends_with(")"))
+ })
+ .count();
- let end_count = self.lines.iter().filter(
- |s| s.starts_with(&(str.clone() + "end ")) || **s == (str.clone() + "end")
- ).count();
+ let end_count = self
+ .lines
+ .iter()
+ .filter(|s| s.starts_with(&(str.clone() + "end ")) || **s == (str.clone() + "end"))
+ .count();
(end_count as isize) - (start_count as isize)
}
diff --git a/src/widgets/luaeditor/mod.rs b/src/widgets/luaeditor/mod.rs
index 723d8b5..9a73e6e 100644
--- a/src/widgets/luaeditor/mod.rs
+++ b/src/widgets/luaeditor/mod.rs
@@ -10,16 +10,19 @@ use ratatui::{
pub mod buffer;
pub mod cursor;
+pub mod theme;
+pub mod treesitter;
use buffer::Buffer;
+use tree_sitter_highlight::HighlightConfiguration;
use self::cursor::CursorMove;
-#[derive(Debug)]
pub struct LuaEditor<'a> {
block: Option<Block<'a>>,
scroll: usize,
buffer: Buffer,
+ highlight_config: HighlightConfiguration,
}
impl<'a> LuaEditor<'a> {
@@ -31,6 +34,7 @@ impl<'a> LuaEditor<'a> {
block: None,
scroll: 0,
buffer: Buffer::from_str(content.as_ref()).unwrap(),
+ highlight_config: treesitter::new_highlight_configuration(),
}
}
@@ -88,18 +92,40 @@ impl Widget for &mut LuaEditor<'_> {
self.block.render(area, buf);
let inner_area = self.block.inner_if_some(area);
- for (i, line) in self.buffer.lines().iter().enumerate().skip(self.scroll) {
- let replace = &line.replace('\t', " ");
- let span = replace.to_span();
- let mut span_area = inner_area.clone();
- span_area.height = 1;
- span_area.y += i as u16;
+ let text = self.text();
+ let highlights = treesitter::highlighter_split(text.as_bytes(), &self.highlight_config);
+
+ let mut span_area = inner_area.clone();
+
+ for (hl, group) in highlights.iter().skip(self.scroll) {
+ let lines: Vec<_> = group.lines().collect();
+
+ for (i, line) in lines.iter().enumerate() {
+ let line = line.replace("\t", " ");
+ let span = line.to_span();
+
+ if !inner_area.contains(span_area.into()) {
+ break;
+ }
+
+ theme::theme_highlight_group(*hl, span).render(span_area, buf);
+
+ if i < lines.len() - 1 {
+ span_area.y += 1;
+ span_area.x = inner_area.x;
+ } else {
+ span_area.x += line.len() as u16;
+ }
+ }
if !inner_area.contains(span_area.into()) {
break;
}
- span.render(span_area, buf)
+ if group.ends_with("\n") {
+ span_area.y += 1;
+ span_area.x = inner_area.x;
+ }
}
let mut cursor_area = inner_area.clone();
@@ -119,14 +145,16 @@ impl Widget for &mut LuaEditor<'_> {
}
}
- self.buffer
- .current_line()
- .chars()
- .nth(self.buffer.cursor().x() as usize)
- .map(|c| if c == '\t' { ' ' } else { c })
- .unwrap_or(' ')
- .to_span()
- .reversed()
- .render(cursor_area, buf);
+ if inner_area.contains(cursor_area.into()) {
+ self.buffer
+ .current_line()
+ .chars()
+ .nth(self.buffer.cursor().x() as usize)
+ .map(|c| if c == '\t' { ' ' } else { c })
+ .unwrap_or(' ')
+ .to_span()
+ .reversed()
+ .render(cursor_area, buf);
+ }
}
}
diff --git a/src/widgets/luaeditor/theme.rs b/src/widgets/luaeditor/theme.rs
new file mode 100644
index 0000000..2af8534
--- /dev/null
+++ b/src/widgets/luaeditor/theme.rs
@@ -0,0 +1,43 @@
+use ratatui::{style::{Color, Stylize}, text::Span};
+use tree_sitter_highlight::Highlight;
+
+
+static HIGHLIGHT_THEME: [Color; 29] = [
+ Color::White, // attribute
+ Color::White, // boolean
+ Color::DarkGray, // comment
+ Color::LightYellow, // conditional
+ Color::White, // constant
+ Color::White, // constant.builtin
+ Color::White, // constructor
+ Color::White, // field
+ Color::White, // function
+ Color::White, // function.builtin
+ Color::White, // function.call
+ Color::Yellow, // keyword
+ Color::LightYellow, // keyword.function
+ Color::White, // keyword.operator
+ Color::LightYellow, // keyword.return
+ Color::White, // label
+ Color::White, // method
+ Color::White, // method.call
+ Color::Blue, // number
+ Color::Gray, // operator
+ Color::White, // parameter
+ Color::White, // preproc
+ Color::Gray, // punctuation.bracket
+ Color::Gray, // punctuation.delimiter
+ Color::LightYellow, // repeat
+ Color::LightGreen, // string
+ Color::Yellow, // string.escape
+ Color::Gray, // variable
+ Color::White, // variable.builtin
+];
+
+pub fn theme_highlight_group(hl: Option<Highlight>, span: Span) -> Span {
+ if let Some(hl) = hl {
+ span.fg(HIGHLIGHT_THEME[hl.0])
+ } else {
+ span
+ }
+}
diff --git a/src/widgets/luaeditor/treesitter.rs b/src/widgets/luaeditor/treesitter.rs
new file mode 100644
index 0000000..57013f9
--- /dev/null
+++ b/src/widgets/luaeditor/treesitter.rs
@@ -0,0 +1,75 @@
+use tree_sitter_highlight::{Highlight, HighlightConfiguration, HighlightEvent, Highlighter};
+
+static HIGHLIGHT_NAMES: [&'static str; 29] = [
+ "attribute",
+ "boolean",
+ "comment",
+ "conditional",
+ "constant",
+ "constant.builtin",
+ "constructor",
+ "field",
+ "function",
+ "function.builtin",
+ "function.call",
+ "keyword",
+ "keyword.function",
+ "keyword.operator",
+ "keyword.return",
+ "label",
+ "method",
+ "method.call",
+ "number",
+ "operator",
+ "parameter",
+ "preproc",
+ "punctuation.bracket",
+ "punctuation.delimiter",
+ "repeat",
+ "string",
+ "string.escape",
+ "variable",
+ "variable.builtin",
+];
+
+pub fn new_highlight_configuration() -> HighlightConfiguration {
+ let mut highlight_config = HighlightConfiguration::new(
+ tree_sitter_lua::language(),
+ "lua",
+ tree_sitter_lua::HIGHLIGHTS_QUERY,
+ tree_sitter_lua::INJECTIONS_QUERY,
+ tree_sitter_lua::LOCALS_QUERY,
+ )
+ .unwrap();
+
+ highlight_config.configure(&HIGHLIGHT_NAMES);
+
+ highlight_config
+}
+
+pub fn highlight_name(h: Highlight) -> &'static str {
+ HIGHLIGHT_NAMES[h.0]
+}
+
+pub fn highlighter_split<'a>(
+ s: &'a [u8],
+ config: &'a HighlightConfiguration,
+) -> Vec<(Option<Highlight>, &'a str)> {
+ let mut splits = Vec::new();
+ let mut current = None;
+
+ let mut highlighter = Highlighter::new();
+ let highlights = highlighter.highlight(config, s, None, |_| None).unwrap();
+
+ for event in highlights {
+ match event.unwrap() {
+ HighlightEvent::Source { start, end } => {
+ splits.push((current, std::str::from_utf8(&s[start..end]).unwrap()));
+ }
+ HighlightEvent::HighlightStart(s) => current = Some(s),
+ HighlightEvent::HighlightEnd => current = None,
+ }
+ }
+
+ splits
+}
diff --git a/src/widgets/sheetview.rs b/src/widgets/sheetview.rs
index 2584c96..c4f7a73 100644
--- a/src/widgets/sheetview.rs
+++ b/src/widgets/sheetview.rs
@@ -18,7 +18,6 @@ use super::luaeditor::LuaEditor;
const DEFAULT_COLUMN_WIDTH: u16 = 10;
-#[derive(Debug)]
pub struct SheetView<'a> {
block: Option<Block<'a>>,
luaeditor: Option<LuaEditor<'a>>,