Bump to 0.6.1: add Ctrl-D/U half-screen scrolling, Ctrl-G position info, and status messages
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -573,7 +573,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shed"
|
name = "shed"
|
||||||
version = "0.6.0"
|
version = "0.6.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ariadne",
|
"ariadne",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
name = "shed"
|
name = "shed"
|
||||||
description = "A linux shell written in rust"
|
description = "A linux shell written in rust"
|
||||||
publish = false
|
publish = false
|
||||||
version = "0.6.0"
|
version = "0.6.1"
|
||||||
|
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
{
|
{
|
||||||
packages.default = pkgs.rustPlatform.buildRustPackage {
|
packages.default = pkgs.rustPlatform.buildRustPackage {
|
||||||
pname = "shed";
|
pname = "shed";
|
||||||
version = "0.6.0";
|
version = "0.6.1";
|
||||||
|
|
||||||
src = self;
|
src = self;
|
||||||
|
|
||||||
|
|||||||
@@ -734,7 +734,7 @@ pub trait Completer {
|
|||||||
let (s, e) = self.token_span();
|
let (s, e) = self.token_span();
|
||||||
orig.get(s..e).unwrap_or(orig)
|
orig.get(s..e).unwrap_or(orig)
|
||||||
}
|
}
|
||||||
fn draw(&mut self, writer: &mut TermWriter) -> ShResult<()>;
|
fn draw(&mut self, writer: &mut TermWriter) -> ShResult<usize>;
|
||||||
fn clear(&mut self, _writer: &mut TermWriter) -> ShResult<()> {
|
fn clear(&mut self, _writer: &mut TermWriter) -> ShResult<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -1122,9 +1122,9 @@ impl FuzzySelector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(&mut self, writer: &mut TermWriter) -> ShResult<()> {
|
pub fn draw(&mut self, writer: &mut TermWriter) -> ShResult<usize> {
|
||||||
if !self.active {
|
if !self.active {
|
||||||
return Ok(());
|
return Ok(0);
|
||||||
}
|
}
|
||||||
let (cols, _) = get_win_size(*TTY_FILENO);
|
let (cols, _) = get_win_size(*TTY_FILENO);
|
||||||
|
|
||||||
@@ -1272,7 +1272,7 @@ impl FuzzySelector {
|
|||||||
writer.flush_write(&buf)?;
|
writer.flush_write(&buf)?;
|
||||||
self.old_layout = Some(new_layout);
|
self.old_layout = Some(new_layout);
|
||||||
|
|
||||||
Ok(())
|
Ok(rows as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&mut self, writer: &mut TermWriter) -> ShResult<()> {
|
pub fn clear(&mut self, writer: &mut TermWriter) -> ShResult<()> {
|
||||||
@@ -1421,7 +1421,7 @@ impl Completer for FuzzyCompleter {
|
|||||||
fn clear(&mut self, writer: &mut TermWriter) -> ShResult<()> {
|
fn clear(&mut self, writer: &mut TermWriter) -> ShResult<()> {
|
||||||
self.selector.clear(writer)
|
self.selector.clear(writer)
|
||||||
}
|
}
|
||||||
fn draw(&mut self, writer: &mut TermWriter) -> ShResult<()> {
|
fn draw(&mut self, writer: &mut TermWriter) -> ShResult<usize> {
|
||||||
self.selector.draw(writer)
|
self.selector.draw(writer)
|
||||||
}
|
}
|
||||||
fn reset(&mut self) {
|
fn reset(&mut self) {
|
||||||
@@ -1497,8 +1497,8 @@ impl Completer for SimpleCompleter {
|
|||||||
self.token_span
|
self.token_span
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&mut self, _writer: &mut TermWriter) -> ShResult<()> {
|
fn draw(&mut self, _writer: &mut TermWriter) -> ShResult<usize> {
|
||||||
Ok(())
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn original_input(&self) -> &str {
|
fn original_input(&self) -> &str {
|
||||||
|
|||||||
@@ -138,6 +138,13 @@ pub fn split_lines_at(lines: &mut Vec<Line>, pos: Pos) -> Vec<Line> {
|
|||||||
rest
|
rest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn split_lines(mut lines: Vec<Line>, pos: Pos) -> (Vec<Line>,Vec<Line>) {
|
||||||
|
let tail = lines[pos.row].split_off(pos.col);
|
||||||
|
let mut rest: Vec<Line> = lines.drain(pos.row + 1..).collect();
|
||||||
|
rest.insert(0, tail);
|
||||||
|
(lines, rest)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn attach_lines(lines: &mut Vec<Line>, other: &mut Vec<Line>) {
|
pub fn attach_lines(lines: &mut Vec<Line>, other: &mut Vec<Line>) {
|
||||||
if other.is_empty() {
|
if other.is_empty() {
|
||||||
return;
|
return;
|
||||||
@@ -1860,6 +1867,16 @@ impl LineBuf {
|
|||||||
let (s, e) = ordered(*s, *e);
|
let (s, e) = ordered(*s, *e);
|
||||||
Some(MotionKind::Block { start: s, end: e })
|
Some(MotionKind::Block { start: s, end: e })
|
||||||
}
|
}
|
||||||
|
dir @ (Motion::HalfScreenUp | Motion::HalfScreenDown) => {
|
||||||
|
let off = match dir {
|
||||||
|
Motion::HalfScreenUp => -(self.get_viewport_height() as isize / 2),
|
||||||
|
Motion::HalfScreenDown => self.get_viewport_height() as isize / 2,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
let row = self.row();
|
||||||
|
let target_row = self.offset_row(off);
|
||||||
|
Some(MotionKind::Line { start: target_row, end: row, inclusive: false })
|
||||||
|
}
|
||||||
Motion::RepeatMotion |
|
Motion::RepeatMotion |
|
||||||
Motion::RepeatMotionRev => unreachable!("Repeat motions should have been resolved in readline/mod.rs"),
|
Motion::RepeatMotionRev => unreachable!("Repeat motions should have been resolved in readline/mod.rs"),
|
||||||
Motion::Global(val) |
|
Motion::Global(val) |
|
||||||
@@ -1907,7 +1924,6 @@ impl LineBuf {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn extract_range(&mut self, motion: &MotionKind) -> Vec<Line> {
|
fn extract_range(&mut self, motion: &MotionKind) -> Vec<Line> {
|
||||||
log::debug!("Extracting range for motion: {:?}", motion);
|
|
||||||
let extracted = match motion {
|
let extracted = match motion {
|
||||||
MotionKind::Char {
|
MotionKind::Char {
|
||||||
start,
|
start,
|
||||||
@@ -2484,9 +2500,31 @@ impl LineBuf {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Verb::EndOfFile => {
|
||||||
|
self.lines.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
Verb::PrintPosition => {
|
||||||
|
let num_lines = self.lines.len();
|
||||||
|
let row = self.row() + 1;
|
||||||
|
let col = self.col() + 1;
|
||||||
|
let total_graphemes = self.count_graphemes();
|
||||||
|
let (left,_) = split_lines(self.lines.clone(), self.cursor.pos);
|
||||||
|
let total_in_left = left.iter().map(|l| l.len()).sum::<usize>();
|
||||||
|
let percentage = if total_graphemes > 0 {
|
||||||
|
(total_in_left as f64 / total_graphemes as f64) * 100.0
|
||||||
|
} else {
|
||||||
|
100.0
|
||||||
|
}.round() as usize;
|
||||||
|
|
||||||
|
let msg = format!("line: {row}/{num_lines}, col: {col} --{percentage}%--");
|
||||||
|
write_meta(|m| {
|
||||||
|
m.post_status_message(msg);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
Verb::Complete
|
Verb::Complete
|
||||||
| Verb::ExMode
|
| Verb::ExMode
|
||||||
| Verb::EndOfFile
|
|
||||||
| Verb::InsertMode
|
| Verb::InsertMode
|
||||||
| Verb::NormalMode
|
| Verb::NormalMode
|
||||||
| Verb::VisualMode
|
| Verb::VisualMode
|
||||||
@@ -2600,7 +2638,13 @@ impl LineBuf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn fix_cursor(&mut self) {
|
pub fn fix_cursor(&mut self) {
|
||||||
|
// we are now going to enforce some invariants and do some bookkeeping
|
||||||
|
if self.lines.is_empty() {
|
||||||
|
// self.lines must always have at least one line
|
||||||
|
self.lines.push(Line::default());
|
||||||
|
}
|
||||||
if self.cursor.pos.row >= self.lines.len() {
|
if self.cursor.pos.row >= self.lines.len() {
|
||||||
|
// clamp this now so self.cur_line() cannot panic
|
||||||
self.cursor.pos.row = self.lines.len().saturating_sub(1);
|
self.cursor.pos.row = self.lines.len().saturating_sub(1);
|
||||||
}
|
}
|
||||||
if self.cursor.exclusive {
|
if self.cursor.exclusive {
|
||||||
@@ -2616,6 +2660,8 @@ impl LineBuf {
|
|||||||
self.cursor.pos.col = line.len();
|
self.cursor.pos.col = line.len();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update viewport scroll offset
|
||||||
self.update_scroll_offset();
|
self.update_scroll_offset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use history::History;
|
use history::History;
|
||||||
use keys::{KeyCode, KeyEvent, ModKeys};
|
use keys::{KeyCode, KeyEvent, ModKeys};
|
||||||
use linebuf::{LineBuf, SelectMode};
|
use linebuf::{LineBuf, SelectMode};
|
||||||
|
use std::collections::VecDeque;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use term::{KeyReader, Layout, LineWriter, PollReader, TermWriter, get_win_size};
|
use term::{KeyReader, Layout, LineWriter, PollReader, TermWriter, get_win_size};
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
@@ -16,8 +17,7 @@ use crate::readline::complete::{FuzzyCompleter, SelectorResponse};
|
|||||||
use crate::readline::term::{Pos, TermReader, calc_str_width};
|
use crate::readline::term::{Pos, TermReader, calc_str_width};
|
||||||
use crate::readline::vimode::{ViEx, ViVerbatim};
|
use crate::readline::vimode::{ViEx, ViVerbatim};
|
||||||
use crate::state::{
|
use crate::state::{
|
||||||
AutoCmdKind, ShellParam, Var, VarFlags, VarKind, read_logic, read_shopts, with_vars, write_meta,
|
AutoCmdKind, ShellParam, Var, VarFlags, VarKind, read_logic, read_meta, read_shopts, with_vars, write_meta, write_vars
|
||||||
write_vars,
|
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
libsh::error::ShResult,
|
libsh::error::ShResult,
|
||||||
@@ -272,6 +272,8 @@ pub struct ShedVi {
|
|||||||
pub ex_history: History,
|
pub ex_history: History,
|
||||||
|
|
||||||
pub needs_redraw: bool,
|
pub needs_redraw: bool,
|
||||||
|
pub ctrl_d_warning_counter: usize,
|
||||||
|
pub status_msgs: VecDeque<(String, Instant)>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShedVi {
|
impl ShedVi {
|
||||||
@@ -293,6 +295,8 @@ impl ShedVi {
|
|||||||
history: History::new()?,
|
history: History::new()?,
|
||||||
ex_history: History::empty(),
|
ex_history: History::empty(),
|
||||||
needs_redraw: true,
|
needs_redraw: true,
|
||||||
|
ctrl_d_warning_counter: 0,
|
||||||
|
status_msgs: VecDeque::new()
|
||||||
};
|
};
|
||||||
write_vars(|v| {
|
write_vars(|v| {
|
||||||
v.set_var(
|
v.set_var(
|
||||||
@@ -325,6 +329,8 @@ impl ShedVi {
|
|||||||
history: History::empty(),
|
history: History::empty(),
|
||||||
ex_history: History::empty(),
|
ex_history: History::empty(),
|
||||||
needs_redraw: true,
|
needs_redraw: true,
|
||||||
|
ctrl_d_warning_counter: 0,
|
||||||
|
status_msgs: VecDeque::new()
|
||||||
};
|
};
|
||||||
write_vars(|v| {
|
write_vars(|v| {
|
||||||
v.set_var(
|
v.set_var(
|
||||||
@@ -883,16 +889,9 @@ impl ShedVi {
|
|||||||
return Ok(Some(ReadlineEvent::Line(buf)));
|
return Ok(Some(ReadlineEvent::Line(buf)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd.verb().is_some_and(|v| v.1 == Verb::EndOfFile) {
|
if (cmd.verb().is_some_and(|v| v.1 == Verb::EndOfFile)
|
||||||
if self.focused_editor().joined().is_empty() {
|
&& self.focused_editor().joined().is_empty())
|
||||||
return Ok(Some(ReadlineEvent::Eof));
|
|| cmd.verb().is_some_and(|v| v.1 == Verb::Quit) {
|
||||||
} else {
|
|
||||||
*self.focused_editor() = LineBuf::new();
|
|
||||||
self.mode = Box::new(ViInsert::new());
|
|
||||||
self.needs_redraw = true;
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
} else if cmd.verb().is_some_and(|v| v.1 == Verb::Quit) {
|
|
||||||
return Ok(Some(ReadlineEvent::Eof));
|
return Ok(Some(ReadlineEvent::Eof));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -902,6 +901,8 @@ impl ShedVi {
|
|||||||
// this is only used for ringing the bell
|
// this is only used for ringing the bell
|
||||||
let has_edit_verb = cmd.verb().is_some_and(|v| v.1.is_edit() && v.1 != Verb::Change);
|
let has_edit_verb = cmd.verb().is_some_and(|v| v.1.is_edit() && v.1 != Verb::Change);
|
||||||
|
|
||||||
|
let is_ctrl_d_motion = cmd.motion().is_some_and(|m| m.1 == Motion::HalfScreenDown);
|
||||||
|
|
||||||
let is_shell_cmd = cmd.verb().is_some_and(|v| matches!(v.1, Verb::ShellCmd(_)));
|
let is_shell_cmd = cmd.verb().is_some_and(|v| matches!(v.1, Verb::ShellCmd(_)));
|
||||||
let is_ex_cmd = cmd.flags.contains(CmdFlags::IS_EX_CMD);
|
let is_ex_cmd = cmd.flags.contains(CmdFlags::IS_EX_CMD);
|
||||||
if is_shell_cmd {
|
if is_shell_cmd {
|
||||||
@@ -913,6 +914,7 @@ impl ShedVi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let before = self.editor.joined();
|
let before = self.editor.joined();
|
||||||
|
let before_cursor = self.editor.cursor;
|
||||||
|
|
||||||
self.exec_cmd(cmd, false)?;
|
self.exec_cmd(cmd, false)?;
|
||||||
|
|
||||||
@@ -922,6 +924,7 @@ impl ShedVi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let after = self.editor.joined();
|
let after = self.editor.joined();
|
||||||
|
let after_cursor = self.editor.cursor;
|
||||||
|
|
||||||
if before != after {
|
if before != after {
|
||||||
self
|
self
|
||||||
@@ -929,6 +932,17 @@ impl ShedVi {
|
|||||||
.update_pending_cmd((&self.editor.joined(), self.editor.cursor_to_flat()));
|
.update_pending_cmd((&self.editor.joined(), self.editor.cursor_to_flat()));
|
||||||
} else if before == after && has_edit_verb {
|
} else if before == after && has_edit_verb {
|
||||||
self.writer.send_bell().ok();
|
self.writer.send_bell().ok();
|
||||||
|
} else if before_cursor == after_cursor && is_ctrl_d_motion {
|
||||||
|
if self.ctrl_d_warning_counter == 3 || self.editor.is_empty() {
|
||||||
|
// our silly user is spamming ctrl+d for some reason
|
||||||
|
// maybe they want to exit the shell?
|
||||||
|
write_meta(|m| {
|
||||||
|
m.post_status_message("Ctrl+D only quits in insert mode. try ':q' or entering insert mode with 'i'".into())
|
||||||
|
});
|
||||||
|
self.ctrl_d_warning_counter = 0;
|
||||||
|
} else {
|
||||||
|
self.ctrl_d_warning_counter += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let hint = self.history.get_hint();
|
let hint = self.history.get_hint();
|
||||||
@@ -1126,7 +1140,9 @@ impl ShedVi {
|
|||||||
self.writer.flush_write(&buf)?;
|
self.writer.flush_write(&buf)?;
|
||||||
|
|
||||||
// Move to end of layout for overlay draws (completer, history search)
|
// Move to end of layout for overlay draws (completer, history search)
|
||||||
let has_overlays = self.completer.is_active() || self.focused_history().fuzzy_finder.is_active();
|
let has_overlays = self.completer.is_active()
|
||||||
|
|| self.focused_history().fuzzy_finder.is_active();
|
||||||
|
|
||||||
let down = new_layout.end.row.saturating_sub(new_layout.cursor.row);
|
let down = new_layout.end.row.saturating_sub(new_layout.cursor.row);
|
||||||
if has_overlays && down > 0 {
|
if has_overlays && down > 0 {
|
||||||
self.writer.flush_write(&format!("\x1b[{down}B"))?;
|
self.writer.flush_write(&format!("\x1b[{down}B"))?;
|
||||||
@@ -1141,9 +1157,11 @@ impl ShedVi {
|
|||||||
// Without PSR, use the content width on the cursor's row
|
// Without PSR, use the content width on the cursor's row
|
||||||
(new_layout.end.col + 1).max(new_layout.cursor.col + 1)
|
(new_layout.end.col + 1).max(new_layout.cursor.col + 1)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut fuzzy_window_rows = 0;
|
||||||
self.completer
|
self.completer
|
||||||
.set_prompt_line_context(preceding_width, new_layout.end.col);
|
.set_prompt_line_context(preceding_width, new_layout.end.col);
|
||||||
self.completer.draw(&mut self.writer)?;
|
fuzzy_window_rows += self.completer.draw(&mut self.writer)?;
|
||||||
|
|
||||||
{
|
{
|
||||||
self.focused_history()
|
self.focused_history()
|
||||||
@@ -1151,10 +1169,37 @@ impl ShedVi {
|
|||||||
.set_prompt_line_context(preceding_width, new_layout.end.col);
|
.set_prompt_line_context(preceding_width, new_layout.end.col);
|
||||||
|
|
||||||
let mut writer = std::mem::take(&mut self.writer);
|
let mut writer = std::mem::take(&mut self.writer);
|
||||||
self.focused_history().fuzzy_finder.draw(&mut writer)?;
|
fuzzy_window_rows += self.focused_history().fuzzy_finder.draw(&mut writer)?;
|
||||||
self.writer = writer;
|
self.writer = writer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while let Some(msg) = write_meta(|m| m.pop_status_message()) {
|
||||||
|
let now = Instant::now();
|
||||||
|
self.status_msgs.push_back((msg,now));
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Some((msg,time)) = self.status_msgs.front() {
|
||||||
|
if time.elapsed().as_secs() < 5 {
|
||||||
|
log::debug!("drawing status message: {msg}");
|
||||||
|
let down = new_layout.end.row - new_layout.cursor.row;
|
||||||
|
log::debug!("status message down: {down}");
|
||||||
|
let fuzzy_rows = fuzzy_window_rows.saturating_sub(1); // the cursor is one row below the top
|
||||||
|
let total = down.saturating_add(fuzzy_rows as u16);
|
||||||
|
let move_down = if total > 0 {
|
||||||
|
format!("\x1b[{total}B")
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
let move_up = total + 2;
|
||||||
|
let col = new_layout.cursor.col + 1;
|
||||||
|
self.writer.flush_write(&format!("{move_down}\n\n\x1b7\x1b[2K{msg}\x1b8\x1b[{move_up}A\x1b[{col}G"))?;
|
||||||
|
new_layout.end.row += (2 + msg.chars().filter(|c| *c == '\n').count()) as u16;
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
self.status_msgs.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.old_layout = Some(new_layout);
|
self.old_layout = Some(new_layout);
|
||||||
self.needs_redraw = false;
|
self.needs_redraw = false;
|
||||||
|
|
||||||
|
|||||||
@@ -243,6 +243,7 @@ pub enum Verb {
|
|||||||
Equalize,
|
Equalize,
|
||||||
AcceptLineOrNewline,
|
AcceptLineOrNewline,
|
||||||
EndOfFile,
|
EndOfFile,
|
||||||
|
PrintPosition,
|
||||||
// Ex-mode verbs
|
// Ex-mode verbs
|
||||||
ExMode,
|
ExMode,
|
||||||
ShellCmd(String),
|
ShellCmd(String),
|
||||||
@@ -335,6 +336,8 @@ pub enum Motion {
|
|||||||
EndOfBuffer,
|
EndOfBuffer,
|
||||||
ToColumn,
|
ToColumn,
|
||||||
ToDelimMatch,
|
ToDelimMatch,
|
||||||
|
HalfScreenDown,
|
||||||
|
HalfScreenUp,
|
||||||
ToBrace(Direction),
|
ToBrace(Direction),
|
||||||
ToBracket(Direction),
|
ToBracket(Direction),
|
||||||
ToParen(Direction),
|
ToParen(Direction),
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ impl ViMode for ViEx {
|
|||||||
Ok(cmd) => Ok(cmd),
|
Ok(cmd) => Ok(cmd),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let msg = e.unwrap_or(format!("Not an editor command: {}", &input));
|
let msg = e.unwrap_or(format!("Not an editor command: {}", &input));
|
||||||
write_meta(|m| m.post_system_message(msg.clone()));
|
write_meta(|m| m.post_status_message(msg.clone()));
|
||||||
Err(ShErr::simple(ShErrKind::ParseErr, msg))
|
Err(ShErr::simple(ShErrKind::ParseErr, msg))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ pub fn common_cmds(key: E) -> Option<ViCmd> {
|
|||||||
E(K::Right, M::NONE) => pending_cmd.set_motion(MotionCmd(1, Motion::ForwardChar)),
|
E(K::Right, M::NONE) => pending_cmd.set_motion(MotionCmd(1, Motion::ForwardChar)),
|
||||||
E(K::Up, M::NONE) => pending_cmd.set_motion(MotionCmd(1, Motion::LineUp)),
|
E(K::Up, M::NONE) => pending_cmd.set_motion(MotionCmd(1, Motion::LineUp)),
|
||||||
E(K::Down, M::NONE) => pending_cmd.set_motion(MotionCmd(1, Motion::LineDown)),
|
E(K::Down, M::NONE) => pending_cmd.set_motion(MotionCmd(1, Motion::LineDown)),
|
||||||
|
E(K::Enter, M::SHIFT) => pending_cmd.set_verb(VerbCmd(1, Verb::InsertChar('\n'))),
|
||||||
E(K::Enter, M::NONE) => pending_cmd.set_verb(VerbCmd(1, Verb::AcceptLineOrNewline)),
|
E(K::Enter, M::NONE) => pending_cmd.set_verb(VerbCmd(1, Verb::AcceptLineOrNewline)),
|
||||||
E(K::Char('D'), M::CTRL) => pending_cmd.set_verb(VerbCmd(1, Verb::EndOfFile)),
|
E(K::Char('D'), M::CTRL) => pending_cmd.set_verb(VerbCmd(1, Verb::EndOfFile)),
|
||||||
E(K::Delete, M::NONE) => {
|
E(K::Delete, M::NONE) => {
|
||||||
|
|||||||
@@ -776,6 +776,36 @@ impl ViMode for ViNormal {
|
|||||||
flags: self.flags(),
|
flags: self.flags(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
E(K::Char('G'), M::CTRL) => {
|
||||||
|
self.pending_seq.clear();
|
||||||
|
Some(ViCmd {
|
||||||
|
register: Default::default(),
|
||||||
|
verb: Some(VerbCmd(1, Verb::PrintPosition)),
|
||||||
|
motion: None,
|
||||||
|
raw_seq: "".into(),
|
||||||
|
flags: self.flags(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
E(K::Char('D'), M::CTRL) => {
|
||||||
|
self.pending_seq.clear();
|
||||||
|
Some(ViCmd {
|
||||||
|
register: Default::default(),
|
||||||
|
verb: None,
|
||||||
|
motion: Some(MotionCmd(1, Motion::HalfScreenDown)),
|
||||||
|
raw_seq: "".into(),
|
||||||
|
flags: self.flags(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
E(K::Char('U'), M::CTRL) => {
|
||||||
|
self.pending_seq.clear();
|
||||||
|
Some(ViCmd {
|
||||||
|
register: Default::default(),
|
||||||
|
verb: None,
|
||||||
|
motion: Some(MotionCmd(1, Motion::HalfScreenUp)),
|
||||||
|
raw_seq: "".into(),
|
||||||
|
flags: self.flags(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
E(K::Char(ch), M::NONE) => self.try_parse(ch),
|
E(K::Char(ch), M::NONE) => self.try_parse(ch),
|
||||||
E(K::Backspace, M::NONE) => Some(ViCmd {
|
E(K::Backspace, M::NONE) => Some(ViCmd {
|
||||||
|
|||||||
@@ -663,6 +663,36 @@ impl ViMode for ViVisual {
|
|||||||
flags: CmdFlags::empty(),
|
flags: CmdFlags::empty(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
E(K::Char('G'), M::CTRL) => {
|
||||||
|
self.pending_seq.clear();
|
||||||
|
Some(ViCmd {
|
||||||
|
register: Default::default(),
|
||||||
|
verb: Some(VerbCmd(1, Verb::PrintPosition)),
|
||||||
|
motion: None,
|
||||||
|
raw_seq: "".into(),
|
||||||
|
flags: CmdFlags::empty()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
E(K::Char('D'), M::CTRL) => {
|
||||||
|
self.pending_seq.clear();
|
||||||
|
Some(ViCmd {
|
||||||
|
register: Default::default(),
|
||||||
|
verb: None,
|
||||||
|
motion: Some(MotionCmd(1, Motion::HalfScreenDown)),
|
||||||
|
raw_seq: "".into(),
|
||||||
|
flags: CmdFlags::empty(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
E(K::Char('U'), M::CTRL) => {
|
||||||
|
self.pending_seq.clear();
|
||||||
|
Some(ViCmd {
|
||||||
|
register: Default::default(),
|
||||||
|
verb: None,
|
||||||
|
motion: Some(MotionCmd(1, Motion::HalfScreenUp)),
|
||||||
|
raw_seq: "".into(),
|
||||||
|
flags: CmdFlags::empty(),
|
||||||
|
})
|
||||||
|
}
|
||||||
E(K::Char('R'), M::CTRL) => {
|
E(K::Char('R'), M::CTRL) => {
|
||||||
let mut chars = self.pending_seq.chars().peekable();
|
let mut chars = self.pending_seq.chars().peekable();
|
||||||
let count = self.parse_count(&mut chars).unwrap_or(1);
|
let count = self.parse_count(&mut chars).unwrap_or(1);
|
||||||
|
|||||||
23
src/state.rs
23
src/state.rs
@@ -1369,7 +1369,12 @@ pub struct MetaTab {
|
|||||||
runtime_stop: Option<Instant>,
|
runtime_stop: Option<Instant>,
|
||||||
|
|
||||||
// pending system messages
|
// pending system messages
|
||||||
system_msg: Vec<String>,
|
// are drawn above the prompt and survive redraws
|
||||||
|
system_msg: VecDeque<String>,
|
||||||
|
|
||||||
|
// same as system messages,
|
||||||
|
// but they appear under the prompt and are erased on redraw
|
||||||
|
status_msg: VecDeque<String>,
|
||||||
|
|
||||||
// pushd/popd stack
|
// pushd/popd stack
|
||||||
dir_stack: VecDeque<PathBuf>,
|
dir_stack: VecDeque<PathBuf>,
|
||||||
@@ -1394,7 +1399,8 @@ impl Default for MetaTab {
|
|||||||
shell_time: Instant::now(),
|
shell_time: Instant::now(),
|
||||||
runtime_start: None,
|
runtime_start: None,
|
||||||
runtime_stop: None,
|
runtime_stop: None,
|
||||||
system_msg: vec![],
|
system_msg: VecDeque::new(),
|
||||||
|
status_msg: VecDeque::new(),
|
||||||
dir_stack: VecDeque::new(),
|
dir_stack: VecDeque::new(),
|
||||||
getopts_offset: 0,
|
getopts_offset: 0,
|
||||||
old_path: None,
|
old_path: None,
|
||||||
@@ -1614,14 +1620,23 @@ impl MetaTab {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn post_system_message(&mut self, message: String) {
|
pub fn post_system_message(&mut self, message: String) {
|
||||||
self.system_msg.push(message);
|
self.system_msg.push_back(message);
|
||||||
}
|
}
|
||||||
pub fn pop_system_message(&mut self) -> Option<String> {
|
pub fn pop_system_message(&mut self) -> Option<String> {
|
||||||
self.system_msg.pop()
|
self.system_msg.pop_front()
|
||||||
}
|
}
|
||||||
pub fn system_msg_pending(&self) -> bool {
|
pub fn system_msg_pending(&self) -> bool {
|
||||||
!self.system_msg.is_empty()
|
!self.system_msg.is_empty()
|
||||||
}
|
}
|
||||||
|
pub fn post_status_message(&mut self, message: String) {
|
||||||
|
self.status_msg.push_back(message);
|
||||||
|
}
|
||||||
|
pub fn pop_status_message(&mut self) -> Option<String> {
|
||||||
|
self.status_msg.pop_front()
|
||||||
|
}
|
||||||
|
pub fn status_msg_pending(&self) -> bool {
|
||||||
|
!self.status_msg.is_empty()
|
||||||
|
}
|
||||||
pub fn dir_stack_top(&self) -> Option<&PathBuf> {
|
pub fn dir_stack_top(&self) -> Option<&PathBuf> {
|
||||||
self.dir_stack.front()
|
self.dir_stack.front()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user