implemented 'gv' to select the previously selected visual selection

This commit is contained in:
2025-05-30 20:15:41 -04:00
parent 09767c9682
commit e7d8b98a73
5 changed files with 107 additions and 26 deletions

View File

@@ -313,7 +313,25 @@ impl History {
.append(true)
.open(&self.path)?;
let entries = self.entries.iter_mut().filter(|ent| ent.new && !ent.command.is_empty());
let last_file_entry = self.entries
.iter()
.filter(|ent| !ent.new)
.next_back()
.map(|ent| ent.command.clone())
.unwrap_or_default();
let entries = self.entries
.iter_mut()
.filter(|ent| {
ent.new &&
!ent.command.is_empty() &&
if self.ignore_dups {
ent.command() != last_file_entry
} else {
true
}
});
let mut data = String::new();
for ent in entries {
ent.new = false;

View File

@@ -32,9 +32,10 @@ pub enum MotionKind {
ScreenLine(isize)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum SelectionAnchor {
Start,
#[default]
End
}
@@ -45,6 +46,12 @@ pub enum SelectionMode {
Block(SelectionAnchor)
}
impl Default for SelectionMode {
fn default() -> Self {
Self::Char(Default::default())
}
}
impl SelectionMode {
pub fn anchor(&self) -> &SelectionAnchor {
match self {
@@ -195,6 +202,7 @@ pub struct LineBuf {
clamp_cursor: bool,
select_mode: Option<SelectionMode>,
selected_range: Option<Range<usize>>,
last_selected_range: Option<Range<usize>>,
first_line_offset: usize,
saved_col: Option<usize>,
term_dims: (usize,usize), // Height, width
@@ -220,7 +228,9 @@ impl LineBuf {
}
pub fn stop_selecting(&mut self) {
self.select_mode = None;
self.selected_range = None;
if self.selected_range().is_some() {
self.last_selected_range = self.selected_range.take();
}
}
pub fn start_selecting(&mut self, mode: SelectionMode) {
self.select_mode = Some(mode);
@@ -1506,6 +1516,16 @@ impl LineBuf {
}
}
}
Verb::VisualModeSelectLast => {
if let Some(range) = self.last_selected_range.as_ref() {
self.selected_range = Some(range.clone());
let mode = self.select_mode.unwrap_or_default();
self.cursor = match mode.anchor() {
SelectionAnchor::Start => range.start,
SelectionAnchor::End => range.end
}
}
}
Verb::SwapVisualAnchor => {
if let Some(range) = self.selected_range() {
if let Some(mut mode) = self.select_mode {
@@ -1934,10 +1954,13 @@ impl Display for LineBuf {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut full_buf = self.buffer.clone();
if let Some(range) = self.selected_range.clone() {
let mode = self.select_mode.unwrap();
let mode = self.select_mode.unwrap_or_default();
match mode.anchor() {
SelectionAnchor::Start => {
let inclusive = range.start..=range.end;
let mut inclusive = range.start..=range.end;
if *inclusive.end() == self.byte_len() {
inclusive = range.start..=range.end.saturating_sub(1);
}
let selected = full_buf[inclusive.clone()].styled(Style::BgWhite | Style::Black);
full_buf.replace_range(inclusive, &selected);
}

View File

@@ -88,11 +88,11 @@ impl Readline for FernVi {
if cmd.should_submit() {
self.term.unposition_cursor()?;
self.term.write("\n");
let command = self.line.to_string();
let command = std::mem::take(&mut self.line).pack_line();
if !command.is_empty() {
// We're just going to trim the command
// reduces clutter in the case of two history commands whose only difference is insignificant whitespace
self.history.push(command.trim().to_string());
self.history.update_pending_cmd(&command);
self.history.save()?;
}
return Ok(command);
@@ -323,6 +323,17 @@ impl FernVi {
Verb::ReplaceMode => {
Box::new(ViReplace::new().with_count(count as u16))
}
Verb::VisualModeSelectLast => {
if self.mode.report_mode() != ModeReport::Visual {
self.line.start_selecting(SelectionMode::Char(SelectionAnchor::End));
}
let mut mode: Box<dyn ViMode> = Box::new(ViVisual::new());
std::mem::swap(&mut mode, &mut self.mode);
self.line.set_cursor_clamp(self.mode.clamp_cursor());
self.line.set_move_cursor_on_undo(self.mode.move_cursor_on_undo());
self.term.write(&mode.cursor_style());
return self.line.exec_cmd(cmd)
}
Verb::VisualMode => {
selecting = true;
self.line.start_selecting(SelectionMode::Char(SelectionAnchor::End));
@@ -331,7 +342,11 @@ impl FernVi {
_ => unreachable!()
};
flog!(DEBUG, self.mode.report_mode());
flog!(DEBUG, mode.report_mode());
std::mem::swap(&mut mode, &mut self.mode);
flog!(DEBUG, self.mode.report_mode());
self.line.set_cursor_clamp(self.mode.clamp_cursor());
self.line.set_move_cursor_on_undo(self.mode.move_cursor_on_undo());
self.term.write(&mode.cursor_style());

View File

@@ -358,6 +358,25 @@ impl ViNormal {
break 'verb_parse None
};
match ch {
'g' => {
if let Some(ch) = chars_clone.peek() {
match ch {
'v' => {
return Some(
ViCmd {
register,
verb: Some(VerbCmd(1, Verb::VisualModeSelectLast)),
motion: None,
raw_seq: self.take_cmd()
}
)
}
_ => break 'verb_parse None
}
} else {
break 'verb_parse None
}
}
'.' => {
return Some(
ViCmd {
@@ -924,6 +943,25 @@ impl ViVisual {
break 'verb_parse None
};
match ch {
'g' => {
if let Some(ch) = chars_clone.peek() {
match ch {
'v' => {
return Some(
ViCmd {
register,
verb: Some(VerbCmd(1, Verb::VisualModeSelectLast)),
motion: None,
raw_seq: self.take_cmd()
}
)
}
_ => break 'verb_parse None
}
} else {
break 'verb_parse None
}
}
'.' => {
return Some(
ViCmd {

View File

@@ -110,6 +110,7 @@ impl ViCmd {
Verb::InsertMode |
Verb::InsertModeLineBreak(_) |
Verb::NormalMode |
Verb::VisualModeSelectLast |
Verb::VisualMode |
Verb::ReplaceMode
)
@@ -159,7 +160,8 @@ pub enum Verb {
NormalMode,
VisualMode,
VisualModeLine,
VisualModeBlock,
VisualModeBlock, // dont even know if im going to implement this
VisualModeSelectLast,
SwapVisualAnchor,
JoinLines,
InsertChar(char),
@@ -241,37 +243,22 @@ impl Verb {
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Motion {
/// Whole current line (not really a movement but a range)
WholeLine,
TextObj(TextObj, Bound),
BeginningOfFirstWord,
/// beginning-of-line
BeginningOfLine,
/// end-of-line
EndOfLine,
/// backward-word, vi-prev-word
BackwardWord(To, Word), // Backward until start of word
/// forward-word, vi-end-word, vi-next-word
ForwardWord(To, Word), // Forward until start/end of word
/// character-search, character-search-backward, vi-char-search
BackwardWord(To, Word),
ForwardWord(To, Word),
CharSearch(Direction,Dest,char),
/// backward-char
BackwardChar,
/// forward-char
ForwardChar,
/// move to the same column on the previous line
LineUp,
/// move to the same column on the previous visual line
ScreenLineUp,
/// move to the same column on the next line
LineDown,
/// move to the same column on the next visual line
ScreenLineDown,
/// Whole user input (not really a movement but a range)
WholeBuffer,
/// beginning-of-register
BeginningOfBuffer,
/// end-of-register
EndOfBuffer,
ToColumn(usize),
Range(usize,usize),
@@ -328,7 +315,7 @@ pub enum TextObj {
/// `i<`, `a<`
Angle,
/// `it`, `at` — HTML/XML tags (if you support it)
/// `it`, `at` — HTML/XML tags
Tag,
/// Custom user-defined objects maybe?