summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app.rs76
-rw-r--r--src/cursor.rs4
-rw-r--r--src/lua/mod.rs9
-rw-r--r--src/sheet/eval.rs55
-rw-r--r--src/sheet/foreach.rs37
-rw-r--r--src/sheet/luaref.rs82
-rw-r--r--src/sheet/mod.rs65
-rw-r--r--src/sheet/register.rs2
-rw-r--r--src/state/editor/buffer.rs (renamed from src/widgets/luaeditor/buffer.rs)13
-rw-r--r--src/state/editor/mod.rs57
-rw-r--r--src/state/log.rs35
-rw-r--r--src/state/mod.rs36
-rw-r--r--src/state/sheetview.rs97
-rw-r--r--src/state/view/mod.rs186
-rw-r--r--src/state/view/mode.rs48
-rw-r--r--src/state/window.rs47
-rw-r--r--src/widgets/logview.rs4
-rw-r--r--src/widgets/luaeditor/mod.rs96
-rw-r--r--src/widgets/sheetview/mod.rs571
19 files changed, 800 insertions, 720 deletions
diff --git a/src/app.rs b/src/app.rs
index b245b5b..89b19dc 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -1,7 +1,11 @@
use std::{io, time::Duration};
use crate::{
- config, lua, sheet::register::Register, state::GlobalState, tui, widgets::{logview::LogView, sheetview::SheetView}
+ config, lua,
+ sheet::register::Register,
+ state::{window::Window, GlobalState},
+ tui,
+ widgets::{logview::LogView, luaeditor::LuaEditor, sheetview::SheetView},
};
use ratatui::{
@@ -13,19 +17,22 @@ use ratatui::{
pub struct App {
exit: bool,
view: SheetView,
- logview: Option<LogView>,
+ editor: LuaEditor,
+ logview: LogView,
}
impl App {
pub fn new() -> Self {
let sheet_id = Register::create(10, 50);
- GlobalState::instance_mut().sheetview.set_active_sheet(Some(sheet_id));
- let view = SheetView::new();
+ GlobalState::instance_mut()
+ .sheetview
+ .set_active_sheet(Some(sheet_id));
Self {
exit: false,
- view,
- logview: None,
+ view: SheetView::new(),
+ editor: LuaEditor::new(),
+ logview: LogView::new(),
}
}
@@ -61,23 +68,48 @@ impl App {
}
fn handle_key_event(&mut self, key_event: KeyEvent) {
+ let focus = { GlobalState::instance().active_window };
match key_event.code {
KeyCode::Char('q') if key_event.modifiers == KeyModifiers::CONTROL => self.exit(),
KeyCode::Char('l') if key_event.modifiers == KeyModifiers::CONTROL => {
- match self.logview {
- Some(_) => self.logview = None,
- None => {
- self.logview = Some(LogView::new())
- }
- }
+ let mut state = GlobalState::instance_mut();
+ state.log.visible = !state.log.visible;
}
- _ => self.view.handle_key_event(key_event),
+ _ => {
+ match focus {
+ Window::View => self.view.handle_key_event(key_event),
+ Window::Editor => self.editor.handle_key_event(key_event),
+ Window::Log => self.logview.handle_key_event(key_event),
+ Window::Error => {},
+ }
+ },
}
}
fn exit(&mut self) {
self.exit = true;
}
+
+ fn area(&self, area: Rect) -> (Rect, Option<Rect>, Option<Rect>) {
+ let state = GlobalState::instance();
+ let mut view = area;
+ let mut editor = None;
+ let mut log = None;
+
+ if state.log.visible {
+ let layout = Layout::vertical([Constraint::Min(1), Constraint::Length(15)]).split(view);
+ view = layout[0];
+ log = Some(layout[1]);
+ }
+
+ if state.editor.visible {
+ let layout = Layout::horizontal([Constraint::Min(1), Constraint::Length(80)]).split(view);
+ view = layout[0];
+ editor = Some(layout[1]);
+ }
+
+ (view, editor, log)
+ }
}
impl Widget for &mut App {
@@ -85,14 +117,16 @@ impl Widget for &mut App {
where
Self: Sized,
{
- match &mut self.logview {
- Some(logview) => {
- 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),
+ let (view, editor, log) = self.area(area);
+
+ self.view.render(view, buf);
+
+ if let Some(editor) = editor {
+ self.editor.render(editor, buf);
+ }
+
+ if let Some(log) = log {
+ self.logview.render(log, buf);
}
}
}
diff --git a/src/cursor.rs b/src/cursor.rs
index ad6703e..8ab538b 100644
--- a/src/cursor.rs
+++ b/src/cursor.rs
@@ -30,13 +30,13 @@ impl Cursor {
}
}
- pub fn with_position(mut self, x: isize, y: isize) -> Self {
+ pub const fn with_position(mut self, x: isize, y: isize) -> Self {
self.x = x;
self.y = y;
self
}
- pub fn with_max(mut self, x: isize, y: isize) -> Self {
+ pub const fn with_max(mut self, x: isize, y: isize) -> Self {
self.x_max = x;
self.y_max = y;
self
diff --git a/src/lua/mod.rs b/src/lua/mod.rs
index fe3555e..7d61e5b 100644
--- a/src/lua/mod.rs
+++ b/src/lua/mod.rs
@@ -6,7 +6,14 @@ use std::{
use lazy_static::lazy_static;
use mlua::prelude::*;
-use crate::{config::{theme::{sheetview::SheetViewTheme, Theme}, GlobalConfig}, sheet::cell::CellRef, state::{sheetview::SheetViewState, GlobalState}};
+use crate::{
+ config::{
+ theme::{sheetview::SheetViewTheme, Theme},
+ GlobalConfig,
+ },
+ sheet::cell::CellRef,
+ state::{view::SheetViewState, GlobalState},
+};
pub mod evalsto;
pub mod iobuffer;
diff --git a/src/sheet/eval.rs b/src/sheet/eval.rs
deleted file mode 100644
index 2108e48..0000000
--- a/src/sheet/eval.rs
+++ /dev/null
@@ -1,55 +0,0 @@
-use std::{
- sync::{Arc, RwLock},
- thread::JoinHandle,
-};
-
-use mlua::prelude::*;
-
-use crate::lua;
-
-use super::{cell::Cell, Sheet};
-
-pub trait EvalFunction {
- fn eval_function(
- &self,
- func: String,
- range: Vec<(usize, usize)>,
- ) -> JoinHandle<Result<Sheet, String>>;
-}
-
-impl EvalFunction for Arc<RwLock<Sheet>> {
- fn eval_function(
- &self,
- func_text: String,
- range: Vec<(usize, usize)>,
- ) -> JoinHandle<Result<Sheet, String>> {
- *self.read().unwrap().progress.lock().unwrap() = 0;
- let this = Arc::clone(self);
- std::thread::spawn(move || {
- 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();
-
- for (i, (row, column)) in range.iter().enumerate() {
- *read_sheet.progress.lock().unwrap() = (i * 100 / range.len()) as u8;
-
- let cellref = read_sheet.get_ref(*row, *column);
-
- match func.call::<_, Cell>(cellref) {
- Ok(cell) => sheet.set_cell(*row, *column, cell),
- Err(error) => return Err(error.to_string()),
- }
- }
- }
- Err(error) => return Err(error.to_string()),
- }
-
- Ok(sheet)
- })
- }
-}
diff --git a/src/sheet/foreach.rs b/src/sheet/foreach.rs
new file mode 100644
index 0000000..a2a3f90
--- /dev/null
+++ b/src/sheet/foreach.rs
@@ -0,0 +1,37 @@
+use std::sync::{Arc, RwLock};
+
+use mlua::prelude::*;
+
+use super::{cell::Cell, Sheet};
+
+pub trait LuaForEach {
+ fn lua_foreach<'lua>(
+ &self,
+ func: LuaFunction<'lua>,
+ range: Vec<(usize, usize)>,
+ ) -> Result<(), LuaError>;
+}
+
+impl LuaForEach for Arc<RwLock<Sheet>> {
+ fn lua_foreach<'lua>(
+ &self,
+ func: LuaFunction<'lua>,
+ range: Vec<(usize, usize)>,
+ ) -> Result<(), LuaError> {
+ let mut this = self.write().unwrap();
+ let mut sheet = this.clone();
+
+ for (row, column) in range.iter() {
+ let cellref = this.get_ref(*row, *column);
+
+ match func.call::<_, Cell>(cellref) {
+ Ok(cell) => sheet.set_cell(*row, *column, cell),
+ Err(error) => return Err(error),
+ }
+ }
+
+ this.apply(sheet);
+
+ Ok(())
+ }
+}
diff --git a/src/sheet/luaref.rs b/src/sheet/luaref.rs
new file mode 100644
index 0000000..fa4b8f9
--- /dev/null
+++ b/src/sheet/luaref.rs
@@ -0,0 +1,82 @@
+use crate::state::GlobalState;
+
+use super::{
+ foreach::LuaForEach,
+ register::{Register, SheetId},
+};
+use mlua::prelude::*;
+
+pub struct SheetLuaRef {
+ id: SheetId,
+}
+
+impl SheetLuaRef {
+ pub fn new(id: SheetId) -> Self {
+ Self { id }
+ }
+
+ pub fn id(&self) -> SheetId {
+ self.id
+ }
+}
+
+impl LuaUserData for SheetLuaRef {
+ fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) {
+ fields.add_field_method_get("width", |_, luaref| {
+ Ok(Register::get(luaref.id).unwrap().read().unwrap().width())
+ });
+
+ fields.add_field_method_get("height", |_, luaref| {
+ Ok(Register::get(luaref.id).unwrap().read().unwrap().height())
+ });
+ }
+
+ fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
+ methods.add_method_mut("cell", |lua, luaref, (row, column): (usize, usize)| {
+ if let Some(rw) = Register::get(luaref.id) {
+ let sheet = rw.read().unwrap();
+ Ok(sheet.get_ref(row, column).into_lua(lua).unwrap())
+ } else {
+ Ok(LuaValue::Nil)
+ }
+ });
+
+ methods.add_method_mut("foreach", |_, luaref, func: LuaFunction| {
+ let range: Vec<_> = {
+ let state = GlobalState::instance();
+ let lock = state.sheetview.active_sheet().unwrap();
+ let sheet = lock.read().unwrap();
+
+ if sheet.id() != luaref.id() {
+ return Ok(());
+ }
+
+ let (width, height) = (sheet.width(), sheet.height());
+
+ if state.sheetview.selection_anchor.is_some() {
+ state.sheetview.selection()
+ } else {
+ let mut cells = Vec::new();
+ for row in 0..height {
+ for column in 0..width {
+ cells.push((row, column));
+ }
+ }
+ cells
+ }
+ };
+
+ Register::get(luaref.id).unwrap().lua_foreach(func, range)
+ })
+ }
+}
+
+impl<'lua> FromLua<'lua> for SheetLuaRef {
+ fn from_lua(value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult<Self> {
+ if let Some(ud) = value.as_userdata() {
+ ud.take()
+ } else {
+ Err(LuaError::runtime("failed to parse sheet"))
+ }
+ }
+}
diff --git a/src/sheet/mod.rs b/src/sheet/mod.rs
index 501b71e..5f28e07 100644
--- a/src/sheet/mod.rs
+++ b/src/sheet/mod.rs
@@ -1,18 +1,14 @@
-use std::sync::Mutex;
-
use cell::{Cell, CellRef};
-use mlua::prelude::*;
-use register::{Register, SheetId};
pub mod cell;
-pub mod eval;
+pub mod foreach;
pub mod register;
+pub mod luaref;
#[derive(Debug, Default)]
pub struct Sheet {
id: register::SheetId,
rows: Vec<Vec<Cell>>,
- progress: Mutex<u8>,
}
impl Sheet {
@@ -20,7 +16,6 @@ impl Sheet {
Self {
id,
rows: vec![vec![Cell::new_empty(); width]; height],
- progress: Mutex::new(0),
}
}
@@ -41,9 +36,8 @@ impl Sheet {
}
pub fn get_ref(&self, row: usize, column: usize) -> Option<CellRef> {
- self.get_cell(row, column).map(|cell| {
- unsafe { CellRef::new(self.id, row, column, cell.clone()) }
- })
+ self.get_cell(row, column)
+ .map(|cell| unsafe { CellRef::new(self.id, row, column, cell.clone()) })
}
pub fn height(&self) -> usize {
@@ -74,10 +68,6 @@ impl Sheet {
pub fn apply(&mut self, other: Sheet) {
self.rows = other.rows;
}
-
- pub fn progress(&self) -> u8 {
- *self.progress.lock().unwrap()
- }
}
impl Clone for Sheet {
@@ -85,54 +75,7 @@ impl Clone for Sheet {
Sheet {
id: self.id,
rows: self.rows.clone(),
- progress: Mutex::new(0),
}
}
}
-pub struct SheetLuaRef {
- id: SheetId,
-}
-
-impl SheetLuaRef {
- pub fn new(id: SheetId) -> Self {
- Self { id }
- }
-
- pub fn id(&self) -> SheetId {
- self.id
- }
-}
-
-impl LuaUserData for SheetLuaRef {
- fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) {
- fields.add_field_method_get("width", |_, luaref| {
- Ok(Register::get(luaref.id).unwrap().read().unwrap().width())
- });
-
- fields.add_field_method_get("height", |_, luaref| {
- Ok(Register::get(luaref.id).unwrap().read().unwrap().height())
- });
- }
-
- fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
- methods.add_method_mut("cell", |lua, luaref, (row, column): (usize, usize)| {
- if let Some(rw) = Register::get(luaref.id) {
- let sheet = rw.read().unwrap();
- Ok(sheet.get_ref(row, column).into_lua(lua).unwrap())
- } else {
- Ok(LuaValue::Nil)
- }
- })
- }
-}
-
-impl<'lua> FromLua<'lua> for SheetLuaRef {
- fn from_lua(value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult<Self> {
- if let Some(ud) = value.as_userdata() {
- ud.take()
- } else {
- Err(LuaError::runtime("failed to parse sheet"))
- }
- }
-}
diff --git a/src/sheet/register.rs b/src/sheet/register.rs
index 039c6f0..c43720d 100644
--- a/src/sheet/register.rs
+++ b/src/sheet/register.rs
@@ -2,7 +2,7 @@ use std::sync::{Arc, RwLock};
use mlua::{Table, UserData, Value};
-use super::{cell::Cell, Sheet, SheetLuaRef};
+use super::{cell::Cell, Sheet, luaref::SheetLuaRef};
static REGISTER: RwLock<Vec<Arc<RwLock<Sheet>>>> = RwLock::new(Vec::new());
diff --git a/src/widgets/luaeditor/buffer.rs b/src/state/editor/buffer.rs
index 1427bdb..fa26eb7 100644
--- a/src/widgets/luaeditor/buffer.rs
+++ b/src/state/editor/buffer.rs
@@ -9,7 +9,7 @@ pub struct Buffer {
}
impl Buffer {
- pub fn new() -> Self {
+ pub const fn new() -> Self {
Self {
lines: Vec::new(),
cursor: Cursor::new().with_position(0, 0).with_max(0, 0),
@@ -163,6 +163,17 @@ impl Buffer {
self.cursor.move_checked(CursorMove::Jump((0, 0)));
self.refresh_max();
}
+
+ pub fn set_lines_from_string<S>(&mut self, content: S)
+ where
+ S: AsRef<str>,
+ {
+ self.set_lines(content.as_ref().lines().map(|s| s.to_string()).collect())
+ }
+
+ pub fn as_string(&self) -> String {
+ self.lines.join("\n")
+ }
}
impl FromStr for Buffer {
diff --git a/src/state/editor/mod.rs b/src/state/editor/mod.rs
new file mode 100644
index 0000000..6fbd2b9
--- /dev/null
+++ b/src/state/editor/mod.rs
@@ -0,0 +1,57 @@
+use self::buffer::Buffer;
+
+use super::GlobalState;
+use mlua::{Lua, UserData};
+
+pub mod buffer;
+
+#[derive(Default, Debug)]
+pub struct EditorState {
+ pub visible: bool,
+ pub buffer: Buffer,
+}
+
+macro_rules! cfg {
+ () => {
+ GlobalState::instance().editor
+ };
+}
+
+macro_rules! cfg_mut {
+ () => {
+ GlobalState::instance_mut().editor
+ };
+}
+
+impl EditorState {
+ pub const fn new() -> Self {
+ Self {
+ visible: false,
+ buffer: Buffer::new(),
+ }
+ }
+
+ pub fn run(lua: &Lua) {
+ let script = { cfg!().buffer.as_string() };
+ if let Err(_error) = lua.load(script).exec() {
+ // TODO: add error handling
+ }
+ }
+}
+
+impl UserData for EditorState {
+ fn add_fields<'lua, F: mlua::prelude::LuaUserDataFields<'lua, Self>>(fields: &mut F) {
+ fields.add_field_function_get("visible", |_, _| Ok(cfg!().visible));
+ fields.add_field_function_set("visible", |_, _, visible: bool| {
+ cfg_mut!().visible = visible;
+ Ok(())
+ })
+ }
+
+ fn add_methods<'lua, M: mlua::prelude::LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
+ methods.add_function("run", |lua, _: ()| {
+ EditorState::run(lua);
+ Ok(())
+ })
+ }
+}
diff --git a/src/state/log.rs b/src/state/log.rs
new file mode 100644
index 0000000..5afa8ca
--- /dev/null
+++ b/src/state/log.rs
@@ -0,0 +1,35 @@
+use mlua::UserData;
+use super::GlobalState;
+
+#[derive(Debug, Default)]
+pub struct LogState {
+ pub visible: bool,
+}
+
+impl LogState {
+ pub const fn new() -> Self {
+ Self { visible: false }
+ }
+}
+
+macro_rules! cfg {
+ () => {
+ GlobalState::instance().log
+ };
+}
+
+macro_rules! cfg_mut {
+ () => {
+ GlobalState::instance_mut().log
+ };
+}
+
+impl UserData for LogState {
+ fn add_fields<'lua, F: mlua::prelude::LuaUserDataFields<'lua, Self>>(fields: &mut F) {
+ fields.add_field_function_get("visible", |_, _| Ok(cfg!().visible));
+ fields.add_field_function_set("visible", |_, _, visible: bool| {
+ cfg_mut!().visible = visible;
+ Ok(())
+ });
+ }
+}
diff --git a/src/state/mod.rs b/src/state/mod.rs
index 5efd770..5200da9 100644
--- a/src/state/mod.rs
+++ b/src/state/mod.rs
@@ -2,12 +2,19 @@ use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use mlua::{UserData, UserDataFields};
+use self::log::LogState;
-pub mod sheetview;
+pub mod editor;
+pub mod log;
+pub mod view;
+pub mod window;
#[derive(Debug, Default)]
pub struct GlobalState {
- pub sheetview: sheetview::SheetViewState,
+ pub sheetview: view::SheetViewState,
+ pub editor: editor::EditorState,
+ pub active_window: window::Window,
+ pub log: log::LogState,
}
static GLOBAL_STATE: RwLock<GlobalState> = RwLock::new(GlobalState::new());
@@ -16,7 +23,10 @@ const DUMMY_STATE: GlobalState = GlobalState::new();
impl GlobalState {
const fn new() -> Self {
Self {
- sheetview: sheetview::SheetViewState::new()
+ sheetview: view::SheetViewState::new(),
+ editor: editor::EditorState::new(),
+ log: LogState::new(),
+ active_window: window::Window::View,
}
}
@@ -27,10 +37,28 @@ impl GlobalState {
pub fn instance_mut() -> RwLockWriteGuard<'static, Self> {
GLOBAL_STATE.write().unwrap()
}
+
+ pub fn set_focus(&mut self, win: window::Window) {
+ self.active_window = win;
+
+ match win {
+ window::Window::Editor => self.editor.visible = true,
+ window::Window::Log => self.log.visible = true,
+ _ => {}
+ }
+ }
}
impl UserData for GlobalState {
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
- fields.add_field_function_get("sheetview", |_, _| Ok(DUMMY_STATE.sheetview))
+ fields.add_field_function_get("view", |_, _| Ok(DUMMY_STATE.sheetview));
+ fields.add_field_function_get("editor", |_, _| Ok(DUMMY_STATE.editor));
+ fields.add_field_function_get("active_window", |_, _| {
+ Ok(GlobalState::instance().active_window)
+ });
+ fields.add_field_function_set("active_window", |_, _, win: window::Window| {
+ GlobalState::instance_mut().set_focus(win);
+ Ok(())
+ });
}
}
diff --git a/src/state/sheetview.rs b/src/state/sheetview.rs
deleted file mode 100644
index 8108a1d..0000000
--- a/src/state/sheetview.rs
+++ /dev/null
@@ -1,97 +0,0 @@
-use std::sync::{Arc, RwLock};
-
-use mlua::{IntoLua, UserData, Value};
-
-use super::GlobalState;
-use crate::{
- cursor::{Cursor, CursorMove},
- sheet::{
- register::{Register, SheetId},
- Sheet, SheetLuaRef,
- },
-};
-
-#[derive(Default, Debug)]
-pub struct SheetViewState {
- pub cursor: Cursor,
- active_sheet: Option<SheetId>,
-}
-
-impl SheetViewState {
- pub const fn new() -> Self {
- Self {
- cursor: Cursor::new(),
- active_sheet: None,
- }
- }
-
- pub fn set_active_sheet(&mut self, sheet: Option<SheetId>) {
- if let Some(id) = sheet {
- if let Some(lock) = Register::get(id) {
- let sheet = lock.read().unwrap();
- self.cursor.set_x_max(sheet.width());
- self.cursor.set_y_max(sheet.height());
- }
- }
-
- self.active_sheet = sheet
- }
-
- pub fn active_sheet(&self) -> Option<Arc<RwLock<Sheet>>> {
- if let Some(id) = self.active_sheet {
- Register::get(id)
- } else {
- None
- }
- }
-}
-
-macro_rules! cfg {
- () => {
- GlobalState::instance().sheetview
- };
-}
-
-macro_rules! cfg_mut {
- () => {
- GlobalState::instance_mut().sheetview
- };
-}
-
-impl UserData for SheetViewState {
- fn add_fields<'lua, F: mlua::prelude::LuaUserDataFields<'lua, Self>>(fields: &mut F) {
- fields.add_field_function_get("active", |lua, _| {
- if let Some(id) = cfg!().active_sheet {
- SheetLuaRef::new(id).into_lua(lua)
- } else {
- Ok(Value::Nil)
- }
- });
-
- fields.add_field_function_set("active", |_, _, sheet: Option<SheetLuaRef>| {
- if let Some(r) = sheet {
- cfg_mut!().active_sheet = Some(r.id())
- } else {
- cfg_mut!().active_sheet = None
- }
-
- Ok(())
- });
-
- fields.add_field_function_get("cursor", |lua, _| {
- let table = lua.create_table()?;
- table.set("row", cfg!().cursor.y())?;
- table.set("column", cfg!().cursor.x())?;
- Ok(table)
- })
- }
-
- fn add_methods<'lua, M: mlua::prelude::LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
- methods.add_function("move_cursor", |_, (row, column): (usize, usize)| {
- cfg_mut!()
- .cursor
- .move_checked(CursorMove::Jump((row, column)));
- Ok(())
- })
- }
-}
diff --git a/src/state/view/mod.rs b/src/state/view/mod.rs
new file mode 100644
index 0000000..73f02ab
--- /dev/null
+++ b/src/state/view/mod.rs
@@ -0,0 +1,186 @@
+use std::sync::{Arc, RwLock};
+
+use mlua::{IntoLua, UserData, Value};
+
+use self::mode::Mode;
+
+use super::GlobalState;
+use crate::{
+ cursor::{Cursor, CursorMove},
+ sheet::{
+ luaref::SheetLuaRef,
+ register::{Register, SheetId},
+ Sheet,
+ },
+};
+
+pub mod mode;
+
+#[derive(Default, Debug)]
+pub struct SheetViewState {
+ pub cursor: Cursor,
+ active_sheet: Option<SheetId>,
+ pub mode: Mode,
+ pub selection_anchor: Option<(usize, usize)>,
+}
+
+impl SheetViewState {
+ pub const fn new() -> Self {
+ Self {
+ cursor: Cursor::new(),
+ active_sheet: None,
+ mode: Mode::Normal,
+ selection_anchor: None,
+ }
+ }
+
+ pub fn set_active_sheet(&mut self, sheet: Option<SheetId>) {
+ if let Some(id) = sheet {
+ if let Some(lock) = Register::get(id) {
+ let sheet = lock.read().unwrap();
+ self.cursor.set_x_max(sheet.width());
+ self.cursor.set_y_max(sheet.height());
+ }
+ }
+
+ self.active_sheet = sheet
+ }
+
+ pub fn active_sheet(&self) -> Option<Arc<RwLock<Sheet>>> {
+ if let Some(id) = self.active_sheet {
+ Register::get(id)
+ } else {
+ None
+ }
+ }
+
+ pub fn cancel_mode(&mut self) {
+ if self.selection_anchor.is_some() {
+ self.mode = Mode::Visual;
+ } else {
+ self.mode = Mode::Normal;
+ }
+ }
+
+ fn selection_range(&self) -> Option<((usize, usize), (usize, usize))> {
+ if let Some(selection) = self.selection_anchor {
+ let row = if selection.0 as usize > self.cursor.y() {
+ (self.cursor.y(), selection.0)
+ } else {
+ (selection.0, self.cursor.y())
+ };
+
+ let column = if selection.1 as usize > self.cursor.x() {
+ (self.cursor.x(), selection.1)
+ } else {
+ (selection.1, self.cursor.x())
+ };
+
+ Some((row, column))
+ } else {
+ None
+ }
+ }
+
+ pub fn selection(&self) -> Vec<(usize, usize)> {
+ let mut selection = Vec::new();
+
+ if let Some((row, column)) = self.selection_range() {
+ for i in row.0..=row.1 {
+ for j in column.0..=column.1 {
+ selection.push((i, j))
+ }
+ }
+ }
+
+ selection
+ }
+
+ pub fn selection_or_cursor(&self) -> Vec<(usize, usize)> {
+ if self.selection_anchor.is_some() {
+ self.selection()
+ } else {
+ vec![(self.cursor.y(), self.cursor.x())]
+ }
+ }
+
+ pub fn selection_contains(&self, row: usize, column: usize) -> bool {
+ if let Some((row_range, column_range)) = self.selection_range() {
+ row >= row_range.0
+ && row <= row_range.1
+ && column >= column_range.0
+ && column <= column_range.1
+ } else {
+ false
+ }
+ }
+}
+
+macro_rules! cfg {
+ () => {
+ GlobalState::instance().sheetview
+ };
+}
+
+macro_rules! cfg_mut {
+ () => {
+ GlobalState::instance_mut().sheetview
+ };
+}
+
+impl UserData for SheetViewState {
+ fn add_fields<'lua, F: mlua::prelude::LuaUserDataFields<'lua, Self>>(fields: &mut F) {
+ fields.add_field_function_get("active", |lua, _| {
+ if let Some(id) = cfg!().active_sheet {
+ SheetLuaRef::new(id).into_lua(lua)
+ } else {
+ Ok(Value::Nil)
+ }
+ });
+
+ fields.add_field_function_set("active", |_, _, sheet: Option<SheetLuaRef>| {
+ if let Some(r) = sheet {
+ cfg_mut!().active_sheet = Some(r.id())
+ } else {
+ cfg_mut!().active_sheet = None
+ }
+
+ Ok(())
+ });
+
+ fields.add_field_function_get("cursor", |lua, _| {
+ let table = lua.create_table()?;
+ table.set("row", cfg!().cursor.y())?;
+ table.set("column", cfg!().cursor.x())?;
+ Ok(table)
+ });
+
+ fields.add_field_function_get("mode", |_, _| Ok(cfg!().mode));
+ fields.add_field_function_set("mode", |_, _, mode: Mode| {
+ let this = &mut cfg_mut!();
+ match mode {
+ Mode::Visual => match this.mode {
+ Mode::Visual => {}
+ _ => this.selection_anchor = Some((this.cursor.y(), this.cursor.x())),
+ },
+ _ => this.selection_anchor = None,
+ }
+ this.mode = mode;
+ Ok(())
+ })
+ }
+
+ fn add_methods<'lua, M: mlua::prelude::LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
+ methods.add_function("move_cursor", |_, (row, column): (usize, usize)| {
+ cfg_mut!()
+ .cursor
+ .move_checked(CursorMove::Jump((row, column)));
+ Ok(())
+ });
+
+ methods.add_function("cancel_mode", |_, _: ()| {
+ cfg_mut!().cancel_mode();
+ Ok(())
+ })
+ }
+}
diff --git a/src/state/view/mode.rs b/src/state/view/mode.rs
new file mode 100644
index 0000000..3fa9c63
--- /dev/null
+++ b/src/state/view/mode.rs
@@ -0,0 +1,48 @@
+use mlua::{FromLua, IntoLua};
+
+#[derive(Default, Debug, Clone, Copy)]
+pub enum Mode {
+ #[default]
+ Normal,
+ Visual,
+ Insert,
+ Command,
+}
+
+impl<'lua> FromLua<'lua> for Mode {
+ fn from_lua(
+ value: mlua::prelude::LuaValue<'lua>,
+ _lua: &'lua mlua::prelude::Lua,
+ ) -> mlua::prelude::LuaResult<Self> {
+ if value.is_string() {
+ match value.as_str().unwrap().to_lowercase().as_ref() {
+ "normal" => Ok(Mode::Normal),
+ "visual" => Ok(Mode::Visual),
+ "insert" => Ok(Mode::Visual),
+ "command" => Ok(Mode::Visual),
+ _ => Err(mlua::Error::runtime(
+ "mode needs to be 'normal', 'visual' or 'insert'",
+ )),
+ }
+ } else {
+ Err(mlua::Error::runtime(
+ "mode needs to be 'normal', 'visual' or 'insert'",
+ ))
+ }
+ }
+}
+
+impl<'lua> IntoLua<'lua> for Mode {
+ fn into_lua(
+ self,
+ lua: &'lua mlua::prelude::Lua,
+ ) -> mlua::prelude::LuaResult<mlua::prelude::LuaValue<'lua>> {
+ match self {
+ Mode::Normal => "normal",
+ Mode::Visual => "insert",
+ Mode::Insert => "visual",
+ Mode::Command => "command",
+ }
+ .into_lua(lua)
+ }
+}
diff --git a/src/state/window.rs b/src/state/window.rs
new file mode 100644
index 0000000..288b5b4
--- /dev/null
+++ b/src/state/window.rs
@@ -0,0 +1,47 @@
+use mlua::{FromLua, IntoLua};
+
+#[derive(Default, Debug, Clone, Copy)]
+pub enum Window {
+ #[default]
+ View,
+ Editor,
+ Log,
+ Error,
+}
+
+impl<'lua> FromLua<'lua> for Window {
+ fn from_lua(
+ value: mlua::prelude::LuaValue<'lua>,
+ _lua: &'lua mlua::prelude::Lua,
+ ) -> mlua::prelude::LuaResult<Self> {
+ if value.is_string() {
+ match value.as_str().unwrap().to_lowercase().as_ref() {
+ "view" => Ok(Window::View),
+ "editor" => Ok(Window::Editor),
+ "log" => Ok(Window::Log),
+ "error" => Ok(Window::Error),
+ _ => Err(mlua::Error::runtime(
+ "window needs to be 'view', 'editor', 'log' or 'error'",
+ )),
+ }
+ } else {
+ Err(mlua::Error::runtime(
+ "window needs to be 'view', 'editor', 'log' or 'error'",
+ ))
+ }
+ }
+}
+
+impl<'lua> IntoLua<'lua> for Window {
+ fn into_lua(
+ self,
+ lua: &'lua mlua::prelude::Lua,
+ ) -> mlua::prelude::LuaResult<mlua::prelude::LuaValue<'lua>> {
+ match self {
+ Window::View => "view",
+ Window::Editor => "editor",
+ Window::Log => "log",
+ Window::Error => "error",
+ }.into_lua(lua)
+ }
+}
diff --git a/src/widgets/logview.rs b/src/widgets/logview.rs
index 35c7bed..5d30b17 100644
--- a/src/widgets/logview.rs
+++ b/src/widgets/logview.rs
@@ -1,4 +1,4 @@
-use ratatui::{style::{Style, Stylize}, text::ToSpan, widgets::Widget};
+use ratatui::{crossterm::event::KeyEvent, style::{Style, Stylize}, text::ToSpan, widgets::Widget};
use crate::lua::iobuffer::iobuffer;
@@ -15,6 +15,8 @@ impl LogView {
bar: StatusBar::new().left(" Log ").left_style(Style::default().on_magenta()),
}
}
+
+ pub fn handle_key_event(&mut self, _event: KeyEvent) {}
}
impl Widget for &mut LogView {
diff --git a/src/widgets/luaeditor/mod.rs b/src/widgets/luaeditor/mod.rs
index 6fc8436..de35c3d 100644
--- a/src/widgets/luaeditor/mod.rs
+++ b/src/widgets/luaeditor/mod.rs
@@ -1,25 +1,22 @@
use ratatui::{
- crossterm::event::{KeyCode, KeyEvent},
+ crossterm::event::{KeyCode, KeyEvent, KeyModifiers},
style::{Style, Stylize},
text::{ToLine, ToSpan},
widgets::Widget,
};
-
-pub mod buffer;
-pub mod theme;
-pub mod treesitter;
-
-use buffer::Buffer;
use tree_sitter_highlight::HighlightConfiguration;
-use crate::cursor::CursorMove;
+use crate::state::{editor::EditorState, window::Window, GlobalState};
+use crate::{cursor::CursorMove, lua};
use super::statusbar::StatusBar;
+pub mod theme;
+pub mod treesitter;
+
pub struct LuaEditor {
bar: StatusBar,
scroll: usize,
- buffer: Buffer,
highlight_config: HighlightConfiguration,
}
@@ -30,35 +27,53 @@ impl LuaEditor {
.left(" [No Name] ")
.left_style(Style::default().on_magenta()),
scroll: 0,
- buffer: Buffer::new(),
highlight_config: treesitter::new_highlight_configuration(),
}
}
pub fn handle_key_event(&mut self, event: KeyEvent) {
match event.code {
- KeyCode::Char(c) => self.buffer.insert(c),
+ KeyCode::Char('r') if event.modifiers == KeyModifiers::CONTROL => {
+ EditorState::run(&lua::get())
+ }
+ KeyCode::Char(c) => GlobalState::instance_mut().editor.buffer.insert(c),
KeyCode::Backspace => {
- self.buffer.delete();
+ GlobalState::instance_mut().editor.buffer.delete();
}
KeyCode::Enter => {
- self.buffer.insert('\n');
+ GlobalState::instance_mut().editor.buffer.insert('\n');
}
- KeyCode::Left => self.buffer.move_cursor(CursorMove::Left(1)),
- KeyCode::Right => self.buffer.move_cursor(CursorMove::Right(1)),
- KeyCode::Up => self.buffer.move_cursor(CursorMove::Up(1)),
- KeyCode::Down => self.buffer.move_cursor(CursorMove::Down(1)),
+ KeyCode::Left => GlobalState::instance_mut()
+ .editor
+ .buffer
+ .move_cursor(CursorMove::Left(1)),
+ KeyCode::Right => GlobalState::instance_mut()
+ .editor
+ .buffer
+ .move_cursor(CursorMove::Right(1)),
+ KeyCode::Up => GlobalState::instance_mut()
+ .editor
+ .buffer
+ .move_cursor(CursorMove::Up(1)),
+ KeyCode::Down => GlobalState::instance_mut()
+ .editor
+ .buffer
+ .move_cursor(CursorMove::Down(1)),
KeyCode::Home => {}
KeyCode::End => {}
KeyCode::PageUp => {}
KeyCode::PageDown => {}
- KeyCode::Tab => self.buffer.insert('\t'),
+ KeyCode::Tab => GlobalState::instance_mut().editor.buffer.insert('\t'),
KeyCode::BackTab => {}
KeyCode::Delete => {}
KeyCode::Insert => {}
KeyCode::F(_) => {}
KeyCode::Null => {}
- KeyCode::Esc => {}
+ KeyCode::Esc => {
+ let mut state = GlobalState::instance_mut();
+ state.editor.visible = false;
+ state.set_focus(Window::View)
+ }
KeyCode::CapsLock => {}
KeyCode::ScrollLock => {}
KeyCode::NumLock => {}
@@ -70,18 +85,6 @@ impl LuaEditor {
KeyCode::Modifier(_) => {}
}
}
-
- pub fn set_text<S>(&mut self, content: S)
- where
- S: AsRef<str>,
- {
- self.buffer
- .set_lines(content.as_ref().lines().map(|s| s.to_string()).collect())
- }
-
- pub fn text(&self) -> String {
- self.buffer.lines().join("\n")
- }
}
impl Widget for &mut LuaEditor {
@@ -89,13 +92,16 @@ impl Widget for &mut LuaEditor {
where
Self: Sized,
{
+ let state = GlobalState::instance();
+ let buffer = &state.editor.buffer;
+
self.bar.render(area, buf);
let inner_area = self.bar.area(area);
- let text = self.text();
+ let text = buffer.as_string();
let highlights = treesitter::highlighter_split(text.as_bytes(), &self.highlight_config);
- let nr_width = (self.buffer.lines().len().to_string().len() + 1).max(4) as u16;
+ let nr_width = (buffer.lines().len().to_string().len() + 1).max(4) as u16;
let mut text_area = inner_area;
text_area.x += nr_width;
@@ -122,7 +128,7 @@ impl Widget for &mut LuaEditor {
}
}
- for i in self.scroll..self.buffer.lines().len() {
+ for i in self.scroll..buffer.lines().len() {
let mut nr_area = span_area;
nr_area.x = inner_area.x;
nr_area.width = nr_width - 1;
@@ -139,13 +145,10 @@ impl Widget for &mut LuaEditor {
let mut cursor_area = text_area;
cursor_area.width = 1;
cursor_area.height = 1;
- cursor_area.y += self.buffer.cursor().y() as u16;
- cursor_area.x += self.buffer.cursor().x() as u16;
+ cursor_area.y += buffer.cursor().y() as u16;
+ cursor_area.x += buffer.cursor().x() as u16;
- let (first, _) = self
- .buffer
- .current_line()
- .split_at(self.buffer.cursor().x());
+ let (first, _) = buffer.current_line().split_at(buffer.cursor().x());
for c in first.chars() {
if c == '\t' {
@@ -153,19 +156,19 @@ impl Widget for &mut LuaEditor {
}
}
- if self.scroll > self.buffer.cursor().y() {
- self.scroll = self.buffer.cursor().y();
- } else if inner_area.height as usize + self.scroll - 1 < self.buffer.cursor().y() {
- self.scroll = self.buffer.cursor().y() - inner_area.height as usize + 1;
+ if self.scroll > buffer.cursor().y() {
+ self.scroll = buffer.cursor().y();
+ } else if inner_area.height as usize + self.scroll - 1 < buffer.cursor().y() {
+ self.scroll = buffer.cursor().y() - inner_area.height as usize + 1;
}
cursor_area.y -= self.scroll as u16;
if inner_area.contains(cursor_area.into()) {
- self.buffer
+ buffer
.current_line()
.chars()
- .nth(self.buffer.cursor().x())
+ .nth(buffer.cursor().x())
.map(|c| if c == '\t' { ' ' } else { c })
.unwrap_or(' ')
.to_span()
@@ -181,7 +184,6 @@ impl Default for LuaEditor {
highlight_config: treesitter::new_highlight_configuration(),
bar: StatusBar::default(),
scroll: 0,
- buffer: Buffer::default(),
}
}
}
diff --git a/src/widgets/sheetview/mod.rs b/src/widgets/sheetview/mod.rs
index 4ae37f0..aac2080 100644
--- a/src/widgets/sheetview/mod.rs
+++ b/src/widgets/sheetview/mod.rs
@@ -1,49 +1,27 @@
-use std::thread::JoinHandle;
-
use ratatui::{
- crossterm::event::{KeyCode, KeyEvent, KeyModifiers},
+ crossterm::event::{KeyCode, KeyEvent},
prelude::*,
style::Stylize,
text::ToLine,
- widgets::{Block, Borders, Clear, Gauge, Paragraph, Widget, Wrap},
+ widgets::Widget,
};
use crate::{
config::GlobalConfig,
cursor::CursorMove,
lua,
- sheet::{
- cell::Cell,
- eval::EvalFunction,
- Sheet,
- },
- state::GlobalState,
+ sheet::cell::Cell,
+ state::{view::mode::Mode, window::Window, GlobalState},
};
-use super::{luaeditor::LuaEditor, statusbar::StatusBar};
+use super::statusbar::StatusBar;
const DEFAULT_COLUMN_WIDTH: u16 = 10;
#[derive(Default)]
-enum SheetViewMode {
- #[default]
- Normal,
- Insert,
- Visual,
- Command,
- CommandError(String),
- Script,
- Processing(Option<JoinHandle<Result<Sheet, String>>>),
- EditorError(String),
-}
-
-#[derive(Default)]
pub struct SheetView {
bar: StatusBar,
- scroll: (u16, u16),
- selection_anchor: Option<(u16, u16)>,
- mode: SheetViewMode,
- editor: LuaEditor,
+ scroll: (usize, usize),
}
impl SheetView {
@@ -56,314 +34,126 @@ impl SheetView {
.right("")
.right_style(Style::default().on_red()),
scroll: (0, 0),
- mode: SheetViewMode::Normal,
- selection_anchor: None,
- editor: LuaEditor::new(),
}
}
- pub fn move_cursor_by(&mut self, delta: (isize, isize)) {
- let mut state = GlobalState::instance_mut();
- state
- .sheetview
- .cursor
- .move_checked(CursorMove::Relative(delta));
+ fn set_mode(&self, mode: Mode) {
+ GlobalState::instance_mut().sheetview.mode = mode;
}
- pub fn selection(&self) -> Vec<(u16, u16)> {
- let mut selection = Vec::new();
-
- if let Some((row, column)) = self.selection_range() {
- for i in row.0..=row.1 {
- for j in column.0..=column.1 {
- selection.push((i, j))
- }
- }
- }
-
- selection
- }
-
- fn is_in_selection(&mut self, row: u16, column: u16) -> bool {
- if let Some((row_range, column_range)) = self.selection_range() {
- row >= row_range.0
- && row <= row_range.1
- && column >= column_range.0
- && column <= column_range.1
- } else {
- false
- }
+ fn move_cursor(&self, cm: CursorMove) {
+ GlobalState::instance_mut()
+ .sheetview
+ .cursor
+ .move_checked(cm)
}
- fn selection_range(&self) -> Option<((u16, u16), (u16, u16))> {
+ fn cursor(&self) -> (usize, usize) {
let state = GlobalState::instance();
- let cursor = &state.sheetview.cursor;
- if let Some(selection) = self.selection_anchor {
- let row = if selection.0 as usize > cursor.y() {
- (cursor.y() as u16, selection.0)
- } else {
- (selection.0, cursor.y() as u16)
- };
-
- let column = if selection.1 as usize > cursor.x() {
- (cursor.x() as u16, selection.1)
- } else {
- (selection.1, cursor.x() as u16)
- };
+ (state.sheetview.cursor.y(), state.sheetview.cursor.x())
+ }
- Some((row, column))
- } else {
- None
- }
+ fn start_selection(&self) {
+ let mut state = GlobalState::instance_mut();
+ state.sheetview.selection_anchor = Some(self.cursor());
+ state.sheetview.mode = Mode::Visual;
}
- fn join_process_handle_on_finished(&mut self) {
- if let SheetViewMode::Processing(opt) = &mut self.mode {
- let handle = opt.take().unwrap();
- if handle.is_finished() {
- match handle.join().unwrap() {
- Ok(sheet) => {
- GlobalState::instance()
- .sheetview
- .active_sheet()
- .unwrap()
- .write()
- .unwrap()
- .apply(sheet);
- self.mode = SheetViewMode::Script;
- }
- Err(message) => self.mode = SheetViewMode::EditorError(message),
- };
- } else {
- self.mode = SheetViewMode::Processing(Some(handle));
- }
- }
+ fn open_editor(&self) {
+ let mut state = GlobalState::instance_mut();
+ state.editor.buffer.set_lines_from_string(
+r#"require('neosheet')
+ .state
+ .view
+ .active:foreach(function(cell)
+ return ""
+end)"#,
+ );
+ state.set_focus(Window::Editor)
}
pub fn handle_key_event(&mut self, event: KeyEvent) {
- match &self.mode {
- SheetViewMode::Normal => match event.code {
- KeyCode::Char('j') => self.move_cursor_by((0, 1)),
- KeyCode::Char('k') => self.move_cursor_by((0, -1)),
- KeyCode::Char('h') => self.move_cursor_by((-1, 0)),
- KeyCode::Char('l') => self.move_cursor_by((1, 0)),
- KeyCode::Char('v') => {
- let state = GlobalState::instance();
- let cursor = &state.sheetview.cursor;
- self.selection_anchor = Some((cursor.y() as u16, cursor.x() as u16));
- self.mode = SheetViewMode::Visual
- }
- KeyCode::Char('s') => {
- self.editor.set_text("function(cell)\n\treturn \"\"\nend");
- self.mode = SheetViewMode::Script;
- }
+ let mode = { GlobalState::instance().sheetview.mode };
+ match mode {
+ Mode::Normal => match event.code {
+ KeyCode::Char('j') => self.move_cursor(CursorMove::Down(1)),
+ KeyCode::Char('k') => self.move_cursor(CursorMove::Up(1)),
+ KeyCode::Char('h') => self.move_cursor(CursorMove::Left(1)),
+ KeyCode::Char('l') => self.move_cursor(CursorMove::Right(1)),
+ KeyCode::Char('v') => self.start_selection(),
+ KeyCode::Char('s') => self.open_editor(),
KeyCode::Char(':') => {
- self.mode = SheetViewMode::Command;
- self.bar.set_input_mode(true)
+ self.set_mode(Mode::Command);
+ self.bar.set_input_mode(true);
}
KeyCode::Enter => {
- self.mode = SheetViewMode::Insert;
+ self.set_mode(Mode::Insert);
self.bar.set_input_mode(true);
}
_ => {}
},
- SheetViewMode::Insert => match event.code {
+ Mode::Insert => match event.code {
KeyCode::Enter => {
- let lock = GlobalState::instance().sheetview.active_sheet().unwrap();
+ let mut state = GlobalState::instance_mut();
+ let lock = state.sheetview.active_sheet().unwrap();
let mut sheet = lock.write().unwrap();
- let state = GlobalState::instance();
- let cursor = &state.sheetview.cursor;
-
- if self.selection_anchor.is_some() {
- self.selection().iter().map(|(r, c)| (*r, *c)).collect()
- } else {
- vec![(cursor.y() as u16, cursor.x() as u16)]
- }
- .into_iter()
- .for_each(|(r, c)| {
- sheet.set_cell(r as usize, c as usize, {
- let s = self.bar.input().unwrap();
- match s.parse() {
- Ok(n) => Cell::Number(n),
- Err(_) => Cell::String(s.to_string()),
- }
- })
- });
+ state
+ .sheetview
+ .selection_or_cursor()
+ .into_iter()
+ .for_each(|(r, c)| {
+ sheet.set_cell(r as usize, c as usize, {
+ let s = self.bar.input().unwrap();
+ match s.parse() {
+ Ok(n) => Cell::Number(n),
+ Err(_) => Cell::String(s.to_string()),
+ }
+ })
+ });
+ state.sheetview.cancel_mode();
self.bar.set_input_mode(false);
- if self.selection_anchor.is_some() {
- self.mode = SheetViewMode::Visual;
- } else {
- self.mode = SheetViewMode::Normal;
- }
}
KeyCode::Esc => {
- if self.selection_anchor.is_some() {
- self.mode = SheetViewMode::Visual;
- } else {
- self.mode = SheetViewMode::Normal;
- }
+ GlobalState::instance_mut().sheetview.cancel_mode();
self.bar.set_input_mode(false);
}
_ => self.bar.handle_keyevent(event),
},
- SheetViewMode::Visual => match event.code {
- KeyCode::Char('j') => self.move_cursor_by((0, 1)),
- KeyCode::Char('k') => self.move_cursor_by((0, -1)),
- KeyCode::Char('h') => self.move_cursor_by((-1, 0)),
- KeyCode::Char('l') => self.move_cursor_by((1, 0)),
- KeyCode::Esc | KeyCode::Char('v') => {
- self.selection_anchor = None;
- self.mode = SheetViewMode::Normal;
- }
- KeyCode::Char('s') => {
- self.editor.set_text("function(cell)\n\treturn \"\"\nend");
- self.mode = SheetViewMode::Script;
- }
+ Mode::Visual => match event.code {
+ KeyCode::Char('j') => self.move_cursor(CursorMove::Down(1)),
+ KeyCode::Char('k') => self.move_cursor(CursorMove::Up(1)),
+ KeyCode::Char('h') => self.move_cursor(CursorMove::Left(1)),
+ KeyCode::Char('l') => self.move_cursor(CursorMove::Right(1)),
+ KeyCode::Char('v') => self.start_selection(),
+ KeyCode::Char('s') => self.open_editor(),
KeyCode::Char(':') => {
- self.mode = SheetViewMode::Command;
- self.bar.set_input_mode(true)
+ self.set_mode(Mode::Command);
+ self.bar.set_input_mode(true);
}
KeyCode::Enter => {
- self.mode = SheetViewMode::Insert;
+ self.set_mode(Mode::Insert);
self.bar.set_input_mode(true);
}
_ => {}
},
- SheetViewMode::Command => match event.code {
+ Mode::Command => match event.code {
KeyCode::Enter => {
- if let Err(error) = lua::get().load(self.bar.input().unwrap_or("")).exec() {
- self.mode = SheetViewMode::CommandError(error.to_string())
- } else if self.selection_anchor.is_some() {
- self.mode = SheetViewMode::Visual;
- } else {
- self.mode = SheetViewMode::Normal;
+ if let Err(_error) = lua::get().load(self.bar.input().unwrap_or("")).exec() {
+ // TODO: push errors to buffer
}
+ GlobalState::instance_mut().sheetview.cancel_mode();
self.bar.set_input_mode(false);
}
KeyCode::Esc => {
- if self.selection_anchor.is_some() {
- self.mode = SheetViewMode::Visual;
- } else {
- self.mode = SheetViewMode::Normal;
- }
+ GlobalState::instance_mut().sheetview.cancel_mode();
self.bar.set_input_mode(false);
}
_ => self.bar.handle_keyevent(event),
},
- SheetViewMode::CommandError(_) => match event.code {
- KeyCode::Esc | KeyCode::Enter => self.mode = SheetViewMode::Normal,
- _ => {}
- },
- SheetViewMode::Script => match event.code {
- KeyCode::Char('r') if event.modifiers == KeyModifiers::CONTROL => {
- let script = self.editor.text();
-
- let (width, height) = {
- let lock = GlobalState::instance().sheetview.active_sheet().unwrap();
- let sheet = lock.read().unwrap();
- (sheet.width(), sheet.height())
- };
-
- let mut cells = Vec::new();
-
- if self.selection_anchor.is_some() {
- cells = self
- .selection()
- .iter()
- .map(|(r, c)| (*r as usize, *c as usize))
- .collect()
- } else {
- for row in 0..height {
- for column in 0..width {
- cells.push((row, column));
- }
- }
- }
-
- self.mode = SheetViewMode::Processing(Some(
- GlobalState::instance()
- .sheetview
- .active_sheet()
- .unwrap()
- .eval_function(script, cells),
- ));
- }
- KeyCode::Esc => {
- if self.selection_anchor.is_some() {
- self.mode = SheetViewMode::Visual;
- } else {
- self.mode = SheetViewMode::Normal;
- }
- }
- _ => self.editor.handle_key_event(event),
- },
- SheetViewMode::Processing(_) => {
- self.join_process_handle_on_finished();
- }
- SheetViewMode::EditorError(_) => match event.code {
- KeyCode::Esc | KeyCode::Enter => self.mode = SheetViewMode::Script,
- _ => {}
- },
- }
- }
-
- fn is_editor_visible(&self) -> bool {
- matches!(
- self.mode,
- SheetViewMode::Script | SheetViewMode::Processing(_) | SheetViewMode::EditorError(_),
- )
- }
-
- fn is_error_window_visible(&self) -> bool {
- matches!(self.mode, SheetViewMode::EditorError(_))
- }
-
- fn is_progress_visible(&self) -> bool {
- matches!(self.mode, SheetViewMode::Processing(_))
- }
-
- fn areas(&self, area: Rect) -> (Rect, Rect, Rect, Rect, Rect) {
- let mut sheet = area;
- let mut editor = Rect::default();
- let mut error = Rect::default();
- let mut progress = Rect::default();
- let mut command_error = Rect::default();
-
- if let SheetViewMode::CommandError(message) = &self.mode {
- let layout = Layout::vertical([
- Constraint::Min(1),
- Constraint::Length(message.lines().count().min(15) as u16 + 1),
- ])
- .split(sheet);
- sheet = layout[0];
- command_error = layout[1];
}
-
- if self.is_editor_visible() {
- let layout =
- Layout::horizontal([Constraint::Min(1), Constraint::Length(50)]).split(sheet);
- sheet = layout[0];
- editor = layout[1];
-
- if self.is_error_window_visible() {
- let layout =
- Layout::vertical([Constraint::Min(1), Constraint::Length(10)]).split(editor);
- editor = layout[0];
- error = layout[1];
- }
-
- if self.is_progress_visible() {
- let layout =
- Layout::vertical([Constraint::Min(1), Constraint::Length(1)]).split(editor);
- editor = layout[0];
- progress = layout[1];
- }
- }
-
- (sheet, editor, error, progress, command_error)
}
}
@@ -373,168 +163,91 @@ impl Widget for &mut SheetView {
Self: Sized,
{
let theme = GlobalConfig::instance().theme.sheetview.clone();
- let progress;
- let (sheet_area, editor_area, error_area, progress_area, cmd_error_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 = GlobalState::instance().sheetview.active_sheet().unwrap();
- let sheet = lock.read().unwrap();
-
- let state = GlobalState::instance();
- let cursor = &state.sheetview.cursor;
-
- let mut sheet_area_inner = self.bar.area(sheet_area);
-
- if self.is_editor_visible() {
- sheet_area_inner = separator.inner(sheet_area_inner);
- }
-
- let viewport_rows = sheet.height().min(sheet_area_inner.height as usize);
- let viewport_columns = sheet
- .width()
- .min((sheet_area_inner.width / DEFAULT_COLUMN_WIDTH) as usize);
- if cursor.y() >= viewport_rows + self.scroll.0 as usize {
- self.scroll.0 = (cursor.y() - viewport_rows) as u16 + 1;
- } else if cursor.y() < self.scroll.0 as usize {
- self.scroll.0 = cursor.y() as u16;
- }
+ let state = GlobalState::instance();
+ let lock = state.sheetview.active_sheet().unwrap();
+ let sheet = lock.read().unwrap();
+ let cursor = &state.sheetview.cursor;
- if cursor.x() >= viewport_columns + self.scroll.1 as usize {
- self.scroll.1 = (cursor.x() - viewport_columns) as u16 + 1;
- } else if cursor.x() < self.scroll.1 as usize {
- self.scroll.1 = cursor.x() as u16;
- }
+ let sheet_area_inner = self.bar.area(area);
- for row in 0..viewport_rows as u16 {
- for column in 0..(viewport_columns + 1) as u16 {
- let (cell_pos_y, cell_pos_x) = (row + self.scroll.0, column + self.scroll.1);
+ let viewport_rows = sheet.height().min(sheet_area_inner.height as usize);
+ let viewport_columns = sheet
+ .width()
+ .min((sheet_area_inner.width / DEFAULT_COLUMN_WIDTH) as usize);
- if let Some(cell_ref) = sheet.get_ref(cell_pos_y as usize, cell_pos_x as usize)
- {
- let cell = cell_ref.value().to_string() + " ";
+ if cursor.y() >= viewport_rows + self.scroll.0 {
+ self.scroll.0 = (cursor.y() - viewport_rows) + 1;
+ } else if cursor.y() < self.scroll.0 {
+ self.scroll.0 = cursor.y();
+ }
- let line =
- if (cell_pos_y, cell_pos_x) == (cursor.y() as u16, cursor.x() as u16) {
- theme
- .cursor
- .get(cell_ref, &lua::get())
- .unwrap_or_default()
- .apply(cell.to_line())
- } else if self.is_in_selection(cell_pos_y, cell_pos_x) {
- theme
- .selection
- .get(cell_ref, &lua::get())
- .unwrap_or_default()
- .apply(cell.to_line())
- } else {
- theme
- .cell
- .get(cell_ref, &lua::get())
- .unwrap_or_default()
- .apply(cell.to_line())
- };
+ if cursor.x() >= viewport_columns + self.scroll.1 as usize {
+ self.scroll.1 = (cursor.x() - viewport_columns) + 1;
+ } else if cursor.x() < self.scroll.1 {
+ self.scroll.1 = cursor.x();
+ }
- let rect = Rect::new(
- 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,
- );
+ for row in 0..viewport_rows {
+ for column in 0..(viewport_columns + 1) {
+ let (cell_pos_y, cell_pos_x) = (row + self.scroll.0, column + self.scroll.1);
- line.render(rect, buf);
- }
- }
- }
+ if let Some(cell_ref) = sheet.get_ref(cell_pos_y as usize, cell_pos_x as usize) {
+ let cell = cell_ref.value().to_string() + " ";
- match &self.mode {
- SheetViewMode::Command => {
- self.bar.set_left(" COMMAND ");
- }
+ let line = if (cell_pos_y, cell_pos_x) == (cursor.y(), cursor.x()) {
+ theme
+ .cursor
+ .get(cell_ref, &lua::get())
+ .unwrap_or_default()
+ .apply(cell.to_line())
+ } else if state.sheetview.selection_contains(cell_pos_y, cell_pos_x) {
+ theme
+ .selection
+ .get(cell_ref, &lua::get())
+ .unwrap_or_default()
+ .apply(cell.to_line())
+ } else {
+ theme
+ .cell
+ .get(cell_ref, &lua::get())
+ .unwrap_or_default()
+ .apply(cell.to_line())
+ };
- SheetViewMode::Insert => {
- self.bar.set_left(" INSERT ");
- }
+ let rect = Rect::new(
+ sheet_area_inner.x + (column as u16) * DEFAULT_COLUMN_WIDTH,
+ sheet_area_inner.y + (row as u16),
+ (sheet_area_inner.width - (column as u16) * DEFAULT_COLUMN_WIDTH)
+ .min(DEFAULT_COLUMN_WIDTH),
+ 1,
+ );
- _ => {
- if self.selection_anchor.is_some() {
- self.bar.set_left(" VISUAL ");
- } else {
- self.bar.set_left(" NORMAL ");
- self.bar.set_middle_alignment(Alignment::Center);
- self.bar.set_middle("Sheet");
- }
+ line.render(rect, buf);
}
}
+ }
- self.bar.render(sheet_area, buf);
-
- if self.is_editor_visible() {
- separator.render(sheet_area, buf);
- self.editor.render(editor_area, buf)
+ match state.sheetview.mode {
+ Mode::Command => {
+ self.bar.set_left(" COMMAND ");
}
- if let SheetViewMode::EditorError(error_msg) = &self.mode {
- let lines = error_msg.lines().collect::<Vec<_>>();
-
- 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 text = Text::from_iter(lines.iter().map(|s| s.to_line().white()));
-
- Paragraph::new(text)
- .wrap(Wrap { trim: true })
- .bg(Color::Rgb(70, 25, 25))
- .render(error_inner, buf);
+ Mode::Insert => {
+ self.bar.set_left(" INSERT ");
}
- if let SheetViewMode::CommandError(message) = &self.mode {
- let lines = message.lines().collect::<Vec<_>>();
-
- 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(cmd_error_area);
- block.render(cmd_error_area, buf);
-
- let text = Text::from_iter(lines.iter().map(|s| s.to_line().white()));
-
- Paragraph::new(text)
- .wrap(Wrap { trim: true })
- .bg(Color::Rgb(70, 25, 25))
- .render(error_inner, buf);
+ _ => {
+ if state.sheetview.selection_anchor.is_some() {
+ self.bar.set_left(" VISUAL ");
+ } else {
+ self.bar.set_left(" NORMAL ");
+ self.bar.set_middle_alignment(Alignment::Center);
+ self.bar.set_middle("Sheet");
+ }
}
-
- progress = sheet.progress();
}
- self.join_process_handle_on_finished();
-
- if self.is_progress_visible() {
- let gauge = Gauge::default()
- .gauge_style(
- Style::default()
- .fg(Color::White)
- .bg(Color::DarkGray)
- .add_modifier(Modifier::ITALIC),
- )
- .percent(progress as u16);
-
- Clear.render(progress_area, buf);
- gauge.render(progress_area, buf);
- }
+ self.bar.render(area, buf);
}
}