diff --git a/docs/command.md b/docs/command.md new file mode 100644 index 0000000..921c735 --- /dev/null +++ b/docs/command.md @@ -0,0 +1,20 @@ +# Command Mode + +You enter command mode by typing `:` while in navigation mode. You can then +type a command and hit `Enter` to execute it or `Esc` to cancel. + +The currently supported commands are: + +* `write [path]` save the current spreadsheet. If the path is provided it will save it to that path. If omitted it will save to the path you are currently editing. `w` is a shorthand alias for this command. +* `insert-rows [number]` Inserts a row into the sheet at your current row. If the number is provided then inserts that many rows. If omitted then just inserts one. +* `insert-cols [number]` Just line `insert-rows` but for columns. +* `rename-sheet [idx] ` rename a sheet. If the idx is provide then renames that sheet. If omitted then it renames the current sheet. +* `new-sheet [name]` Creates a new sheet. If the name is provided then uses that. If omitted then uses a default sheet name. +* `select-sheet ` Select a sheet by name. +* `edit ` Edit a new spreadsheet at the current path. `e` is a shorthand alias for this command. +* `quit` Quits the application. `q` is a shorthand alias for this command. + + + diff --git a/docs/edit.md b/docs/edit.md new file mode 100644 index 0000000..67c9165 --- /dev/null +++ b/docs/edit.md @@ -0,0 +1,31 @@ +# Edit Mode + +You enter Edit mode by hitting `e` or `i` while in navigation mode. Type +what you want into the cell. + +Starting with: + +* `=` will treat what you type as a formula. +* `$` will treat it as us currency. + +Typing a number will treat the contents as a number. While typing non-numeric +text will treat it as text content. + + + +For the most part this should work the same way you expect a spreadsheet to +work. + +* `Enter` will update the cell contents. +* `Esc` will cancel editing the cell and leave it unedited. +* `Ctrl-p` will paste the range selection if it exists into the cell. + +`Ctrl-r` will enter range select mode when editing a formula. You can navigate +around the sheet and hit space to select that cell in the sheet to set the +start of the range. Navigate some more and hit space to set the end of the +range. + +You can find the functions we support documented here: +[ironcalc docs](https://docs.ironcalc.com/functions/lookup-and-reference.html) + diff --git a/docs/index.md b/docs/index.md index cbf1ad6..c317fa9 100644 --- a/docs/index.md +++ b/docs/index.md @@ -33,123 +33,9 @@ The sheetui user interface is loosely inspired by vim. It is a modal interface that is entirely keyboard driven. At nearly any time you can type `Alt-h` to get some context sensitive help. -### Navigation Mode +### Modal Docs -The interface will start out in navigation mode. You can navigate around the -table and between the sheets using the following keybinds: - -**Cell Navigation** - -* `h`, ⬆️, and `TAB` will move one cell to the left. -* `l` and, ➡️ will move one cell to the right. -* `j`, ⬇️, and `Enter` will move one cell down. -* `k` ⬆️, will move one cell up. -* `d` will delete the contents of the selected cell leaving style untouched -* `D` will delete the contents of the selected cell including any style -* `gg` will go to the top row in the current column - -**Sheet Navigation** - -* `Ctrl-n` moves to the next sheet -* `Ctrl-p` moves to the prev sheet - -Sheet navigation moving will loop around when you reach the ends. - -**Numeric prefixes** - -You can prefix each of the keybinds above with a numeric prefix to do them that -many times. So typing `123h` will move to the left 123 times. Hitting `Esc` -will clear the numeric prefix if you want to cancel it. - -**Modifying the Sheet or Cells** - -* `e` or `i` will enter CellEdit mode for the current cell. -* `Ctrl-h` will shorten the width of the column you are on. -* `Ctrl-l` will lengthen the width of the column you are on. - -**Other Keybindings** - -* `Ctrl-r` will enter range selection mode. -* `v` will enter range selection mode with the start of the range already selected. -* `Ctrl-s` will save the sheet. -* `Ctrl-c`, `y` Copy the cell or range contents. -* `Ctrl-v`, `p` Paste into the sheet. -* `Ctrl-Shift-C` Copy the cell or range formatted content. -* `q` will exit the application. -* `:` will enter CommandMode. - -Range selections made from navigation mode will be available to paste into a Cell Edit. - - - -### CellEdit Mode - -You enter CellEdit mode by hitting `e` or `i` while in navigation mode. Type -what you want into the cell. - -Starting with: - -* `=` will treat what you type as a formula. -* `$` will treat it as us currency. - -Typing a number will treat the contents as a number. While typing non-numeric -text will treat it as text content. - - - -For the most part this should work the same way you expect a spreadsheet to -work. - -* `Enter` will update the cell contents. -* `Esc` will cancel editing the cell and leave it unedited. -* `Ctrl-p` will paste the range selection if it exists into the cell. - -`Ctrl-r` will enter range select mode when editing a formula. You can navigate -around the sheet and hit space to select that cell in the sheet to set the -start of the range. Navigate some more and hit space to set the end of the -range. - -You can find the functions we support documented here: -[ironcalc docs](https://docs.ironcalc.com/functions/lookup-and-reference.html) - -### Command Mode - -You enter command mode by typing `:` while in navigation mode. You can then -type a command and hit `Enter` to execute it or `Esc` to cancel. - -The currently supported commands are: - -* `write [path]` save the current spreadsheet. If the path is provided it will save it to that path. If omitted it will save to the path you are currently editing. `w` is a shorthand alias for this command. -* `insert-rows [number]` Inserts a row into the sheet at your current row. If the number is provided then inserts that many rows. If omitted then just inserts one. -* `insert-cols [number]` Just line `insert-rows` but for columns. -* `rename-sheet [idx] ` rename a sheet. If the idx is provide then renames that sheet. If omitted then it renames the current sheet. -* `new-sheet [name]` Creates a new sheet. If the name is provided then uses that. If omitted then uses a default sheet name. -* `select-sheet ` Select a sheet by name. -* `edit ` Edit a new spreadsheet at the current path. `e` is a shorthand alias for this command. -* `quit` Quits the application. `q` is a shorthand alias for this command. - - - -### Range Select Mode - -Range Select mode copies a range reference for use later or delete a range's contents. You can enter range -select mode from CellEdit mode with `CTRL-r`. - -* `h`, `j`, `k`, `l` will navigate around the sheet. -* `Ctrl-n`, `Ctrl-p` will navigate between sheets. -* `Ctrl-c`, `y` Copy the cell or range contents. -* `Ctrl-Shift-C`, 'Y' Copy the cell or range formatted content. -* `The spacebar will select the start and end of the range respectively. -* `d` will delete the contents of the range leaving any style untouched -* `D` will delete the contents of the range including any style - -When you have selected the end of the range you will exit range select mode and -the range reference will be placed into the cell contents you are editing. - - +* [Navigation](./navigation.md) +* [Edit](./edit.md) +* [Visual](./visual.md) +* [Command](./command.md) diff --git a/docs/intro.md b/docs/intro.md new file mode 100644 index 0000000..8ed16c5 --- /dev/null +++ b/docs/intro.md @@ -0,0 +1,23 @@ +# Intro + +## Supported formats + +Currently we only support the [ironcalc](https://docs.ironcalc.com/) xlsx +features for spreadsheet. I plan to handle csv import and export at some point. +I also might support other export formats as well but for the moment just csv +and it's variants such as tsv are in the roadmap. + +## User Interface + +The sheetui user interface is loosely inspired by vim. It is a modal interface +that is entirely keyboard driven. At nearly any time you can type `Alt-h` to +get some context sensitive help. + +## Modal Docs + +To get help on each modality in command mode `:` type + +* `help navigate` +* `help edit` +* `help command` +* `help visual` diff --git a/docs/navigation.md b/docs/navigation.md new file mode 100644 index 0000000..54ba0ea --- /dev/null +++ b/docs/navigation.md @@ -0,0 +1,50 @@ +# Navigation Mode + +The interface will start out in navigation mode. You can navigate around the +table and between the sheets using the following keybinds: + +## Cell Navigation + +* `h`, ⬆️, and `TAB` will move one cell to the left. +* `l` and, ➡️ will move one cell to the right. +* `j`, ⬇️, and `Enter` will move one cell down. +* `k` ⬆️, will move one cell up. +* `d` will delete the contents of the selected cell leaving style untouched +* `D` will delete the contents of the selected cell including any style +* `gg` will go to the top row in the current column + +## Sheet Navigation + +* `Ctrl-n` moves to the next sheet +* `Ctrl-p` moves to the prev sheet + +Sheet navigation moving will loop around when you reach the ends. + +## Numeric prefixes + +You can prefix each of the keybinds above with a numeric prefix to do them that +many times. So typing `123h` will move to the left 123 times. Hitting `Esc` +will clear the numeric prefix if you want to cancel it. + +**Modifying the Sheet or Cells** + +* `e` or `i` will enter CellEdit mode for the current cell. +* `Ctrl-h` will shorten the width of the column you are on. +* `Ctrl-l` will lengthen the width of the column you are on. + +## Other Keybindings + +* `Ctrl-r` will enter range selection mode. +* `v` will enter range selection mode with the start of the range already selected. +* `Ctrl-s` will save the sheet. +* `Ctrl-c`, `y` Copy the cell or range contents. +* `Ctrl-v`, `p` Paste into the sheet. +* `Ctrl-Shift-C` Copy the cell or range formatted content. +* `q` will exit the application. +* `:` will enter CommandMode. + +Range selections made from navigation mode will be available to paste into a Cell Edit. + + + diff --git a/docs/visual.md b/docs/visual.md new file mode 100644 index 0000000..1a90caa --- /dev/null +++ b/docs/visual.md @@ -0,0 +1,19 @@ +# Range Select Mode + +Range Select mode copies a range reference for use later or delete a range's contents. You can enter range +select mode from CellEdit mode with `CTRL-r`. + +* `h`, `j`, `k`, `l` will navigate around the sheet. +* `Ctrl-n`, `Ctrl-p` will navigate between sheets. +* `Ctrl-c`, `y` Copy the cell or range contents. +* `Ctrl-Shift-C`, 'Y' Copy the cell or range formatted content. +* `The spacebar will select the start and end of the range respectively. +* `d` will delete the contents of the range leaving any style untouched +* `D` will delete the contents of the range including any style + +When you have selected the end of the range you will exit range select mode and +the range reference will be placed into the cell contents you are editing. + + diff --git a/result b/result index eacebb1..774cfe9 120000 --- a/result +++ b/result @@ -1 +1 @@ -/nix/store/k826wsv9zc73jamdff1yl1rky2bw9lc6-sheetui-0.1.0 \ No newline at end of file +/nix/store/mvnsm5ndvx4psah2d3y6yd2vwkypy9m7-sheetui-0.1.0 \ No newline at end of file diff --git a/src/ui/help/mod.rs b/src/ui/help/mod.rs new file mode 100644 index 0000000..898253c --- /dev/null +++ b/src/ui/help/mod.rs @@ -0,0 +1,12 @@ +use ratatui::text::Text; +use tui_markdown; + +pub fn render_topic(topic: &str) -> Text<'static> { + match topic { + "navigate" => tui_markdown::from_str(include_str!("../../../docs/navigation.md")), + "edit" => tui_markdown::from_str(include_str!("../../../docs/edit.md")), + "command" => tui_markdown::from_str(include_str!("../../../docs/command.md")), + "visual" => tui_markdown::from_str(include_str!("../../../docs/visual.md")), + _ => tui_markdown::from_str(include_str!("../../../docs/intro.md")), + } +} diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 0f7f110..662426d 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -7,14 +7,12 @@ use anyhow::{anyhow, Result}; use crossterm::event::{self, Event, KeyCode, KeyEventKind, KeyModifiers}; use ironcalc::base::{expressions::types::Area, Model}; use ratatui::{ - buffer::Buffer, - layout::{Constraint, Flex, Layout}, - style::{Modifier, Style}, - widgets::Block, + buffer::Buffer, layout::{Constraint, Flex, Layout}, style::{Modifier, Style}, text::{Line, Text}, widgets::Block }; use tui_prompts::{State, Status, TextPrompt, TextState}; use tui_textarea::{CursorMove, TextArea}; +mod help; mod cmd; pub mod render; #[cfg(test)] @@ -81,7 +79,7 @@ pub struct AppState<'ws> { pub char_queue: Vec, pub range_select: RangeSelection, dirty: bool, - popup: Vec, + popup: Text<'ws>, clipboard: Option, } @@ -296,55 +294,16 @@ impl<'ws> Workspace<'ws> { Ok(None) } - fn render_help_text(&self) -> Vec { + fn render_help_text(&self) -> Text<'static> { // TODO(zaphar): We should be sourcing these from our actual help documentation. // Ideally we would also render the markdown content properly. // https://github.com/zaphar/sheetsui/issues/22 match self.state.modality() { - Modality::Navigate => vec![ - "Navigate Mode:".to_string(), - "* e,i: Enter edit mode for current cell".to_string(), - "* ENTER/RETURN: Go down one cell".to_string(), - "* TAB: Go over one cell".to_string(), - "* h,j,k,l: vim style navigation".to_string(), - "* d: clear cell contents leaving style untouched".to_string(), - "* D: clear cell contents including style".to_string(), - "* CTRl-r: Add a row".to_string(), - "* CTRl-c: Add a column".to_string(), - "* CTRl-l: Grow column width by 1".to_string(), - "* CTRl-h: Shrink column width by 1".to_string(), - "* CTRl-n: Next sheet. Starts over at beginning if at end.".to_string(), - "* CTRl-p: Previous sheet. Starts over at end if at beginning.".to_string(), - "* ALT-h: Previous sheet. Starts over at end if at beginning.".to_string(), - "* q exit".to_string(), - "* Ctrl-S Save sheet".to_string(), - ], - Modality::CellEdit => vec![ - "Edit Mode:".to_string(), - "* ENTER/RETURN: Exit edit mode and save changes".to_string(), - "* Ctrl-r: Enter Range Selection mode".to_string(), - "* v: Enter Range Selection mode with the start of the range already selected" - .to_string(), - "* ESC: Exit edit mode and discard changes".to_string(), - "Otherwise edit as normal".to_string(), - ], - Modality::Command => vec![ - "Command Mode:".to_string(), - "* ESC: Exit command mode".to_string(), - "* CTRL-?: Exit command mode".to_string(), - "* ENTER/RETURN: run command and exit command mode".to_string(), - ], - Modality::RangeSelect => vec![ - "Range Selection Mode:".to_string(), - "* ESC: Exit command mode".to_string(), - "* h,j,k,l: vim style navigation".to_string(), - "* d: delete the contents of the range leaving style untouched".to_string(), - "* D: clear cell contents including style".to_string(), - "* Spacebar: Select start and end of range".to_string(), - "* CTRl-n: Next sheet. Starts over at beginning if at end.".to_string(), - "* CTRl-p: Previous sheet. Starts over at end if at beginning.".to_string(), - ], - _ => vec!["General help".to_string()], + Modality::Navigate => help::render_topic("navigate"), + Modality::CellEdit => help::render_topic("edit"), + Modality::Command => help::render_topic("command"), + Modality::RangeSelect => help::render_topic("visual"), + _ => help::render_topic(""), } } @@ -424,8 +383,8 @@ impl<'ws> Workspace<'ws> { self.load_into(path)?; Ok(None) } - Ok(Some(Cmd::Help(_maybe_topic))) => { - self.enter_dialog_mode(vec!["TODO help topic".to_owned()]); + Ok(Some(Cmd::Help(maybe_topic))) => { + self.enter_dialog_mode(help::render_topic(maybe_topic.unwrap_or(""))); Ok(None) } Ok(Some(Cmd::Write(maybe_path))) => { @@ -515,11 +474,11 @@ impl<'ws> Workspace<'ws> { Ok(None) } Ok(None) => { - self.enter_dialog_mode(vec![format!("Unrecognized commmand {}", cmd_text)]); + self.enter_dialog_mode(vec![Line::from(format!("Unrecognized commmand {}", cmd_text))]); Ok(None) } Err(msg) => { - self.enter_dialog_mode(vec![msg.to_owned()]); + self.enter_dialog_mode(vec![Line::from(msg.to_owned())]); Ok(None) } } @@ -931,8 +890,8 @@ impl<'ws> Workspace<'ws> { self.state.command_state.focus(); } - fn enter_dialog_mode(&mut self, msg: Vec) { - self.state.popup = msg; + fn enter_dialog_mode>>(&mut self, msg: T) { + self.state.popup = msg.into(); self.state.modality_stack.push(Modality::Dialog); }