Skip to content

Commit 92b36f6

Browse files
committed
sh: fix searching command history in vi mode
1 parent 0fffea1 commit 92b36f6

File tree

2 files changed

+63
-66
lines changed

2 files changed

+63
-66
lines changed

sh/src/cli/vi/mod.rs

Lines changed: 59 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use crate::cli::vi::cursor::{Cursor, MotionCommand, MotionError};
1414
use crate::cli::vi::word::{current_bigword, BigWordIter};
1515
use crate::parse::word_parser::parse_word;
1616
use crate::pattern::{FilenamePattern, HistoryPattern};
17+
use crate::shell::history::History;
1718
use crate::shell::Shell;
1819
use crate::wordexp::expand_word;
1920
use crate::wordexp::pathname::glob;
@@ -297,23 +298,57 @@ pub struct ViEditor {
297298
/// 0 means the `edit_line`, from 1 on
298299
/// its an index into the history starting
299300
/// from the most recent command
300-
current_command_in_history: usize,
301+
current_history_command: usize,
301302
}
302303

303304
impl ViEditor {
304305
fn edit_current_line(&mut self, shell: &Shell) -> &mut Vec<u8> {
305-
if self.current_command_in_history != 0 {
306+
if self.current_history_command != 0 {
306307
self.edit_line = shell
307308
.history
308-
.get_reverse(self.current_command_in_history - 1)
309+
.get_reverse(self.current_history_command - 1)
309310
.unwrap()
310311
.as_bytes()
311312
.to_vec();
312-
self.current_command_in_history = 0;
313+
self.current_history_command = 0;
313314
}
314315
&mut self.edit_line
315316
}
316317

318+
fn set_current_history_command(&mut self, index: usize) {
319+
if self.current_history_command == 0 {
320+
self.saved_edit_line_position = self.cursor.position;
321+
}
322+
self.current_history_command = index;
323+
self.cursor.position = 0;
324+
}
325+
326+
fn find_in_history(
327+
&self,
328+
history: &History,
329+
pattern: &HistoryPattern,
330+
reverse: bool,
331+
) -> Option<usize> {
332+
if reverse {
333+
if self.current_history_command == 0 {
334+
return None;
335+
}
336+
let skipped = history.entries_count() - self.current_history_command + 1;
337+
history
338+
.entries()
339+
.skip(skipped)
340+
.position(|e| pattern.matches(&e.command))
341+
.map(|i| history.entries_count() - i - skipped)
342+
} else {
343+
history
344+
.entries()
345+
.rev()
346+
.skip(self.current_history_command)
347+
.position(|e| pattern.matches(&e.command))
348+
.map(|i| self.current_history_command + i + 1)
349+
}
350+
}
351+
317352
fn execute_command(
318353
&mut self,
319354
command: Command,
@@ -333,7 +368,7 @@ impl ViEditor {
333368
result.push(b'\n');
334369
self.mode = EditorMode::Insert;
335370
self.cursor.position = 0;
336-
self.current_command_in_history = 0;
371+
self.current_history_command = 0;
337372
self.edit_line.clear();
338373
return Ok(Action::Execute(result));
339374
}
@@ -630,59 +665,50 @@ impl ViEditor {
630665
CommandOp::UndoLastCommand => {}
631666
CommandOp::UndoAll => {}
632667
CommandOp::PreviousShellCommand => {
633-
if self.current_command_in_history == 0 {
668+
if self.current_history_command == 0 {
634669
self.saved_edit_line_position = self.cursor.position;
635670
self.cursor.position = 0;
636671
}
637672
let number = command.count.unwrap_or(1);
638-
if self.current_command_in_history + number > shell.history.entries_count() {
673+
if self.current_history_command + number > shell.history.entries_count() {
639674
return Err(CommandError);
640675
}
641-
self.current_command_in_history += number;
676+
self.current_history_command += number;
642677
self.cursor.position = 0;
643678
}
644679
CommandOp::NextShellCommand => {
645680
let number = command.count.unwrap_or(1);
646-
if number > self.current_command_in_history {
647-
self.current_command_in_history = 0;
681+
if number > self.current_history_command {
682+
self.current_history_command = 0;
648683
return Err(CommandError);
649684
}
650-
self.current_command_in_history -= number;
651-
if self.current_command_in_history == 0 {
685+
self.current_history_command -= number;
686+
if self.current_history_command == 0 {
652687
self.cursor.position = self.saved_edit_line_position;
653688
} else {
654689
self.cursor.position = 0;
655690
}
656691
}
657692
CommandOp::OldestShellCommand => {
658693
let number = command.count.unwrap_or(shell.history.entries_count());
659-
if self.current_command_in_history == 0 {
694+
if self.current_history_command == 0 {
660695
self.saved_edit_line_position = self.cursor.position;
661696
}
662-
if number > self.current_command_in_history {
697+
if number > self.current_history_command {
663698
return Err(CommandError);
664699
}
665-
self.current_command_in_history = number;
700+
self.current_history_command = number;
666701
self.cursor.position = 0;
667702
}
668703
CommandOp::SearchPattern { pattern, reverse } => {
669704
let history_pattern = HistoryPattern::new(pattern).map_err(|_| CommandError)?;
670-
let result = shell.history.find_pattern(
671-
&history_pattern,
672-
self.current_command_in_history,
673-
reverse,
674-
);
705+
let result = self.find_in_history(&shell.history, &history_pattern, reverse);
675706
self.last_search = Some(LastSearch {
676707
pattern: history_pattern,
677708
reverse,
678709
});
679710
if let Some(index) = result {
680-
let index = index + 1;
681-
if self.current_command_in_history == 0 {
682-
self.saved_edit_line_position = self.cursor.position;
683-
}
684-
self.current_command_in_history = index;
685-
self.cursor.position = 0;
711+
self.set_current_history_command(index)
686712
} else {
687713
return Err(CommandError);
688714
}
@@ -694,34 +720,23 @@ impl ViEditor {
694720
} else {
695721
last_search.reverse
696722
};
697-
let result = shell.history.find_pattern(
698-
&last_search.pattern,
699-
self.current_command_in_history,
700-
reverse,
701-
);
702-
if let Some(index) = result {
703-
let index = index + 1;
704-
if self.current_command_in_history == 0 {
705-
self.saved_edit_line_position = self.cursor.position;
706-
}
707-
self.current_command_in_history = index;
708-
self.cursor.position = 0;
709-
} else {
710-
return Err(CommandError);
711-
}
723+
let index = self
724+
.find_in_history(&shell.history, &last_search.pattern, reverse)
725+
.ok_or(CommandError)?;
726+
self.set_current_history_command(index);
712727
}
713728
CommandOp::Move(_) => unreachable!(),
714729
}
715730
Ok(Action::None)
716731
}
717732

718733
pub fn current_line<'a>(&'a self, shell: &'a Shell) -> &'a [u8] {
719-
if self.current_command_in_history == 0 {
734+
if self.current_history_command == 0 {
720735
&self.edit_line
721736
} else {
722737
shell
723738
.history
724-
.get_reverse(self.current_command_in_history - 1)
739+
.get_reverse(self.current_history_command - 1)
725740
.unwrap()
726741
.as_bytes()
727742
}
@@ -819,7 +834,7 @@ impl Default for ViEditor {
819834
last_nonmotion_command: None,
820835
last_search: None,
821836
save_buffer: Vec::new(),
822-
current_command_in_history: 0,
837+
current_history_command: 0,
823838
}
824839
}
825840
}

sh/src/shell/history.rs

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
// SPDX-License-Identifier: MIT
88
//
99

10-
use crate::pattern::HistoryPattern;
1110
use crate::shell::environment::Environment;
1211
use std::collections::VecDeque;
1312
use std::io::ErrorKind;
@@ -38,8 +37,8 @@ impl<'s> EndPoint<'s> {
3837

3938
#[derive(Debug, Clone, PartialEq, Eq)]
4039
pub struct Entry {
41-
command: String,
42-
command_number: u32,
40+
pub command: String,
41+
pub command_number: u32,
4342
}
4443

4544
fn list_entries<'a>(entries: impl Iterator<Item = &'a Entry>, include_numbers: bool) -> String {
@@ -160,25 +159,8 @@ impl History {
160159
.map(|e| e.command.as_str())
161160
}
162161

163-
pub fn find_pattern(
164-
&self,
165-
pattern: &HistoryPattern,
166-
reverse_start: usize,
167-
reverse: bool,
168-
) -> Option<usize> {
169-
if reverse {
170-
self.entries
171-
.iter()
172-
.rev()
173-
.take(reverse_start)
174-
.position(|e| pattern.matches(&e.command))
175-
} else {
176-
self.entries
177-
.iter()
178-
.rev()
179-
.skip(reverse_start)
180-
.position(|e| pattern.matches(&e.command))
181-
}
162+
pub fn entries(&self) -> impl DoubleEndedIterator<Item = &Entry> {
163+
self.entries.iter()
182164
}
183165

184166
pub fn entries_count(&self) -> usize {

0 commit comments

Comments
 (0)