Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow editing any Nushell Value #56

Merged
merged 4 commits into from
Apr 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@ name = "nu_plugin_explore"
anyhow = "1.0.73"
console = "0.15.7"
crossterm = "0.27.0"
nuon = { git = "https://github.com/nushell/nushell", rev = "55edef5ddaf3d3d55290863446c2dd50c012e9bc", package = "nuon" }
nu-plugin = { git = "https://github.com/nushell/nushell", rev = "55edef5ddaf3d3d55290863446c2dd50c012e9bc", package = "nu-plugin" }
nu-protocol = { git = "https://github.com/nushell/nushell", rev = "55edef5ddaf3d3d55290863446c2dd50c012e9bc", package = "nu-protocol", features = ["plugin"] }
ratatui = "0.26.1"
url = "2.4.0"

[dev-dependencies]
nuon = { git = "https://github.com/nushell/nushell", rev = "55edef5ddaf3d3d55290863446c2dd50c012e9bc", package = "nuon" }

[target.'cfg(target_os = "macos")'.dependencies]
crossterm = { version = "0.27.0", features = ["use-dev-tty"] }

Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,10 @@ in order to help, you can have a look at
- [x] show true tables as such
- [x] get the config from `$env.config` => can parse configuration from CLI
- [x] add check for the config to make sure it's valid
- [ ] support for editing cells in INSERT mode
- [x] support for editing cells in INSERT mode
- [x] string cells
- [ ] other simple cells
- [x] other simple cells
- [x] all the cells
- [x] detect if a string is of a particular type, path, URL, ...

## internal
Expand Down
16 changes: 3 additions & 13 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,21 +105,11 @@ impl App {
self.mode = Mode::Bottom;
}

pub(super) fn enter_editor(&mut self) -> Result<(), String> {
pub(super) fn enter_editor(&mut self) {
let value = self.value_under_cursor(None);

if matches!(value, Value::String { .. }) {
self.mode = Mode::Insert;
self.editor = Editor::from_value(&value);

Ok(())
} else {
// TODO: support more diverse cell edition
Err(format!(
"can only edit string cells, found {}",
value.get_type()
))
}
self.mode = Mode::Insert;
self.editor = Editor::from_value(&value);
}

pub(crate) fn value_under_cursor(&self, alternate_cursor: Option<CellPath>) -> Value {
Expand Down
245 changes: 187 additions & 58 deletions src/edit.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crossterm::event::KeyCode;
use nuon::{from_nuon, to_nuon};
use ratatui::{
prelude::Rect,
style::Style,
Expand All @@ -17,6 +18,13 @@ pub struct Editor {
width: usize,
}

#[derive(Debug, PartialEq)]
pub enum EditorTransition {
Continue,
Quit,
Value(Value),
}

impl Editor {
/// set the width of the editor
///
Expand All @@ -27,7 +35,8 @@ impl Editor {

pub(super) fn from_value(value: &Value) -> Self {
Self {
buffer: value.to_expanded_string(" ", &nu_protocol::Config::default()),
// NOTE: `value` should be a valid [`Value`] and thus the conversion should never fail
buffer: to_nuon(value, true, None, None, None).unwrap(),
cursor_position: (0, 0),
width: 0,
}
Expand Down Expand Up @@ -107,7 +116,7 @@ impl Editor {
self.delete_char(0);
}

pub(super) fn handle_key(&mut self, key: &KeyCode) -> Option<Option<Value>> {
pub(super) fn handle_key(&mut self, key: &KeyCode) -> Result<EditorTransition, String> {
match key {
KeyCode::Left => self.move_cursor_left(),
KeyCode::Right => self.move_cursor_right(),
Expand All @@ -116,15 +125,15 @@ impl Editor {
KeyCode::Char(c) => self.enter_char(*c),
KeyCode::Backspace => self.delete_char_before_cursor(),
KeyCode::Delete => self.delete_char_under_cursor(),
KeyCode::Enter => {
let val = Value::string(self.buffer.clone(), Span::unknown());
return Some(Some(val));
}
KeyCode::Esc => return Some(None),
KeyCode::Enter => match from_nuon(&self.buffer, Some(Span::unknown())) {
Ok(val) => return Ok(EditorTransition::Value(val)),
Err(err) => return Err(format!("could not convert back from NUON: {}", err)),
},
KeyCode::Esc => return Ok(EditorTransition::Quit),
_ => {}
}

None
Ok(EditorTransition::Continue)
}

pub(super) fn render(&self, frame: &mut Frame, config: &Config) {
Expand Down Expand Up @@ -171,76 +180,196 @@ mod tests {
use crossterm::event::KeyCode;
use nu_protocol::Value;

use super::Editor;
use super::{Editor, EditorTransition};

#[test]
fn edit_cells() {
let mut editor = Editor::default();
editor.set_width(10 + 2);
editor.buffer = r#""""#.to_string();

// NOTE: for the NUON conversion to work, the test string buffer needs to be wrapped in
// parentheses.
// in order not to make the strokes clunky, the quotes are added in the for loop below and
// are implicite in the strokes below.
let strokes = vec![
(KeyCode::Enter, "", Some(Some(Value::test_string("")))),
(KeyCode::Char('a'), "a", None),
(KeyCode::Char('b'), "ab", None),
(KeyCode::Char('c'), "abc", None),
(KeyCode::Char('d'), "abcd", None),
(KeyCode::Char('e'), "abcde", None),
(KeyCode::Left, "abcde", None),
(KeyCode::Char('f'), "abcdfe", None),
(KeyCode::Left, "abcdfe", None),
(KeyCode::Left, "abcdfe", None),
(KeyCode::Char('g'), "abcgdfe", None),
(KeyCode::Right, "abcgdfe", None),
(KeyCode::Right, "abcgdfe", None),
(KeyCode::Right, "abcgdfe", None),
(KeyCode::Up, "abcgdfe", None),
(KeyCode::Down, "abcgdfe", None),
(KeyCode::Char('h'), "abcgdfeh", None),
(KeyCode::Char('i'), "abcgdfehi", None),
(KeyCode::Char('j'), "abcgdfehij", None),
(KeyCode::Char('k'), "abcgdfehijk", None),
(KeyCode::Char('l'), "abcgdfehijkl", None),
(KeyCode::Up, "abcgdfehijkl", None),
(KeyCode::Char('m'), "abmcgdfehijkl", None),
(KeyCode::Down, "abmcgdfehijkl", None),
(KeyCode::Left, "abmcgdfehijkl", None),
(KeyCode::Char('n'), "abmcgdfehijknl", None),
(KeyCode::Left, "abmcgdfehijknl", None),
(KeyCode::Left, "abmcgdfehijknl", None),
(KeyCode::Left, "abmcgdfehijknl", None),
(KeyCode::Left, "abmcgdfehijknl", None),
(KeyCode::Left, "abmcgdfehijknl", None),
(KeyCode::Char('o'), "abmcgdfeohijknl", None),
(KeyCode::Right, "abmcgdfeohijknl", None),
(KeyCode::Right, "abmcgdfeohijknl", None),
(
KeyCode::Enter,
"",
Ok(EditorTransition::Value(Value::test_string(""))),
),
(KeyCode::Right, "", Ok(EditorTransition::Continue)),
(KeyCode::Char('a'), "a", Ok(EditorTransition::Continue)),
(KeyCode::Char('b'), "ab", Ok(EditorTransition::Continue)),
(KeyCode::Char('c'), "abc", Ok(EditorTransition::Continue)),
(KeyCode::Char('d'), "abcd", Ok(EditorTransition::Continue)),
(KeyCode::Char('e'), "abcde", Ok(EditorTransition::Continue)),
(KeyCode::Left, "abcde", Ok(EditorTransition::Continue)),
(KeyCode::Char('f'), "abcdfe", Ok(EditorTransition::Continue)),
(KeyCode::Left, "abcdfe", Ok(EditorTransition::Continue)),
(KeyCode::Left, "abcdfe", Ok(EditorTransition::Continue)),
(
KeyCode::Char('g'),
"abcgdfe",
Ok(EditorTransition::Continue),
),
(KeyCode::Right, "abcgdfe", Ok(EditorTransition::Continue)),
(KeyCode::Right, "abcgdfe", Ok(EditorTransition::Continue)),
(KeyCode::Right, "abcgdfe", Ok(EditorTransition::Continue)),
(KeyCode::Up, "abcgdfe", Ok(EditorTransition::Continue)),
(KeyCode::Down, "abcgdfe", Ok(EditorTransition::Continue)),
(
KeyCode::Char('h'),
"abcgdfeh",
Ok(EditorTransition::Continue),
),
(
KeyCode::Char('i'),
"abcgdfehi",
Ok(EditorTransition::Continue),
),
(
KeyCode::Char('j'),
"abcgdfehij",
Ok(EditorTransition::Continue),
),
(
KeyCode::Char('k'),
"abcgdfehijk",
Ok(EditorTransition::Continue),
),
(
KeyCode::Char('l'),
"abcgdfehijkl",
Ok(EditorTransition::Continue),
),
(KeyCode::Up, "abcgdfehijkl", Ok(EditorTransition::Continue)),
(
KeyCode::Char('m'),
"abmcgdfehijkl",
Ok(EditorTransition::Continue),
),
(
KeyCode::Down,
"abmcgdfehijkl",
Ok(EditorTransition::Continue),
),
(
KeyCode::Left,
"abmcgdfehijkl",
Ok(EditorTransition::Continue),
),
(
KeyCode::Char('n'),
"abmcgdfehijknl",
Ok(EditorTransition::Continue),
),
(
KeyCode::Left,
"abmcgdfehijknl",
Ok(EditorTransition::Continue),
),
(
KeyCode::Left,
"abmcgdfehijknl",
Ok(EditorTransition::Continue),
),
(
KeyCode::Left,
"abmcgdfehijknl",
Ok(EditorTransition::Continue),
),
(
KeyCode::Left,
"abmcgdfehijknl",
Ok(EditorTransition::Continue),
),
(
KeyCode::Left,
"abmcgdfehijknl",
Ok(EditorTransition::Continue),
),
(
KeyCode::Char('o'),
"abmcgdfeohijknl",
Some(Some(Value::test_string("abmcgdfeohijknl"))),
),
(KeyCode::Right, "abmcgdfeohijknl", None),
(KeyCode::Right, "abmcgdfeohijknl", None),
(KeyCode::Char('p'), "abmcgdfeohijkpnl", None),
(KeyCode::Backspace, "abmcgdfeohijknl", None),
(KeyCode::Backspace, "abmcgdfeohijnl", None),
(KeyCode::Backspace, "abmcgdfeohinl", None),
(KeyCode::Up, "abmcgdfeohinl", None),
(KeyCode::Delete, "amcgdfeohinl", None),
(KeyCode::Delete, "acgdfeohinl", None),
(KeyCode::Delete, "agdfeohinl", None),
(KeyCode::Esc, "agdfeohinl", Some(None)),
Ok(EditorTransition::Continue),
),
(
KeyCode::Right,
"abmcgdfeohijknl",
Ok(EditorTransition::Continue),
),
(
KeyCode::Right,
"abmcgdfeohijknl",
Ok(EditorTransition::Continue),
),
(
KeyCode::Enter,
"abmcgdfeohijknl",
Ok(EditorTransition::Value(Value::test_string(
"abmcgdfeohijknl",
))),
),
(
KeyCode::Right,
"abmcgdfeohijknl",
Ok(EditorTransition::Continue),
),
(
KeyCode::Right,
"abmcgdfeohijknl",
Ok(EditorTransition::Continue),
),
(
KeyCode::Char('p'),
"abmcgdfeohijkpnl",
Ok(EditorTransition::Continue),
),
(
KeyCode::Backspace,
"abmcgdfeohijknl",
Ok(EditorTransition::Continue),
),
(
KeyCode::Backspace,
"abmcgdfeohijnl",
Ok(EditorTransition::Continue),
),
(
KeyCode::Backspace,
"abmcgdfeohinl",
Ok(EditorTransition::Continue),
),
(KeyCode::Up, "abmcgdfeohinl", Ok(EditorTransition::Continue)),
(
KeyCode::Delete,
"amcgdfeohinl",
Ok(EditorTransition::Continue),
),
(
KeyCode::Delete,
"acgdfeohinl",
Ok(EditorTransition::Continue),
),
(
KeyCode::Delete,
"agdfeohinl",
Ok(EditorTransition::Continue),
),
(KeyCode::Esc, "agdfeohinl", Ok(EditorTransition::Quit)),
(
KeyCode::Enter,
"agdfeohinl",
Some(Some(Value::test_string("agdfeohinl"))),
Ok(EditorTransition::Value(Value::test_string("agdfeohinl"))),
),
];

for (key, expected_buffer, expected) in strokes {
let result = editor.handle_key(&key);

assert_eq!(result, expected);
assert_eq!(editor.buffer, expected_buffer.to_string());
assert_eq!(editor.buffer, format!(r#""{}""#, expected_buffer));
}
}
}
Loading
Loading