From 464efa69709464dc331616f12292e2ccdc56f658 Mon Sep 17 00:00:00 2001 From: gwenn Date: Sun, 8 Sep 2024 11:51:42 +0200 Subject: [PATCH 01/17] Use select instead of poll on MacOS for /dev/tty See #802 --- src/tty/unix.rs | 84 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 61 insertions(+), 23 deletions(-) diff --git a/src/tty/unix.rs b/src/tty/unix.rs index 08d754810..e3e378d64 100644 --- a/src/tty/unix.rs +++ b/src/tty/unix.rs @@ -1,6 +1,4 @@ //! Unix specific definitions -#[cfg(feature = "buffer-redux")] -use buffer_redux::BufReader; use std::cmp; use std::collections::HashMap; use std::fs::{File, OpenOptions}; @@ -13,12 +11,16 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{self, SyncSender}; use std::sync::{Arc, Mutex}; +#[cfg(feature = "buffer-redux")] +use buffer_redux::BufReader; use log::{debug, warn}; use nix::errno::Errno; use nix::poll::{self, PollFlags, PollTimeout}; use nix::sys::select::{self, FdSet}; #[cfg(not(feature = "termios"))] use nix::sys::termios::Termios; +#[cfg(target_os = "macos")] +use nix::sys::time::TimeValLike; use nix::unistd::{close, isatty, read, write}; #[cfg(feature = "termios")] use termios::Termios; @@ -195,6 +197,8 @@ pub struct PosixRawReader { key_map: PosixKeyMap, // external print reader pipe_reader: Option, + #[cfg(target_os = "macos")] + is_dev_tty: bool, } impl AsFd for PosixRawReader { @@ -243,6 +247,7 @@ impl PosixRawReader { config: &Config, key_map: PosixKeyMap, pipe_reader: Option, + #[cfg(target_os = "macos")] is_dev_tty: bool, ) -> Self { let inner = TtyIn { fd, sigwinch_pipe }; #[cfg(any(not(feature = "buffer-redux"), test))] @@ -259,6 +264,8 @@ impl PosixRawReader { parser: Parser::new(), key_map, pipe_reader, + #[cfg(target_os = "macos")] + is_dev_tty, } } @@ -306,9 +313,8 @@ impl PosixRawReader { match self.poll(timeout) { // Ignore poll errors, it's very likely we'll pick them up on // the next read anyway. - Ok(0) | Err(_) => Ok(E::ESC), - Ok(n) => { - debug_assert!(n > 0, "{}", n); + Ok(false) | Err(_) => Ok(E::ESC), + Ok(true) => { // recurse, and add the alt modifier. let E(k, m) = self._do_escape_sequence(false)?; Ok(E(k, m | M::ALT)) @@ -702,38 +708,53 @@ impl PosixRawReader { }) } - fn poll(&mut self, timeout_ms: PollTimeout) -> Result { + fn poll(&mut self, timeout: PollTimeout) -> Result { let n = self.tty_in.buffer().len(); if n > 0 { - return Ok(n as i32); + return Ok(true); } + #[cfg(target_os = "macos")] + if self.is_dev_tty { + // poll doesn't work for /dev/tty on MacOS but select does + return Ok(match self.select(Some(timeout), false /* ignored */)? { + // ugly but ESC means timeout... + Event::KeyPress(KeyEvent::ESC) => false, + _ => true, + }); + } + debug!(target: "rustyline", "poll with: {:?}", timeout); let mut fds = [poll::PollFd::new(self.as_fd(), PollFlags::POLLIN)]; - let r = poll::poll(&mut fds, timeout_ms); + let r = poll::poll(&mut fds, timeout); + debug!(target: "rustyline", "poll returns: {:?}", r); match r { - Ok(n) => Ok(n), + Ok(n) => Ok(n != 0), Err(Errno::EINTR) => { if self.tty_in.get_ref().sigwinch()? { Err(ReadlineError::WindowResized) } else { - Ok(0) // Ignore EINTR while polling + Ok(false) // Ignore EINTR while polling } } Err(e) => Err(e.into()), } } - fn select(&mut self, single_esc_abort: bool) -> Result { + // timeout is used only with /dev/tty on MacOs + fn select(&mut self, timeout: Option, single_esc_abort: bool) -> Result { let tty_in = self.as_fd(); let sigwinch_pipe = self .tty_in .get_ref() .sigwinch_pipe .map(|fd| unsafe { BorrowedFd::borrow_raw(fd) }); - let pipe_reader = self - .pipe_reader - .as_ref() - .map(|pr| pr.lock().unwrap().0.as_raw_fd()) - .map(|fd| unsafe { BorrowedFd::borrow_raw(fd) }); + let pipe_reader = if timeout.is_some() { + None + } else { + self.pipe_reader + .as_ref() + .map(|pr| pr.lock().unwrap().0.as_raw_fd()) + .map(|fd| unsafe { BorrowedFd::borrow_raw(fd) }) + }; loop { let mut readfds = FdSet::new(); if let Some(sigwinch_pipe) = sigwinch_pipe { @@ -743,7 +764,14 @@ impl PosixRawReader { if let Some(pipe_reader) = pipe_reader { readfds.insert(pipe_reader); } - if let Err(err) = select::select(None, Some(&mut readfds), None, None, None) { + let mut timeout = match timeout { + Some(pt) => pt + .as_millis() + .map(|ms| nix::sys::time::TimeVal::milliseconds(ms as i64)), + None => None, + }; + if let Err(err) = select::select(None, Some(&mut readfds), None, None, timeout.as_mut()) + { if err == Errno::EINTR && self.tty_in.get_ref().sigwinch()? { return Err(ReadlineError::WindowResized); } else if err != Errno::EINTR { @@ -756,8 +784,16 @@ impl PosixRawReader { self.tty_in.get_ref().sigwinch()?; return Err(ReadlineError::WindowResized); } else if readfds.contains(tty_in) { - // prefer user input over external print - return self.next_key(single_esc_abort).map(Event::KeyPress); + if timeout.is_some() { + // ugly but ENTER means success (no timeout)... + return Ok(Event::KeyPress(KeyEvent::ENTER)); + } else { + // prefer user input over external print + return self.next_key(single_esc_abort).map(Event::KeyPress); + } + } else if timeout.is_some() { + // ugly but ESC means timeout... + return Ok(Event::KeyPress(KeyEvent::ESC)); } else if let Some(ref pipe_reader) = self.pipe_reader { let mut guard = pipe_reader.lock().unwrap(); let mut buf = [0; 1]; @@ -776,7 +812,7 @@ impl RawReader for PosixRawReader { #[cfg(not(feature = "signal-hook"))] fn wait_for_input(&mut self, single_esc_abort: bool) -> Result { match self.pipe_reader { - Some(_) => self.select(single_esc_abort), + Some(_) => self.select(None, single_esc_abort), None => self.next_key(single_esc_abort).map(Event::KeyPress), } } @@ -800,7 +836,7 @@ impl RawReader for PosixRawReader { self.timeout_ms }; match self.poll(timeout_ms) { - Ok(0) => { + Ok(false) => { // single escape } Ok(_) => { @@ -1117,14 +1153,14 @@ impl Renderer for PosixRenderer { } fn move_cursor_at_leftmost(&mut self, rdr: &mut PosixRawReader) -> Result<()> { - if rdr.poll(PollTimeout::ZERO)? != 0 { + if rdr.poll(PollTimeout::ZERO)? { debug!(target: "rustyline", "cannot request cursor location"); return Ok(()); } /* Report cursor location */ self.write_and_flush("\x1b[6n")?; /* Read the response: ESC [ rows ; cols R */ - if rdr.poll(PollTimeout::from(100u8))? == 0 + if !rdr.poll(PollTimeout::from(100u8))? || rdr.next_char()? != '\x1b' || rdr.next_char()? != '[' || read_digits_until(rdr, ';')?.is_none() @@ -1420,6 +1456,8 @@ impl Term for PosixTerminal { config, key_map, self.pipe_reader.clone(), + #[cfg(target_os = "macos")] + self.close_on_drop, ) } From 33302a898af792ca415b892fdc41b8125e41d108 Mon Sep 17 00:00:00 2001 From: gwenn Date: Sun, 8 Sep 2024 13:12:47 +0200 Subject: [PATCH 02/17] Oops --- src/tty/unix.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tty/unix.rs b/src/tty/unix.rs index e3e378d64..47e5e8d74 100644 --- a/src/tty/unix.rs +++ b/src/tty/unix.rs @@ -19,7 +19,6 @@ use nix::poll::{self, PollFlags, PollTimeout}; use nix::sys::select::{self, FdSet}; #[cfg(not(feature = "termios"))] use nix::sys::termios::Termios; -#[cfg(target_os = "macos")] use nix::sys::time::TimeValLike; use nix::unistd::{close, isatty, read, write}; #[cfg(feature = "termios")] From 792f9a743a9e6a4bab180364e1f0523e603b20f1 Mon Sep 17 00:00:00 2001 From: gwenn Date: Sun, 8 Sep 2024 13:16:12 +0200 Subject: [PATCH 03/17] Oops --- src/tty/unix.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tty/unix.rs b/src/tty/unix.rs index 47e5e8d74..3ec5a74b1 100644 --- a/src/tty/unix.rs +++ b/src/tty/unix.rs @@ -818,7 +818,7 @@ impl RawReader for PosixRawReader { #[cfg(feature = "signal-hook")] fn wait_for_input(&mut self, single_esc_abort: bool) -> Result { - self.select(single_esc_abort) + self.select(None, single_esc_abort) } fn next_key(&mut self, single_esc_abort: bool) -> Result { From cf5351b770681d64b392761c32ad29303ad51607 Mon Sep 17 00:00:00 2001 From: gwenn Date: Fri, 11 Oct 2024 18:52:26 +0200 Subject: [PATCH 04/17] Fix clippy needless_lifetimes warnings --- src/edit.rs | 8 ++++---- src/keymap.rs | 2 +- src/lib.rs | 2 +- src/tty/test.rs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/edit.rs b/src/edit.rs index af75622ec..b18de9e2e 100644 --- a/src/edit.rs +++ b/src/edit.rs @@ -253,13 +253,13 @@ impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> { } } -impl<'out, 'prompt, H: Helper> Invoke for State<'out, 'prompt, H> { +impl Invoke for State<'_, '_, H> { fn input(&self) -> &str { self.line.as_str() } } -impl<'out, 'prompt, H: Helper> Refresher for State<'out, 'prompt, H> { +impl Refresher for State<'_, '_, H> { fn refresh_line(&mut self) -> Result<()> { let prompt_size = self.prompt_size; self.hint(); @@ -325,7 +325,7 @@ impl<'out, 'prompt, H: Helper> Refresher for State<'out, 'prompt, H> { } } -impl<'out, 'prompt, H: Helper> fmt::Debug for State<'out, 'prompt, H> { +impl fmt::Debug for State<'_, '_, H> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("State") .field("prompt", &self.prompt) @@ -338,7 +338,7 @@ impl<'out, 'prompt, H: Helper> fmt::Debug for State<'out, 'prompt, H> { } } -impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> { +impl State<'_, '_, H> { pub fn clear_screen(&mut self) -> Result<()> { self.out.clear_screen()?; self.layout.cursor = Position::default(); diff --git a/src/keymap.rs b/src/keymap.rs index 9bef72798..c8561207c 100644 --- a/src/keymap.rs +++ b/src/keymap.rs @@ -1125,7 +1125,7 @@ impl<'b> InputState<'b> { } #[cfg(feature = "custom-bindings")] -impl<'b> InputState<'b> { +impl InputState<'_> { /// Application customized binding fn custom_binding( &self, diff --git a/src/lib.rs b/src/lib.rs index c48757e42..701565037 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -967,7 +967,7 @@ struct Iter<'a, H: Helper, I: History> { prompt: &'a str, } -impl<'a, H: Helper, I: History> Iterator for Iter<'a, H, I> { +impl Iterator for Iter<'_, H, I> { type Item = Result; fn next(&mut self) -> Option> { diff --git a/src/tty/test.rs b/src/tty/test.rs index 00ef42152..3478baf9f 100644 --- a/src/tty/test.rs +++ b/src/tty/test.rs @@ -21,7 +21,7 @@ impl RawMode for Mode { } } -impl<'a> RawReader for Iter<'a, KeyEvent> { +impl RawReader for Iter<'_, KeyEvent> { type Buffer = Buffer; fn wait_for_input(&mut self, single_esc_abort: bool) -> Result { From e97e64f18aa66bf4a498a33b2e8cd1a481f22bb5 Mon Sep 17 00:00:00 2001 From: gwenn Date: Sun, 27 Oct 2024 11:31:20 +0100 Subject: [PATCH 05/17] Test completer impls --- src/completion.rs | 15 ++++++++++++--- src/tty/unix.rs | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/completion.rs b/src/completion.rs index e466d977b..9566f7845 100644 --- a/src/completion.rs +++ b/src/completion.rs @@ -496,6 +496,8 @@ fn find_unclosed_quote(s: &str) -> Option<(usize, Quote)> { #[cfg(test)] mod tests { + use super::{Completer, FilenameCompleter}; + #[test] pub fn extract_word() { let break_chars = super::default_break_chars; @@ -604,16 +606,23 @@ mod tests { #[test] pub fn candidate_impls() { struct StrCmp; - impl super::Completer for StrCmp { + impl Completer for StrCmp { type Candidate = &'static str; } struct RcCmp; - impl super::Completer for RcCmp { + impl Completer for RcCmp { type Candidate = std::rc::Rc; } struct ArcCmp; - impl super::Completer for ArcCmp { + impl Completer for ArcCmp { type Candidate = std::sync::Arc; } } + + #[test] + pub fn completer_impls() { + struct Wrapper(T); + let boxed = Box::new(FilenameCompleter::new()); + let _ = Wrapper(boxed); + } } diff --git a/src/tty/unix.rs b/src/tty/unix.rs index 054b4f118..f08d700b5 100644 --- a/src/tty/unix.rs +++ b/src/tty/unix.rs @@ -1032,7 +1032,7 @@ impl Renderer for PosixRenderer { } // position the cursor within the line if cursor.col > 0 { - write!(self.buffer, "\r\x1b[{}C", cursor.col).unwrap(); + write!(self.buffer, "\r\x1b[{}C", cursor.col)?; } else { self.buffer.push('\r'); } From 13b459420b28708d1e4e601e96bb129a184ba55e Mon Sep 17 00:00:00 2001 From: gwenn Date: Sun, 27 Oct 2024 12:09:57 +0100 Subject: [PATCH 06/17] Fix vertical movements in multi-lines input Take into account prompt offset --- src/edit.rs | 4 ++-- src/line_buffer.rs | 50 ++++++++++++++++++++++++++++++++++------------ 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/src/edit.rs b/src/edit.rs index b18de9e2e..dd856b9e4 100644 --- a/src/edit.rs +++ b/src/edit.rs @@ -578,7 +578,7 @@ impl State<'_, '_, H> { /// Moves the cursor to the same column in the line above pub fn edit_move_line_up(&mut self, n: RepeatCount) -> Result { - if self.line.move_to_line_up(n) { + if self.line.move_to_line_up(n, &self.layout) { self.move_cursor(CmdKind::MoveCursor)?; Ok(true) } else { @@ -588,7 +588,7 @@ impl State<'_, '_, H> { /// Moves the cursor to the same column in the line above pub fn edit_move_line_down(&mut self, n: RepeatCount) -> Result { - if self.line.move_to_line_down(n) { + if self.line.move_to_line_down(n, &self.layout) { self.move_cursor(CmdKind::MoveCursor)?; Ok(true) } else { diff --git a/src/line_buffer.rs b/src/line_buffer.rs index 3f2dda9a5..4a10cae81 100644 --- a/src/line_buffer.rs +++ b/src/line_buffer.rs @@ -1,5 +1,6 @@ //! Line buffer with current cursor position use crate::keymap::{At, CharSearch, Movement, RepeatCount, Word}; +use crate::layout::Layout; use std::cmp::min; use std::fmt; use std::iter; @@ -582,7 +583,7 @@ impl LineBuffer { } /// Moves the cursor to the same column in the line above - pub fn move_to_line_up(&mut self, n: RepeatCount) -> bool { + pub fn move_to_line_up(&mut self, n: RepeatCount, layout: &Layout) -> bool { match self.buf[..self.pos].rfind('\n') { Some(off) => { let column = self.buf[off + 1..self.pos].graphemes(true).count(); @@ -596,9 +597,14 @@ impl LineBuffer { dest_end = dest_start - 1; dest_start = self.buf[..dest_end].rfind('\n').map_or(0, |n| n + 1); } + let offset = if dest_start == 0 { + layout.prompt_size.col + } else { + 0 + }; let gidx = self.buf[dest_start..dest_end] .grapheme_indices(true) - .nth(column); + .nth(column.saturating_sub(offset)); self.pos = gidx.map_or(off, |(idx, _)| dest_start + idx); // if there's no enough columns true @@ -652,11 +658,16 @@ impl LineBuffer { } /// Moves the cursor to the same column in the line above - pub fn move_to_line_down(&mut self, n: RepeatCount) -> bool { + pub fn move_to_line_down(&mut self, n: RepeatCount, layout: &Layout) -> bool { match self.buf[self.pos..].find('\n') { Some(off) => { let line_start = self.buf[..self.pos].rfind('\n').map_or(0, |n| n + 1); - let column = self.buf[line_start..self.pos].graphemes(true).count(); + let offset = if line_start == 0 { + layout.prompt_size.col + } else { + 0 + }; + let column = self.buf[line_start..self.pos].graphemes(true).count() + offset; let mut dest_start = self.pos + off + 1; let mut dest_end = self.buf[dest_start..] .find('\n') @@ -1182,7 +1193,10 @@ mod test { use super::{ ChangeListener, DeleteListener, Direction, LineBuffer, NoListener, WordAction, MAX_LINE, }; - use crate::keymap::{At, CharSearch, Word}; + use crate::{ + keymap::{At, CharSearch, Word}, + layout::Layout, + }; struct Listener { deleted_str: Option, @@ -1819,40 +1833,50 @@ mod test { fn move_by_line() { let text = "aa123\nsdf bc\nasdf"; let mut s = LineBuffer::init(text, 14); + let mut layout = Layout::default(); // move up - let ok = s.move_to_line_up(1); + let ok = s.move_to_line_up(1, &layout); assert_eq!(7, s.pos); assert!(ok); - let ok = s.move_to_line_up(1); + let ok = s.move_to_line_up(1, &layout); assert_eq!(1, s.pos); assert!(ok); - let ok = s.move_to_line_up(1); + let ok = s.move_to_line_up(1, &layout); assert_eq!(1, s.pos); assert!(!ok); // move down - let ok = s.move_to_line_down(1); + let ok = s.move_to_line_down(1, &layout); assert_eq!(7, s.pos); assert!(ok); - let ok = s.move_to_line_down(1); + let ok = s.move_to_line_down(1, &layout); assert_eq!(14, s.pos); assert!(ok); - let ok = s.move_to_line_down(1); + let ok = s.move_to_line_down(1, &layout); assert_eq!(14, s.pos); assert!(!ok); // move by multiple steps - let ok = s.move_to_line_up(2); + let ok = s.move_to_line_up(2, &layout); assert_eq!(1, s.pos); assert!(ok); - let ok = s.move_to_line_down(2); + let ok = s.move_to_line_down(2, &layout); assert_eq!(14, s.pos); assert!(ok); + + // non-empty prompt + layout.prompt_size.col = 2; + s.move_to_line_up(1, &layout); + assert_eq!(7, s.pos); + s.move_to_line_up(1, &layout); + assert_eq!(0, s.pos); + s.move_to_line_down(1, &layout); + assert_eq!(8, s.pos); } #[test] From 376a6fce008d75c4adc5dfccde410c593f69b25a Mon Sep 17 00:00:00 2001 From: gwenn Date: Sun, 27 Oct 2024 19:12:17 +0100 Subject: [PATCH 07/17] Reduce size - screen size / position: (u16, u16) - tabstop: u8 - indent size: u8 - repeat count: u16 --- src/config.rs | 20 ++++++++++---------- src/edit.rs | 6 +++--- src/keymap.rs | 6 ++++-- src/layout.rs | 4 ++-- src/lib.rs | 17 +++++++++-------- src/line_buffer.rs | 10 +++++++--- src/tty/mod.rs | 10 +++++----- src/tty/test.rs | 8 ++++---- src/tty/unix.rs | 28 ++++++++++++---------------- src/tty/windows.rs | 18 +++++++++--------- 10 files changed, 65 insertions(+), 62 deletions(-) diff --git a/src/config.rs b/src/config.rs index a4c3d12bc..b0092dcce 100644 --- a/src/config.rs +++ b/src/config.rs @@ -28,9 +28,9 @@ pub struct Config { /// Whether to use stdio or not behavior: Behavior, /// Horizontal space taken by a tab. - tab_stop: usize, + tab_stop: u8, /// Indentation size for indent/dedent commands - indent_size: usize, + indent_size: u8, /// Check if cursor position is at leftmost before displaying prompt check_cursor_position: bool, /// Bracketed paste on unix platform @@ -160,11 +160,11 @@ impl Config { /// /// By default, 8. #[must_use] - pub fn tab_stop(&self) -> usize { + pub fn tab_stop(&self) -> u8 { self.tab_stop } - pub(crate) fn set_tab_stop(&mut self, tab_stop: usize) { + pub(crate) fn set_tab_stop(&mut self, tab_stop: u8) { self.tab_stop = tab_stop; } @@ -180,11 +180,11 @@ impl Config { /// /// By default, 2. #[must_use] - pub fn indent_size(&self) -> usize { + pub fn indent_size(&self) -> u8 { self.indent_size } - pub(crate) fn set_indent_size(&mut self, indent_size: usize) { + pub(crate) fn set_indent_size(&mut self, indent_size: u8) { self.indent_size = indent_size; } @@ -433,7 +433,7 @@ impl Builder { /// /// By default, `8` #[must_use] - pub fn tab_stop(mut self, tab_stop: usize) -> Self { + pub fn tab_stop(mut self, tab_stop: u8) -> Self { self.set_tab_stop(tab_stop); self } @@ -451,7 +451,7 @@ impl Builder { /// /// By default, `2` #[must_use] - pub fn indent_size(mut self, indent_size: usize) -> Self { + pub fn indent_size(mut self, indent_size: u8) -> Self { self.set_indent_size(indent_size); self } @@ -568,7 +568,7 @@ pub trait Configurer { /// Horizontal space taken by a tab. /// /// By default, `8` - fn set_tab_stop(&mut self, tab_stop: usize) { + fn set_tab_stop(&mut self, tab_stop: u8) { self.config_mut().set_tab_stop(tab_stop); } @@ -581,7 +581,7 @@ pub trait Configurer { /// Indentation size for indent/dedent commands /// /// By default, `2` - fn set_indent_size(&mut self, size: usize) { + fn set_indent_size(&mut self, size: u8) { self.config_mut().set_indent_size(size); } diff --git a/src/edit.rs b/src/edit.rs index b18de9e2e..aa2b37160 100644 --- a/src/edit.rs +++ b/src/edit.rs @@ -353,7 +353,7 @@ impl State<'_, '_, H> { let prompt_size = self.prompt_size; let no_previous_hint = self.hint.is_none(); self.hint(); - let width = ch.width().unwrap_or(0); + let width = u16::try_from(ch.width().unwrap_or(0)).unwrap(); if n == 1 && width != 0 // Ctrl-V + \t or \n ... && self.layout.cursor.col + width < self.out.get_columns() @@ -382,7 +382,7 @@ impl State<'_, '_, H> { pub fn edit_replace_char(&mut self, ch: char, n: RepeatCount) -> Result<()> { self.changes.begin(); let succeed = if let Some(chars) = self.line.delete(n, &mut self.changes) { - let count = chars.graphemes(true).count(); + let count = u16::try_from(chars.graphemes(true).count()).unwrap(); self.line.insert(ch, count, &mut self.changes); self.line.move_backward(1); true @@ -732,7 +732,7 @@ impl State<'_, '_, H> { } /// Change the indentation of the lines covered by movement - pub fn edit_indent(&mut self, mvt: &Movement, amount: usize, dedent: bool) -> Result<()> { + pub fn edit_indent(&mut self, mvt: &Movement, amount: u8, dedent: bool) -> Result<()> { if self.line.indent(mvt, amount, dedent, &mut self.changes) { self.refresh_line() } else { diff --git a/src/keymap.rs b/src/keymap.rs index c8561207c..f47298bf8 100644 --- a/src/keymap.rs +++ b/src/keymap.rs @@ -10,7 +10,7 @@ use crate::{Config, EditMode}; use crate::{Event, EventContext, EventHandler}; /// The number of times one command should be repeated. -pub type RepeatCount = usize; +pub type RepeatCount = u16; /// Commands #[derive(Debug, Clone, Eq, PartialEq)] @@ -183,7 +183,9 @@ impl Cmd { let last_insert = wrt.last_insert(); if let Movement::ForwardChar(0) = mvt { Self::Replace( - Movement::ForwardChar(last_insert.as_ref().map_or(0, String::len)), + Movement::ForwardChar( + u16::try_from(last_insert.as_ref().map_or(0, String::len)).unwrap(), + ), last_insert, ) } else { diff --git a/src/layout.rs b/src/layout.rs index 5aa8834fa..82329635b 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -2,8 +2,8 @@ use std::cmp::Ordering; #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] pub struct Position { - pub col: usize, // The leftmost column is number 0. - pub row: usize, // The highest row is number 0. + pub col: u16, // The leftmost column is number 0. + pub row: u16, // The highest row is number 0. } impl PartialOrd for Position { diff --git a/src/lib.rs b/src/lib.rs index 701565037..3e7c220db 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -292,15 +292,16 @@ fn page_completions( cols, candidates .iter() - .map(|s| s.display().width()) + .map(|s| u16::try_from(s.display().width()).unwrap()) .max() .unwrap() + min_col_pad, ); let num_cols = cols / max_width; + let nbc = u16::try_from(candidates.len()).unwrap(); let mut pause_row = s.out.get_rows() - 1; - let num_rows = candidates.len().div_ceil(num_cols); + let num_rows = nbc.div_ceil(num_cols); let mut ab = String::new(); for row in 0..num_rows { if row == pause_row { @@ -334,15 +335,15 @@ fn page_completions( ab.clear(); for col in 0..num_cols { let i = (col * num_rows) + row; - if i < candidates.len() { - let candidate = &candidates[i].display(); - let width = candidate.width(); + if i < nbc { + let candidate = &candidates[i as usize].display(); + let width = u16::try_from(candidate.width()).unwrap(); if let Some(highlighter) = s.highlighter() { ab.push_str(&highlighter.highlight_candidate(candidate, CompletionType::List)); } else { ab.push_str(candidate); } - if ((col + 1) * num_rows) + row < candidates.len() { + if ((col + 1) * num_rows) + row < nbc { for _ in width..max_width { ab.push(' '); } @@ -776,7 +777,7 @@ impl Editor { cmd, Cmd::AcceptLine | Cmd::Newline | Cmd::AcceptOrInsertLine { .. } ) { - self.term.cursor = s.layout.cursor.col; + self.term.cursor = s.layout.cursor.col as usize; } // Execute things can be done solely on a state object @@ -894,7 +895,7 @@ impl Editor { /// If output stream is a tty, this function returns its width and height as /// a number of characters. - pub fn dimensions(&mut self) -> Option<(usize, usize)> { + pub fn dimensions(&mut self) -> Option<(u16, u16)> { if self.term.is_output_tty() { let out = self.term.create_writer(); Some((out.get_columns(), out.get_rows())) diff --git a/src/line_buffer.rs b/src/line_buffer.rs index 3f2dda9a5..2cceb6668 100644 --- a/src/line_buffer.rs +++ b/src/line_buffer.rs @@ -202,7 +202,7 @@ impl LineBuffer { } self.buf[self.pos..] .grapheme_indices(true) - .take(n) + .take(usize::from(n)) .last() .map(|(i, s)| i + self.pos + s.len()) } @@ -216,7 +216,7 @@ impl LineBuffer { self.buf[..self.pos] .grapheme_indices(true) .rev() - .take(n) + .take(usize::from(n)) .last() .map(|(i, _)| i) } @@ -231,6 +231,7 @@ impl LineBuffer { n: RepeatCount, cl: &mut C, ) -> Option { + let n = usize::from(n); let shift = ch.len_utf8() * n; if self.must_truncate(self.buf.len() + shift) { return None; @@ -257,6 +258,7 @@ impl LineBuffer { n: RepeatCount, cl: &mut C, ) -> Option { + let n = usize::from(n); let shift = text.len() * n; if text.is_empty() || self.must_truncate(self.buf.len() + shift) { return None; @@ -682,6 +684,7 @@ impl LineBuffer { } fn search_char_pos(&self, cs: CharSearch, n: RepeatCount) -> Option { + let n = usize::from(n); let mut shift = 0; let search_result = match cs { CharSearch::Backward(c) | CharSearch::BackwardAfter(c) => self.buf[..self.pos] @@ -1083,7 +1086,7 @@ impl LineBuffer { pub fn indent( &mut self, mvt: &Movement, - amount: usize, + amount: u8, dedent: bool, cl: &mut C, ) -> bool { @@ -1108,6 +1111,7 @@ impl LineBuffer { Movement::LineUp(n) => self.n_lines_up(n), Movement::LineDown(n) => self.n_lines_down(n), }; + let amount = usize::from(amount); let (start, end) = pair.unwrap_or((self.pos, self.pos)); let start = self.buf[..start].rfind('\n').map_or(0, |pos| pos + 1); let end = self.buf[end..] diff --git a/src/tty/mod.rs b/src/tty/mod.rs index 22dcb8a2b..bdf216911 100644 --- a/src/tty/mod.rs +++ b/src/tty/mod.rs @@ -108,9 +108,9 @@ pub trait Renderer { /// Update the number of columns/rows in the current terminal. fn update_size(&mut self); /// Get the number of columns in the current terminal. - fn get_columns(&self) -> usize; + fn get_columns(&self) -> u16; /// Get the number of rows in the current terminal. - fn get_rows(&self) -> usize; + fn get_rows(&self) -> u16; /// Check if output supports colors. fn colors_enabled(&self) -> bool; @@ -119,7 +119,7 @@ pub trait Renderer { } // ignore ANSI escape sequence -fn width(s: &str, esc_seq: &mut u8) -> usize { +fn width(s: &str, esc_seq: &mut u8) -> u16 { if *esc_seq == 1 { if s == "[" { // CSI @@ -145,7 +145,7 @@ fn width(s: &str, esc_seq: &mut u8) -> usize { } else if s == "\n" { 0 } else { - s.width() + u16::try_from(s.width()).unwrap() } } @@ -168,7 +168,7 @@ pub trait Term { fn new( color_mode: ColorMode, behavior: Behavior, - tab_stop: usize, + tab_stop: u8, bell_style: BellStyle, enable_bracketed_paste: bool, enable_signals: bool, diff --git a/src/tty/test.rs b/src/tty/test.rs index 3478baf9f..38bc9bc1e 100644 --- a/src/tty/test.rs +++ b/src/tty/test.rs @@ -114,7 +114,7 @@ impl Renderer for Sink { fn calculate_position(&self, s: &str, orig: Position) -> Position { let mut pos = orig; - pos.col += s.len(); + pos.col += u16::try_from(s.len()).unwrap(); pos } @@ -136,11 +136,11 @@ impl Renderer for Sink { fn update_size(&mut self) {} - fn get_columns(&self) -> usize { + fn get_columns(&self) -> u16 { 80 } - fn get_rows(&self) -> usize { + fn get_rows(&self) -> u16 { 24 } @@ -183,7 +183,7 @@ impl Term for DummyTerminal { fn new( color_mode: ColorMode, _behavior: Behavior, - _tab_stop: usize, + _tab_stop: u8, bell_style: BellStyle, _enable_bracketed_paste: bool, _enable_signals: bool, diff --git a/src/tty/unix.rs b/src/tty/unix.rs index f08d700b5..ff87c69b7 100644 --- a/src/tty/unix.rs +++ b/src/tty/unix.rs @@ -41,7 +41,7 @@ const BRACKETED_PASTE_OFF: &str = "\x1b[?2004l"; nix::ioctl_read_bad!(win_size, libc::TIOCGWINSZ, libc::winsize); -fn get_win_size(fd: RawFd) -> (usize, usize) { +fn get_win_size(fd: RawFd) -> (u16, u16) { use std::mem::zeroed; if cfg!(test) { @@ -56,15 +56,11 @@ fn get_win_size(fd: RawFd) -> (usize, usize) { // zero. If host application didn't initialize the correct // size before start we treat zero size as 80 columns and // infinite rows - let cols = if size.ws_col == 0 { - 80 - } else { - size.ws_col as usize - }; + let cols = if size.ws_col == 0 { 80 } else { size.ws_col }; let rows = if size.ws_row == 0 { - usize::MAX + u16::MAX } else { - size.ws_row as usize + size.ws_row }; (cols, rows) } @@ -892,15 +888,15 @@ impl Receiver for Utf8 { /// Console output writer pub struct PosixRenderer { out: RawFd, - cols: usize, // Number of columns in terminal + cols: u16, // Number of columns in terminal buffer: String, - tab_stop: usize, + tab_stop: u16, colors_enabled: bool, bell_style: BellStyle, } impl PosixRenderer { - fn new(out: RawFd, tab_stop: usize, colors_enabled: bool, bell_style: BellStyle) -> Self { + fn new(out: RawFd, tab_stop: u16, colors_enabled: bool, bell_style: BellStyle) -> Self { let (cols, _) = get_win_size(out); Self { out, @@ -1100,13 +1096,13 @@ impl Renderer for PosixRenderer { self.cols = cols; } - fn get_columns(&self) -> usize { + fn get_columns(&self) -> u16 { self.cols } /// Try to get the number of rows in the current terminal, /// or assume 24 if it fails. - fn get_rows(&self) -> usize { + fn get_rows(&self) -> u16 { let (_, rows) = get_win_size(self.out); rows } @@ -1262,7 +1258,7 @@ pub struct PosixTerminal { is_out_a_tty: bool, close_on_drop: bool, pub(crate) color_mode: ColorMode, - tab_stop: usize, + tab_stop: u8, bell_style: BellStyle, enable_bracketed_paste: bool, raw_mode: Arc, @@ -1296,7 +1292,7 @@ impl Term for PosixTerminal { fn new( color_mode: ColorMode, behavior: Behavior, - tab_stop: usize, + tab_stop: u8, bell_style: BellStyle, enable_bracketed_paste: bool, enable_signals: bool, @@ -1424,7 +1420,7 @@ impl Term for PosixTerminal { fn create_writer(&self) -> PosixRenderer { PosixRenderer::new( self.tty_out, - self.tab_stop, + u16::from(self.tab_stop), self.colors_enabled(), self.bell_style, ) diff --git a/src/tty/windows.rs b/src/tty/windows.rs index 02cf69a4b..8ce8a2154 100644 --- a/src/tty/windows.rs +++ b/src/tty/windows.rs @@ -52,13 +52,13 @@ fn check(rc: BOOL) -> io::Result<()> { } } -fn get_win_size(handle: HANDLE) -> (usize, usize) { +fn get_win_size(handle: HANDLE) -> (u16, u16) { let mut info = unsafe { mem::zeroed() }; match unsafe { console::GetConsoleScreenBufferInfo(handle, &mut info) } { FALSE => (80, 24), _ => ( - info.dwSize.X as usize, - (1 + info.srWindow.Bottom - info.srWindow.Top) as usize, + u16::try_from(info.dwSize.X).unwrap(), + u16::try_from(1 + info.srWindow.Bottom - info.srWindow.Top).unwrap(), ), // (info.srWindow.Right - info.srWindow.Left + 1) } } @@ -286,7 +286,7 @@ fn read_input(handle: HANDLE, max_count: u32) -> Result { pub struct ConsoleRenderer { conout: HANDLE, - cols: usize, // Number of columns in terminal + cols: u16, // Number of columns in terminal buffer: String, utf16: Vec, colors_enabled: bool, @@ -343,7 +343,7 @@ impl ConsoleRenderer { // You can't have both ENABLE_WRAP_AT_EOL_OUTPUT and // ENABLE_VIRTUAL_TERMINAL_PROCESSING. So we need to wrap manually. - fn wrap_at_eol(&mut self, s: &str, mut col: usize) -> usize { + fn wrap_at_eol(&mut self, s: &str, mut col: u16) -> u16 { let mut esc_seq = 0; for c in s.graphemes(true) { if c == "\n" { @@ -501,7 +501,7 @@ impl Renderer for ConsoleRenderer { pos.col = 0; pos.row += 1; } else { - let cw = c.width(); + let cw = u16::try_from(c.width()).unwrap(); pos.col += cw; if pos.col > self.cols { pos.row += 1; @@ -544,13 +544,13 @@ impl Renderer for ConsoleRenderer { self.cols = cols; } - fn get_columns(&self) -> usize { + fn get_columns(&self) -> u16 { self.cols } /// Try to get the number of rows in the current terminal, /// or assume 24 if it fails. - fn get_rows(&self) -> usize { + fn get_rows(&self) -> u16 { let (_, rows) = get_win_size(self.conout); rows } @@ -659,7 +659,7 @@ impl Term for Console { fn new( color_mode: ColorMode, behavior: Behavior, - _tab_stop: usize, + _tab_stop: u8, bell_style: BellStyle, _enable_bracketed_paste: bool, _enable_signals: bool, From 43bb4a48b6d8e08c9fd43494e8842b8079ed3c06 Mon Sep 17 00:00:00 2001 From: gwenn Date: Fri, 15 Nov 2024 17:00:07 +0100 Subject: [PATCH 08/17] Prepare release --- Cargo.toml | 38 +++++++++++++++++++++++++++---------- README.md | 2 +- rustyline-derive/Cargo.toml | 9 +++++++-- 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 97734f3d5..d3f87d557 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustyline" -version = "14.0.0" +version = "15.0.0" authors = ["Katsu Kawakami "] edition = "2021" description = "Rustyline, a readline implementation based on Antirez's Linenoise" @@ -11,10 +11,7 @@ keywords = ["readline"] license = "MIT" categories = ["command-line-interface"] -exclude = [ - "/.github/*", - "/rustfmt.toml", -] +exclude = ["/.github/*", "/rustfmt.toml"] [badges] maintenance = { status = "actively-developed" } @@ -29,7 +26,10 @@ cfg-if = "1.0" home = { version = "0.5.4", optional = true } # For History fd-lock = { version = "4.0.0", optional = true } -rusqlite = { version = "0.32.0", optional = true, default-features = false, features = ["bundled", "backup"] } +rusqlite = { version = "0.32.0", optional = true, default-features = false, features = [ + "bundled", + "backup", +] } libc = "0.2.155" log = "0.4.22" unicode-width = "0.2.0" @@ -39,10 +39,16 @@ memchr = "2.7" radix_trie = { version = "0.2", optional = true } regex = { version = "1.10", optional = true } # For derive -rustyline-derive = { version = "0.10.0", optional = true, path = "rustyline-derive" } +rustyline-derive = { version = "0.11.0", optional = true, path = "rustyline-derive" } [target.'cfg(unix)'.dependencies] -nix = { version = "0.29", default-features = false, features = ["fs", "ioctl", "poll", "signal", "term"] } +nix = { version = "0.29", default-features = false, features = [ + "fs", + "ioctl", + "poll", + "signal", + "term", +] } utf8parse = "0.2" skim = { version = "0.10", optional = true, default-features = false } signal-hook = { version = "0.3", optional = true, default-features = false } @@ -50,7 +56,13 @@ termios = { version = "0.3.3", optional = true } buffer-redux = { version = "1.0", optional = true, default-features = false } [target.'cfg(windows)'.dependencies] -windows-sys = { version = "0.59.0", features = ["Win32_Foundation", "Win32_System_Console", "Win32_Security", "Win32_System_Threading", "Win32_UI_Input_KeyboardAndMouse"] } +windows-sys = { version = "0.59.0", features = [ + "Win32_Foundation", + "Win32_System_Console", + "Win32_Security", + "Win32_System_Threading", + "Win32_UI_Input_KeyboardAndMouse", +] } clipboard-win = "5.0" [dev-dependencies] @@ -96,7 +108,13 @@ name = "sqlite_history" required-features = ["with-sqlite-history"] [package.metadata.docs.rs] -features = ["custom-bindings", "derive", "with-dirs", "with-file-history", "with-fuzzy"] +features = [ + "custom-bindings", + "derive", + "with-dirs", + "with-file-history", + "with-fuzzy", +] all-features = false no-default-features = true default-target = "x86_64-unknown-linux-gnu" diff --git a/README.md b/README.md index d6bf9c684..f95d14020 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ to your `Cargo.toml`: ```toml [dependencies] -rustyline = "14.0.0" +rustyline = "15.0.0" ``` ## Features diff --git a/rustyline-derive/Cargo.toml b/rustyline-derive/Cargo.toml index d1596578c..6713b789c 100644 --- a/rustyline-derive/Cargo.toml +++ b/rustyline-derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustyline-derive" -version = "0.10.0" +version = "0.11.0" authors = ["gwenn"] edition = "2018" description = "Rustyline macros implementation of #[derive(Completer, Helper, Hinter, Highlighter)]" @@ -19,6 +19,11 @@ maintenance = { status = "actively-developed" } proc-macro = true [dependencies] -syn = { version = "2.0.72", default-features = false, features = ["derive", "parsing", "printing", "proc-macro"] } +syn = { version = "2.0.72", default-features = false, features = [ + "derive", + "parsing", + "printing", + "proc-macro", +] } quote = { version = "1.0.36", default-features = false } proc-macro2 = { version = "1.0.86", default-features = false } From 2c048eed22d8fb2555eeadd8d8bcf58b5b1ed327 Mon Sep 17 00:00:00 2001 From: gwenn Date: Sat, 23 Nov 2024 11:14:04 +0100 Subject: [PATCH 09/17] Fix clippy warnings --- rustyline-derive/src/lib.rs | 2 +- src/tty/unix.rs | 2 +- src/undo.rs | 13 +++++++------ 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/rustyline-derive/src/lib.rs b/rustyline-derive/src/lib.rs index e23908c84..e7082ab6d 100644 --- a/rustyline-derive/src/lib.rs +++ b/rustyline-derive/src/lib.rs @@ -10,7 +10,7 @@ fn get_field_by_attr<'a>(data: &'a Data, ident: &str) -> Option<(usize, &'a Fiel attr.path().is_ident("rustyline") && attr .parse_args::() - .map_or(false, |arg| arg.is_ident(ident)) + .is_ok_and(|arg| arg.is_ident(ident)) }) }); diff --git a/src/tty/unix.rs b/src/tty/unix.rs index f08d700b5..9672138c9 100644 --- a/src/tty/unix.rs +++ b/src/tty/unix.rs @@ -751,7 +751,7 @@ impl PosixRawReader { continue; } }; - if sigwinch_pipe.map_or(false, |fd| readfds.contains(fd)) { + if sigwinch_pipe.is_some_and(|fd| readfds.contains(fd)) { self.tty_in.get_ref().sigwinch()?; return Err(ReadlineError::WindowResized); } else if readfds.contains(tty_in) { diff --git a/src/undo.rs b/src/undo.rs index 924319f24..cd5cae27e 100644 --- a/src/undo.rs +++ b/src/undo.rs @@ -147,7 +147,7 @@ impl Changeset { pub(crate) fn insert(&mut self, idx: usize, c: char) { debug!(target: "rustyline", "Changeset::insert({}, {:?})", idx, c); self.redos.clear(); - if !c.is_alphanumeric() || !self.undos.last().map_or(false, |lc| lc.insert_seq(idx)) { + if !c.is_alphanumeric() || !self.undos.last().is_some_and(|lc| lc.insert_seq(idx)) { self.undos.push(Self::insert_char(idx, c)); return; } @@ -188,7 +188,7 @@ impl Changeset { || !self .undos .last() - .map_or(false, |lc| lc.delete_seq(indx, string.as_ref().len())) + .is_some_and(|lc| lc.delete_seq(indx, string.as_ref().len())) { self.undos.push(Change::Delete { idx: indx, @@ -217,9 +217,10 @@ impl Changeset { fn single_char(s: &str) -> bool { let mut graphemes = s.graphemes(true); - graphemes.next().map_or(false, |grapheme| { - grapheme.chars().all(char::is_alphanumeric) - }) && graphemes.next().is_none() + graphemes + .next() + .is_some_and(|grapheme| grapheme.chars().all(char::is_alphanumeric)) + && graphemes.next().is_none() } pub(crate) fn replace + Into + Debug>( @@ -231,7 +232,7 @@ impl Changeset { debug!(target: "rustyline", "Changeset::replace({}, {:?}, {:?})", indx, old_, new_); self.redos.clear(); - if !self.undos.last().map_or(false, |lc| lc.replace_seq(indx)) { + if !self.undos.last().is_some_and(|lc| lc.replace_seq(indx)) { self.undos.push(Change::Replace { idx: indx, old: old_.into(), From 3bd2a1baa7d426f9efcdbfeb2411efc01359b2dd Mon Sep 17 00:00:00 2001 From: gwenn Date: Fri, 6 Dec 2024 18:14:52 +0100 Subject: [PATCH 10/17] Fix clippy warnings --- src/history.rs | 2 +- src/sqlite_history.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/history.rs b/src/history.rs index f3237d7aa..51abe637f 100644 --- a/src/history.rs +++ b/src/history.rs @@ -259,7 +259,7 @@ impl MemHistory { return true; } if line.is_empty() - || (self.ignore_space && line.chars().next().map_or(true, char::is_whitespace)) + || (self.ignore_space && line.chars().next().is_none_or(char::is_whitespace)) { return true; } diff --git a/src/sqlite_history.rs b/src/sqlite_history.rs index 867f5b7d5..3d1fd8c95 100644 --- a/src/sqlite_history.rs +++ b/src/sqlite_history.rs @@ -162,7 +162,7 @@ COMMIT; return true; } if line.is_empty() - || (self.ignore_space && line.chars().next().map_or(true, char::is_whitespace)) + || (self.ignore_space && line.chars().next().is_none_or(char::is_whitespace)) { return true; } @@ -372,7 +372,7 @@ PRAGMA incremental_vacuum; // TODO check that there is no memory entries (session_id == 0) ? self.reset(path)?; self.check_schema()?; - } else if self.path.as_ref().map_or(true, |p| p != path) { + } else if self.path.as_ref().is_none_or(|p| p != path) { self.reset(path)?; self.check_schema()?; } From ffb94cc0c34e43cf1fb284eef10e121b3a986aa0 Mon Sep 17 00:00:00 2001 From: gwenn Date: Sat, 14 Dec 2024 14:26:54 +0100 Subject: [PATCH 11/17] Introduce Unit --- src/edit.rs | 7 +++---- src/keymap.rs | 3 ++- src/layout.rs | 16 ++++++++++++++-- src/lib.rs | 8 ++++---- src/tty/mod.rs | 12 +++++------- src/tty/test.rs | 6 +++--- src/tty/unix.rs | 18 +++++++++--------- src/tty/windows.rs | 19 +++++++++---------- 8 files changed, 49 insertions(+), 40 deletions(-) diff --git a/src/edit.rs b/src/edit.rs index aa2b37160..c875ab81c 100644 --- a/src/edit.rs +++ b/src/edit.rs @@ -3,7 +3,6 @@ use log::debug; use std::fmt; use unicode_segmentation::UnicodeSegmentation; -use unicode_width::UnicodeWidthChar; use super::{Context, Helper, Result}; use crate::error::ReadlineError; @@ -12,7 +11,7 @@ use crate::hint::Hint; use crate::history::SearchDirection; use crate::keymap::{Anchor, At, CharSearch, Cmd, Movement, RepeatCount, Word}; use crate::keymap::{InputState, Invoke, Refresher}; -use crate::layout::{Layout, Position}; +use crate::layout::{cwidh, Layout, Position}; use crate::line_buffer::{ ChangeListener, DeleteListener, Direction, LineBuffer, NoListener, WordAction, MAX_LINE, }; @@ -353,7 +352,7 @@ impl State<'_, '_, H> { let prompt_size = self.prompt_size; let no_previous_hint = self.hint.is_none(); self.hint(); - let width = u16::try_from(ch.width().unwrap_or(0)).unwrap(); + let width = cwidh(ch); if n == 1 && width != 0 // Ctrl-V + \t or \n ... && self.layout.cursor.col + width < self.out.get_columns() @@ -382,7 +381,7 @@ impl State<'_, '_, H> { pub fn edit_replace_char(&mut self, ch: char, n: RepeatCount) -> Result<()> { self.changes.begin(); let succeed = if let Some(chars) = self.line.delete(n, &mut self.changes) { - let count = u16::try_from(chars.graphemes(true).count()).unwrap(); + let count = RepeatCount::try_from(chars.graphemes(true).count()).unwrap(); self.line.insert(ch, count, &mut self.changes); self.line.move_backward(1); true diff --git a/src/keymap.rs b/src/keymap.rs index f47298bf8..8288d2e22 100644 --- a/src/keymap.rs +++ b/src/keymap.rs @@ -184,7 +184,8 @@ impl Cmd { if let Movement::ForwardChar(0) = mvt { Self::Replace( Movement::ForwardChar( - u16::try_from(last_insert.as_ref().map_or(0, String::len)).unwrap(), + RepeatCount::try_from(last_insert.as_ref().map_or(0, String::len)) + .unwrap(), ), last_insert, ) diff --git a/src/layout.rs b/src/layout.rs index 82329635b..4e73a4c8a 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -1,9 +1,21 @@ use std::cmp::Ordering; +/// Height, width +pub type Unit = u16; + +pub(crate) fn cwidh(c: char) -> Unit { + use unicode_width::UnicodeWidthChar; + Unit::try_from(c.width().unwrap_or(0)).unwrap() +} +pub(crate) fn swidth(s: &str) -> Unit { + use unicode_width::UnicodeWidthStr; + Unit::try_from(s.width()).unwrap() +} + #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] pub struct Position { - pub col: u16, // The leftmost column is number 0. - pub row: u16, // The highest row is number 0. + pub col: Unit, // The leftmost column is number 0. + pub row: Unit, // The highest row is number 0. } impl PartialOrd for Position { diff --git a/src/lib.rs b/src/lib.rs index 3e7c220db..664366880 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,7 +49,6 @@ use log::debug; #[cfg(feature = "derive")] #[cfg_attr(docsrs, doc(cfg(feature = "derive")))] pub use rustyline_derive::{Completer, Helper, Highlighter, Hinter, Validator}; -use unicode_width::UnicodeWidthStr; use crate::tty::{Buffer, RawMode, RawReader, Renderer, Term, Terminal}; @@ -66,6 +65,7 @@ pub use crate::keymap::{Anchor, At, CharSearch, Cmd, InputMode, Movement, Repeat use crate::keymap::{Bindings, InputState, Refresher}; pub use crate::keys::{KeyCode, KeyEvent, Modifiers}; use crate::kill_ring::KillRing; +use crate::layout::{swidth, Unit}; pub use crate::tty::ExternalPrinter; pub use crate::undo::Changeset; use crate::validate::Validator; @@ -292,7 +292,7 @@ fn page_completions( cols, candidates .iter() - .map(|s| u16::try_from(s.display().width()).unwrap()) + .map(|s| swidth(s.display())) .max() .unwrap() + min_col_pad, @@ -337,7 +337,7 @@ fn page_completions( let i = (col * num_rows) + row; if i < nbc { let candidate = &candidates[i as usize].display(); - let width = u16::try_from(candidate.width()).unwrap(); + let width = swidth(candidate); if let Some(highlighter) = s.highlighter() { ab.push_str(&highlighter.highlight_candidate(candidate, CompletionType::List)); } else { @@ -895,7 +895,7 @@ impl Editor { /// If output stream is a tty, this function returns its width and height as /// a number of characters. - pub fn dimensions(&mut self) -> Option<(u16, u16)> { + pub fn dimensions(&mut self) -> Option<(Unit, Unit)> { if self.term.is_output_tty() { let out = self.term.create_writer(); Some((out.get_columns(), out.get_rows())) diff --git a/src/tty/mod.rs b/src/tty/mod.rs index bdf216911..52522b46b 100644 --- a/src/tty/mod.rs +++ b/src/tty/mod.rs @@ -1,11 +1,9 @@ //! This module implements and describes common TTY methods & traits -use unicode_width::UnicodeWidthStr; - use crate::config::{Behavior, BellStyle, ColorMode, Config}; use crate::highlight::Highlighter; use crate::keys::KeyEvent; -use crate::layout::{Layout, Position}; +use crate::layout::{swidth, Layout, Position, Unit}; use crate::line_buffer::LineBuffer; use crate::{Cmd, Result}; @@ -108,9 +106,9 @@ pub trait Renderer { /// Update the number of columns/rows in the current terminal. fn update_size(&mut self); /// Get the number of columns in the current terminal. - fn get_columns(&self) -> u16; + fn get_columns(&self) -> Unit; /// Get the number of rows in the current terminal. - fn get_rows(&self) -> u16; + fn get_rows(&self) -> Unit; /// Check if output supports colors. fn colors_enabled(&self) -> bool; @@ -119,7 +117,7 @@ pub trait Renderer { } // ignore ANSI escape sequence -fn width(s: &str, esc_seq: &mut u8) -> u16 { +fn width(s: &str, esc_seq: &mut u8) -> Unit { if *esc_seq == 1 { if s == "[" { // CSI @@ -145,7 +143,7 @@ fn width(s: &str, esc_seq: &mut u8) -> u16 { } else if s == "\n" { 0 } else { - u16::try_from(s.width()).unwrap() + swidth(s) } } diff --git a/src/tty/test.rs b/src/tty/test.rs index 38bc9bc1e..97470dcec 100644 --- a/src/tty/test.rs +++ b/src/tty/test.rs @@ -7,7 +7,7 @@ use crate::config::{Behavior, BellStyle, ColorMode, Config}; use crate::error::ReadlineError; use crate::highlight::Highlighter; use crate::keys::KeyEvent; -use crate::layout::{Layout, Position}; +use crate::layout::{Layout, Position, Unit}; use crate::line_buffer::LineBuffer; use crate::{Cmd, Result}; @@ -136,11 +136,11 @@ impl Renderer for Sink { fn update_size(&mut self) {} - fn get_columns(&self) -> u16 { + fn get_columns(&self) -> Unit { 80 } - fn get_rows(&self) -> u16 { + fn get_rows(&self) -> Unit { 24 } diff --git a/src/tty/unix.rs b/src/tty/unix.rs index ff87c69b7..feda3432e 100644 --- a/src/tty/unix.rs +++ b/src/tty/unix.rs @@ -29,7 +29,7 @@ use super::{width, Event, RawMode, RawReader, Renderer, Term}; use crate::config::{Behavior, BellStyle, ColorMode, Config}; use crate::highlight::Highlighter; use crate::keys::{KeyCode as K, KeyEvent, KeyEvent as E, Modifiers as M}; -use crate::layout::{Layout, Position}; +use crate::layout::{Layout, Position, Unit}; use crate::line_buffer::LineBuffer; use crate::{error, Cmd, ReadlineError, Result}; @@ -41,7 +41,7 @@ const BRACKETED_PASTE_OFF: &str = "\x1b[?2004l"; nix::ioctl_read_bad!(win_size, libc::TIOCGWINSZ, libc::winsize); -fn get_win_size(fd: RawFd) -> (u16, u16) { +fn get_win_size(fd: RawFd) -> (Unit, Unit) { use std::mem::zeroed; if cfg!(test) { @@ -58,7 +58,7 @@ fn get_win_size(fd: RawFd) -> (u16, u16) { // infinite rows let cols = if size.ws_col == 0 { 80 } else { size.ws_col }; let rows = if size.ws_row == 0 { - u16::MAX + Unit::MAX } else { size.ws_row }; @@ -888,15 +888,15 @@ impl Receiver for Utf8 { /// Console output writer pub struct PosixRenderer { out: RawFd, - cols: u16, // Number of columns in terminal + cols: Unit, // Number of columns in terminal buffer: String, - tab_stop: u16, + tab_stop: Unit, colors_enabled: bool, bell_style: BellStyle, } impl PosixRenderer { - fn new(out: RawFd, tab_stop: u16, colors_enabled: bool, bell_style: BellStyle) -> Self { + fn new(out: RawFd, tab_stop: Unit, colors_enabled: bool, bell_style: BellStyle) -> Self { let (cols, _) = get_win_size(out); Self { out, @@ -1096,13 +1096,13 @@ impl Renderer for PosixRenderer { self.cols = cols; } - fn get_columns(&self) -> u16 { + fn get_columns(&self) -> Unit { self.cols } /// Try to get the number of rows in the current terminal, /// or assume 24 if it fails. - fn get_rows(&self) -> u16 { + fn get_rows(&self) -> Unit { let (_, rows) = get_win_size(self.out); rows } @@ -1420,7 +1420,7 @@ impl Term for PosixTerminal { fn create_writer(&self) -> PosixRenderer { PosixRenderer::new( self.tty_out, - u16::from(self.tab_stop), + Unit::from(self.tab_stop), self.colors_enabled(), self.bell_style, ) diff --git a/src/tty/windows.rs b/src/tty/windows.rs index 8ce8a2154..1d843f8ef 100644 --- a/src/tty/windows.rs +++ b/src/tty/windows.rs @@ -13,7 +13,6 @@ use std::sync::Arc; use log::{debug, warn}; use unicode_segmentation::UnicodeSegmentation; -use unicode_width::UnicodeWidthStr; use windows_sys::Win32::Foundation::{self as foundation, BOOL, FALSE, HANDLE, TRUE}; use windows_sys::Win32::System::Console as console; use windows_sys::Win32::System::Threading as threading; @@ -23,7 +22,7 @@ use super::{width, Event, RawMode, RawReader, Renderer, Term}; use crate::config::{Behavior, BellStyle, ColorMode, Config}; use crate::highlight::Highlighter; use crate::keys::{KeyCode as K, KeyEvent, Modifiers as M}; -use crate::layout::{Layout, Position}; +use crate::layout::{swidth, Layout, Position, Unit}; use crate::line_buffer::LineBuffer; use crate::{error, Cmd, Result}; @@ -52,13 +51,13 @@ fn check(rc: BOOL) -> io::Result<()> { } } -fn get_win_size(handle: HANDLE) -> (u16, u16) { +fn get_win_size(handle: HANDLE) -> (Unit, Unit) { let mut info = unsafe { mem::zeroed() }; match unsafe { console::GetConsoleScreenBufferInfo(handle, &mut info) } { FALSE => (80, 24), _ => ( - u16::try_from(info.dwSize.X).unwrap(), - u16::try_from(1 + info.srWindow.Bottom - info.srWindow.Top).unwrap(), + Unit::try_from(info.dwSize.X).unwrap(), + Unit::try_from(1 + info.srWindow.Bottom - info.srWindow.Top).unwrap(), ), // (info.srWindow.Right - info.srWindow.Left + 1) } } @@ -286,7 +285,7 @@ fn read_input(handle: HANDLE, max_count: u32) -> Result { pub struct ConsoleRenderer { conout: HANDLE, - cols: u16, // Number of columns in terminal + cols: Unit, // Number of columns in terminal buffer: String, utf16: Vec, colors_enabled: bool, @@ -343,7 +342,7 @@ impl ConsoleRenderer { // You can't have both ENABLE_WRAP_AT_EOL_OUTPUT and // ENABLE_VIRTUAL_TERMINAL_PROCESSING. So we need to wrap manually. - fn wrap_at_eol(&mut self, s: &str, mut col: u16) -> u16 { + fn wrap_at_eol(&mut self, s: &str, mut col: Unit) -> Unit { let mut esc_seq = 0; for c in s.graphemes(true) { if c == "\n" { @@ -501,7 +500,7 @@ impl Renderer for ConsoleRenderer { pos.col = 0; pos.row += 1; } else { - let cw = u16::try_from(c.width()).unwrap(); + let cw = swidth(c); pos.col += cw; if pos.col > self.cols { pos.row += 1; @@ -544,13 +543,13 @@ impl Renderer for ConsoleRenderer { self.cols = cols; } - fn get_columns(&self) -> u16 { + fn get_columns(&self) -> Unit { self.cols } /// Try to get the number of rows in the current terminal, /// or assume 24 if it fails. - fn get_rows(&self) -> u16 { + fn get_rows(&self) -> Unit { let (_, rows) = get_win_size(self.conout); rows } From cbced2a11f538c26c267ddd8497af2b3c65eac04 Mon Sep 17 00:00:00 2001 From: gwenn Date: Sat, 14 Dec 2024 15:33:46 +0100 Subject: [PATCH 12/17] Fix merge error --- src/edit.rs | 2 +- src/layout.rs | 3 ++- src/line_buffer.rs | 10 +++++----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/edit.rs b/src/edit.rs index 9d85ab6b5..6dc21928d 100644 --- a/src/edit.rs +++ b/src/edit.rs @@ -11,7 +11,7 @@ use crate::hint::Hint; use crate::history::SearchDirection; use crate::keymap::{Anchor, At, CharSearch, Cmd, Movement, RepeatCount, Word}; use crate::keymap::{InputState, Invoke, Refresher}; -use crate::layout::{cwidh, Layout, Position}; +use crate::layout::{cwidh, gcount, Layout, Position}; use crate::line_buffer::{ ChangeListener, DeleteListener, Direction, LineBuffer, NoListener, WordAction, MAX_LINE, }; diff --git a/src/layout.rs b/src/layout.rs index 4e73a4c8a..ba8d603f7 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -2,11 +2,12 @@ use std::cmp::Ordering; /// Height, width pub type Unit = u16; - +/// Character width / number of columns pub(crate) fn cwidh(c: char) -> Unit { use unicode_width::UnicodeWidthChar; Unit::try_from(c.width().unwrap_or(0)).unwrap() } +/// String width / number of columns pub(crate) fn swidth(s: &str) -> Unit { use unicode_width::UnicodeWidthStr; Unit::try_from(s.width()).unwrap() diff --git a/src/line_buffer.rs b/src/line_buffer.rs index 6c7974b7d..38c8812ff 100644 --- a/src/line_buffer.rs +++ b/src/line_buffer.rs @@ -1,6 +1,6 @@ //! Line buffer with current cursor position use crate::keymap::{At, CharSearch, Movement, RepeatCount, Word}; -use crate::layout::Layout; +use crate::layout::{swidth, Layout}; use std::cmp::min; use std::fmt; use std::iter; @@ -588,7 +588,7 @@ impl LineBuffer { pub fn move_to_line_up(&mut self, n: RepeatCount, layout: &Layout) -> bool { match self.buf[..self.pos].rfind('\n') { Some(off) => { - let column = self.buf[off + 1..self.pos].graphemes(true).count(); + let column = swidth(&self.buf[off + 1..self.pos]); let mut dest_start = self.buf[..off].rfind('\n').map_or(0, |n| n + 1); let mut dest_end = off; @@ -606,7 +606,7 @@ impl LineBuffer { }; let gidx = self.buf[dest_start..dest_end] .grapheme_indices(true) - .nth(column.saturating_sub(offset)); + .nth(column.saturating_sub(offset) as usize); self.pos = gidx.map_or(off, |(idx, _)| dest_start + idx); // if there's no enough columns true @@ -669,7 +669,7 @@ impl LineBuffer { } else { 0 }; - let column = self.buf[line_start..self.pos].graphemes(true).count() + offset; + let column = swidth(&self.buf[line_start..self.pos]) + offset; let mut dest_start = self.pos + off + 1; let mut dest_end = self.buf[dest_start..] .find('\n') @@ -685,7 +685,7 @@ impl LineBuffer { } self.pos = self.buf[dest_start..dest_end] .grapheme_indices(true) - .nth(column) + .nth(column as usize) .map_or(dest_end, |(idx, _)| dest_start + idx); // if there's no enough columns debug_assert!(self.pos <= self.buf.len()); true From 7521e9b55318a05764b8057bb1496f2b5f88acd5 Mon Sep 17 00:00:00 2001 From: gwenn Date: Sat, 14 Dec 2024 15:37:33 +0100 Subject: [PATCH 13/17] Oops --- src/edit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/edit.rs b/src/edit.rs index 6dc21928d..9d85ab6b5 100644 --- a/src/edit.rs +++ b/src/edit.rs @@ -11,7 +11,7 @@ use crate::hint::Hint; use crate::history::SearchDirection; use crate::keymap::{Anchor, At, CharSearch, Cmd, Movement, RepeatCount, Word}; use crate::keymap::{InputState, Invoke, Refresher}; -use crate::layout::{cwidh, gcount, Layout, Position}; +use crate::layout::{cwidh, Layout, Position}; use crate::line_buffer::{ ChangeListener, DeleteListener, Direction, LineBuffer, NoListener, WordAction, MAX_LINE, }; From 1807fe0ea64bf6c06c11f1bc1ed0c08c1f3e0b93 Mon Sep 17 00:00:00 2001 From: gwenn Date: Sat, 14 Dec 2024 15:50:07 +0100 Subject: [PATCH 14/17] Introduce Event::Timeout --- src/keymap.rs | 1 + src/tty/mod.rs | 2 ++ src/tty/unix.rs | 9 +++------ 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/keymap.rs b/src/keymap.rs index 784c91016..5be616001 100644 --- a/src/keymap.rs +++ b/src/keymap.rs @@ -441,6 +441,7 @@ impl<'b> InputState<'b> { tty::Event::ExternalPrint(msg) => { wrt.external_print(msg)?; } + _ => {} } } } diff --git a/src/tty/mod.rs b/src/tty/mod.rs index 62484f042..37f80936e 100644 --- a/src/tty/mod.rs +++ b/src/tty/mod.rs @@ -19,6 +19,8 @@ pub trait RawMode: Sized { pub enum Event { KeyPress(KeyEvent), ExternalPrint(String), + #[cfg(unix)] + Timeout(bool), } /// Translate bytes read from stdin to keys. diff --git a/src/tty/unix.rs b/src/tty/unix.rs index 3ec5a74b1..c8b82ec9a 100644 --- a/src/tty/unix.rs +++ b/src/tty/unix.rs @@ -716,8 +716,7 @@ impl PosixRawReader { if self.is_dev_tty { // poll doesn't work for /dev/tty on MacOS but select does return Ok(match self.select(Some(timeout), false /* ignored */)? { - // ugly but ESC means timeout... - Event::KeyPress(KeyEvent::ESC) => false, + Event::Timeout(true) => false, _ => true, }); } @@ -784,15 +783,13 @@ impl PosixRawReader { return Err(ReadlineError::WindowResized); } else if readfds.contains(tty_in) { if timeout.is_some() { - // ugly but ENTER means success (no timeout)... - return Ok(Event::KeyPress(KeyEvent::ENTER)); + return Ok(Event::Timeout(false)); } else { // prefer user input over external print return self.next_key(single_esc_abort).map(Event::KeyPress); } } else if timeout.is_some() { - // ugly but ESC means timeout... - return Ok(Event::KeyPress(KeyEvent::ESC)); + return Ok(Event::Timeout(true)); } else if let Some(ref pipe_reader) = self.pipe_reader { let mut guard = pipe_reader.lock().unwrap(); let mut buf = [0; 1]; From ea504426911679b9e9487734d3d4efa4acc60e5e Mon Sep 17 00:00:00 2001 From: gwenn Date: Sat, 14 Dec 2024 16:03:24 +0100 Subject: [PATCH 15/17] Try to fix clippy warning --- src/tty/mod.rs | 2 +- src/tty/unix.rs | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/tty/mod.rs b/src/tty/mod.rs index 37f80936e..491315482 100644 --- a/src/tty/mod.rs +++ b/src/tty/mod.rs @@ -19,7 +19,7 @@ pub trait RawMode: Sized { pub enum Event { KeyPress(KeyEvent), ExternalPrint(String), - #[cfg(unix)] + #[cfg(target_os = "macos")] Timeout(bool), } diff --git a/src/tty/unix.rs b/src/tty/unix.rs index c8b82ec9a..2f5a4d5c5 100644 --- a/src/tty/unix.rs +++ b/src/tty/unix.rs @@ -782,14 +782,17 @@ impl PosixRawReader { self.tty_in.get_ref().sigwinch()?; return Err(ReadlineError::WindowResized); } else if readfds.contains(tty_in) { + #[cfg(target_os = "macos")] if timeout.is_some() { return Ok(Event::Timeout(false)); - } else { - // prefer user input over external print - return self.next_key(single_esc_abort).map(Event::KeyPress); } + // prefer user input over external print + return self.next_key(single_esc_abort).map(Event::KeyPress); } else if timeout.is_some() { + #[cfg(target_os = "macos")] return Ok(Event::Timeout(true)); + #[cfg(not(target_os = "macos"))] + unreachable!() } else if let Some(ref pipe_reader) = self.pipe_reader { let mut guard = pipe_reader.lock().unwrap(); let mut buf = [0; 1]; From 2085d0c018f6ae0b1716dc09db6659c8ad87e813 Mon Sep 17 00:00:00 2001 From: gwenn Date: Sat, 14 Dec 2024 16:18:04 +0100 Subject: [PATCH 16/17] Fix build on unix --- src/keymap.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/keymap.rs b/src/keymap.rs index 5be616001..ac32156a9 100644 --- a/src/keymap.rs +++ b/src/keymap.rs @@ -441,6 +441,7 @@ impl<'b> InputState<'b> { tty::Event::ExternalPrint(msg) => { wrt.external_print(msg)?; } + #[cfg(target_os = "macos")] _ => {} } } From fcd5e6c0d26e594fb8f8adf43fe7c0fae7b6c425 Mon Sep 17 00:00:00 2001 From: gwenn Date: Sat, 14 Dec 2024 16:58:59 +0100 Subject: [PATCH 17/17] Use is_unsupported_term on both unix and windows --- src/tty/mod.rs | 31 +++++++++++++++++++++++++++++++ src/tty/unix.rs | 30 +----------------------------- src/tty/windows.rs | 3 +-- 3 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/tty/mod.rs b/src/tty/mod.rs index d4bceb6c2..abc02ccec 100644 --- a/src/tty/mod.rs +++ b/src/tty/mod.rs @@ -1,5 +1,8 @@ //! This module implements and describes common TTY methods & traits +/// Unsupported Terminals that don't support RAW mode +const UNSUPPORTED_TERM: [&str; 3] = ["dumb", "cons25", "emacs"]; + use crate::config::{Behavior, BellStyle, ColorMode, Config}; use crate::highlight::Highlighter; use crate::keys::KeyEvent; @@ -200,6 +203,22 @@ pub trait Term { fn set_cursor_visibility(&mut self, visible: bool) -> Result>; } +/// Check TERM environment variable to see if current term is in our +/// unsupported list +fn is_unsupported_term() -> bool { + match std::env::var("TERM") { + Ok(term) => { + for iter in &UNSUPPORTED_TERM { + if (*iter).eq_ignore_ascii_case(&term) { + return true; + } + } + false + } + Err(_) => false, + } +} + // If on Windows platform import Windows TTY module // and re-export into mod.rs scope #[cfg(all(windows, not(target_arch = "wasm32")))] @@ -218,3 +237,15 @@ pub use self::unix::*; mod test; #[cfg(any(test, target_arch = "wasm32"))] pub use self::test::*; + +#[cfg(test)] +mod test_ { + #[test] + fn test_unsupported_term() { + std::env::set_var("TERM", "xterm"); + assert!(!super::is_unsupported_term()); + + std::env::set_var("TERM", "dumb"); + assert!(super::is_unsupported_term()); + } +} diff --git a/src/tty/unix.rs b/src/tty/unix.rs index 0ef853921..1c25bbb07 100644 --- a/src/tty/unix.rs +++ b/src/tty/unix.rs @@ -34,9 +34,6 @@ use crate::layout::{Layout, Position, Unit}; use crate::line_buffer::LineBuffer; use crate::{error, Cmd, ReadlineError, Result}; -/// Unsupported Terminals that don't support RAW mode -const UNSUPPORTED_TERM: [&str; 3] = ["dumb", "cons25", "emacs"]; - const BRACKETED_PASTE_ON: &str = "\x1b[?2004h"; const BRACKETED_PASTE_OFF: &str = "\x1b[?2004l"; @@ -70,22 +67,6 @@ fn get_win_size(fd: RawFd) -> (Unit, Unit) { } } -/// Check TERM environment variable to see if current term is in our -/// unsupported list -fn is_unsupported_term() -> bool { - match std::env::var("TERM") { - Ok(term) => { - for iter in &UNSUPPORTED_TERM { - if (*iter).eq_ignore_ascii_case(&term) { - return true; - } - } - false - } - Err(_) => false, - } -} - /// Return whether or not STDIN, STDOUT or STDERR is a TTY fn is_a_tty(fd: RawFd) -> bool { isatty(fd).unwrap_or(false) @@ -1357,7 +1338,7 @@ impl Term for PosixTerminal { false, ) }; - let unsupported = is_unsupported_term(); + let unsupported = super::is_unsupported_term(); let sigwinch = if !unsupported && is_in_a_tty && is_out_a_tty { Some(SigWinCh::install_sigwinch_handler()?) } else { @@ -1686,15 +1667,6 @@ mod test { assert_eq!(0, pos.row); } - #[test] - fn test_unsupported_term() { - std::env::set_var("TERM", "xterm"); - assert!(!super::is_unsupported_term()); - - std::env::set_var("TERM", "dumb"); - assert!(super::is_unsupported_term()); - } - #[test] fn test_send() { fn assert_send() {} diff --git a/src/tty/windows.rs b/src/tty/windows.rs index 1d843f8ef..dc714031e 100644 --- a/src/tty/windows.rs +++ b/src/tty/windows.rs @@ -718,9 +718,8 @@ impl Term for Console { }) } - /// Checking for an unsupported TERM in windows is a no-op fn is_unsupported(&self) -> bool { - false + super::is_unsupported_term() } fn is_input_tty(&self) -> bool {