Repository: kkawakam/rustyline (rust)

Issue:
Option to disable bell sound
The bell rings too often in Windows, when using tab completion

I am using nushell, and filed a ticket on them to disable the bell, but apparently rustyline is the culprit https://github.com/nushell/nushell/issues/446 

Rustyline could support bell-style as seen elsewhere, but option to kill that altogether from using application would be nice.

Pull request description:
Support bell style
Should fix #281

Patch:
diff --git a/TODO.md b/TODO.md
index eb7cc25..7189a31 100644
--- a/TODO.md
+++ b/TODO.md
@@ -4,7 +4,7 @@ API
 Async (#126)
 
 Bell
-- [ ] bell-style
+- [X] bell-style
 
 Color
 - [X] ANSI Colors & Windows 10+
diff --git a/src/config.rs b/src/config.rs
index 52cbb74..01c744a 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -20,6 +20,8 @@ pub struct Config {
     /// If true, each nonblank line returned by `readline` will be
     /// automatically added to the history.
     auto_add_history: bool,
+    /// Beep or Flash or nothing
+    bell_style: BellStyle,
     /// if colors should be enabled.
     color_mode: ColorMode,
     /// Whether to use stdout or stderr
@@ -93,6 +95,11 @@ impl Config {
         self.auto_add_history
     }
 
+    /// Bell style: beep, flash or nothing.
+    pub fn bell_style(&self) -> BellStyle {
+        self.bell_style
+    }
+
     /// Tell if colors should be enabled.
     ///
     /// By default, they are except if stdout is not a TTY.
@@ -133,6 +140,7 @@ impl Default for Config {
             keyseq_timeout: -1,
             edit_mode: EditMode::Emacs,
             auto_add_history: false,
+            bell_style: BellStyle::default(),
             color_mode: ColorMode::Enabled,
             output_stream: OutputStreamType::Stdout,
             tab_stop: 8,
@@ -140,6 +148,31 @@ impl Default for Config {
     }
 }
 
+/// Beep or flash or nothing
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum BellStyle {
+    /// Beep
+    Audible,
+    /// Silent
+    None,
+    /// Flash screen (not supported)
+    Visible,
+}
+
+/// `Audible` by default on unix (overriden by current Terminal settings).
+/// `None` on windows.
+impl Default for BellStyle {
+    #[cfg(windows)]
+    fn default() -> Self {
+        BellStyle::None
+    }
+
+    #[cfg(unix)]
+    fn default() -> Self {
+        BellStyle::Audible
+    }
+}
+
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub enum HistoryDuplicates {
     AlwaysAdd,
@@ -254,6 +287,12 @@ impl Builder {
         self
     }
 
+    /// Set bell style: beep, flash or nothing.
+    pub fn bell_style(mut self, bell_style: BellStyle) -> Self {
+        self.set_bell_style(bell_style);
+        self
+    }
+
     /// Forces colorization on or off.
     ///
     /// By default, colorization is on except if stdout is not a TTY.
@@ -344,6 +383,11 @@ pub trait Configurer {
         self.config_mut().auto_add_history = yes;
     }
 
+    /// Set bell style: beep, flash or nothing.
+    fn set_bell_style(&mut self, bell_style: BellStyle) {
+        self.config_mut().bell_style = bell_style;
+    }
+
     /// Forces colorization on or off.
     ///
     /// By default, colorization is on except if stdout is not a TTY.
diff --git a/src/lib.rs b/src/lib.rs
index 59c2834..718781f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -708,6 +708,7 @@ impl<H: Helper> Editor<H> {
             config.color_mode(),
             config.output_stream(),
             config.tab_stop(),
+            config.bell_style(),
         );
         Self {
             term,
diff --git a/src/tty/mod.rs b/src/tty/mod.rs
index 9aea6f6..d58762c 100644
--- a/src/tty/mod.rs
+++ b/src/tty/mod.rs
@@ -1,9 +1,8 @@
 //! This module implements and describes common TTY methods & traits
-use std::io::{self, Write};
 use unicode_segmentation::UnicodeSegmentation;
 use unicode_width::UnicodeWidthStr;
 
-use crate::config::{ColorMode, Config, OutputStreamType};
+use crate::config::{BellStyle, ColorMode, Config, OutputStreamType};
 use crate::highlight::Highlighter;
 use crate::keys::KeyPress;
 use crate::line_buffer::LineBuffer;
@@ -60,12 +59,7 @@ pub trait Renderer {
 
     /// Beep, used for completion when there is nothing to complete or when all
     /// the choices were already shown.
-    fn beep(&mut self) -> Result<()> {
-        // TODO bell-style
-        io::stderr().write_all(b"\x07")?;
-        io::stderr().flush()?;
-        Ok(())
-    }
+    fn beep(&mut self) -> Result<()>;
 
     /// Clear the screen. Used to handle ctrl+l
     fn clear_screen(&mut self) -> Result<()>;
@@ -162,7 +156,12 @@ pub trait Term {
     type Writer: Renderer<Reader = Self::Reader>; // rl_outstream
     type Mode: RawMode;
 
-    fn new(color_mode: ColorMode, stream: OutputStreamType, tab_stop: usize) -> Self;
+    fn new(
+        color_mode: ColorMode,
+        stream: OutputStreamType,
+        tab_stop: usize,
+        bell_style: BellStyle,
+    ) -> Self;
     /// Check if current terminal can provide a rich line-editing user
     /// interface.
     fn is_unsupported(&self) -> bool;
diff --git a/src/tty/unix.rs b/src/tty/unix.rs
index 84936b1..d86e54a 100644
--- a/src/tty/unix.rs
+++ b/src/tty/unix.rs
@@ -16,7 +16,7 @@ use unicode_segmentation::UnicodeSegmentation;
 use utf8parse::{Parser, Receiver};
 
 use super::{truncate, width, Position, RawMode, RawReader, Renderer, Term};
-use crate::config::{ColorMode, Config, OutputStreamType};
+use crate::config::{BellStyle, ColorMode, Config, OutputStreamType};
 use crate::error;
 use crate::highlight::Highlighter;
 use crate::keys::{self, KeyPress};
@@ -453,10 +453,16 @@ pub struct PosixRenderer {
     buffer: String,
     tab_stop: usize,
     colors_enabled: bool,
+    bell_style: BellStyle,
 }
 
 impl PosixRenderer {
-    fn new(out: OutputStreamType, tab_stop: usize, colors_enabled: bool) -> Self {
+    fn new(
+        out: OutputStreamType,
+        tab_stop: usize,
+        colors_enabled: bool,
+        bell_style: BellStyle,
+    ) -> Self {
         let (cols, _) = get_win_size(&out);
         Self {
             out,
@@ -464,6 +470,7 @@ impl PosixRenderer {
             buffer: String::with_capacity(1024),
             tab_stop,
             colors_enabled,
+            bell_style,
         }
     }
 }
@@ -620,6 +627,17 @@ impl Renderer for PosixRenderer {
         pos
     }
 
+    fn beep(&mut self) -> Result<()> {
+        match self.bell_style {
+            BellStyle::Audible => {
+                io::stderr().write_all(b"\x07")?;
+                io::stderr().flush()?;
+                Ok(())
+            }
+            _ => Ok(()),
+        }
+    }
+
     /// Clear the screen. Used to handle ctrl+l
     fn clear_screen(&mut self) -> Result<()> {
         self.write_and_flush(b"\x1b[H\x1b[2J")
@@ -726,6 +744,7 @@ pub struct PosixTerminal {
     pub(crate) color_mode: ColorMode,
     stream_type: OutputStreamType,
     tab_stop: usize,
+    bell_style: BellStyle,
 }
 
 impl PosixTerminal {
@@ -743,7 +762,12 @@ impl Term for PosixTerminal {
     type Reader = PosixRawReader;
     type Writer = PosixRenderer;
 
-    fn new(color_mode: ColorMode, stream_type: OutputStreamType, tab_stop: usize) -> Self {
+    fn new(
+        color_mode: ColorMode,
+        stream_type: OutputStreamType,
+        tab_stop: usize,
+        bell_style: BellStyle,
+    ) -> Self {
         let term = Self {
             unsupported: is_unsupported_term(),
             stdin_isatty: is_a_tty(STDIN_FILENO),
@@ -751,6 +775,7 @@ impl Term for PosixTerminal {
             color_mode,
             stream_type,
             tab_stop,
+            bell_style,
         };
         if !term.unsupported && term.stdin_isatty && term.stdstream_isatty {
             install_sigwinch_handler();
@@ -824,7 +849,12 @@ impl Term for PosixTerminal {
     }
 
     fn create_writer(&self) -> PosixRenderer {
-        PosixRenderer::new(self.stream_type, self.tab_stop, self.colors_enabled())
+        PosixRenderer::new(
+            self.stream_type,
+            self.tab_stop,
+            self.colors_enabled(),
+            self.bell_style,
+        )
     }
 }
 
@@ -853,12 +883,12 @@ fn write_and_flush(out: OutputStreamType, buf: &[u8]) -> Result<()> {
 #[cfg(test)]
 mod test {
     use super::{Position, PosixRenderer, PosixTerminal, Renderer};
-    use crate::config::OutputStreamType;
+    use crate::config::{BellStyle, OutputStreamType};
 
     #[test]
     #[ignore]
     fn prompt_with_ansi_escape_codes() {
-        let out = PosixRenderer::new(OutputStreamType::Stdout, 4, true);
+        let out = PosixRenderer::new(OutputStreamType::Stdout, 4, true, BellStyle::default());
         let pos = out.calculate_position("\x1b[1;32m>>\x1b[0m ", Position::default());
         assert_eq!(3, pos.col);
         assert_eq!(0, pos.row);
diff --git a/src/tty/windows.rs b/src/tty/windows.rs
index d36e849..a878e86 100644
--- a/src/tty/windows.rs
+++ b/src/tty/windows.rs
@@ -10,8 +10,7 @@ use winapi::um::winnt::{CHAR, HANDLE};
 use winapi::um::{consoleapi, handleapi, processenv, winbase, wincon, winuser};
 
 use super::{truncate, Position, RawMode, RawReader, Renderer, Term};
-use crate::config::OutputStreamType;
-use crate::config::{ColorMode, Config};
+use crate::config::{BellStyle, ColorMode, Config, OutputStreamType};
 use crate::error;
 use crate::highlight::Highlighter;
 use crate::keys::{self, KeyPress};
@@ -252,10 +251,16 @@ pub struct ConsoleRenderer {
     cols: usize, // Number of columns in terminal
     buffer: String,
     colors_enabled: bool,
+    bell_style: BellStyle,
 }
 
 impl ConsoleRenderer {
-    fn new(handle: HANDLE, out: OutputStreamType, colors_enabled: bool) -> ConsoleRenderer {
+    fn new(
+        handle: HANDLE,
+        out: OutputStreamType,
+        colors_enabled: bool,
+        bell_style: BellStyle,
+    ) -> ConsoleRenderer {
         // Multi line editing is enabled by ENABLE_WRAP_AT_EOL_OUTPUT mode
         let (cols, _) = get_win_size(handle);
         ConsoleRenderer {
@@ -264,6 +269,7 @@ impl ConsoleRenderer {
             cols,
             buffer: String::with_capacity(1024),
             colors_enabled,
+            bell_style,
         }
     }
 
@@ -405,6 +411,17 @@ impl Renderer for ConsoleRenderer {
         pos
     }
 
+    fn beep(&mut self) -> Result<()> {
+        match self.bell_style {
+            BellStyle::Audible => {
+                io::stderr().write_all(b"\x07")?;
+                io::stderr().flush()?;
+                Ok(())
+            }
+            _ => Ok(()),
+        }
+    }
+
     /// Clear the screen. Used to handle ctrl+l
     fn clear_screen(&mut self) -> Result<()> {
         let info = self.get_console_screen_buffer_info()?;
@@ -467,6 +484,7 @@ pub struct Console {
     pub(crate) color_mode: ColorMode,
     ansi_colors_supported: bool,
     stream_type: OutputStreamType,
+    bell_style: BellStyle,
 }
 
 impl Console {
@@ -485,7 +503,12 @@ impl Term for Console {
     type Reader = ConsoleRawReader;
     type Writer = ConsoleRenderer;
 
-    fn new(color_mode: ColorMode, stream_type: OutputStreamType, _tab_stop: usize) -> Console {
+    fn new(
+        color_mode: ColorMode,
+        stream_type: OutputStreamType,
+        _tab_stop: usize,
+        bell_style: BellStyle,
+    ) -> Console {
         use std::ptr;
         let stdin_handle = get_std_handle(STDIN_FILENO);
         let stdin_isatty = match stdin_handle {
@@ -517,6 +540,7 @@ impl Term for Console {
             color_mode,
             ansi_colors_supported: false,
             stream_type,
+            bell_style,
         }
     }
 
@@ -593,6 +617,7 @@ impl Term for Console {
             self.stdstream_handle,
             self.stream_type,
             self.colors_enabled(),
+            self.bell_style,
         )
     }
 }


Test patch:
diff --git a/src/tty/test.rs b/src/tty/test.rs
index bb968e7..ff70756 100644
--- a/src/tty/test.rs
+++ b/src/tty/test.rs
@@ -4,7 +4,7 @@ use std::slice::Iter;
 use std::vec::IntoIter;
 
 use super::{truncate, Position, RawMode, RawReader, Renderer, Term};
-use crate::config::{ColorMode, Config, OutputStreamType};
+use crate::config::{BellStyle, ColorMode, Config, OutputStreamType};
 use crate::error::ReadlineError;
 use crate::highlight::Highlighter;
 use crate::keys::KeyPress;
@@ -141,6 +141,7 @@ pub struct DummyTerminal {
     pub keys: Vec<KeyPress>,
     pub cursor: usize, // cursor position before last command
     pub color_mode: ColorMode,
+    pub bell_style: BellStyle,
 }
 
 impl Term for DummyTerminal {
@@ -148,11 +149,17 @@ impl Term for DummyTerminal {
     type Reader = IntoIter<KeyPress>;
     type Writer = Sink;
 
-    fn new(color_mode: ColorMode, _stream: OutputStreamType, _tab_stop: usize) -> DummyTerminal {
+    fn new(
+        color_mode: ColorMode,
+        _stream: OutputStreamType,
+        _tab_stop: usize,
+        bell_style: BellStyle,
+    ) -> DummyTerminal {
         DummyTerminal {
             keys: Vec::new(),
             cursor: 0,
             color_mode,
+            bell_style,
         }
     }