Added auto-indent to multi-line editing -m Added shopt for enabling/disabling auto-indent -m Fixed some multi-line editing render bugs -m Scrolling up in history now preserves undo/redo history in the pending command
This commit is contained in:
@@ -84,12 +84,6 @@ in
|
|||||||
default = true;
|
default = true;
|
||||||
description = "Whether to enable syntax highlighting in the shell";
|
description = "Whether to enable syntax highlighting in the shell";
|
||||||
};
|
};
|
||||||
tabStop = lib.mkOption {
|
|
||||||
type = lib.types.int;
|
|
||||||
default = 4;
|
|
||||||
description = "The number of spaces to use for tab stop in the shell";
|
|
||||||
};
|
|
||||||
|
|
||||||
extraPostConfig = lib.mkOption {
|
extraPostConfig = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
default = "";
|
default = "";
|
||||||
@@ -123,7 +117,6 @@ in
|
|||||||
"shopt prompt.trunc_prompt_path=${toString cfg.settings.promptPathSegments}"
|
"shopt prompt.trunc_prompt_path=${toString cfg.settings.promptPathSegments}"
|
||||||
"shopt prompt.comp_limit=${toString cfg.settings.completionLimit}"
|
"shopt prompt.comp_limit=${toString cfg.settings.completionLimit}"
|
||||||
"shopt prompt.highlight=${boolToString cfg.settings.syntaxHighlighting}"
|
"shopt prompt.highlight=${boolToString cfg.settings.syntaxHighlighting}"
|
||||||
"shopt prompt.tab_stop=${toString cfg.settings.tabStop}"
|
|
||||||
])
|
])
|
||||||
cfg.settings.extraPostConfig
|
cfg.settings.extraPostConfig
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -265,11 +265,12 @@ impl LexStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !found_fd && !self.flags.contains(LexFlags::LEX_UNFINISHED) {
|
if !found_fd && !self.flags.contains(LexFlags::LEX_UNFINISHED) {
|
||||||
|
let span_start = self.cursor;
|
||||||
self.cursor = pos;
|
self.cursor = pos;
|
||||||
return Some(Err(ShErr::full(
|
return Some(Err(ShErr::full(
|
||||||
ShErrKind::ParseErr,
|
ShErrKind::ParseErr,
|
||||||
"Invalid redirection",
|
"Invalid redirection",
|
||||||
Span::new(self.cursor..pos, self.source.clone()),
|
Span::new(span_start..pos, self.source.clone()),
|
||||||
)));
|
)));
|
||||||
} else {
|
} else {
|
||||||
tk = self.get_token(self.cursor..pos, TkRule::Redir);
|
tk = self.get_token(self.cursor..pos, TkRule::Redir);
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use std::{
|
|||||||
time::{Duration, SystemTime, UNIX_EPOCH},
|
time::{Duration, SystemTime, UNIX_EPOCH},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::libsh::error::{ShErr, ShErrKind, ShResult};
|
use crate::{libsh::error::{ShErr, ShErrKind, ShResult}, prompt::readline::linebuf::LineBuf};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use super::vicmd::Direction; // surprisingly useful
|
use super::vicmd::Direction; // surprisingly useful
|
||||||
@@ -207,7 +207,7 @@ fn dedupe_entries(entries: &[HistEntry]) -> Vec<HistEntry> {
|
|||||||
|
|
||||||
pub struct History {
|
pub struct History {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
pub pending: Option<(String, usize)>, // command, cursor_pos
|
pub pending: Option<LineBuf>, // command, cursor_pos
|
||||||
entries: Vec<HistEntry>,
|
entries: Vec<HistEntry>,
|
||||||
search_mask: Vec<HistEntry>,
|
search_mask: Vec<HistEntry>,
|
||||||
no_matches: bool,
|
no_matches: bool,
|
||||||
@@ -272,7 +272,7 @@ impl History {
|
|||||||
|
|
||||||
pub fn update_pending_cmd(&mut self, buf: (&str, usize)) {
|
pub fn update_pending_cmd(&mut self, buf: (&str, usize)) {
|
||||||
let cursor_pos = if let Some(pending) = &self.pending {
|
let cursor_pos = if let Some(pending) = &self.pending {
|
||||||
pending.1
|
pending.cursor.get()
|
||||||
} else {
|
} else {
|
||||||
buf.1
|
buf.1
|
||||||
};
|
};
|
||||||
@@ -282,7 +282,10 @@ impl History {
|
|||||||
term: cmd.clone(),
|
term: cmd.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.pending = Some((cmd, cursor_pos));
|
if let Some(pending) = &mut self.pending {
|
||||||
|
pending.set_buffer(cmd);
|
||||||
|
pending.cursor.set(cursor_pos);
|
||||||
|
}
|
||||||
self.constrain_entries(constraint);
|
self.constrain_entries(constraint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,7 +343,7 @@ impl History {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_hint(&self) -> Option<String> {
|
pub fn get_hint(&self) -> Option<String> {
|
||||||
if self.at_pending() && self.pending.as_ref().is_some_and(|p| !p.0.is_empty()) {
|
if self.at_pending() && self.pending.as_ref().is_some_and(|p| !p.buffer.is_empty()) {
|
||||||
let entry = self.hint_entry()?;
|
let entry = self.hint_entry()?;
|
||||||
Some(entry.command().to_string())
|
Some(entry.command().to_string())
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ use crate::{
|
|||||||
libsh::{
|
libsh::{
|
||||||
error::ShResult,
|
error::ShResult,
|
||||||
term::{Style, Styled},
|
term::{Style, Styled},
|
||||||
},
|
}, parse::lex::{LexFlags, LexStream, Tk, TkFlags, TkRule}, prelude::*, prompt::readline::{markers, register::write_register}, state::read_shopts
|
||||||
prelude::*, prompt::readline::{markers, register::write_register},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const PUNCTUATION: [&str; 3] = ["?", "!", "."];
|
const PUNCTUATION: [&str; 3] = ["?", "!", "."];
|
||||||
@@ -327,6 +326,7 @@ pub struct LineBuf {
|
|||||||
|
|
||||||
pub insert_mode_start_pos: Option<usize>,
|
pub insert_mode_start_pos: Option<usize>,
|
||||||
pub saved_col: Option<usize>,
|
pub saved_col: Option<usize>,
|
||||||
|
pub auto_indent_level: usize,
|
||||||
|
|
||||||
pub undo_stack: Vec<Edit>,
|
pub undo_stack: Vec<Edit>,
|
||||||
pub redo_stack: Vec<Edit>,
|
pub redo_stack: Vec<Edit>,
|
||||||
@@ -409,7 +409,6 @@ impl LineBuf {
|
|||||||
.unwrap_or(self.buffer.len())
|
.unwrap_or(self.buffer.len())
|
||||||
}
|
}
|
||||||
/// Update self.grapheme_indices with the indices of the current buffer
|
/// Update self.grapheme_indices with the indices of the current buffer
|
||||||
#[track_caller]
|
|
||||||
pub fn update_graphemes(&mut self) {
|
pub fn update_graphemes(&mut self) {
|
||||||
let indices: Vec<_> = self.buffer.grapheme_indices(true).map(|(i, _)| i).collect();
|
let indices: Vec<_> = self.buffer.grapheme_indices(true).map(|(i, _)| i).collect();
|
||||||
self.cursor.set_max(indices.len());
|
self.cursor.set_max(indices.len());
|
||||||
@@ -1884,6 +1883,29 @@ impl LineBuf {
|
|||||||
let end = start + gr.len();
|
let end = start + gr.len();
|
||||||
self.buffer.replace_range(start..end, new);
|
self.buffer.replace_range(start..end, new);
|
||||||
}
|
}
|
||||||
|
pub fn calc_indent_level(&mut self) {
|
||||||
|
let input = Arc::new(self.buffer.clone());
|
||||||
|
let Ok(tokens) = LexStream::new(input, LexFlags::LEX_UNFINISHED).collect::<ShResult<Vec<Tk>>>() else {
|
||||||
|
log::error!("Failed to lex buffer for indent calculation");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let mut level: usize = 0;
|
||||||
|
for tk in tokens {
|
||||||
|
if tk.flags.contains(TkFlags::KEYWORD) {
|
||||||
|
match tk.as_str() {
|
||||||
|
"then" | "do" => level += 1,
|
||||||
|
"done" | "fi" | "esac" => level = level.saturating_sub(1),
|
||||||
|
_ => { /* Continue */ }
|
||||||
|
}
|
||||||
|
} else if tk.class == TkRule::BraceGrpStart {
|
||||||
|
level += 1;
|
||||||
|
} else if tk.class == TkRule::BraceGrpEnd {
|
||||||
|
level = level.saturating_sub(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.auto_indent_level = level;
|
||||||
|
}
|
||||||
pub fn eval_motion(&mut self, verb: Option<&Verb>, motion: MotionCmd) -> MotionKind {
|
pub fn eval_motion(&mut self, verb: Option<&Verb>, motion: MotionCmd) -> MotionKind {
|
||||||
let buffer = self.buffer.clone();
|
let buffer = self.buffer.clone();
|
||||||
if self.has_hint() {
|
if self.has_hint() {
|
||||||
@@ -2669,12 +2691,13 @@ impl LineBuf {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Verb::Dedent => {
|
Verb::Dedent => {
|
||||||
let Some((start, end)) = self.range_from_motion(&motion) else {
|
let Some((start, mut end)) = self.range_from_motion(&motion) else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
if self.grapheme_at(start) == Some("\t") {
|
if self.grapheme_at(start) == Some("\t") {
|
||||||
self.remove(start);
|
self.remove(start);
|
||||||
}
|
}
|
||||||
|
end = end.min(self.grapheme_indices().len().saturating_sub(1));
|
||||||
let mut range_indices = self.grapheme_indices()[start..end].to_vec().into_iter();
|
let mut range_indices = self.grapheme_indices()[start..end].to_vec().into_iter();
|
||||||
while let Some(idx) = range_indices.next() {
|
while let Some(idx) = range_indices.next() {
|
||||||
let gr = self.grapheme_at(idx).unwrap();
|
let gr = self.grapheme_at(idx).unwrap();
|
||||||
@@ -2704,14 +2727,32 @@ impl LineBuf {
|
|||||||
Verb::Equalize => todo!(),
|
Verb::Equalize => todo!(),
|
||||||
Verb::InsertModeLineBreak(anchor) => {
|
Verb::InsertModeLineBreak(anchor) => {
|
||||||
let (mut start, end) = self.this_line();
|
let (mut start, end) = self.this_line();
|
||||||
|
let auto_indent = read_shopts(|o| o.prompt.auto_indent);
|
||||||
if start == 0 && end == self.cursor.max {
|
if start == 0 && end == self.cursor.max {
|
||||||
match anchor {
|
match anchor {
|
||||||
Anchor::After => {
|
Anchor::After => {
|
||||||
self.push('\n');
|
self.push('\n');
|
||||||
|
if auto_indent {
|
||||||
|
log::debug!("Calculating indent level for new line");
|
||||||
|
self.calc_indent_level();
|
||||||
|
log::debug!("Auto-indent level: {}", self.auto_indent_level);
|
||||||
|
let tabs = (0..self.auto_indent_level).map(|_| '\t');
|
||||||
|
for tab in tabs {
|
||||||
|
log::debug!("Pushing tab for auto-indent");
|
||||||
|
self.push(tab);
|
||||||
|
}
|
||||||
|
}
|
||||||
self.cursor.set(self.cursor_max());
|
self.cursor.set(self.cursor_max());
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
Anchor::Before => {
|
Anchor::Before => {
|
||||||
|
if auto_indent {
|
||||||
|
self.calc_indent_level();
|
||||||
|
let tabs = (0..self.auto_indent_level).map(|_| '\t');
|
||||||
|
for tab in tabs {
|
||||||
|
self.insert_at(0, tab);
|
||||||
|
}
|
||||||
|
}
|
||||||
self.insert_at(0, '\n');
|
self.insert_at(0, '\n');
|
||||||
self.cursor.set(0);
|
self.cursor.set(0);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@@ -2724,11 +2765,28 @@ impl LineBuf {
|
|||||||
Anchor::After => {
|
Anchor::After => {
|
||||||
self.cursor.set(end);
|
self.cursor.set(end);
|
||||||
self.insert_at_cursor('\n');
|
self.insert_at_cursor('\n');
|
||||||
|
self.cursor.add(1);
|
||||||
|
if auto_indent {
|
||||||
|
self.calc_indent_level();
|
||||||
|
let tabs = (0..self.auto_indent_level).map(|_| '\t');
|
||||||
|
for tab in tabs {
|
||||||
|
self.insert_at_cursor(tab);
|
||||||
|
self.cursor.add(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Anchor::Before => {
|
Anchor::Before => {
|
||||||
self.cursor.set(start);
|
self.cursor.set(start);
|
||||||
self.insert_at_cursor('\n');
|
self.insert_at_cursor('\n');
|
||||||
self.cursor.add(1);
|
self.cursor.add(1);
|
||||||
|
if auto_indent {
|
||||||
|
self.calc_indent_level();
|
||||||
|
let tabs = (0..self.auto_indent_level).map(|_| '\t');
|
||||||
|
for tab in tabs {
|
||||||
|
self.insert_at_cursor(tab);
|
||||||
|
self.cursor.add(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -249,7 +249,8 @@ impl FernVi {
|
|||||||
|
|
||||||
if cmd.should_submit() {
|
if cmd.should_submit() {
|
||||||
self.editor.set_hint(None);
|
self.editor.set_hint(None);
|
||||||
self.print_line()?;
|
self.editor.cursor.set(self.editor.cursor_max()); // Move the cursor to the very end
|
||||||
|
self.print_line()?; // Redraw
|
||||||
self.writer.flush_write("\n")?;
|
self.writer.flush_write("\n")?;
|
||||||
let buf = self.editor.take_buf();
|
let buf = self.editor.take_buf();
|
||||||
// Save command to history if auto_hist is enabled
|
// Save command to history if auto_hist is enabled
|
||||||
@@ -305,8 +306,7 @@ impl FernVi {
|
|||||||
pub fn get_layout(&mut self, line: &str) -> Layout {
|
pub fn get_layout(&mut self, line: &str) -> Layout {
|
||||||
let to_cursor = self.editor.slice_to_cursor().unwrap_or_default();
|
let to_cursor = self.editor.slice_to_cursor().unwrap_or_default();
|
||||||
let (cols, _) = get_win_size(*TTY_FILENO);
|
let (cols, _) = get_win_size(*TTY_FILENO);
|
||||||
let tab_stop = crate::state::read_shopts(|s| s.prompt.tab_stop) as u16;
|
Layout::from_parts(cols, &self.prompt, to_cursor, line)
|
||||||
Layout::from_parts(tab_stop, cols, &self.prompt, to_cursor, line)
|
|
||||||
}
|
}
|
||||||
pub fn scroll_history(&mut self, cmd: ViCmd) {
|
pub fn scroll_history(&mut self, cmd: ViCmd) {
|
||||||
/*
|
/*
|
||||||
@@ -324,19 +324,21 @@ impl FernVi {
|
|||||||
};
|
};
|
||||||
let entry = self.history.scroll(count);
|
let entry = self.history.scroll(count);
|
||||||
if let Some(entry) = entry {
|
if let Some(entry) = entry {
|
||||||
let cursor_pos = self.editor.cursor.get();
|
let editor = std::mem::take(&mut self.editor);
|
||||||
let pending = self.editor.take_buf();
|
|
||||||
self.editor.set_buffer(entry.command().to_string());
|
self.editor.set_buffer(entry.command().to_string());
|
||||||
if self.history.pending.is_none() {
|
if self.history.pending.is_none() {
|
||||||
self.history.pending = Some((pending, cursor_pos));
|
self.history.pending = Some(editor);
|
||||||
}
|
}
|
||||||
self.editor.set_hint(None);
|
self.editor.set_hint(None);
|
||||||
self.editor.move_cursor_to_end();
|
self.editor.move_cursor_to_end();
|
||||||
} else if let Some(pending) = self.history.pending.take() {
|
} else if let Some(pending) = self.history.pending.take() {
|
||||||
self.editor.set_buffer(pending.0);
|
self.editor = pending;
|
||||||
self.editor.cursor.set(pending.1);
|
} else {
|
||||||
self.editor.set_hint(None);
|
// If we are here it should mean we are on our pending command
|
||||||
}
|
// And the user tried to scroll history down
|
||||||
|
// Since there is no "future" history, we should just bell and do nothing
|
||||||
|
self.writer.send_bell().ok();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn should_accept_hint(&self, event: &KeyEvent) -> bool {
|
pub fn should_accept_hint(&self, event: &KeyEvent) -> bool {
|
||||||
if self.editor.cursor_at_max() && self.editor.has_hint() {
|
if self.editor.cursor_at_max() && self.editor.has_hint() {
|
||||||
|
|||||||
@@ -107,6 +107,30 @@ fn write_all(fd: RawFd, buf: &str) -> nix::Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if a string ends with a newline, ignoring any trailing ANSI escape sequences.
|
||||||
|
fn ends_with_newline(s: &str) -> bool {
|
||||||
|
let bytes = s.as_bytes();
|
||||||
|
let mut i = bytes.len();
|
||||||
|
while i > 0 {
|
||||||
|
// ANSI CSI sequences end with an alphabetic byte (e.g. \x1b[0m)
|
||||||
|
if bytes[i - 1].is_ascii_alphabetic() {
|
||||||
|
let term = i - 1;
|
||||||
|
let mut j = term;
|
||||||
|
// Walk back past parameter bytes (digits and ';')
|
||||||
|
while j > 0 && (bytes[j - 1].is_ascii_digit() || bytes[j - 1] == b';') {
|
||||||
|
j -= 1;
|
||||||
|
}
|
||||||
|
// Check for CSI introducer \x1b[
|
||||||
|
if j >= 2 && bytes[j - 1] == b'[' && bytes[j - 2] == 0x1b {
|
||||||
|
i = j - 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i > 0 && bytes[i - 1] == b'\n'
|
||||||
|
}
|
||||||
|
|
||||||
// Big credit to rustyline for this
|
// Big credit to rustyline for this
|
||||||
fn width(s: &str, esc_seq: &mut u8) -> u16 {
|
fn width(s: &str, esc_seq: &mut u8) -> u16 {
|
||||||
let w_calc = width_calculator();
|
let w_calc = width_calculator();
|
||||||
@@ -734,15 +758,14 @@ impl Layout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn from_parts(
|
pub fn from_parts(
|
||||||
tab_stop: u16,
|
|
||||||
term_width: u16,
|
term_width: u16,
|
||||||
prompt: &str,
|
prompt: &str,
|
||||||
to_cursor: &str,
|
to_cursor: &str,
|
||||||
to_end: &str,
|
to_end: &str,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let prompt_end = Self::calc_pos(tab_stop, term_width, prompt, Pos { col: 0, row: 0 });
|
let prompt_end = Self::calc_pos(term_width, prompt, Pos { col: 0, row: 0 });
|
||||||
let cursor = Self::calc_pos(tab_stop, term_width, to_cursor, prompt_end);
|
let cursor = Self::calc_pos(term_width, to_cursor, prompt_end);
|
||||||
let end = Self::calc_pos(tab_stop, term_width, to_end, prompt_end);
|
let end = Self::calc_pos(term_width, to_end, prompt_end);
|
||||||
Layout {
|
Layout {
|
||||||
w_calc: width_calculator(),
|
w_calc: width_calculator(),
|
||||||
prompt_end,
|
prompt_end,
|
||||||
@@ -751,7 +774,8 @@ impl Layout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn calc_pos(tab_stop: u16, term_width: u16, s: &str, orig: Pos) -> Pos {
|
pub fn calc_pos(term_width: u16, s: &str, orig: Pos) -> Pos {
|
||||||
|
const TAB_STOP: u16 = 8;
|
||||||
let mut pos = orig;
|
let mut pos = orig;
|
||||||
let mut esc_seq = 0;
|
let mut esc_seq = 0;
|
||||||
for c in s.graphemes(true) {
|
for c in s.graphemes(true) {
|
||||||
@@ -760,7 +784,7 @@ impl Layout {
|
|||||||
pos.col = 0;
|
pos.col = 0;
|
||||||
}
|
}
|
||||||
let c_width = if c == "\t" {
|
let c_width = if c == "\t" {
|
||||||
tab_stop - (pos.col % tab_stop)
|
TAB_STOP - (pos.col % TAB_STOP)
|
||||||
} else {
|
} else {
|
||||||
width(c, &mut esc_seq)
|
width(c, &mut esc_seq)
|
||||||
};
|
};
|
||||||
@@ -790,7 +814,6 @@ pub struct TermWriter {
|
|||||||
t_cols: Col, // terminal width
|
t_cols: Col, // terminal width
|
||||||
buffer: String,
|
buffer: String,
|
||||||
w_calc: Box<dyn WidthCalculator>,
|
w_calc: Box<dyn WidthCalculator>,
|
||||||
tab_stop: u16,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TermWriter {
|
impl TermWriter {
|
||||||
@@ -802,7 +825,6 @@ impl TermWriter {
|
|||||||
t_cols,
|
t_cols,
|
||||||
buffer: String::new(),
|
buffer: String::new(),
|
||||||
w_calc,
|
w_calc,
|
||||||
tab_stop: 8, // TODO: add a way to configure this
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_cursor_movement(&self, old: Pos, new: Pos) -> ShResult<String> {
|
pub fn get_cursor_movement(&self, old: Pos, new: Pos) -> ShResult<String> {
|
||||||
@@ -959,7 +981,7 @@ impl LineWriter for TermWriter {
|
|||||||
self.buffer.push_str(prompt);
|
self.buffer.push_str(prompt);
|
||||||
self.buffer.push_str(line);
|
self.buffer.push_str(line);
|
||||||
|
|
||||||
if end.col == 0 && end.row > 0 && !self.buffer.ends_with('\n') {
|
if end.col == 0 && end.row > 0 && !ends_with_newline(&self.buffer) {
|
||||||
// The line has wrapped. We need to use our own line break.
|
// The line has wrapped. We need to use our own line break.
|
||||||
self.buffer.push('\n');
|
self.buffer.push('\n');
|
||||||
}
|
}
|
||||||
|
|||||||
38
src/shopt.rs
38
src/shopt.rs
@@ -371,7 +371,7 @@ pub struct ShOptPrompt {
|
|||||||
pub edit_mode: FernEditMode,
|
pub edit_mode: FernEditMode,
|
||||||
pub comp_limit: usize,
|
pub comp_limit: usize,
|
||||||
pub highlight: bool,
|
pub highlight: bool,
|
||||||
pub tab_stop: usize,
|
pub auto_indent: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShOptPrompt {
|
impl ShOptPrompt {
|
||||||
@@ -413,15 +413,15 @@ impl ShOptPrompt {
|
|||||||
};
|
};
|
||||||
self.highlight = val;
|
self.highlight = val;
|
||||||
}
|
}
|
||||||
"tab_stop" => {
|
"auto_indent" => {
|
||||||
let Ok(val) = val.parse::<usize>() else {
|
let Ok(val) = val.parse::<bool>() else {
|
||||||
return Err(ShErr::simple(
|
return Err(ShErr::simple(
|
||||||
ShErrKind::SyntaxErr,
|
ShErrKind::SyntaxErr,
|
||||||
"shopt: expected a positive integer for tab_stop value",
|
"shopt: expected 'true' or 'false' for auto_indent value",
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
self.tab_stop = val;
|
self.auto_indent = val;
|
||||||
}
|
}
|
||||||
"custom" => {
|
"custom" => {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
@@ -440,7 +440,7 @@ impl ShOptPrompt {
|
|||||||
"edit_mode",
|
"edit_mode",
|
||||||
"comp_limit",
|
"comp_limit",
|
||||||
"highlight",
|
"highlight",
|
||||||
"tab_stop",
|
"auto_indent",
|
||||||
"custom",
|
"custom",
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
@@ -480,11 +480,11 @@ impl ShOptPrompt {
|
|||||||
output.push_str(&format!("{}", self.highlight));
|
output.push_str(&format!("{}", self.highlight));
|
||||||
Ok(Some(output))
|
Ok(Some(output))
|
||||||
}
|
}
|
||||||
"tab_stop" => {
|
"auto_indent" => {
|
||||||
let mut output = String::from("The number of spaces used by the tab character '\\t'\n");
|
let mut output = String::from("Whether to automatically indent new lines in multiline commands\n");
|
||||||
output.push_str(&format!("{}", self.tab_stop));
|
output.push_str(&format!("{}", self.auto_indent));
|
||||||
Ok(Some(output))
|
Ok(Some(output))
|
||||||
}
|
}
|
||||||
_ => Err(
|
_ => Err(
|
||||||
ShErr::simple(
|
ShErr::simple(
|
||||||
ShErrKind::SyntaxErr,
|
ShErrKind::SyntaxErr,
|
||||||
@@ -499,7 +499,7 @@ impl ShOptPrompt {
|
|||||||
"edit_mode",
|
"edit_mode",
|
||||||
"comp_limit",
|
"comp_limit",
|
||||||
"highlight",
|
"highlight",
|
||||||
"tab_stop",
|
"auto_indent",
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -515,7 +515,7 @@ impl Display for ShOptPrompt {
|
|||||||
output.push(format!("edit_mode = {}", self.edit_mode));
|
output.push(format!("edit_mode = {}", self.edit_mode));
|
||||||
output.push(format!("comp_limit = {}", self.comp_limit));
|
output.push(format!("comp_limit = {}", self.comp_limit));
|
||||||
output.push(format!("highlight = {}", self.highlight));
|
output.push(format!("highlight = {}", self.highlight));
|
||||||
output.push(format!("tab_stop = {}", self.tab_stop));
|
output.push(format!("auto_indent = {}", self.auto_indent));
|
||||||
|
|
||||||
let final_output = output.join("\n");
|
let final_output = output.join("\n");
|
||||||
|
|
||||||
@@ -530,7 +530,7 @@ impl Default for ShOptPrompt {
|
|||||||
edit_mode: FernEditMode::Vi,
|
edit_mode: FernEditMode::Vi,
|
||||||
comp_limit: 100,
|
comp_limit: 100,
|
||||||
highlight: true,
|
highlight: true,
|
||||||
tab_stop: 4,
|
auto_indent: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user