init
This commit is contained in:
commit
be6653be6f
15 changed files with 829 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
Cargo.lock
|
6
.gitmodules
vendored
Normal file
6
.gitmodules
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
[submodule "ts/tree-sitter-json"]
|
||||
path = ts/tree-sitter-json
|
||||
url = https://github.com/tree-sitter/tree-sitter-json
|
||||
[submodule "ts/tree-sitter-lua"]
|
||||
path = ts/tree-sitter-lua
|
||||
url = https://github.com/MunifTanjim/tree-sitter-lua
|
19
Cargo.toml
Normal file
19
Cargo.toml
Normal file
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "luna"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.53"
|
||||
mlua = { version = "0.7.3", features = ["luajit", "serialize", "send"] }
|
||||
relm4 = { version = "0.4.2", features = ["macros"] }
|
||||
serde_json = "1.0.78"
|
||||
tree-sitter = "0.20.4"
|
||||
tree-sitter-highlight = "0.20.1"
|
||||
|
||||
[features]
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0.72"
|
7
assets/style.css
Normal file
7
assets/style.css
Normal file
|
@ -0,0 +1,7 @@
|
|||
textview text {
|
||||
background-color: #2a2c39;
|
||||
}
|
||||
|
||||
textview {
|
||||
color: #f8f8f2;
|
||||
}
|
14
build.rs
Normal file
14
build.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
fn main() {
|
||||
cc::Build::new()
|
||||
.include("ts/tree-sitter-json")
|
||||
.file("ts/tree-sitter-json/src/parser.c")
|
||||
.compile("tree-sitter-json");
|
||||
|
||||
cc::Build::new()
|
||||
.include("ts/tree-sitter-lua")
|
||||
.files([
|
||||
"ts/tree-sitter-lua/src/parser.c",
|
||||
"ts/tree-sitter-lua/src/scanner.c",
|
||||
])
|
||||
.compile("tree-sitter-lua");
|
||||
}
|
12
rustfmt.toml
Normal file
12
rustfmt.toml
Normal file
|
@ -0,0 +1,12 @@
|
|||
unstable_features = true
|
||||
binop_separator = "Back"
|
||||
format_code_in_doc_comments = true
|
||||
format_macro_matchers = true
|
||||
format_strings = true
|
||||
imports_layout = "HorizontalVertical"
|
||||
match_block_trailing_comma = true
|
||||
merge_imports = true
|
||||
normalize_comments = true
|
||||
use_field_init_shorthand = true
|
||||
use_try_shorthand = true
|
||||
wrap_comments = true
|
99
src/hl.rs
Normal file
99
src/hl.rs
Normal file
|
@ -0,0 +1,99 @@
|
|||
use relm4::gtk::{prelude::*, TextBuffer};
|
||||
use tree_sitter::Language;
|
||||
use tree_sitter_highlight::{Highlight, HighlightConfiguration, HighlightEvent};
|
||||
|
||||
const HIGHLIGHT_NAMES: &[&str] = &[
|
||||
"attribute",
|
||||
"comment",
|
||||
"conditional",
|
||||
"constant",
|
||||
"function",
|
||||
"function.builtin",
|
||||
"keyword",
|
||||
"label",
|
||||
"number",
|
||||
"operator",
|
||||
"punctuation",
|
||||
"repeat",
|
||||
"string",
|
||||
"type",
|
||||
"variable",
|
||||
];
|
||||
|
||||
pub const JSON_HL_QUERY: &str = include_str!("../ts/json_highlights.scm");
|
||||
pub const LUA_HL_QUERY: &str = include_str!("../ts/lua_highlights.scm");
|
||||
|
||||
pub fn highlight_text_buffer(buf: &TextBuffer, lang: Language, highlight_query: &str) {
|
||||
buf.remove_all_tags(&buf.start_iter(), &buf.end_iter());
|
||||
|
||||
let mut conf = HighlightConfiguration::new(lang, highlight_query, "", "").unwrap();
|
||||
conf.configure(HIGHLIGHT_NAMES);
|
||||
|
||||
let table = buf.tag_table();
|
||||
let tag = |name: &str| table.lookup(name).expect("missing text tag!");
|
||||
|
||||
let txt = buf.text(&buf.start_iter(), &buf.end_iter(), false);
|
||||
crate::HIGHLIGHTER.with(|hl| {
|
||||
let mut hl = hl.borrow_mut();
|
||||
let hls = hl.highlight(&conf, txt.as_bytes(), None, |_| None);
|
||||
|
||||
if let Ok(hls) = hls.and_then(|hls| hls.collect::<Result<Vec<_>, _>>()) {
|
||||
let mut acc = HighlightAcc::default();
|
||||
|
||||
for hl in hls {
|
||||
acc.push(hl);
|
||||
}
|
||||
|
||||
for Span {
|
||||
start,
|
||||
end,
|
||||
hl: Highlight(hl),
|
||||
} in acc.spans
|
||||
{
|
||||
buf.apply_tag(
|
||||
&tag(HIGHLIGHT_NAMES[hl]),
|
||||
&buf.iter_at_offset(start as i32),
|
||||
&buf.iter_at_offset(end as i32),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
buf.apply_tag(&tag("error"), &buf.start_iter(), &buf.end_iter());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct HighlightAcc {
|
||||
prev_pos: usize,
|
||||
pos: usize,
|
||||
cur_hl: Option<Highlight>,
|
||||
pub spans: Vec<Span>,
|
||||
}
|
||||
|
||||
impl HighlightAcc {
|
||||
pub fn push(&mut self, ev: HighlightEvent) {
|
||||
match ev {
|
||||
HighlightEvent::Source { end, .. } => {
|
||||
self.prev_pos = self.pos;
|
||||
self.pos = end;
|
||||
},
|
||||
HighlightEvent::HighlightStart(hl) => self.cur_hl = Some(hl),
|
||||
HighlightEvent::HighlightEnd => {
|
||||
if let Some(hl) = self.cur_hl.take() {
|
||||
self.spans.push(Span {
|
||||
start: self.prev_pos,
|
||||
end: self.pos,
|
||||
hl,
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Span {
|
||||
start: usize,
|
||||
end: usize,
|
||||
hl: Highlight,
|
||||
}
|
34
src/lua.rs
Normal file
34
src/lua.rs
Normal file
|
@ -0,0 +1,34 @@
|
|||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use mlua::{DeserializeOptions, Function, Lua, LuaSerdeExt, Value};
|
||||
|
||||
pub fn try_eval(lua: &Lua, src: &str) -> mlua::Result<(String, String)> {
|
||||
lua.scope(|s| {
|
||||
let output = Rc::new(RefCell::new(String::new()));
|
||||
let output_ = Rc::clone(&output);
|
||||
let print = s.create_function(move |lua, x: Value| {
|
||||
let s = lua
|
||||
.globals()
|
||||
.get::<_, Function>("tostring")?
|
||||
.call::<_, String>(x)?;
|
||||
|
||||
let mut output = output_.borrow_mut();
|
||||
output.push_str(&s);
|
||||
output.push('\n');
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
lua.globals().set("print", print)?;
|
||||
|
||||
let res = lua.from_value_with::<serde_json::Value>(
|
||||
lua.load(src).eval()?,
|
||||
DeserializeOptions::new()
|
||||
.deny_unsupported_types(false)
|
||||
.deny_recursive_tables(false),
|
||||
)?;
|
||||
let res = serde_json::to_string_pretty(&res).unwrap();
|
||||
|
||||
Ok((output.take(), res))
|
||||
})
|
||||
}
|
27
src/main.rs
Normal file
27
src/main.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
use relm4::{gtk, RelmApp};
|
||||
use std::cell::RefCell;
|
||||
use tree_sitter_highlight::Highlighter;
|
||||
use tree_sitter::Language;
|
||||
|
||||
mod hl;
|
||||
mod lua;
|
||||
mod ui;
|
||||
|
||||
thread_local! {
|
||||
pub static HIGHLIGHTER: RefCell<Highlighter> = RefCell::new(Highlighter::new());
|
||||
}
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
gtk::init()?;
|
||||
relm4::set_global_css(include_bytes!("../assets/style.css"));
|
||||
let model = ui::AppModel::new()?;
|
||||
let app = RelmApp::new(model);
|
||||
app.run();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn tree_sitter_json() -> Language;
|
||||
fn tree_sitter_lua() -> Language;
|
||||
}
|
116
src/ui/entry.rs
Normal file
116
src/ui/entry.rs
Normal file
|
@ -0,0 +1,116 @@
|
|||
use relm4::{
|
||||
factory::{DynamicIndex, FactoryPrototype, FactoryVecDeque},
|
||||
gtk::{self, prelude::*, Orientation, TextBuffer},
|
||||
};
|
||||
|
||||
use crate::hl::{self, highlight_text_buffer};
|
||||
|
||||
use super::AppMsg;
|
||||
|
||||
pub enum Entry {
|
||||
Error(TextBuffer),
|
||||
LuaData {
|
||||
src: TextBuffer,
|
||||
out: String,
|
||||
result: TextBuffer,
|
||||
},
|
||||
}
|
||||
|
||||
impl FactoryPrototype for Entry {
|
||||
type Factory = FactoryVecDeque<Self>;
|
||||
type Widgets = EntryWidgets;
|
||||
type View = gtk::Box;
|
||||
type Msg = AppMsg;
|
||||
type Root = gtk::Box;
|
||||
|
||||
fn position(&self, _key: &DynamicIndex) {}
|
||||
|
||||
fn init_view(&self, _key: &DynamicIndex, _sender: relm4::Sender<Self::Msg>) -> Self::Widgets {
|
||||
let (first_buf, second_buf) = match self {
|
||||
Self::Error(e) => (e, None),
|
||||
Self::LuaData { src, result, .. } => (src, Some(result)),
|
||||
};
|
||||
|
||||
if let Some(json) = second_buf {
|
||||
highlight_text_buffer(
|
||||
first_buf,
|
||||
unsafe { crate::tree_sitter_lua() },
|
||||
hl::LUA_HL_QUERY,
|
||||
);
|
||||
|
||||
highlight_text_buffer(
|
||||
json,
|
||||
unsafe { crate::tree_sitter_json() },
|
||||
hl::JSON_HL_QUERY,
|
||||
);
|
||||
} else {
|
||||
first_buf.apply_tag(
|
||||
&first_buf.tag_table().lookup("error").unwrap(),
|
||||
&first_buf.start_iter(),
|
||||
&first_buf.end_iter(),
|
||||
);
|
||||
}
|
||||
|
||||
let main_box = gtk::Box::new(Orientation::Vertical, 5);
|
||||
main_box.set_valign(gtk::Align::Start);
|
||||
main_box.set_baseline_position(gtk::BaselinePosition::Top);
|
||||
|
||||
let first_view = gtk::TextView::builder()
|
||||
.height_request(20)
|
||||
.editable(false)
|
||||
.monospace(true)
|
||||
.buffer(first_buf)
|
||||
.build();
|
||||
|
||||
let second_view = match self {
|
||||
Self::LuaData { out, .. } if !out.is_empty() => {
|
||||
let buf = gtk::TextBuffer::new(None);
|
||||
buf.set_text(out);
|
||||
Some(
|
||||
gtk::TextView::builder()
|
||||
.height_request(20)
|
||||
.editable(false)
|
||||
.monospace(true)
|
||||
.buffer(&buf)
|
||||
.build(),
|
||||
)
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let third_view = second_buf.map(|b| {
|
||||
gtk::TextView::builder()
|
||||
.height_request(20)
|
||||
.editable(false)
|
||||
.monospace(true)
|
||||
.buffer(b)
|
||||
.build()
|
||||
});
|
||||
|
||||
main_box.append(&first_view);
|
||||
if let Some(v) = second_view {
|
||||
main_box.append(&v);
|
||||
}
|
||||
if let Some(v) = third_view {
|
||||
main_box.append(&v);
|
||||
}
|
||||
|
||||
main_box.set_visible(true);
|
||||
|
||||
|
||||
EntryWidgets { main_box }
|
||||
}
|
||||
|
||||
fn view(&self, _key: &DynamicIndex, _widgets: &Self::Widgets) {
|
||||
// This widget is never updated :P
|
||||
}
|
||||
|
||||
fn root_widget(widgets: &Self::Widgets) -> &Self::Root {
|
||||
&widgets.main_box
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EntryWidgets {
|
||||
main_box: gtk::Box,
|
||||
}
|
283
src/ui/mod.rs
Normal file
283
src/ui/mod.rs
Normal file
|
@ -0,0 +1,283 @@
|
|||
use std::{
|
||||
sync::{Arc, Mutex},
|
||||
thread,
|
||||
};
|
||||
|
||||
use entry::Entry;
|
||||
use mlua::Lua;
|
||||
use relm4::{
|
||||
factory::FactoryVecDeque,
|
||||
gtk::{self, gdk, prelude::*, Inhibit, Orientation, TextBuffer, TextTag, TextTagTable},
|
||||
send,
|
||||
AppUpdate,
|
||||
Model,
|
||||
WidgetPlus,
|
||||
Widgets,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
hl::{self, highlight_text_buffer},
|
||||
lua::try_eval,
|
||||
};
|
||||
|
||||
mod entry;
|
||||
|
||||
pub struct AppModel {
|
||||
lua: Arc<Mutex<Lua>>,
|
||||
input: TextBuffer,
|
||||
entries: FactoryVecDeque<Entry>,
|
||||
loading: bool,
|
||||
history: Vec<String>,
|
||||
history_idx: usize,
|
||||
}
|
||||
|
||||
impl AppModel {
|
||||
pub fn new() -> anyhow::Result<Self> {
|
||||
let table = TextTagTable::new();
|
||||
|
||||
fn tag(name: &str, color: &str) -> TextTag {
|
||||
TextTag::builder().name(name).foreground(color).build()
|
||||
}
|
||||
|
||||
table.add(
|
||||
&TextTag::builder()
|
||||
.name("error")
|
||||
.foreground("red")
|
||||
.background("black")
|
||||
.build(),
|
||||
);
|
||||
|
||||
table.add(&tag("attribute", "#50fa7b"));
|
||||
table.add(&tag("comment", "#6272a4"));
|
||||
table.add(&tag("conditional", "#ff79c6"));
|
||||
table.add(&tag("constant", "#6be5fd"));
|
||||
table.add(&tag("function", "#50fa7b"));
|
||||
table.add(&tag("function.builtin", "#8be9fd"));
|
||||
table.add(&tag("keyword", "#ff79c6"));
|
||||
table.add(&tag("label", "#bd93f9"));
|
||||
table.add(&tag("number", "#bd93f9"));
|
||||
table.add(&tag("operator", "#ff79c6"));
|
||||
table.add(&tag("punctuation", "#f8f8f2"));
|
||||
table.add(&tag("repeat", "#ff79c6"));
|
||||
table.add(&tag("string", "#f1fa8c"));
|
||||
table.add(&tag("type", "#8be9fd"));
|
||||
table.add(&tag("variable", "#f8f8f2"));
|
||||
|
||||
Ok(Self {
|
||||
lua: unsafe { Arc::new(Mutex::new(Lua::unsafe_new())) },
|
||||
input: TextBuffer::new(Some(&table)),
|
||||
entries: FactoryVecDeque::new(),
|
||||
loading: false,
|
||||
history: Vec::new(),
|
||||
history_idx: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl AppUpdate for AppModel {
|
||||
fn update(
|
||||
&mut self,
|
||||
msg: Self::Msg,
|
||||
_components: &Self::Components,
|
||||
sender: relm4::Sender<Self::Msg>,
|
||||
) -> bool {
|
||||
match msg {
|
||||
AppMsg::Eval => {
|
||||
self.loading = true;
|
||||
let src = self
|
||||
.input
|
||||
.text(&self.input.start_iter(), &self.input.end_iter(), false)
|
||||
.to_string();
|
||||
let lua = Arc::clone(&self.lua);
|
||||
thread::spawn(move || match try_eval(&*lua.lock().unwrap(), &src) {
|
||||
Ok((out, r)) => {
|
||||
send!(
|
||||
sender,
|
||||
AppMsg::AddEntry(StringEntry::LuaData {
|
||||
src,
|
||||
result: r,
|
||||
out,
|
||||
})
|
||||
);
|
||||
send!(sender, AppMsg::ClearInput);
|
||||
},
|
||||
|
||||
Err(e) => send!(sender, AppMsg::AddEntry(StringEntry::Error(e.to_string()))),
|
||||
});
|
||||
},
|
||||
AppMsg::AddEntry(e) => {
|
||||
self.entries.push_front(match e {
|
||||
StringEntry::Error(e) => Entry::Error(
|
||||
gtk::TextBuffer::builder()
|
||||
.tag_table(&self.input.tag_table())
|
||||
.text(&e)
|
||||
.build(),
|
||||
),
|
||||
StringEntry::LuaData { src, out, result } => Entry::LuaData {
|
||||
src: gtk::TextBuffer::builder()
|
||||
.tag_table(&self.input.tag_table())
|
||||
.text(&src)
|
||||
.build(),
|
||||
result: gtk::TextBuffer::builder()
|
||||
.tag_table(&self.input.tag_table())
|
||||
.text(&result)
|
||||
.build(),
|
||||
out,
|
||||
},
|
||||
});
|
||||
self.loading = false;
|
||||
},
|
||||
AppMsg::ClearInput => {
|
||||
let input = self
|
||||
.input
|
||||
.text(&self.input.start_iter(), &self.input.end_iter(), false)
|
||||
.to_string();
|
||||
|
||||
if !input.is_empty() {
|
||||
self.history.push(input);
|
||||
}
|
||||
self.history_idx += 1;
|
||||
self.input.set_text("");
|
||||
},
|
||||
AppMsg::ClearEntries => self.entries.clear(),
|
||||
AppMsg::InputUpdate => highlight_text_buffer(
|
||||
&self.input,
|
||||
unsafe { crate::tree_sitter_lua() },
|
||||
hl::LUA_HL_QUERY,
|
||||
),
|
||||
AppMsg::History(HistoryChange::Prev) => {
|
||||
if self.history.len() == self.history_idx {
|
||||
let input = self
|
||||
.input
|
||||
.text(&self.input.start_iter(), &self.input.end_iter(), false)
|
||||
.to_string();
|
||||
|
||||
if !input.is_empty() {
|
||||
self.history.push(input);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(idx) = self.history_idx.checked_sub(1) {
|
||||
self.history_idx = idx;
|
||||
self.input.set_text(&self.history[idx]);
|
||||
send!(sender, AppMsg::InputUpdate);
|
||||
}
|
||||
},
|
||||
AppMsg::History(HistoryChange::Next) => {
|
||||
match (self.history_idx + 1).cmp(&self.history.len()) {
|
||||
std::cmp::Ordering::Less => {
|
||||
self.history_idx += 1;
|
||||
self.input.set_text(&self.history[self.history_idx]);
|
||||
},
|
||||
std::cmp::Ordering::Equal => {
|
||||
self.history_idx += 1;
|
||||
self.input.set_text("");
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
send!(sender, AppMsg::InputUpdate);
|
||||
},
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Model for AppModel {
|
||||
type Msg = AppMsg;
|
||||
type Widgets = AppWidgets;
|
||||
type Components = ();
|
||||
}
|
||||
|
||||
pub enum StringEntry {
|
||||
Error(String),
|
||||
LuaData {
|
||||
src: String,
|
||||
out: String,
|
||||
result: String,
|
||||
},
|
||||
}
|
||||
|
||||
pub enum AppMsg {
|
||||
Eval,
|
||||
AddEntry(StringEntry),
|
||||
ClearInput,
|
||||
ClearEntries,
|
||||
InputUpdate,
|
||||
History(HistoryChange),
|
||||
}
|
||||
|
||||
pub enum HistoryChange {
|
||||
Next,
|
||||
Prev,
|
||||
}
|
||||
|
||||
#[relm4::widget(pub)]
|
||||
impl Widgets<AppModel, ()> for AppWidgets {
|
||||
view! {
|
||||
gtk::ApplicationWindow {
|
||||
set_title: Some("luna"),
|
||||
set_default_width: 300,
|
||||
set_default_height: 200,
|
||||
|
||||
set_child = Some(>k::Box) {
|
||||
set_orientation: Orientation::Vertical,
|
||||
set_margin_all: 5,
|
||||
set_spacing: 5,
|
||||
|
||||
append: scroll = >k::ScrolledWindow {
|
||||
set_child = Some(>k::Box) {
|
||||
set_orientation: Orientation::Vertical,
|
||||
set_hexpand: true,
|
||||
set_vexpand: true,
|
||||
set_spacing: 8,
|
||||
|
||||
factory!(model.entries),
|
||||
}
|
||||
},
|
||||
append = >k::Box {
|
||||
set_orientation: Orientation::Horizontal,
|
||||
|
||||
append = >k::TextView {
|
||||
set_hexpand: true,
|
||||
set_buffer: Some(&model.input),
|
||||
set_monospace: true,
|
||||
|
||||
add_controller = >k::EventControllerKey {
|
||||
connect_key_pressed(sender) => move |_, key, _, state| {
|
||||
send!(sender, AppMsg::InputUpdate);
|
||||
if !state.contains(gdk::ModifierType::SHIFT_MASK) {
|
||||
match key {
|
||||
gdk::Key::Return => {
|
||||
send!(sender, AppMsg::Eval);
|
||||
return Inhibit(true);
|
||||
}
|
||||
gdk::Key::Up => {
|
||||
send!(sender, AppMsg::History(HistoryChange::Prev));
|
||||
return Inhibit(true);
|
||||
}
|
||||
gdk::Key::Down => {
|
||||
send!(sender, AppMsg::History(HistoryChange::Next));
|
||||
return Inhibit(true);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Inhibit(false)
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
append = >k::Spinner {
|
||||
set_spinning: watch! { model.loading },
|
||||
},
|
||||
|
||||
append = >k::Button {
|
||||
set_label: "Clear List",
|
||||
|
||||
connect_clicked(sender) => move |_| send!(sender, AppMsg::ClearEntries),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
14
ts/json_highlights.scm
Normal file
14
ts/json_highlights.scm
Normal file
|
@ -0,0 +1,14 @@
|
|||
(true) @boolean
|
||||
(false) @boolean
|
||||
(null) @constant.builtin
|
||||
(number) @number
|
||||
(pair key: (string) @label)
|
||||
(pair value: (string) @string)
|
||||
(array (string) @string)
|
||||
(string_content (escape_sequence) @string.escape)
|
||||
(ERROR) @error
|
||||
"," @punctuation.delimiter
|
||||
"[" @punctuation.bracket
|
||||
"]" @punctuation.bracket
|
||||
"{" @punctuation.bracket
|
||||
"}" @punctuation.bracket
|
194
ts/lua_highlights.scm
Normal file
194
ts/lua_highlights.scm
Normal file
|
@ -0,0 +1,194 @@
|
|||
;;; Builtins
|
||||
|
||||
[
|
||||
(false)
|
||||
(true)
|
||||
] @boolean
|
||||
|
||||
(nil) @constant.builtin
|
||||
|
||||
((identifier) @variable.builtin
|
||||
(#match? @variable.builtin "self"))
|
||||
|
||||
;; Keywords
|
||||
|
||||
"return" @keyword.return
|
||||
|
||||
[
|
||||
"goto"
|
||||
"in"
|
||||
"local"
|
||||
] @keyword
|
||||
|
||||
(label_statement) @label
|
||||
|
||||
(break_statement) @keyword
|
||||
|
||||
(do_statement
|
||||
[
|
||||
"do"
|
||||
"end"
|
||||
] @keyword)
|
||||
|
||||
(while_statement
|
||||
[
|
||||
"while"
|
||||
"do"
|
||||
"end"
|
||||
] @repeat)
|
||||
|
||||
(repeat_statement
|
||||
[
|
||||
"repeat"
|
||||
"until"
|
||||
] @repeat)
|
||||
|
||||
(if_statement
|
||||
[
|
||||
"if"
|
||||
"elseif"
|
||||
"else"
|
||||
"then"
|
||||
"end"
|
||||
] @conditional)
|
||||
|
||||
(elseif_statement
|
||||
[
|
||||
"elseif"
|
||||
"then"
|
||||
"end"
|
||||
] @conditional)
|
||||
|
||||
(else_statement
|
||||
[
|
||||
"else"
|
||||
"end"
|
||||
] @conditional)
|
||||
|
||||
(for_statement
|
||||
[
|
||||
"for"
|
||||
"do"
|
||||
"end"
|
||||
] @repeat)
|
||||
|
||||
(function_declaration
|
||||
[
|
||||
"function"
|
||||
"end"
|
||||
] @keyword)
|
||||
|
||||
(function_definition
|
||||
[
|
||||
"function"
|
||||
"end"
|
||||
] @keyword)
|
||||
|
||||
;; Operators
|
||||
|
||||
[
|
||||
"and"
|
||||
"not"
|
||||
"or"
|
||||
] @keyword.operator
|
||||
|
||||
[
|
||||
"+"
|
||||
"-"
|
||||
"*"
|
||||
"/"
|
||||
"%"
|
||||
"^"
|
||||
"#"
|
||||
"=="
|
||||
"~="
|
||||
"<="
|
||||
">="
|
||||
"<"
|
||||
">"
|
||||
"="
|
||||
"&"
|
||||
"~"
|
||||
"|"
|
||||
"<<"
|
||||
">>"
|
||||
"//"
|
||||
".."
|
||||
] @operator
|
||||
|
||||
;; Punctuations
|
||||
|
||||
[
|
||||
";"
|
||||
":"
|
||||
","
|
||||
"."
|
||||
] @punctuation.delimiter
|
||||
|
||||
;; Brackets
|
||||
|
||||
[
|
||||
"("
|
||||
")"
|
||||
"["
|
||||
"]"
|
||||
"{"
|
||||
"}"
|
||||
] @punctuation.bracket
|
||||
|
||||
;; Variables
|
||||
|
||||
(identifier) @variable
|
||||
|
||||
;; Constants
|
||||
|
||||
(vararg_expression) @constant
|
||||
|
||||
((identifier) @constant
|
||||
(#lua-match? @constant "^[A-Z][A-Z_0-9]*$"))
|
||||
|
||||
;; Tables
|
||||
|
||||
(field name: (identifier) @field)
|
||||
|
||||
(dot_index_expression field: (identifier) @field)
|
||||
|
||||
(table_constructor
|
||||
[
|
||||
"{"
|
||||
"}"
|
||||
] @constructor)
|
||||
|
||||
;; Functions
|
||||
|
||||
(parameters (identifier) @parameter)
|
||||
|
||||
(function_call name: (identifier) @function)
|
||||
(function_declaration name: (identifier) @function)
|
||||
|
||||
(function_call name: (dot_index_expression field: (identifier) @function))
|
||||
(function_declaration name: (dot_index_expression field: (identifier) @function))
|
||||
|
||||
(method_index_expression method: (identifier) @method)
|
||||
|
||||
(function_call
|
||||
(identifier) @function.builtin
|
||||
(#any-of? @function.builtin
|
||||
;; built-in functions in Lua 5.1
|
||||
"assert" "collectgarbage" "dofile" "error" "getfenv" "getmetatable" "ipairs"
|
||||
"load" "loadfile" "loadstring" "module" "next" "pairs" "pcall" "print"
|
||||
"rawequal" "rawget" "rawset" "require" "select" "setfenv" "setmetatable"
|
||||
"tonumber" "tostring" "type" "unpack" "xpcall"))
|
||||
|
||||
;; Others
|
||||
|
||||
(comment) @comment
|
||||
|
||||
(hash_bang_line) @comment
|
||||
|
||||
(number) @number
|
||||
|
||||
(string) @string
|
||||
|
||||
;; Error
|
||||
(ERROR) @error
|
1
ts/tree-sitter-json
Submodule
1
ts/tree-sitter-json
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 203e239408d642be83edde8988d6e7b20a19f0e8
|
1
ts/tree-sitter-lua
Submodule
1
ts/tree-sitter-lua
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 547184a6cfcc900fcac4a2a56538fa8bcdb293e6
|
Loading…
Reference in a new issue