Early implementation of Verbatim mode
This commit is contained in:
@@ -11,6 +11,7 @@ bitflags! {
|
|||||||
const EX = 0b0001000;
|
const EX = 0b0001000;
|
||||||
const OP_PENDING = 0b0010000;
|
const OP_PENDING = 0b0010000;
|
||||||
const REPLACE = 0b0100000;
|
const REPLACE = 0b0100000;
|
||||||
|
const VERBATIM = 0b1000000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -284,7 +284,7 @@ pub fn read_key(node: Node) -> ShResult<()> {
|
|||||||
}
|
}
|
||||||
Ok(n) => {
|
Ok(n) => {
|
||||||
let mut reader = PollReader::new();
|
let mut reader = PollReader::new();
|
||||||
reader.feed_bytes(&buf[..n]);
|
reader.feed_bytes(&buf[..n], false);
|
||||||
let Some(key) = reader.read_key()? else {
|
let Some(key) = reader.read_key()? else {
|
||||||
state::set_status(1);
|
state::set_status(1);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ impl KeyEvent {
|
|||||||
|
|
||||||
// If more than one grapheme, it's not a single key event
|
// If more than one grapheme, it's not a single key event
|
||||||
if graphemes.next().is_some() {
|
if graphemes.next().is_some() {
|
||||||
return E(K::Null, mods); // Or panic, or wrap in Grapheme if desired
|
return E(K::Null, mods);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut chars = first.chars();
|
let mut chars = first.chars();
|
||||||
@@ -184,6 +184,7 @@ impl KeyEvent {
|
|||||||
seq.push(*ch);
|
seq.push(*ch);
|
||||||
}
|
}
|
||||||
KeyCode::Grapheme(gr) => seq.push_str(gr),
|
KeyCode::Grapheme(gr) => seq.push_str(gr),
|
||||||
|
KeyCode::Verbatim(s) => seq.push_str(s),
|
||||||
}
|
}
|
||||||
|
|
||||||
if needs_angle_bracket {
|
if needs_angle_bracket {
|
||||||
@@ -203,6 +204,7 @@ pub enum KeyCode {
|
|||||||
BracketedPasteEnd,
|
BracketedPasteEnd,
|
||||||
Char(char),
|
Char(char),
|
||||||
Grapheme(Arc<str>),
|
Grapheme(Arc<str>),
|
||||||
|
Verbatim(Arc<str>), // For sequences that should be treated as literal input, not parsed into a KeyCode
|
||||||
Delete,
|
Delete,
|
||||||
Down,
|
Down,
|
||||||
End,
|
End,
|
||||||
|
|||||||
@@ -1991,13 +1991,19 @@ impl LineBuf {
|
|||||||
.slice_to_cursor()
|
.slice_to_cursor()
|
||||||
.map(|s| s.to_string())
|
.map(|s| s.to_string())
|
||||||
.unwrap_or(self.buffer.clone());
|
.unwrap_or(self.buffer.clone());
|
||||||
|
|
||||||
|
let mut level: usize = 0;
|
||||||
|
|
||||||
|
if to_cursor.ends_with("\\\n") {
|
||||||
|
level += 1; // Line continuation, so we need to add an extra level
|
||||||
|
}
|
||||||
|
|
||||||
let input = Arc::new(to_cursor);
|
let input = Arc::new(to_cursor);
|
||||||
let Ok(tokens) = LexStream::new(input, LexFlags::LEX_UNFINISHED).collect::<ShResult<Vec<Tk>>>()
|
let Ok(tokens) = LexStream::new(input, LexFlags::LEX_UNFINISHED).collect::<ShResult<Vec<Tk>>>()
|
||||||
else {
|
else {
|
||||||
log::error!("Failed to lex buffer for indent calculation");
|
log::error!("Failed to lex buffer for indent calculation");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let mut level: usize = 0;
|
|
||||||
let mut last_keyword: Option<String> = None;
|
let mut last_keyword: Option<String> = None;
|
||||||
for tk in tokens {
|
for tk in tokens {
|
||||||
if tk.flags.contains(TkFlags::KEYWORD) {
|
if tk.flags.contains(TkFlags::KEYWORD) {
|
||||||
@@ -3095,6 +3101,7 @@ impl LineBuf {
|
|||||||
| Verb::InsertMode
|
| Verb::InsertMode
|
||||||
| Verb::NormalMode
|
| Verb::NormalMode
|
||||||
| Verb::VisualMode
|
| Verb::VisualMode
|
||||||
|
| Verb::VerbatimMode
|
||||||
| Verb::ReplaceMode
|
| Verb::ReplaceMode
|
||||||
| Verb::VisualModeLine
|
| Verb::VisualModeLine
|
||||||
| Verb::VisualModeBlock
|
| Verb::VisualModeBlock
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ use crate::parse::lex::{LexStream, QuoteState};
|
|||||||
use crate::{prelude::*, state};
|
use crate::{prelude::*, state};
|
||||||
use crate::readline::complete::FuzzyCompleter;
|
use crate::readline::complete::FuzzyCompleter;
|
||||||
use crate::readline::term::{Pos, TermReader, calc_str_width};
|
use crate::readline::term::{Pos, TermReader, calc_str_width};
|
||||||
use crate::readline::vimode::ViEx;
|
use crate::readline::vimode::{ViEx, ViVerbatim};
|
||||||
use crate::state::{AutoCmdKind, ShellParam, VarFlags, VarKind, read_logic, read_shopts, with_vars, write_meta, write_vars};
|
use crate::state::{AutoCmdKind, ShellParam, VarFlags, VarKind, read_logic, read_shopts, with_vars, write_meta, write_vars};
|
||||||
use crate::{
|
use crate::{
|
||||||
libsh::error::ShResult,
|
libsh::error::ShResult,
|
||||||
@@ -269,7 +269,8 @@ impl ShedVi {
|
|||||||
|
|
||||||
/// Feed raw bytes from stdin into the reader's buffer
|
/// Feed raw bytes from stdin into the reader's buffer
|
||||||
pub fn feed_bytes(&mut self, bytes: &[u8]) {
|
pub fn feed_bytes(&mut self, bytes: &[u8]) {
|
||||||
self.reader.feed_bytes(bytes);
|
let verbatim = self.mode.report_mode() == ModeReport::Verbatim;
|
||||||
|
self.reader.feed_bytes(bytes, verbatim);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark that the display needs to be redrawn (e.g., after SIGWINCH)
|
/// Mark that the display needs to be redrawn (e.g., after SIGWINCH)
|
||||||
@@ -323,6 +324,7 @@ impl ShedVi {
|
|||||||
ModeReport::Ex => flags |= KeyMapFlags::EX,
|
ModeReport::Ex => flags |= KeyMapFlags::EX,
|
||||||
ModeReport::Visual => flags |= KeyMapFlags::VISUAL,
|
ModeReport::Visual => flags |= KeyMapFlags::VISUAL,
|
||||||
ModeReport::Replace => flags |= KeyMapFlags::REPLACE,
|
ModeReport::Replace => flags |= KeyMapFlags::REPLACE,
|
||||||
|
ModeReport::Verbatim => flags |= KeyMapFlags::VERBATIM,
|
||||||
ModeReport::Unknown => todo!(),
|
ModeReport::Unknown => todo!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -835,6 +837,10 @@ impl ShedVi {
|
|||||||
Box::new(ViEx::new())
|
Box::new(ViEx::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Verb::VerbatimMode => {
|
||||||
|
Box::new(ViVerbatim::new().with_count(count as u16))
|
||||||
|
}
|
||||||
|
|
||||||
Verb::NormalMode => Box::new(ViNormal::new()),
|
Verb::NormalMode => Box::new(ViNormal::new()),
|
||||||
|
|
||||||
Verb::ReplaceMode => Box::new(ViReplace::new()),
|
Verb::ReplaceMode => Box::new(ViReplace::new()),
|
||||||
@@ -863,10 +869,9 @@ impl ShedVi {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
self.swap_mode(&mut mode);
|
self.swap_mode(&mut mode);
|
||||||
|
|
||||||
if self.mode.report_mode() == ModeReport::Ex {
|
if matches!(self.mode.report_mode(), ModeReport::Ex | ModeReport::Verbatim) {
|
||||||
self.saved_mode = Some(mode);
|
self.saved_mode = Some(mode);
|
||||||
write_vars(|v| v.set_var("SHED_VI_MODE", VarKind::Str(self.mode.report_mode().to_string()), VarFlags::NONE))?;
|
write_vars(|v| v.set_var("SHED_VI_MODE", VarKind::Str(self.mode.report_mode().to_string()), VarFlags::NONE))?;
|
||||||
self.prompt.refresh()?;
|
self.prompt.refresh()?;
|
||||||
@@ -1007,7 +1012,7 @@ impl ShedVi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if cmd.flags.contains(CmdFlags::EXIT_CUR_MODE) {
|
if cmd.flags.contains(CmdFlags::EXIT_CUR_MODE) {
|
||||||
let mut mode: Box<dyn ViMode> = if self.mode.report_mode() == ModeReport::Ex {
|
let mut mode: Box<dyn ViMode> = if matches!(self.mode.report_mode(), ModeReport::Ex | ModeReport::Verbatim) {
|
||||||
if let Some(saved) = self.saved_mode.take() {
|
if let Some(saved) = self.saved_mode.take() {
|
||||||
saved
|
saved
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::{
|
|||||||
env,
|
env,
|
||||||
fmt::{Debug, Write},
|
fmt::{Debug, Write},
|
||||||
io::{BufRead, BufReader, Read},
|
io::{BufRead, BufReader, Read},
|
||||||
os::fd::{AsFd, BorrowedFd, RawFd},
|
os::fd::{AsFd, BorrowedFd, RawFd}, sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use nix::{
|
use nix::{
|
||||||
@@ -457,6 +457,27 @@ impl Perform for KeyCollector {
|
|||||||
};
|
};
|
||||||
KeyEvent(key, mods)
|
KeyEvent(key, mods)
|
||||||
}
|
}
|
||||||
|
([],'u') => {
|
||||||
|
let codepoint = params.first().copied().unwrap_or(0);
|
||||||
|
let mods = params
|
||||||
|
.get(1)
|
||||||
|
.map(|&m| Self::parse_modifiers(m))
|
||||||
|
.unwrap_or(ModKeys::empty());
|
||||||
|
let key = match codepoint {
|
||||||
|
9 => KeyCode::Tab,
|
||||||
|
13 => KeyCode::Enter,
|
||||||
|
27 => KeyCode::Esc,
|
||||||
|
127 => KeyCode::Backspace,
|
||||||
|
_ => {
|
||||||
|
if let Some(ch) = char::from_u32(codepoint as u32) {
|
||||||
|
KeyCode::Char(ch)
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
KeyEvent(key, mods)
|
||||||
|
}
|
||||||
// SGR mouse: CSI < button;x;y M/m (ignore mouse events for now)
|
// SGR mouse: CSI < button;x;y M/m (ignore mouse events for now)
|
||||||
([b'<'], 'M') | ([b'<'], 'm') => {
|
([b'<'], 'M') | ([b'<'], 'm') => {
|
||||||
return;
|
return;
|
||||||
@@ -494,17 +515,19 @@ impl PollReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn feed_bytes(&mut self, bytes: &[u8]) {
|
pub fn feed_bytes(&mut self, bytes: &[u8], verbatim: bool) {
|
||||||
if bytes == [b'\x1b'] {
|
if verbatim {
|
||||||
|
let seq = String::from_utf8_lossy(bytes).to_string();
|
||||||
|
self.collector.push(KeyEvent(KeyCode::Verbatim(Arc::from(seq.as_str())), ModKeys::empty()));
|
||||||
|
} else if bytes == [b'\x1b'] {
|
||||||
// Single escape byte - user pressed ESC key
|
// Single escape byte - user pressed ESC key
|
||||||
self
|
self
|
||||||
.collector
|
.collector
|
||||||
.push(KeyEvent(KeyCode::Esc, ModKeys::empty()));
|
.push(KeyEvent(KeyCode::Esc, ModKeys::empty()));
|
||||||
return;
|
} else {
|
||||||
}
|
// Feed all bytes through vte parser
|
||||||
|
self.parser.advance(&mut self.collector, bytes);
|
||||||
// Feed all bytes through vte parser
|
}
|
||||||
self.parser.advance(&mut self.collector, bytes);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -178,6 +178,7 @@ impl ViCmd {
|
|||||||
matches!(
|
matches!(
|
||||||
v.1,
|
v.1,
|
||||||
Verb::Change
|
Verb::Change
|
||||||
|
| Verb::VerbatimMode
|
||||||
| Verb::ExMode
|
| Verb::ExMode
|
||||||
| Verb::InsertMode
|
| Verb::InsertMode
|
||||||
| Verb::InsertModeLineBreak(_)
|
| Verb::InsertModeLineBreak(_)
|
||||||
@@ -231,6 +232,7 @@ pub enum Verb {
|
|||||||
RepeatLast,
|
RepeatLast,
|
||||||
Put(Anchor),
|
Put(Anchor),
|
||||||
ReplaceMode,
|
ReplaceMode,
|
||||||
|
VerbatimMode,
|
||||||
InsertMode,
|
InsertMode,
|
||||||
InsertModeLineBreak(Anchor),
|
InsertModeLineBreak(Anchor),
|
||||||
NormalMode,
|
NormalMode,
|
||||||
|
|||||||
@@ -64,6 +64,10 @@ impl ViMode for ViInsert {
|
|||||||
));
|
));
|
||||||
self.register_and_return()
|
self.register_and_return()
|
||||||
}
|
}
|
||||||
|
E(K::Char('V'), M::CTRL) => {
|
||||||
|
self.pending_cmd.set_verb(VerbCmd(1, Verb::VerbatimMode));
|
||||||
|
self.register_and_return()
|
||||||
|
}
|
||||||
E(K::Char('H'), M::CTRL) | E(K::Backspace, M::NONE) => {
|
E(K::Char('H'), M::CTRL) | E(K::Backspace, M::NONE) => {
|
||||||
self.pending_cmd.set_verb(VerbCmd(1, Verb::Delete));
|
self.pending_cmd.set_verb(VerbCmd(1, Verb::Delete));
|
||||||
self
|
self
|
||||||
|
|||||||
@@ -13,12 +13,14 @@ pub mod normal;
|
|||||||
pub mod replace;
|
pub mod replace;
|
||||||
pub mod visual;
|
pub mod visual;
|
||||||
pub mod ex;
|
pub mod ex;
|
||||||
|
pub mod verbatim;
|
||||||
|
|
||||||
pub use ex::ViEx;
|
pub use ex::ViEx;
|
||||||
pub use insert::ViInsert;
|
pub use insert::ViInsert;
|
||||||
pub use normal::ViNormal;
|
pub use normal::ViNormal;
|
||||||
pub use replace::ViReplace;
|
pub use replace::ViReplace;
|
||||||
pub use visual::ViVisual;
|
pub use visual::ViVisual;
|
||||||
|
pub use verbatim::ViVerbatim;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum ModeReport {
|
pub enum ModeReport {
|
||||||
@@ -27,6 +29,7 @@ pub enum ModeReport {
|
|||||||
Ex,
|
Ex,
|
||||||
Visual,
|
Visual,
|
||||||
Replace,
|
Replace,
|
||||||
|
Verbatim,
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,6 +41,7 @@ impl Display for ModeReport {
|
|||||||
ModeReport::Ex => write!(f, "COMMAND"),
|
ModeReport::Ex => write!(f, "COMMAND"),
|
||||||
ModeReport::Visual => write!(f, "VISUAL"),
|
ModeReport::Visual => write!(f, "VISUAL"),
|
||||||
ModeReport::Replace => write!(f, "REPLACE"),
|
ModeReport::Replace => write!(f, "REPLACE"),
|
||||||
|
ModeReport::Verbatim => write!(f, "VERBATIM"),
|
||||||
ModeReport::Unknown => write!(f, "UNKNOWN"),
|
ModeReport::Unknown => write!(f, "UNKNOWN"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
66
src/readline/vimode/verbatim.rs
Normal file
66
src/readline/vimode/verbatim.rs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
use super::{common_cmds, CmdReplay, ModeReport, ViMode};
|
||||||
|
use crate::readline::keys::{KeyCode as K, KeyEvent as E, ModKeys as M};
|
||||||
|
use crate::readline::register::Register;
|
||||||
|
use crate::readline::vicmd::{
|
||||||
|
CmdFlags, Direction, Motion, MotionCmd, RegisterName, To, Verb, VerbCmd, ViCmd, Word
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Debug)]
|
||||||
|
pub struct ViVerbatim {
|
||||||
|
sent_cmd: Vec<ViCmd>,
|
||||||
|
repeat_count: u16
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ViVerbatim {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
pub fn with_count(self, repeat_count: u16) -> Self {
|
||||||
|
Self { repeat_count, ..self }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ViMode for ViVerbatim {
|
||||||
|
fn handle_key(&mut self, key: E) -> Option<ViCmd> {
|
||||||
|
match key {
|
||||||
|
E(K::Verbatim(seq),_mods) => {
|
||||||
|
let cmd = ViCmd { register: RegisterName::default(),
|
||||||
|
verb: Some(VerbCmd(1,Verb::Insert(seq.to_string()))),
|
||||||
|
motion: None,
|
||||||
|
raw_seq: seq.to_string(),
|
||||||
|
flags: CmdFlags::EXIT_CUR_MODE
|
||||||
|
};
|
||||||
|
self.sent_cmd.push(cmd.clone());
|
||||||
|
Some(cmd)
|
||||||
|
}
|
||||||
|
_ => common_cmds(key),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_repeatable(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_replay(&self) -> Option<CmdReplay> {
|
||||||
|
Some(CmdReplay::mode(self.sent_cmd.clone(), self.repeat_count))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cursor_style(&self) -> String {
|
||||||
|
"\x1b[6 q".to_string()
|
||||||
|
}
|
||||||
|
fn pending_seq(&self) -> Option<String> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
fn move_cursor_on_undo(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
fn clamp_cursor(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
fn hist_scroll_start_pos(&self) -> Option<To> {
|
||||||
|
Some(To::End)
|
||||||
|
}
|
||||||
|
fn report_mode(&self) -> ModeReport {
|
||||||
|
ModeReport::Insert
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user