commit 190519a21c84c0753cc1ad7913f70751d59ac4d1
parent 38afe4005ef57754ea4204a1d0833b51b8ce06cb
Author: Sylvia Ivory <git@sivory.net>
Date: Sun, 29 Jun 2025 16:15:21 -0700
Implement LuaExecutor trait
Diffstat:
6 files changed, 87 insertions(+), 38 deletions(-)
diff --git a/src/completion.rs b/src/completion.rs
@@ -1,3 +1,5 @@
+use std::sync::Arc;
+
use emmylua_parser::{
LuaAst, LuaAstNode, LuaAstToken, LuaBlock, LuaNameExpr, LuaParser, LuaSyntaxTree,
};
@@ -5,7 +7,7 @@ use mlua::prelude::*;
use reedline::{Completer, Span, Suggestion};
use rowan::TextRange;
-use crate::parse;
+use crate::{lua::LuaExecutor, parse};
#[derive(Debug)]
struct Variable {
@@ -20,7 +22,7 @@ struct Scope {
}
pub struct LuaCompleter {
- lua: Lua,
+ lua_executor: Arc<dyn LuaExecutor>,
tree: LuaSyntaxTree,
scopes: Vec<Scope>,
@@ -28,9 +30,9 @@ pub struct LuaCompleter {
}
impl LuaCompleter {
- pub fn new(lua: Lua) -> Self {
+ pub fn new(lua_executor: Arc<dyn LuaExecutor>) -> Self {
Self {
- lua,
+ lua_executor,
tree: LuaParser::parse("", parse::config()),
scopes: Vec::new(),
text: String::new(),
@@ -44,7 +46,7 @@ impl LuaCompleter {
}
fn globals(&self) -> Vec<String> {
- self.lua
+ self.lua_executor
.globals()
.pairs()
.flatten()
@@ -241,8 +243,14 @@ impl Completer for LuaCompleter {
#[cfg(test)]
mod tests {
+ use crate::lua::MluaExecutor;
+
use super::*;
+ fn lua_executor() -> Arc<dyn LuaExecutor> {
+ Arc::new(MluaExecutor::new())
+ }
+
fn line_to_position(line: usize, text: &str) -> u32 {
let split = text.split("\n").collect::<Vec<_>>();
split[0..line].join("\n").len() as u32
@@ -250,7 +258,7 @@ mod tests {
#[test]
fn locals() {
- let mut completer = LuaCompleter::new(Lua::new());
+ let mut completer = LuaCompleter::new(lua_executor());
let text = r#"
local function foo(a, b)
@@ -327,7 +335,7 @@ mod tests {
#[test]
fn upvalues() {
- let lua = Lua::new();
+ let lua = lua_executor();
lua.globals().set("foobar", "").unwrap();
let mut completer = LuaCompleter::new(lua);
diff --git a/src/editor.rs b/src/editor.rs
@@ -15,36 +15,26 @@ use reedline::{
};
use crate::{
- completion::LuaCompleter, hinter::LuaHinter, inspect::{display_basic, TableFormat},
- parse::LuaHighlighter, validator::LuaValidator,
+ completion::LuaCompleter,
+ hinter::LuaHinter,
+ inspect::{TableFormat, display_basic},
+ lua::{LuaExecutor, MluaExecutor},
+ parse::LuaHighlighter,
+ validator::LuaValidator,
};
pub struct Editor {
prompt: DefaultPrompt,
editor: Reedline,
- lua: Lua,
+ lua_executor: Arc<dyn LuaExecutor>,
table_format: TableFormat,
- cancel_lua: Arc<AtomicBool>,
}
impl Editor {
pub fn new() -> LuaResult<Self> {
- let lua = Lua::new();
- let version: String = lua.globals().get("_VERSION")?;
-
- let cancel_lua = Arc::new(AtomicBool::new(false));
-
- let inner_cancel = cancel_lua.clone();
- lua.set_hook(LuaHookTriggers::EVERY_LINE, move |_lua, _debug| {
- if inner_cancel.load(Ordering::Relaxed) {
- inner_cancel.store(false, Ordering::Relaxed);
-
- return Err(LuaError::runtime("cancelled"));
- }
-
- Ok(LuaVmState::Continue)
- });
+ let mlua = Arc::new(MluaExecutor::new());
+ let version: String = mlua.globals().get("_VERSION")?;
let prompt = DefaultPrompt::new(
DefaultPromptSegment::Basic(version),
@@ -70,7 +60,9 @@ impl Editor {
let mut editor = Reedline::create()
.with_validator(Box::new(LuaValidator::new()))
- .with_completer(Box::new(LuaCompleter::new(lua.clone())))
+ .with_completer(Box::new(LuaCompleter::new(
+ mlua.clone() as Arc<dyn LuaExecutor>
+ )))
.with_highlighter(Box::new(LuaHighlighter))
.with_hinter(Box::new(LuaHinter))
.with_edit_mode(Box::new(Emacs::new(keybindings)))
@@ -87,19 +79,18 @@ impl Editor {
Ok(Self {
prompt,
editor,
- lua,
table_format: TableFormat::ComfyTable(true),
- cancel_lua,
+ lua_executor: mlua,
})
}
fn register_ctrl_c(&self, is_running_lua: Arc<AtomicBool>) {
- let inner_cancel = self.cancel_lua.clone();
+ let executor = self.lua_executor.clone();
ctrlc::set_handler(move || {
if is_running_lua.load(Ordering::Relaxed) {
- inner_cancel.store(true, Ordering::Relaxed);
+ executor.cancel();
} else {
process::exit(0)
}
@@ -178,7 +169,7 @@ impl Editor {
}
fn eval(&self, line: &str) -> LuaResult<()> {
- let value: LuaValue = self.lua.load(line).set_name("=stdin").eval()?;
+ let value: LuaValue = self.lua_executor.exec(line)?;
let stringify = match value {
LuaValue::Table(tbl) => self.table_format.format(&tbl, true)?,
diff --git a/src/hinter.rs b/src/hinter.rs
@@ -59,9 +59,7 @@ impl Hinter for LuaHinter {
let s = format!(" ({})", display_basic(&value, false));
if use_ansi_coloring {
- Color::DarkGray
- .paint(s)
- .to_string()
+ Color::DarkGray.paint(s).to_string()
} else {
s
}
diff --git a/src/inspect.rs b/src/inspect.rs
@@ -5,7 +5,7 @@ use std::{
};
use aho_corasick::AhoCorasick;
-use comfy_table::{presets::UTF8_FULL_CONDENSED, Table};
+use comfy_table::{Table, presets::UTF8_FULL_CONDENSED};
use lazy_static::lazy_static;
use mlua::prelude::*;
use nu_ansi_term::{AnsiString, AnsiStrings, Color};
@@ -115,7 +115,7 @@ fn remove_invalid(mut bytes: &[u8]) -> String {
for bad_byte in &invalid[..error_len] {
// this *might* cause some false positives
- buffer.push_str(&format!("\u{FFFD}{:X?}", bad_byte));
+ buffer.push_str(&format!("\u{FFFD}{bad_byte:X?}"));
}
bytes = &invalid[error_len..];
diff --git a/src/lua.rs b/src/lua.rs
@@ -0,0 +1,51 @@
+use std::sync::{
+ Arc,
+ atomic::{AtomicBool, Ordering},
+};
+
+use mlua::prelude::*;
+
+pub trait LuaExecutor: Send + Sync {
+ fn exec(&self, code: &str) -> LuaResult<LuaValue>;
+ fn globals(&self) -> LuaTable;
+ fn cancel(&self);
+}
+
+pub struct MluaExecutor {
+ lua: Lua,
+ cancelled: Arc<AtomicBool>,
+}
+
+impl MluaExecutor {
+ pub fn new() -> Self {
+ let lua = Lua::new();
+ let cancelled = Arc::new(AtomicBool::new(false));
+
+ let inner_cancelled = cancelled.clone();
+ lua.set_hook(LuaHookTriggers::EVERY_LINE, move |_lua, _debug| {
+ if inner_cancelled.load(Ordering::Relaxed) {
+ inner_cancelled.store(false, Ordering::Relaxed);
+
+ return Err(LuaError::runtime("cancelled"));
+ }
+
+ Ok(LuaVmState::Continue)
+ });
+
+ Self { lua, cancelled }
+ }
+}
+
+impl LuaExecutor for MluaExecutor {
+ fn exec(&self, code: &str) -> LuaResult<LuaValue> {
+ self.lua.load(code).set_name("=repl").eval()
+ }
+
+ fn globals(&self) -> LuaTable {
+ self.lua.globals()
+ }
+
+ fn cancel(&self) {
+ self.cancelled.store(true, Ordering::Relaxed);
+ }
+}
diff --git a/src/main.rs b/src/main.rs
@@ -11,13 +11,14 @@ use emmylua_parser::{LuaParser, ParserConfig};
use mlua::prelude::*;
use reedline::Highlighter;
-use inspect::{inspect, comfy_table};
+use inspect::{comfy_table, inspect};
use parse::LuaHighlighter;
mod completion;
mod editor;
mod hinter;
mod inspect;
+mod lua;
mod parse;
mod validator;