work started on refactoring LineBuf

This commit is contained in:
2026-03-18 15:34:12 -04:00
parent 782a3820da
commit 7c8a418f96
8 changed files with 662 additions and 3396 deletions

7
Cargo.lock generated
View File

@@ -577,6 +577,7 @@ dependencies = [
"regex",
"scopeguard",
"serde_json",
"smallvec",
"tempfile",
"unicode-segmentation",
"unicode-width",
@@ -584,6 +585,12 @@ dependencies = [
"yansi",
]
[[package]]
name = "smallvec"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "strsim"
version = "0.11.1"

View File

@@ -35,6 +35,7 @@ rand = "0.10.0"
regex = "1.11.1"
scopeguard = "1.2.0"
serde_json = "1.0.149"
smallvec = { version = "1.15.1", features = ["write"] }
tempfile = "3.24.0"
unicode-segmentation = "1.12.0"
unicode-width = "0.2.0"

View File

@@ -160,6 +160,12 @@ fn main() -> ExitCode {
on_exit_autocmds.exec();
write_jobs(|j| j.hang_up());
let code = QUIT_CODE.load(Ordering::SeqCst) as u8;
if code == 0 && isatty(STDIN_FILENO).unwrap_or_default() {
write(borrow_fd(STDERR_FILENO), b"\nexit\n").ok();
}
ExitCode::from(QUIT_CODE.load(Ordering::SeqCst) as u8)
}

View File

@@ -19,7 +19,7 @@ use crate::{
readline::{
Marker, annotate_input_recursive,
keys::{KeyCode as C, KeyEvent as K, ModKeys as M},
linebuf::{ClampedUsize, LineBuf},
linebuf::LineBuf,
markers::{self, is_marker},
term::{LineWriter, TermWriter, calc_str_width, get_win_size},
vimode::{ViInsert, ViMode},
@@ -818,7 +818,6 @@ impl QueryEditor {
self.available_width = width;
}
pub fn update_scroll_offset(&mut self) {
self.linebuf.update_graphemes();
let cursor_pos = self.linebuf.cursor.get();
if cursor_pos < self.scroll_offset + 1 {
self.scroll_offset = self.linebuf.cursor.ret_sub(1);
@@ -829,10 +828,8 @@ impl QueryEditor {
.cursor
.ret_sub(self.available_width.saturating_sub(1));
}
let max_offset = self
.linebuf
.grapheme_indices()
.len()
let max_offset = self.linebuf
.count_graphemes()
.saturating_sub(self.available_width);
self.scroll_offset = self.scroll_offset.min(max_offset);
}

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,7 @@ use vimode::{CmdReplay, ModeReport, ViInsert, ViMode, ViNormal, ViReplace, ViVis
use crate::builtin::keymap::{KeyMapFlags, KeyMapMatch};
use crate::expand::expand_prompt;
use crate::libsh::error::{ShErr, ShErrKind};
use crate::libsh::utils::AutoCmdVecUtils;
use crate::parse::lex::{LexStream, QuoteState};
use crate::readline::complete::{FuzzyCompleter, SelectorResponse};
@@ -882,7 +883,9 @@ impl ShedVi {
self.needs_redraw = true;
return Ok(None);
}
}
} else if cmd.verb().is_some_and(|v| v.1 == Verb::Quit) {
return Ok(Some(ReadlineEvent::Eof));
}
let has_edit_verb = cmd.verb().is_some_and(|v| v.1.is_edit());
let is_shell_cmd = cmd.verb().is_some_and(|v| matches!(v.1, Verb::ShellCmd(_)));

View File

@@ -2,7 +2,7 @@ use std::path::PathBuf;
use bitflags::bitflags;
use crate::readline::vimode::ex::SubFlags;
use crate::readline::{linebuf::Grapheme, vimode::ex::SubFlags};
use super::register::{RegisterContent, append_register, read_register, write_register};
@@ -161,23 +161,11 @@ impl ViCmd {
self.motion.as_ref().is_some_and(|m| {
matches!(
m.1,
Motion::LineUp | Motion::LineDown | Motion::LineUpCharwise | Motion::LineDownCharwise
Motion::LineUp | Motion::LineDown
)
})
}
/// If a ViCmd has a linewise motion, but no verb, we change it to charwise
pub fn alter_line_motion_if_no_verb(&mut self) {
if self.is_line_motion()
&& self.verb.is_none()
&& let Some(motion) = self.motion.as_mut()
{
match motion.1 {
Motion::LineUp => motion.1 = Motion::LineUpCharwise,
Motion::LineDown => motion.1 = Motion::LineDownCharwise,
_ => unreachable!(),
}
}
}
pub fn is_mode_transition(&self) -> bool {
self.verb.as_ref().is_some_and(|v| {
matches!(
@@ -261,6 +249,7 @@ pub enum Verb {
Read(ReadSrc),
Write(WriteDest),
Edit(PathBuf),
Quit,
Substitute(String, String, SubFlags),
RepeatSubstitute,
RepeatGlobal,
@@ -326,23 +315,20 @@ impl Verb {
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Motion {
WholeLineInclusive, // whole line including the linebreak
WholeLineExclusive, // whole line excluding the linebreak
WholeLine,
TextObj(TextObj),
EndOfLastWord,
BeginningOfFirstWord,
BeginningOfLine,
EndOfLine,
WordMotion(To, Word, Direction),
CharSearch(Direction, Dest, char),
CharSearch(Direction, Dest, Grapheme),
BackwardChar,
ForwardChar,
BackwardCharForced, // These two variants can cross line boundaries
ForwardCharForced,
LineUp,
LineUpCharwise,
LineDown,
LineDownCharwise,
WholeBuffer,
StartOfBuffer,
EndOfBuffer,
@@ -382,8 +368,6 @@ impl Motion {
&self,
Self::BeginningOfLine
| Self::BeginningOfFirstWord
| Self::LineDownCharwise
| Self::LineUpCharwise
| Self::ToColumn
| Self::TextObj(TextObj::Sentence(_))
| Self::TextObj(TextObj::Paragraph(_))
@@ -398,7 +382,7 @@ impl Motion {
pub fn is_linewise(&self) -> bool {
matches!(
self,
Self::WholeLineInclusive | Self::WholeLineExclusive | Self::LineUp | Self::LineDown
Self::WholeLine | Self::LineUp | Self::LineDown
)
}
}

View File

@@ -280,6 +280,7 @@ fn parse_ex_command(chars: &mut Peekable<Chars<'_>>) -> Result<Option<Verb>, Opt
_ if "delete".starts_with(&cmd_name) => Ok(Some(Verb::Delete)),
_ if "yank".starts_with(&cmd_name) => Ok(Some(Verb::Yank)),
_ if "put".starts_with(&cmd_name) => Ok(Some(Verb::Put(Anchor::After))),
_ if "quit".starts_with(&cmd_name) => Ok(Some(Verb::Quit)),
_ if "read".starts_with(&cmd_name) => parse_read(chars),
_ if "write".starts_with(&cmd_name) => parse_write(chars),
_ if "edit".starts_with(&cmd_name) => parse_edit(chars),