Skip to content

Commit

Permalink
Merge branch 'main' into feature-color-themes
Browse files Browse the repository at this point in the history
  • Loading branch information
adaschma committed Sep 13, 2024
2 parents 5502fcd + b20afdc commit af96157
Show file tree
Hide file tree
Showing 10 changed files with 142 additions and 42 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,8 @@ jobs:
- name: Tests
run: cargo llvm-cov nextest --all ${{ matrix.flags }} --lcov --output-path lcov.info

- name: Check lockfile
run: cargo check --locked ${{ matrix.flags }} --all-targets --all

- name: Doctests
run: cargo test --doc ${{ matrix.flags }}
29 changes: 18 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ license = "MIT"
name = "reedline"
repository = "https://github.com/nushell/reedline"
rust-version = "1.63.0"
version = "0.32.0"
version = "0.34.0"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
Expand All @@ -21,7 +21,7 @@ chrono = { version = "0.4.19", default-features = false, features = [
"serde",
] }
crossbeam = { version = "0.8.2", optional = true }
crossterm = { version = "0.27.0", features = ["serde"] }
crossterm = { version = "0.28.1", features = ["serde"] }
fd-lock = "4.0.2"
itertools = "0.12.0"
nu-ansi-term = "0.50.0"
Expand Down
6 changes: 0 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,12 +234,6 @@ Reedline has now all the basic features to become the primary line editor for [n

For more ideas check out the [feature discussion](https://github.com/nushell/reedline/issues/63) or hop on the `#reedline` channel of the [nushell discord](https://discordapp.com/invite/NtAbbGn).

### Development history

If you want to follow along with the history of how reedline got started, you can watch the [recordings](https://youtube.com/playlist?list=PLP2yfE2-FXdQw0I6O4YdIX_mzBeF5TDdv) of [JT](https://github.com/jntrnr)'s [live-coding streams](https://www.twitch.tv/jntrnr).

[Playlist: Creating a line editor in Rust](https://youtube.com/playlist?list=PLP2yfE2-FXdQw0I6O4YdIX_mzBeF5TDdv)

### Alternatives

For currently more mature Rust line editing check out:
Expand Down
6 changes: 3 additions & 3 deletions src/edit_mode/vi/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,10 +212,10 @@ impl Command {
},
Self::Change => {
let op = match motion {
Motion::End => Some(vec![ReedlineOption::Edit(EditCommand::ClearToLineEnd)]),
Motion::End => Some(vec![ReedlineOption::Edit(EditCommand::CutToLineEnd)]),
Motion::Line => Some(vec![
ReedlineOption::Edit(EditCommand::MoveToStart { select: false }),
ReedlineOption::Edit(EditCommand::ClearToLineEnd),
ReedlineOption::Edit(EditCommand::MoveToLineStart { select: false }),
ReedlineOption::Edit(EditCommand::CutToLineEnd),
]),
Motion::NextWord => Some(vec![ReedlineOption::Edit(EditCommand::CutWordRight)]),
Motion::NextBigWord => {
Expand Down
64 changes: 64 additions & 0 deletions src/edit_mode/vi/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,34 @@ mod tests {
#[case(&['d', 'e'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutWordRight])]))]
#[case(&['d', 'b'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutWordLeft])]))]
#[case(&['d', 'B'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutBigWordLeft])]))]
#[case(&['c', 'c'], ReedlineEvent::Multiple(vec![
ReedlineEvent::Edit(vec![EditCommand::MoveToLineStart { select: false }]), ReedlineEvent::Edit(vec![EditCommand::CutToLineEnd]), ReedlineEvent::Repaint]))]
#[case(&['c', 'w'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutWordRight]), ReedlineEvent::Repaint]))]
#[case(&['c', 'W'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutBigWordRight]), ReedlineEvent::Repaint]))]
#[case(&['c', 'e'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutWordRight]), ReedlineEvent::Repaint]))]
#[case(&['c', 'b'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutWordLeft]), ReedlineEvent::Repaint]))]
#[case(&['c', 'B'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutBigWordLeft]), ReedlineEvent::Repaint]))]
#[case(&['d', 'h'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::Backspace])]))]
#[case(&['d', 'l'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::Delete])]))]
#[case(&['2', 'd', 'd'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutCurrentLine]), ReedlineEvent::Edit(vec![EditCommand::CutCurrentLine])]))]
// #[case(&['d', 'j'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutCurrentLine]), ReedlineEvent::Edit(vec![EditCommand::CutCurrentLine])]))]
// #[case(&['d', 'k'], ReedlineEvent::Multiple(vec![ReedlineEvent::Up, ReedlineEvent::Edit(vec![EditCommand::CutCurrentLine]), ReedlineEvent::Edit(vec![EditCommand::CutCurrentLine])]))]
#[case(&['d', 'E'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutBigWordRight])]))]
#[case(&['d', '0'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutFromLineStart])]))]
#[case(&['d', '^'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutFromLineStart])]))]
#[case(&['d', '$'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutToLineEnd])]))]
#[case(&['d', 'f', 'a'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutRightUntil('a')])]))]
#[case(&['d', 't', 'a'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutRightBefore('a')])]))]
#[case(&['d', 'F', 'a'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutLeftUntil('a')])]))]
#[case(&['d', 'T', 'a'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutLeftBefore('a')])]))]
#[case(&['c', 'E'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutBigWordRight]), ReedlineEvent::Repaint]))]
#[case(&['c', '0'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutFromLineStart]), ReedlineEvent::Repaint]))]
#[case(&['c', '^'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutFromLineStart]), ReedlineEvent::Repaint]))]
#[case(&['c', '$'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutToLineEnd]), ReedlineEvent::Repaint]))]
#[case(&['c', 'f', 'a'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutRightUntil('a')]), ReedlineEvent::Repaint]))]
#[case(&['c', 't', 'a'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutRightBefore('a')]), ReedlineEvent::Repaint]))]
#[case(&['c', 'F', 'a'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutLeftUntil('a')]), ReedlineEvent::Repaint]))]
#[case(&['c', 'T', 'a'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutLeftBefore('a')]), ReedlineEvent::Repaint]))]
fn test_reedline_move(#[case] input: &[char], #[case] expected: ReedlineEvent) {
let mut vi = Vi::default();
let res = vi_parse(input);
Expand All @@ -518,6 +546,42 @@ mod tests {
assert_eq!(output, expected);
}

#[rstest]
#[case(&['f', 'a'], &[';'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::MoveRightUntil{c: 'a',select: false}])]))]
#[case(&['f', 'a'], &[','], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::MoveLeftUntil{c: 'a', select: false}])]))]
#[case(&['F', 'a'], &[','], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::MoveRightUntil{c: 'a', select: false}])]))]
#[case(&['F', 'a'], &[';'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::MoveLeftUntil{c: 'a', select: false}])]))]
#[case(&['f', 'a'], &['d', ';'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutRightUntil('a')])]))]
#[case(&['f', 'a'], &['d', ','], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutLeftUntil('a')])]))]
#[case(&['F', 'a'], &['d', ','], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutRightUntil('a')])]))]
#[case(&['F', 'a'], &['d', ';'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutLeftUntil('a')])]))]
#[case(&['f', 'a'], &['c', ';'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutRightUntil('a')]), ReedlineEvent::Repaint]))]
#[case(&['f', 'a'], &['c', ','], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutLeftUntil('a')]), ReedlineEvent::Repaint]))]
#[case(&['F', 'a'], &['c', ','], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutRightUntil('a')]), ReedlineEvent::Repaint]))]
#[case(&['F', 'a'], &['c', ';'], ReedlineEvent::Multiple(vec![ReedlineEvent::Edit(vec![EditCommand::CutLeftUntil('a')]), ReedlineEvent::Repaint]))]
fn test_reedline_memory_move(
#[case] before: &[char],
#[case] now: &[char],
#[case] expected: ReedlineEvent,
) {
let mut vi = Vi::default();
let _ = vi_parse(before).to_reedline_event(&mut vi);
let output = vi_parse(now).to_reedline_event(&mut vi);

assert_eq!(output, expected);
}

#[rstest]
#[case(&['c', 'w'], &['c', 'e'])]
#[case(&['c', 'W'], &['c', 'E'])]
fn test_reedline_move_synonm(#[case] synonym: &[char], #[case] original: &[char]) {
let mut vi = Vi::default();
let output = vi_parse(synonym).to_reedline_event(&mut vi);
let expected = vi_parse(original).to_reedline_event(&mut vi);

assert_eq!(output, expected);
}

#[rstest]
#[case(&['2', 'k'], ReedlineEvent::Multiple(vec![ReedlineEvent::UntilFound(vec![
ReedlineEvent::MenuUp,
Expand Down
4 changes: 2 additions & 2 deletions src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1706,10 +1706,10 @@ impl Reedline {
match &mut self.buffer_editor {
Some(BufferEditor {
ref mut command,
temp_file,
ref temp_file,
}) => {
{
let mut file = File::create(&temp_file)?;
let mut file = File::create(temp_file)?;
write!(file, "{}", self.editor.get_buffer())?;
}
{
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@
//!
//! ## Are we prompt yet? (Development status)
//!
//! Nushell has now all the basic features to become the primary line editor for [nushell](https://github.com/nushell/nushell
//! Reedline has now all the basic features to become the primary line editor for [nushell](https://github.com/nushell/nushell
//! )
//!
//! - General editing functionality, that should feel familiar coming from other shells (e.g. bash, fish, zsh).
Expand Down
47 changes: 42 additions & 5 deletions src/menu/ide_menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,11 @@ impl IdeMenu {
};

if use_ansi_coloring {
let match_len = self.working_details.shortest_base_string.len();
let match_len = self
.working_details
.shortest_base_string
.len()
.min(string.len());

// Split string so the match text can be styled
let (match_str, remaining_str) = string.split_at(match_len);
Expand Down Expand Up @@ -1054,10 +1058,7 @@ fn truncate_string_list(list: &mut [String], truncation_chars: &str) {
let mut new_line = String::new();
for grapheme in chars.into_iter().rev() {
if to_replace > 0 {
new_line.insert_str(
0,
&truncation_chars[truncation_len - to_replace].to_string(),
);
new_line.insert(0, truncation_chars[truncation_len - to_replace]);
to_replace -= 1;
} else {
new_line.insert_str(0, grapheme);
Expand Down Expand Up @@ -1404,4 +1405,40 @@ mod tests {
"cursor should be at the end after completion"
);
}

#[test]
fn test_regression_panic_on_long_item() {
let commands = vec![
"hello world 2".into(),
"hello another very large option for hello word that will force one column".into(),
"this is the reedline crate".into(),
"abaaabas".into(),
"abaaacas".into(),
];

let mut completer = Box::new(crate::DefaultCompleter::new_with_wordlen(commands, 2));

let mut menu = IdeMenu::default().with_name("testmenu");
menu.working_details = IdeMenuDetails {
cursor_col: 50,
menu_width: 50,
completion_width: 50,
description_width: 50,
description_is_right: true,
space_left: 50,
space_right: 50,
description_offset: 50,
shortest_base_string: String::new(),
};
let mut editor = Editor::default();
// backtick at the end of the line
editor.set_buffer(
"hello another very large option for hello word that will force one colu".to_string(),
UndoBehavior::CreateUndoPoint,
);

menu.update_values(&mut editor, &mut *completer);

menu.menu_string(500, true);
}
}
19 changes: 7 additions & 12 deletions src/painting/painter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -500,23 +500,18 @@ impl Painter {
/// Clear the screen by printing enough whitespace to start the prompt or
/// other output back at the first line of the terminal.
pub(crate) fn clear_screen(&mut self) -> Result<()> {
self.stdout.queue(cursor::Hide)?;
let (_, num_lines) = terminal::size()?;
for _ in 0..2 * num_lines {
self.stdout.queue(Print("\n"))?;
}
self.stdout.queue(MoveTo(0, 0))?;
self.stdout.queue(cursor::Show)?;

self.stdout.flush()?;
self.stdout
.queue(Clear(ClearType::All))?
.queue(MoveTo(0, 0))?
.flush()?;
self.initialize_prompt_position(None)
}

pub(crate) fn clear_scrollback(&mut self) -> Result<()> {
self.stdout
.queue(crossterm::terminal::Clear(ClearType::All))?
.queue(crossterm::terminal::Clear(ClearType::Purge))?
.queue(cursor::MoveTo(0, 0))?
.queue(Clear(ClearType::All))?
.queue(Clear(ClearType::Purge))?
.queue(MoveTo(0, 0))?
.flush()?;
self.initialize_prompt_position(None)
}
Expand Down

0 comments on commit af96157

Please sign in to comment.