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..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 = ch.width().unwrap_or(0); + 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 = chars.graphemes(true).count(); + let count = RepeatCount::try_from(chars.graphemes(true).count()).unwrap(); self.line.insert(ch, count, &mut self.changes); self.line.move_backward(1); true @@ -732,7 +731,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..8288d2e22 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,10 @@ 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( + RepeatCount::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..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: usize, // The leftmost column is number 0. - pub row: usize, // 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 701565037..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,15 +292,16 @@ fn page_completions( cols, candidates .iter() - .map(|s| s.display().width()) + .map(|s| swidth(s.display())) .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 = swidth(candidate); 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<(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/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..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) -> usize; + fn get_columns(&self) -> Unit; /// Get the number of rows in the current terminal. - fn get_rows(&self) -> usize; + 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) -> usize { +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) -> usize { } else if s == "\n" { 0 } else { - s.width() + swidth(s) } } @@ -168,7 +166,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..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}; @@ -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) -> Unit { 80 } - fn get_rows(&self) -> usize { + fn get_rows(&self) -> Unit { 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 9672138c9..c4e027b6f 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) -> (usize, usize) { +fn get_win_size(fd: RawFd) -> (Unit, Unit) { 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 + Unit::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: Unit, // Number of columns in terminal buffer: String, - tab_stop: usize, + tab_stop: Unit, 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: Unit, 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) -> Unit { 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) -> Unit { 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, + Unit::from(self.tab_stop), self.colors_enabled(), self.bell_style, ) diff --git a/src/tty/windows.rs b/src/tty/windows.rs index 02cf69a4b..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) -> (usize, usize) { +fn get_win_size(handle: HANDLE) -> (Unit, Unit) { 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, + 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: usize, // 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: usize) -> usize { + 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 = c.width(); + 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) -> usize { + 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) -> usize { + fn get_rows(&self) -> Unit { let (_, rows) = get_win_size(self.conout); rows } @@ -659,7 +658,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,