From dc64a685320cd7788bbeaab0b731de5415d39de2 Mon Sep 17 00:00:00 2001 From: Miraculous Owonubi Date: Sat, 19 Aug 2023 14:33:29 +0100 Subject: [PATCH 1/6] fix: restore terminal mode --- src/tty/unix.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tty/unix.rs b/src/tty/unix.rs index 20faf0c41..872c96e1c 100644 --- a/src/tty/unix.rs +++ b/src/tty/unix.rs @@ -14,7 +14,7 @@ use nix::errno::Errno; use nix::poll::{self, PollFlags}; use nix::sys::select::{self, FdSet}; use nix::unistd::{close, isatty, read, write}; -use termios::{tcgetattr, tcsetattr, Termios}; +use termios::{tcsetattr, Termios}; use unicode_segmentation::UnicodeSegmentation; use utf8parse::{Parser, Receiver}; @@ -107,7 +107,7 @@ impl RawMode for PosixMode { /// Disable RAW mode for the terminal. fn disable_raw_mode(&self) -> Result<()> { let mut termios = self.termios; - tcgetattr(self.tty_in, &mut termios)?; + tcsetattr(self.tty_in, termios::TCSADRAIN, &mut termios)?; // disable bracketed paste if let Some(out) = self.tty_out { write_all(out, BRACKETED_PASTE_OFF)?; From 2fd3f2e25bb8354eb09434595c46ccd4d8852be3 Mon Sep 17 00:00:00 2001 From: Miraculous Owonubi Date: Sat, 19 Aug 2023 15:10:52 +0100 Subject: [PATCH 2/6] fix clippy --- src/tty/unix.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tty/unix.rs b/src/tty/unix.rs index 872c96e1c..61a604b92 100644 --- a/src/tty/unix.rs +++ b/src/tty/unix.rs @@ -106,8 +106,7 @@ pub type Mode = PosixMode; impl RawMode for PosixMode { /// Disable RAW mode for the terminal. fn disable_raw_mode(&self) -> Result<()> { - let mut termios = self.termios; - tcsetattr(self.tty_in, termios::TCSADRAIN, &mut termios)?; + tcsetattr(self.tty_in, termios::TCSADRAIN, &self.termios)?; // disable bracketed paste if let Some(out) = self.tty_out { write_all(out, BRACKETED_PASTE_OFF)?; From 049121540400f9f0fe7bfda17358c0a7c238289c Mon Sep 17 00:00:00 2001 From: gwenn Date: Tue, 29 Aug 2023 19:06:59 +0200 Subject: [PATCH 3/6] [WIP] Bump nix dependency to version 0.27 --- Cargo.toml | 2 +- src/tty/unix.rs | 23 ++++++++++------------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5be68e087..a92a2a847 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,7 @@ regex = { version = "1.5.5", optional = true } rustyline-derive = { version = "0.9.0", optional = true, path = "rustyline-derive" } [target.'cfg(unix)'.dependencies] -nix = { version = "0.26", default-features = false, features = ["fs", "ioctl", "poll", "signal", "term"] } +nix = { version = "0.27", 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 } diff --git a/src/tty/unix.rs b/src/tty/unix.rs index 61a604b92..9df6059d6 100644 --- a/src/tty/unix.rs +++ b/src/tty/unix.rs @@ -3,7 +3,7 @@ use std::cmp; use std::collections::HashMap; use std::fs::{File, OpenOptions}; use std::io::{self, BufReader, ErrorKind, Read, Write}; -use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd}; +use std::os::unix::io::{AsFd, BorrowedFd, IntoRawFd, RawFd}; use std::os::unix::net::UnixStream; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{self, SyncSender}; @@ -181,11 +181,10 @@ pub struct PosixRawReader { key_map: PosixKeyMap, // external print reader pipe_reader: Option, - fds: FdSet, } -impl AsRawFd for PosixRawReader { - fn as_raw_fd(&self) -> RawFd { +impl AsFd for PosixRawReader { + fn as_fd(&self) -> BorrowedFd<'_> { self.tty_in.get_ref().fd } } @@ -235,7 +234,6 @@ impl PosixRawReader { parser: Parser::new(), key_map, pipe_reader, - fds: FdSet::new(), } } @@ -684,7 +682,7 @@ impl PosixRawReader { if n > 0 { return Ok(n as i32); } - let mut fds = [poll::PollFd::new(self.as_raw_fd(), PollFlags::POLLIN)]; + let mut fds = [poll::PollFd::new(self, PollFlags::POLLIN)]; let r = poll::poll(&mut fds, timeout_ms); match r { Ok(n) => Ok(n), @@ -700,24 +698,23 @@ impl PosixRawReader { } fn select(&mut self, single_esc_abort: bool) -> Result { - let tty_in = self.as_raw_fd(); + let tty_in = self.as_fd(); let sigwinch_pipe = self.tty_in.get_ref().sigwinch_pipe; let pipe_reader = self .pipe_reader .as_ref() - .map(|pr| pr.lock().unwrap().0.as_raw_fd()); + .map(|pr| pr.lock().unwrap().0.as_fd()); loop { - let mut readfds = self.fds; - readfds.clear(); + let mut readfds = FdSet::new(); if let Some(sigwinch_pipe) = sigwinch_pipe { readfds.insert(sigwinch_pipe); } - readfds.insert(tty_in); + readfds.insert(&tty_in); if let Some(pipe_reader) = pipe_reader { readfds.insert(pipe_reader); } if let Err(err) = select::select( - readfds.highest().map(|h| h + 1), + None, Some(&mut readfds), None, None, @@ -734,7 +731,7 @@ impl PosixRawReader { if sigwinch_pipe.map_or(false, |fd| readfds.contains(fd)) { self.tty_in.get_ref().sigwinch()?; return Err(ReadlineError::WindowResized); - } else if readfds.contains(tty_in) { + } else if readfds.contains(&tty_in) { // prefer user input over external print return self.next_key(single_esc_abort).map(Event::KeyPress); } else if let Some(ref pipe_reader) = self.pipe_reader { From 02e35f4a561b9fbc61bd93e1825a4127229bb71e Mon Sep 17 00:00:00 2001 From: gwenn Date: Tue, 29 Aug 2023 19:59:26 +0200 Subject: [PATCH 4/6] No highligh_char on final refresh --- examples/example.rs | 4 ++-- examples/read_password.rs | 2 +- src/command.rs | 4 +++- src/edit.rs | 8 ++++++-- src/highlight.rs | 14 +++++++++----- src/lib.rs | 2 ++ 6 files changed, 23 insertions(+), 11 deletions(-) diff --git a/examples/example.rs b/examples/example.rs index 8ed975b52..cc3e2a0cc 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -41,8 +41,8 @@ impl Highlighter for MyHelper { self.highlighter.highlight(line, pos) } - fn highlight_char(&self, line: &str, pos: usize) -> bool { - self.highlighter.highlight_char(line, pos) + fn highlight_char(&self, line: &str, pos: usize, forced: bool) -> bool { + self.highlighter.highlight_char(line, pos, forced) } } diff --git a/examples/read_password.rs b/examples/read_password.rs index f80081032..ddcdd79e9 100644 --- a/examples/read_password.rs +++ b/examples/read_password.rs @@ -20,7 +20,7 @@ impl Highlighter for MaskingHighlighter { } } - fn highlight_char(&self, _line: &str, _pos: usize) -> bool { + fn highlight_char(&self, line: &str, pos: usize, forced: bool) -> bool { self.masking } } diff --git a/src/command.rs b/src/command.rs index 3216f0e2e..d99c95e53 100644 --- a/src/command.rs +++ b/src/command.rs @@ -25,10 +25,12 @@ pub fn execute( match cmd { Cmd::EndOfFile | Cmd::AcceptLine | Cmd::AcceptOrInsertLine { .. } | Cmd::Newline => { - if s.has_hint() || !s.is_default_prompt() { + if s.has_hint() || !s.is_default_prompt() || s.highlight_char { // Force a refresh without hints to leave the previous // line as the user typed it after a newline. + s.forced_refresh = true; s.refresh_line_with_msg(None)?; + s.forced_refresh = false; } } _ => {} diff --git a/src/edit.rs b/src/edit.rs index e1dcb9f97..e6881eb44 100644 --- a/src/edit.rs +++ b/src/edit.rs @@ -35,7 +35,8 @@ pub struct State<'out, 'prompt, H: Helper> { pub helper: Option<&'out H>, pub ctx: Context<'out>, // Give access to history for `hinter` pub hint: Option>, // last hint displayed - highlight_char: bool, // `true` if a char has been highlighted + pub highlight_char: bool, // `true` if a char has been highlighted + pub forced_refresh: bool, // `true` if line is redraw without hint or highlight_char } enum Info<'m> { @@ -65,6 +66,7 @@ impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> { ctx, hint: None, highlight_char: false, + forced_refresh: false, } } @@ -205,7 +207,8 @@ impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> { fn highlight_char(&mut self) -> bool { if let Some(highlighter) = self.highlighter() { - let highlight_char = highlighter.highlight_char(&self.line, self.line.pos()); + let highlight_char = + highlighter.highlight_char(&self.line, self.line.pos(), self.forced_refresh); if highlight_char { self.highlight_char = true; true @@ -762,6 +765,7 @@ pub fn init_state<'out, H: Helper>( ctx: Context::new(history), hint: Some(Box::new("hint".to_owned())), highlight_char: false, + forced_refresh: false, } } diff --git a/src/highlight.rs b/src/highlight.rs index 00b616fd3..fbe57a28f 100644 --- a/src/highlight.rs +++ b/src/highlight.rs @@ -52,8 +52,8 @@ pub trait Highlighter { /// /// Used to optimize refresh when a character is inserted or the cursor is /// moved. - fn highlight_char(&self, line: &str, pos: usize) -> bool { - let _ = (line, pos); + fn highlight_char(&self, line: &str, pos: usize, forced: bool) -> bool { + let _ = (line, pos, forced); false } } @@ -85,8 +85,8 @@ impl<'r, H: ?Sized + Highlighter> Highlighter for &'r H { (**self).highlight_candidate(candidate, completion) } - fn highlight_char(&self, line: &str, pos: usize) -> bool { - (**self).highlight_char(line, pos) + fn highlight_char(&self, line: &str, pos: usize, forced: bool) -> bool { + (**self).highlight_char(line, pos, forced) } } @@ -124,7 +124,11 @@ impl Highlighter for MatchingBracketHighlighter { Borrowed(line) } - fn highlight_char(&self, line: &str, pos: usize) -> bool { + fn highlight_char(&self, line: &str, pos: usize, forced: bool) -> bool { + if forced { + self.bracket.set(None); + return false; + } // will highlight matching brace/bracket/parenthesis if it exists self.bracket.set(check_bracket(line, pos)); self.bracket.get().is_some() diff --git a/src/lib.rs b/src/lib.rs index 2cb0256c4..e15bb3029 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -787,7 +787,9 @@ impl Editor { // Move to end, in case cursor was in the middle of the line, so that // next thing application prints goes after the input + s.forced_refresh = true; s.edit_move_buffer_end()?; + s.forced_refresh = false; if cfg!(windows) { let _ = original_mode; // silent warning From 92f750b08edf89b191067cc9d9694bbdecd13e7c Mon Sep 17 00:00:00 2001 From: gwenn Date: Sat, 2 Sep 2023 11:15:59 +0200 Subject: [PATCH 5/6] Use unsafe borrow_raw method --- src/tty/unix.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/tty/unix.rs b/src/tty/unix.rs index 9df6059d6..39bf961cc 100644 --- a/src/tty/unix.rs +++ b/src/tty/unix.rs @@ -3,7 +3,7 @@ use std::cmp; use std::collections::HashMap; use std::fs::{File, OpenOptions}; use std::io::{self, BufReader, ErrorKind, Read, Write}; -use std::os::unix::io::{AsFd, BorrowedFd, IntoRawFd, RawFd}; +use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, IntoRawFd, RawFd}; use std::os::unix::net::UnixStream; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{self, SyncSender}; @@ -185,7 +185,8 @@ pub struct PosixRawReader { impl AsFd for PosixRawReader { fn as_fd(&self) -> BorrowedFd<'_> { - self.tty_in.get_ref().fd + let fd = self.tty_in.get_ref().fd; + unsafe { BorrowedFd::borrow_raw(fd) } } } @@ -699,27 +700,26 @@ impl PosixRawReader { fn select(&mut self, single_esc_abort: bool) -> Result { let tty_in = self.as_fd(); - let sigwinch_pipe = self.tty_in.get_ref().sigwinch_pipe; + 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_fd()); + .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 { + if let Some(ref sigwinch_pipe) = sigwinch_pipe { readfds.insert(sigwinch_pipe); } readfds.insert(&tty_in); - if let Some(pipe_reader) = pipe_reader { + if let Some(ref pipe_reader) = pipe_reader { readfds.insert(pipe_reader); } - if let Err(err) = select::select( - None, - Some(&mut readfds), - None, - None, - None, - ) { + if let Err(err) = select::select(None, Some(&mut readfds), None, None, None) { if err == Errno::EINTR && self.tty_in.get_ref().sigwinch()? { return Err(ReadlineError::WindowResized); } else if err != Errno::EINTR { @@ -728,7 +728,7 @@ impl PosixRawReader { continue; } }; - if sigwinch_pipe.map_or(false, |fd| readfds.contains(fd)) { + if sigwinch_pipe.map_or(false, |fd| readfds.contains(&fd)) { self.tty_in.get_ref().sigwinch()?; return Err(ReadlineError::WindowResized); } else if readfds.contains(&tty_in) { From 6d736c8bc80592cf9950ed835c2e6e3e6875a63f Mon Sep 17 00:00:00 2001 From: gwenn Date: Sat, 2 Sep 2023 11:39:17 +0200 Subject: [PATCH 6/6] Rustdoc `forced` flag --- src/highlight.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/highlight.rs b/src/highlight.rs index fbe57a28f..f3a816cc5 100644 --- a/src/highlight.rs +++ b/src/highlight.rs @@ -49,6 +49,7 @@ pub trait Highlighter { } /// Tells if `line` needs to be highlighted when a specific char is typed or /// when cursor is moved under a specific char. + /// `forced` flag is `true` mainly when user presses Enter (i.e. transient vs final highlight). /// /// Used to optimize refresh when a character is inserted or the cursor is /// moved.