summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNathan Reiner <nathan@nathanreiner.xyz>2024-07-26 10:32:55 +0200
committerNathan Reiner <nathan@nathanreiner.xyz>2024-07-26 10:32:55 +0200
commit6ca07d6af8a338e76817d06c6c6c6f13e64fba9c (patch)
tree32680f0942da8c45af8425ebc20330f5456a9213
parent8b7c56df1940d2ac6e3ece3385464009e46382fa (diff)
add neosheetrc support
-rw-r--r--Cargo.lock61
-rw-r--r--Cargo.toml4
-rw-r--r--src/app.rs30
-rw-r--r--src/config/constants.rs23
-rw-r--r--src/config/mod.rs37
-rw-r--r--src/config/theme/colorpair.rs106
-rw-r--r--src/config/theme/mod.rs30
-rw-r--r--src/config/theme/sheetview.rs69
-rw-r--r--src/lua/mod.rs85
-rw-r--r--src/lua/runtime.rs52
-rw-r--r--src/main.rs1
-rw-r--r--src/sheet/eval.rs147
-rw-r--r--src/widgets/sheetview/mod.rs (renamed from src/widgets/sheetview.rs)144
13 files changed, 543 insertions, 246 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 20ecfaf..6e6f391 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -117,12 +117,44 @@ dependencies = [
]
[[package]]
+name = "dirs"
+version = "5.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
+dependencies = [
+ "libc",
+ "option-ext",
+ "redox_users",
+ "windows-sys",
+]
+
+[[package]]
name = "either"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
+name = "getrandom"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -166,6 +198,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
+name = "libredox"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
+dependencies = [
+ "bitflags",
+ "libc",
+]
+
+[[package]]
name = "lock_api"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -236,6 +278,8 @@ dependencies = [
name = "neosheet"
version = "0.1.0"
dependencies = [
+ "dirs",
+ "lazy_static",
"mlua",
"ratatui",
"tree-sitter-highlight",
@@ -258,6 +302,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
+name = "option-ext"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
+
+[[package]]
name = "parking_lot"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -341,6 +391,17 @@ dependencies = [
]
[[package]]
+name = "redox_users"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891"
+dependencies = [
+ "getrandom",
+ "libredox",
+ "thiserror",
+]
+
+[[package]]
name = "regex"
version = "1.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index b4f83df..4529a57 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,7 +4,9 @@ version = "0.1.0"
edition = "2021"
[dependencies]
-mlua = { version = "0.9.9", features = ["luajit"] }
+dirs = "5.0.1"
+lazy_static = "1.5.0"
+mlua = { version = "0.9.9", features = ["luajit", "send"] }
ratatui = "0.27.0"
tree-sitter-highlight = "0.22.6"
tree-sitter-lua = "0.1.0"
diff --git a/src/app.rs b/src/app.rs
index 937c7d7..b86e6ab 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -1,6 +1,11 @@
use std::{io, time::Duration};
-use crate::{sheet::register::Register, tui, widgets::{logview::LogView, sheetview::SheetView}};
+use crate::{
+ config, lua,
+ sheet::register::Register,
+ tui,
+ widgets::{logview::LogView, sheetview::SheetView},
+};
use ratatui::{
crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers},
@@ -29,12 +34,22 @@ impl App<'_> {
}
}
- let view = SheetView::new(sheet_id).block(Block::bordered().title("Sheet"));
+ let view = SheetView::new(sheet_id);
- Self { exit: false, view, logview: None }
+ Self {
+ exit: false,
+ view,
+ logview: None,
+ }
}
pub fn run(&mut self, terminal: &mut tui::Tui) -> io::Result<()> {
+ if let Err(e) = lua::source(&config::constants::USER_RC_PATH) {
+ self.exit = true;
+ tui::restore()?;
+ println!("{}", e.to_string());
+ }
+
while !self.exit {
terminal.draw(|frame| self.render_frame(frame))?;
self.handle_events()?;
@@ -65,7 +80,9 @@ impl App<'_> {
KeyCode::Char('l') if key_event.modifiers == KeyModifiers::CONTROL => {
match self.logview {
Some(_) => self.logview = None,
- None => self.logview = Some(LogView::new().block(Block::bordered().title(" Log "))),
+ None => {
+ self.logview = Some(LogView::new().block(Block::bordered().title(" Log ")))
+ }
}
}
_ => self.view.handle_key_event(key_event),
@@ -84,10 +101,11 @@ impl Widget for &mut App<'_> {
{
match &mut self.logview {
Some(logview) => {
- let layout = Layout::vertical([Constraint::Min(2), Constraint::Length(15)]).split(area);
+ let layout =
+ Layout::vertical([Constraint::Min(2), Constraint::Length(15)]).split(area);
self.view.render(layout[0], buf);
logview.render(layout[1], buf);
- },
+ }
None => self.view.render(area, buf),
}
}
diff --git a/src/config/constants.rs b/src/config/constants.rs
new file mode 100644
index 0000000..5a8283a
--- /dev/null
+++ b/src/config/constants.rs
@@ -0,0 +1,23 @@
+use lazy_static::lazy_static;
+
+lazy_static! {
+ pub static ref USER_CONFIG_DIR: String = {
+ match dirs::config_local_dir() {
+ Some(mut d) => {
+ d.push("neosheet");
+ d.as_path().to_str().unwrap_or("").to_string()
+ }
+ None => String::new(),
+ }
+ };
+ pub static ref USER_RC_PATH: String = {
+ match dirs::config_local_dir() {
+ Some(mut d) => {
+ d.push("neosheet");
+ d.push("init.lua");
+ d.as_path().to_str().unwrap_or("").to_string()
+ }
+ None => String::new(),
+ }
+ };
+}
diff --git a/src/config/mod.rs b/src/config/mod.rs
new file mode 100644
index 0000000..e6c4952
--- /dev/null
+++ b/src/config/mod.rs
@@ -0,0 +1,37 @@
+use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
+
+use mlua::{UserData, UserDataFields};
+
+use self::theme::Theme;
+
+pub mod theme;
+pub mod constants;
+
+pub struct GlobalConfig {
+ pub theme: Theme,
+}
+
+static GLOBAL_CONFIG: RwLock<GlobalConfig> = RwLock::new(GlobalConfig::new());
+const DUMMY_CONFIG: GlobalConfig = GlobalConfig::new();
+
+impl GlobalConfig {
+ pub const fn new() -> Self {
+ Self {
+ theme: Theme::new(),
+ }
+ }
+
+ pub fn instance() -> RwLockReadGuard<'static, Self> {
+ GLOBAL_CONFIG.read().unwrap()
+ }
+
+ pub fn instance_mut() -> RwLockWriteGuard<'static, Self> {
+ GLOBAL_CONFIG.write().unwrap()
+ }
+}
+
+impl UserData for GlobalConfig {
+ fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
+ fields.add_field_function_get("theme", |_, _| Ok(DUMMY_CONFIG.theme))
+ }
+}
diff --git a/src/config/theme/colorpair.rs b/src/config/theme/colorpair.rs
new file mode 100644
index 0000000..77e6af4
--- /dev/null
+++ b/src/config/theme/colorpair.rs
@@ -0,0 +1,106 @@
+use std::str::FromStr;
+
+use mlua::{FromLua, IntoLua};
+use ratatui::style::{Color, Styled, Stylize};
+
+#[derive(Clone)]
+pub struct ColorPair {
+ pub fg: Color,
+ pub bg: Color,
+}
+
+impl ColorPair {
+ pub const fn new(fg: Color, bg: Color) -> Self {
+ ColorPair { bg, fg }
+ }
+
+ pub fn apply<S, I>(&self, s: S) -> <I as Styled>::Item
+ where
+ S: Styled<Item = I>,
+ I: Styled,
+ {
+ s.bg(self.bg).fg(self.fg)
+ }
+}
+
+impl<'lua> FromLua<'lua> for ColorPair {
+ fn from_lua(
+ value: mlua::prelude::LuaValue<'lua>,
+ _lua: &'lua mlua::prelude::Lua,
+ ) -> mlua::prelude::LuaResult<Self> {
+ if value.is_table() {
+ let table = value.as_table().unwrap();
+ if let Ok(fg) = table.get::<_, LuaColor>(1) {
+ if let Ok(bg) = table.get::<_, LuaColor>(2) {
+ return Ok(ColorPair::new(fg.into(), bg.into()));
+ }
+ } else if let Ok(fg) = table.get::<_, LuaColor>("fg") {
+ if let Ok(bg) = table.get::<_, LuaColor>("bg") {
+ return Ok(ColorPair::new(fg.into(), bg.into()));
+ }
+ }
+ }
+
+ return Err(mlua::Error::runtime("could not parse colorpair"));
+ }
+}
+
+impl<'lua> IntoLua<'lua> for ColorPair {
+ fn into_lua(
+ self,
+ lua: &'lua mlua::prelude::Lua,
+ ) -> mlua::prelude::LuaResult<mlua::prelude::LuaValue<'lua>> {
+ let table = lua.create_table()?;
+ table.set("fg", LuaColor::from(self.fg))?;
+ table.set("bg", LuaColor::from(self.bg))?;
+ Ok(table.into_lua(lua)?)
+ }
+}
+
+struct LuaColor {
+ color: Color,
+}
+
+impl LuaColor {
+ fn new(color: Color) -> Self {
+ Self { color }
+ }
+}
+
+impl From<Color> for LuaColor {
+ fn from(value: Color) -> Self {
+ LuaColor::new(value)
+ }
+}
+
+impl Into<Color> for LuaColor {
+ fn into(self) -> Color {
+ self.color
+ }
+}
+
+impl<'lua> FromLua<'lua> for LuaColor {
+ fn from_lua(
+ value: mlua::prelude::LuaValue<'lua>,
+ _lua: &'lua mlua::prelude::Lua,
+ ) -> mlua::prelude::LuaResult<Self> {
+ if value.is_string() {
+ let str = value.as_str().unwrap();
+ match Color::from_str(str) {
+ Ok(color) => Ok(color.into()),
+ Err(_) => Err(mlua::Error::runtime("color has wrong format")),
+ }
+ } else {
+ Err(mlua::Error::runtime("color has wrong format"))
+ }
+ }
+}
+
+impl<'lua> IntoLua<'lua> for LuaColor {
+ fn into_lua(
+ self,
+ lua: &'lua mlua::prelude::Lua,
+ ) -> mlua::prelude::LuaResult<mlua::prelude::LuaValue<'lua>> {
+ Ok(self.color.to_string().to_lowercase().into_lua(lua)?)
+ }
+}
diff --git a/src/config/theme/mod.rs b/src/config/theme/mod.rs
new file mode 100644
index 0000000..b7baf3a
--- /dev/null
+++ b/src/config/theme/mod.rs
@@ -0,0 +1,30 @@
+
+use mlua::UserData;
+
+use self::sheetview::SheetViewTheme;
+
+use super::DUMMY_CONFIG;
+
+pub mod colorpair;
+pub mod sheetview;
+
+#[derive(Clone)]
+pub struct Theme {
+ pub sheetview: SheetViewTheme,
+}
+
+impl Theme {
+ pub const fn new() -> Self {
+ Self {
+ sheetview: SheetViewTheme::new(),
+ }
+ }
+}
+
+impl UserData for Theme {
+ fn add_fields<'lua, F: mlua::prelude::LuaUserDataFields<'lua, Self>>(fields: &mut F) {
+ fields.add_field_function_get("sheetview", |_, _| {
+ Ok(DUMMY_CONFIG.theme.sheetview)
+ })
+ }
+}
diff --git a/src/config/theme/sheetview.rs b/src/config/theme/sheetview.rs
new file mode 100644
index 0000000..de5751a
--- /dev/null
+++ b/src/config/theme/sheetview.rs
@@ -0,0 +1,69 @@
+use mlua::{IntoLua, Table, UserData};
+use ratatui::style::Color;
+
+use crate::config::GlobalConfig;
+
+use super::colorpair::ColorPair;
+
+#[derive(Clone)]
+pub struct SheetViewTheme {
+ pub cursor: ColorPair,
+ pub selection: ColorPair,
+ pub cell: (ColorPair, ColorPair),
+}
+
+impl SheetViewTheme {
+ pub const fn new() -> Self {
+ Self {
+ cursor: ColorPair::new(Color::Black, Color::White),
+ selection: ColorPair::new(Color::White, Color::DarkGray),
+ cell: (
+ ColorPair::new(Color::White, Color::Black),
+ ColorPair::new(Color::White, Color::DarkGray),
+ ),
+ }
+ }
+}
+
+macro_rules! cfg {
+ () => {
+ GlobalConfig::instance().theme.sheetview
+ };
+}
+
+macro_rules! cfg_mut {
+ () => {
+ GlobalConfig::instance_mut().theme.sheetview
+ };
+}
+
+impl UserData for SheetViewTheme {
+ fn add_fields<'lua, F: mlua::prelude::LuaUserDataFields<'lua, Self>>(fields: &mut F) {
+ fields.add_field_function_get("cursor", |_, _| Ok(cfg!().cursor.clone()));
+
+ fields.add_field_function_set("cursor", |_, _, pair: ColorPair| {
+ cfg_mut!().cursor = pair;
+ Ok(())
+ });
+
+ fields.add_field_function_get("selection", |_, _| Ok(cfg!().selection.clone()));
+
+ fields.add_field_function_set("selection", |_, _, pair: ColorPair| {
+ cfg_mut!().selection = pair;
+ Ok(())
+ });
+
+ fields.add_field_function_get("cell", |lua, _| {
+ let table = lua.create_table()?;
+ let sel = cfg!().cell.clone();
+ table.set(1, sel.0)?;
+ table.set(2, sel.1)?;
+ Ok(table.into_lua(lua)?)
+ });
+
+ fields.add_field_function_set("cell", |_, _, pair: Table| {
+ cfg_mut!().cell = (pair.get(1)?, pair.get(2)?);
+ Ok(())
+ });
+ }
+}
diff --git a/src/lua/mod.rs b/src/lua/mod.rs
index 5d45ee9..0ddcd7f 100644
--- a/src/lua/mod.rs
+++ b/src/lua/mod.rs
@@ -1,10 +1,34 @@
-use std::time::{SystemTime, UNIX_EPOCH};
+use std::{
+ path::Path,
+ sync::{Mutex, MutexGuard},
+};
+use lazy_static::lazy_static;
use mlua::prelude::*;
-use crate::sheet::{cell::CellRef, register::Register};
+use crate::sheet::cell::CellRef;
pub mod iobuffer;
+pub mod runtime;
+
+lazy_static! {
+ static ref LUA: Mutex<Lua> = {
+ let lock = Mutex::new(Lua::new());
+
+ {
+ let lua = lock.lock().unwrap();
+
+ let print_binding = lua.create_function(print).unwrap();
+ lua.globals().set("print", print_binding).unwrap();
+
+ runtime::math(&lua).unwrap();
+ runtime::neosheet(&lua).unwrap();
+ runtime::package(&lua).unwrap();
+ }
+
+ lock
+ };
+}
fn print(_: &Lua, args: LuaMultiValue) -> LuaResult<()> {
let mut writer = iobuffer::iobuffer().write().unwrap();
@@ -28,53 +52,20 @@ fn print(_: &Lua, args: LuaMultiValue) -> LuaResult<()> {
Ok(())
}
-pub fn new_instance() -> LuaResult<Lua> {
- let lua = Lua::new();
-
- let print_binding = lua.create_function(print)?;
- lua.globals().set("print", print_binding)?;
-
- {
- let math = lua.globals().get::<_, LuaTable>("math");
- let randomseed = math.unwrap().get::<_, LuaFunction>("randomseed").unwrap();
-
- let seed = SystemTime::now()
- .duration_since(UNIX_EPOCH)
- .unwrap()
- .as_millis();
- randomseed.call::<_, ()>(seed).unwrap();
- }
-
- let neosheet = lua.create_table()?;
-
- let register = Register;
- neosheet.set("sheets", register)?;
-
- lua.globals().set("neosheet", neosheet)?;
-
- Ok(lua)
+pub fn get() -> MutexGuard<'static, Lua> {
+ LUA.lock().unwrap()
}
-#[cfg(test)]
-mod test {
- use mlua::Function;
-
- use super::new_instance;
+pub fn source(path: &str) -> LuaResult<()> {
+ let path = Path::new(path);
- #[test]
- fn function_eval() {
- let lua = new_instance().unwrap();
- let func: Function = lua
- .load(
- r#"
- function(a, b, c)
- return a + b * c
- end
- "#,
- )
- .eval()
- .unwrap();
- eprintln!("{}", func.call::<_, String>((1, 2, 3, 4)).unwrap());
- assert!(false);
+ if path.exists() && path.is_file() {
+ get().load(path).exec()?;
+ Ok(())
+ } else {
+ Err(LuaError::runtime(format!(
+ "could not source {:?} (file not found)",
+ path
+ )))
}
}
diff --git a/src/lua/runtime.rs b/src/lua/runtime.rs
new file mode 100644
index 0000000..dfb37e6
--- /dev/null
+++ b/src/lua/runtime.rs
@@ -0,0 +1,52 @@
+use mlua::{Function, Lua, Result, Table};
+use std::time::{SystemTime, UNIX_EPOCH};
+
+use crate::{
+ config::{self, GlobalConfig},
+ sheet::register::Register,
+};
+
+pub fn neosheet(lua: &Lua) -> Result<()> {
+ let exports = lua.create_table()?;
+
+ exports
+ .set("sheets", lua.create_proxy::<Register>()?)
+ .unwrap();
+ exports
+ .set("config", lua.create_userdata(GlobalConfig::new())?)
+ .unwrap();
+
+ lua.globals()
+ .get::<_, Table>("package")?
+ .get::<_, Table>("loaded")?
+ .set("neosheet", exports)?;
+
+ Ok(())
+}
+
+pub fn math(lua: &Lua) -> Result<()> {
+ let math = lua.globals().get::<_, Table>("math");
+ let randomseed = math.unwrap().get::<_, Function>("randomseed").unwrap();
+
+ let seed = SystemTime::now()
+ .duration_since(UNIX_EPOCH)
+ .unwrap()
+ .as_millis();
+ randomseed.call::<_, ()>(seed).unwrap();
+ Ok(())
+}
+
+pub fn package(lua: &Lua) -> Result<()> {
+ let package = lua.globals().get::<_, Table>("package")?;
+ let path = package.get::<_, String>("path")?;
+ package.set(
+ "path",
+ format!(
+ "{};{}/?.lua;{}/?/init.lua",
+ path,
+ config::constants::USER_CONFIG_DIR.as_str(),
+ config::constants::USER_CONFIG_DIR.as_str()
+ ),
+ )?;
+ Ok(())
+}
diff --git a/src/main.rs b/src/main.rs
index bbfafb1..776489f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -6,6 +6,7 @@ pub mod sheet;
pub mod tui;
pub mod widgets;
pub mod lua;
+pub mod config;
fn run() -> io::Result<()> {
let mut terminal = tui::init()?;
diff --git a/src/sheet/eval.rs b/src/sheet/eval.rs
index 4633949..2108e48 100644
--- a/src/sheet/eval.rs
+++ b/src/sheet/eval.rs
@@ -1,10 +1,12 @@
use std::{
- sync::{mpsc::channel, Arc, RwLock, RwLockReadGuard},
+ sync::{Arc, RwLock},
thread::JoinHandle,
};
use mlua::prelude::*;
+use crate::lua;
+
use super::{cell::Cell, Sheet};
pub trait EvalFunction {
@@ -24,147 +26,30 @@ impl EvalFunction for Arc<RwLock<Sheet>> {
*self.read().unwrap().progress.lock().unwrap() = 0;
let this = Arc::clone(self);
std::thread::spawn(move || {
- let chunks = range.chunks(
- (range.len() / std::thread::available_parallelism().unwrap())
- .min(range.len())
- .max(1000),
- );
-
- if chunks.len() > 4 {
- let (tx, rx) = channel();
-
- std::thread::scope(|s| {
- for chunk in chunks {
- let tx = tx.clone();
- let this = Arc::clone(&this);
- let func_text = func_text.clone();
-
- s.spawn(move || {
- let tx = tx.clone();
- let read_sheet = this.read().unwrap();
- let result = EvalRange::new(chunk.iter(), func_text, read_sheet);
- match result {
- Ok(evaluator) => {
- for result in evaluator {
- if result.is_err() {
- tx.send(result).unwrap();
- break;
- }
-
- tx.send(result).unwrap();
- }
- }
- Err(error) => tx.send(Err(error)).unwrap(),
- }
- });
- }
-
- drop(tx);
+ let lua = lua::get();
+ let read_sheet = this.read().unwrap();
+ let mut sheet = read_sheet.clone();
+ let result = lua.load(func_text).eval::<LuaFunction>();
+ match result {
+ Ok(func) => {
let read_sheet = this.read().unwrap();
- let mut sheet = read_sheet.clone();
- let mut count = 0;
+ for (i, (row, column)) in range.iter().enumerate() {
+ *read_sheet.progress.lock().unwrap() = (i * 100 / range.len()) as u8;
- for result in rx {
- count += 1;
- *read_sheet.progress.lock().unwrap() = (count * 100 / range.len()) as u8;
+ let cellref = read_sheet.get_ref(*row, *column);
- match result {
- Ok((row, column, cell)) => sheet.set_cell(row, column, cell),
+ match func.call::<_, Cell>(cellref) {
+ Ok(cell) => sheet.set_cell(*row, *column, cell),
Err(error) => return Err(error.to_string()),
}
}
-
- crate::lua::new_instance().unwrap().expire_registry_values();
- Ok(sheet)
- })
- } else {
- let read_sheet = this.read().unwrap();
- let mut sheet = read_sheet.clone();
- let result = EvalRange::new((&range).iter(), func_text, read_sheet);
-
- match result {
- Ok(evaluator) => {
- for result in evaluator {
- match result {
- Ok((row, column, cell)) => sheet.set_cell(row, column, cell),
- Err(error) => return Err(error.to_string()),
- }
- }
- }
- Err(error) => return Err(error),
}
-
- crate::lua::new_instance().unwrap().expire_registry_values();
-
- Ok(sheet)
+ Err(error) => return Err(error.to_string()),
}
- })
- }
-}
-
-struct EvalRange<'a, I>
-where
- I: Iterator<Item = &'a (usize, usize)>,
-{
- lua: Lua,
- reg_func_key: LuaRegistryKey,
- range: I,
- read_sheet: RwLockReadGuard<'a, Sheet>,
-}
-
-impl<'a, I> EvalRange<'a, I>
-where
- I: Iterator<Item = &'a (usize, usize)>,
-{
- fn new(
- range: I,
- script: String,
- read_sheet: RwLockReadGuard<'a, Sheet>,
- ) -> Result<Self, String> {
- let lua = crate::lua::new_instance().unwrap();
- let result = lua
- .load(script.clone())
- .set_name("Temp Script")
- .eval::<LuaFunction>();
-
- if result.is_err() {
- return Err(result.err().unwrap().to_string());
- }
- let lua = crate::lua::new_instance().unwrap();
- let func = lua
- .load(script)
- .set_name("Temp Script")
- .eval::<LuaFunction>()
- .unwrap();
-
- Ok(Self {
- reg_func_key: lua.create_registry_value(func).unwrap(),
- lua,
- range,
- read_sheet,
+ Ok(sheet)
})
}
}
-
-impl<'a, I> Iterator for EvalRange<'a, I>
-where
- I: Iterator<Item = &'a (usize, usize)>,
-{
- type Item = Result<(usize, usize, Cell), String>;
-
- fn next(&mut self) -> Option<Self::Item> {
- let func: LuaFunction = self.lua.registry_value(&self.reg_func_key).unwrap();
- if let Some((row, column)) = self.range.next() {
- let cellref = self.read_sheet.get_ref(*row, *column);
- match func.call::<_, Cell>(cellref) {
- Ok(cell) => Some(Ok((*row, *column, cell))),
- Err(error) => Some(Err(error.to_string())),
- }
- } else {
- None
- }
- }
-}
diff --git a/src/widgets/sheetview.rs b/src/widgets/sheetview/mod.rs
index c4f7a73..37e3d16 100644
--- a/src/widgets/sheetview.rs
+++ b/src/widgets/sheetview/mod.rs
@@ -1,25 +1,25 @@
use std::thread::JoinHandle;
-use layout::Offset;
use ratatui::{
crossterm::event::{KeyCode, KeyEvent, KeyModifiers},
prelude::*,
style::Stylize,
text::ToLine,
- widgets::{block::BlockExt, Block, Clear, Gauge, Widget},
+ widgets::{Block, Borders, Clear, Gauge, Paragraph, Widget, Wrap},
};
-use crate::sheet::{
+use crate::{config::GlobalConfig, sheet::{
+ eval::EvalFunction,
register::{Register, SheetId},
- eval::EvalFunction, Sheet,
-};
+ Sheet,
+}};
use super::luaeditor::LuaEditor;
const DEFAULT_COLUMN_WIDTH: u16 = 10;
pub struct SheetView<'a> {
- block: Option<Block<'a>>,
+ block: Block<'a>,
luaeditor: Option<LuaEditor<'a>>,
sheet: SheetId,
selection: Option<(u16, u16)>,
@@ -32,7 +32,15 @@ pub struct SheetView<'a> {
impl<'a> SheetView<'a> {
pub fn new(sheet: SheetId) -> Self {
Self {
- block: None,
+ block: Block::default()
+ .title_bottom(" Sheet ")
+ .title_style(Style::default().on_magenta().black())
+ .borders(Borders::BOTTOM)
+ .border_style(
+ Style::default()
+ .bg(Color::Rgb(29, 32, 33))
+ .fg(Color::Rgb(29, 32, 33)),
+ ),
luaeditor: None,
sheet,
selection: None,
@@ -43,11 +51,6 @@ impl<'a> SheetView<'a> {
}
}
- pub fn block(mut self, block: Block<'a>) -> Self {
- self.block = Some(block);
- self
- }
-
pub fn sheet(mut self, sheet: SheetId) -> Self {
self.sheet = sheet;
self
@@ -197,14 +200,43 @@ impl<'a> SheetView<'a> {
KeyCode::Char('l') => self.move_cursor_by((0, 1)),
KeyCode::Char('v') => self.toggle_select_mode(),
KeyCode::Char('s') => {
- let editor = LuaEditor::new("function(cell)\n\treturn \"\"\nend")
- .block(Some(Block::bordered().title(" Temp Script ")));
+ let editor = LuaEditor::new("function(cell)\n\treturn \"\"\nend");
self.luaeditor = Some(editor)
}
_ => {}
}
}
}
+
+ fn areas(&self, area: Rect) -> (Rect, Rect, Rect, Rect) {
+ let mut sheet = area;
+ let mut editor = Rect::default();
+ let mut error = Rect::default();
+ let mut progress = Rect::default();
+
+ if self.luaeditor.is_some() {
+ let layout =
+ Layout::horizontal([Constraint::Min(1), Constraint::Length(50)]).split(sheet);
+ sheet = layout[0];
+ editor = layout[1];
+
+ if self.error_window.is_some() {
+ let layout =
+ Layout::vertical([Constraint::Min(1), Constraint::Length(10)]).split(editor);
+ editor = layout[0];
+ error = layout[1];
+ }
+
+ if self.process_handle.is_some() {
+ let layout =
+ Layout::vertical([Constraint::Min(1), Constraint::Length(1)]).split(editor);
+ editor = layout[0];
+ progress = layout[1];
+ }
+ }
+
+ (sheet, editor, error, progress)
+ }
}
impl Widget for &mut SheetView<'_> {
@@ -212,25 +244,29 @@ impl Widget for &mut SheetView<'_> {
where
Self: Sized,
{
+ let theme = GlobalConfig::instance().theme.sheetview.clone();
let progress;
+ let (sheet_area, editor_area, error_area, progress_area) = self.areas(area);
+ let separator = Block::default().borders(Borders::RIGHT).border_style(
+ Style::default()
+ .bg(Color::Rgb(29, 32, 33))
+ .fg(Color::Rgb(29, 32, 33)),
+ );
{
let lock = Register::get(self.sheet).unwrap();
let sheet = lock.read().unwrap();
- let splits = Layout::horizontal([Constraint::Min(1), Constraint::Length(50)]).split(area);
- let block_area = if self.luaeditor.is_some() {
- splits[0]
- } else {
- area
- };
+ let mut sheet_area_inner = self.block.inner(sheet_area);
- let sheet_area = self.block.inner_if_some(block_area);
+ if self.luaeditor.is_some() {
+ sheet_area_inner = separator.inner(sheet_area_inner);
+ }
let viewport_rows = sheet.height().min(sheet_area.height as usize);
let viewport_columns = sheet
.width()
- .min((sheet_area.width / DEFAULT_COLUMN_WIDTH) as usize);
+ .min((sheet_area_inner.width / DEFAULT_COLUMN_WIDTH) as usize);
if self.cursor.0 >= viewport_rows as u16 + self.scroll.0 {
self.scroll.0 = self.cursor.0 - viewport_rows as u16 + 1;
@@ -252,19 +288,19 @@ impl Widget for &mut SheetView<'_> {
let cell = cell.to_string() + " ";
let line = if (cell_pos_y, cell_pos_x) == self.cursor {
- cell.to_line().bg(Color::Rgb(120, 90, 90)).white()
+ theme.cursor.apply(cell.to_line())
} else if self.is_in_selection(cell_pos_y, cell_pos_x) {
- cell.to_line().bg(Color::Rgb(120, 120, 90)).white()
+ theme.selection.apply(cell.to_line())
} else if (row + column) % 2 == 0 {
- cell.to_line().bg(Color::Rgb(30, 30, 30)).white()
+ theme.cell.0.apply(cell.to_line())
} else {
- cell.to_line().bg(Color::Rgb(50, 50, 50)).white()
+ theme.cell.1.apply(cell.to_line())
};
let rect = Rect::new(
- sheet_area.x + column * DEFAULT_COLUMN_WIDTH,
- sheet_area.y + row,
- (sheet_area.width - column * DEFAULT_COLUMN_WIDTH)
+ sheet_area_inner.x + column * DEFAULT_COLUMN_WIDTH,
+ sheet_area_inner.y + row,
+ (sheet_area_inner.width - column * DEFAULT_COLUMN_WIDTH)
.min(DEFAULT_COLUMN_WIDTH),
1,
);
@@ -274,33 +310,30 @@ impl Widget for &mut SheetView<'_> {
}
}
- self.block.render(block_area, buf);
+ self.block.clone().render(sheet_area, buf);
if let Some(textarea) = &mut self.luaeditor {
- textarea.render(splits[1], buf)
+ separator.render(self.block.inner(sheet_area), buf);
+ textarea.render(editor_area, buf)
}
if let Some(error_msg) = &self.error_window {
let lines = error_msg.lines().collect::<Vec<_>>();
- let height = lines.len() as u16 + 2;
- let width = lines.iter().map(|s| s.len()).max().unwrap_or(0) as u16 + 2;
- let centered = Rect::new(
- (area.width - width) / 2,
- (area.height - height) / 2,
- width,
- height,
- );
+ let block = Block::default()
+ .title_bottom(" Error ")
+ .title_style(Style::default().on_red())
+ .border_style(Style::default().black().on_black())
+ .borders(Borders::BOTTOM);
+ let error_inner = block.inner(error_area);
+ block.render(error_area, buf);
- let block = Block::bordered().red().on_black().bold().title(" Error ");
- let inner_centered = block.inner(centered);
- Clear::default().render(centered, buf);
- block.render(centered, buf);
+ let text = Text::from_iter(lines.iter().map(|s| s.to_line().white()));
- for (i, line) in lines.iter().enumerate() {
- let line = line.replace('\t', " ").red().on_black().bold();
- line.render(inner_centered.offset(Offset { x: 0, y: i as i32 }), buf);
- }
+ Paragraph::new(text)
+ .wrap(Wrap { trim: true })
+ .bg(Color::Rgb(70, 25, 25))
+ .render(error_inner, buf);
}
progress = sheet.progress();
@@ -309,28 +342,17 @@ impl Widget for &mut SheetView<'_> {
self.join_process_handle_on_finished();
if self.process_handle.is_some() {
- let height = 3;
- let width = area.width / 2;
-
- let centered = Rect::new(
- (area.width - width) / 2,
- (area.height - height) / 2,
- width,
- height,
- );
-
let gauge = Gauge::default()
- .block(Block::bordered().title("Progress").on_black())
.gauge_style(
Style::default()
.fg(Color::White)
- .bg(Color::Black)
+ .bg(Color::DarkGray)
.add_modifier(Modifier::ITALIC),
)
.percent(progress as u16);
- Clear::default().render(centered, buf);
- gauge.render(centered, buf);
+ Clear::default().render(progress_area, buf);
+ gauge.render(progress_area, buf);
}
}
}