on master help sign in
Move terminal claim/restore code to helix-tui
3d85024Michael Davis
Cargo.lock+1
@@ -1201,6 +1201,7 @@ dependencies = [
1201 "crossterm",
1201 "crossterm",
1202 "helix-core",
1202 "helix-core",
1203 "helix-view",
1203 "helix-view",
1204+ "log",
1204 "serde",
1205 "serde",
1205 "termini",
1206 "termini",
1206 "unicode-segmentation",
1207 "unicode-segmentation",
helix-term/src/application.rs+21−77
@@ -30,22 +30,14 @@ use crate::{
30
30
31use log::{debug, error, warn};
31use log::{debug, error, warn};
32use std::{
32use std::{
33− io::{stdin, stdout, Write},
33+ io::{stdin, stdout},
34 sync::Arc,
34 sync::Arc,
35 time::{Duration, Instant},
35 time::{Duration, Instant},
36};
36};
37
37
38use anyhow::{Context, Error};
38use anyhow::{Context, Error};
39
39
40−use crossterm::{
40+use crossterm::{event::Event as CrosstermEvent, tty::IsTty};
41− event::{
42− DisableBracketedPaste, DisableFocusChange, DisableMouseCapture, EnableBracketedPaste,
43− EnableFocusChange, EnableMouseCapture, Event as CrosstermEvent, KeyboardEnhancementFlags,
44− PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags,
45− },
46− execute, terminal,
47− tty::IsTty,
48−};
49#[cfg(not(windows))]
41#[cfg(not(windows))]
50use {
42use {
51 signal_hook::{consts::signal, low_level},
43 signal_hook::{consts::signal, low_level},
@@ -63,10 +55,12 @@ use tui::backend::CrosstermBackend;
63use tui::backend::TestBackend;
55use tui::backend::TestBackend;
64
56
65#[cfg(not(feature = "integration"))]
57#[cfg(not(feature = "integration"))]
66−type Terminal = tui::terminal::Terminal<CrosstermBackend<std::io::Stdout>>;
58+type TerminalBackend = CrosstermBackend<std::io::Stdout>;
67
59
68#[cfg(feature = "integration")]
60#[cfg(feature = "integration")]
61+type TerminalBackend = TestBackend;
62+
69−type Terminal = tui::terminal::Terminal<TestBackend>;
63+type Terminal = tui::terminal::Terminal<TerminalBackend>;
70
64
71pub struct Application {
65pub struct Application {
72 compositor: Compositor,
66 compositor: Compositor,
@@ -108,26 +102,6 @@ fn setup_integration_logging() {
108 .apply();
102 .apply();
109}
103}
110
104
111−fn restore_term() -> Result<(), Error> {
112− let mut stdout = stdout();
113− // reset cursor shape
114− write!(stdout, "\x1B[0 q")?;
115− if matches!(terminal::supports_keyboard_enhancement(), Ok(true)) {
116− execute!(stdout, PopKeyboardEnhancementFlags)?;
117− }
118− // Ignore errors on disabling, this might trigger on windows if we call
119− // disable without calling enable previously
120− let _ = execute!(stdout, DisableMouseCapture);
121− execute!(
122− stdout,
123− DisableBracketedPaste,
124− DisableFocusChange,
125− terminal::LeaveAlternateScreen
126− )?;
127− terminal::disable_raw_mode()?;
128− Ok(())
129−}
130−
131impl Application {
105impl Application {
132 pub fn new(
106 pub fn new(
133 args: Args,
107 args: Args,
@@ -472,13 +446,7 @@ impl Application {
472 pub async fn handle_signals(&mut self, signal: i32) {
446 pub async fn handle_signals(&mut self, signal: i32) {
473 match signal {
447 match signal {
474 signal::SIGTSTP => {
448 signal::SIGTSTP => {
475− // restore cursor
476− use helix_view::graphics::CursorKind;
477− self.terminal
478− .backend_mut()
479− .show_cursor(CursorKind::Block)
480− .ok();
481− restore_term().unwrap();
449+ self.restore_term().unwrap();
482 low_level::emulate_default_handler(signal::SIGTSTP).unwrap();
450 low_level::emulate_default_handler(signal::SIGTSTP).unwrap();
483 }
451 }
484 signal::SIGCONT => {
452 signal::SIGCONT => {
@@ -1054,37 +1022,19 @@ impl Application {
1054 }
1022 }
1055 }
1023 }
1056
1024
1057− async fn claim_term(&mut self) -> Result<(), Error> {
1025+ async fn claim_term(&mut self) -> std::io::Result<()> {
1058− use helix_view::graphics::CursorKind;
1059− terminal::enable_raw_mode()?;
1060− if self.terminal.cursor_kind() == CursorKind::Hidden {
1061− self.terminal.backend_mut().hide_cursor().ok();
1062− }
1063− let mut stdout = stdout();
1064− execute!(
1065− stdout,
1066− terminal::EnterAlternateScreen,
1067− EnableBracketedPaste,
1068− EnableFocusChange
1069− )?;
1070− execute!(stdout, terminal::Clear(terminal::ClearType::All))?;
1071− if self.config.load().editor.mouse {
1026+ let terminal_config = self.config.load().editor.clone().into();
1072− execute!(stdout, EnableMouseCapture)?;
1027+ self.terminal.claim(terminal_config)
1073− }
1074− if matches!(terminal::supports_keyboard_enhancement(), Ok(true)) {
1075− log::debug!("The enhanced keyboard protocol is supported on this terminal");
1076− execute!(
1077− stdout,
1078− PushKeyboardEnhancementFlags(
1079− KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
1080− | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS
1081− )
1082− )?;
1083− } else {
1084− log::debug!("The enhanced keyboard protocol is not supported on this terminal");
1085− }
1028+ }
1086
1029
1087− Ok(())
1030+ fn restore_term(&mut self) -> std::io::Result<()> {
1031+ let terminal_config = self.config.load().editor.clone().into();
1032+ use helix_view::graphics::CursorKind;
1033+ self.terminal
1034+ .backend_mut()
1035+ .show_cursor(CursorKind::Block)
1036+ .ok();
1037+ self.terminal.restore(terminal_config)
1088 }
1038 }
1089
1039
1090 pub async fn run<S>(&mut self, input_stream: &mut S) -> Result<i32, Error>
1040 pub async fn run<S>(&mut self, input_stream: &mut S) -> Result<i32, Error>
@@ -1099,7 +1049,7 @@ impl Application {
1099 // We can't handle errors properly inside this closure. And it's
1049 // We can't handle errors properly inside this closure. And it's
1100 // probably not a good idea to `unwrap()` inside a panic handler.
1050 // probably not a good idea to `unwrap()` inside a panic handler.
1101 // So we just ignore the `Result`.
1051 // So we just ignore the `Result`.
1102− let _ = restore_term();
1052+ let _ = TerminalBackend::force_restore();
1103 hook(info);
1053 hook(info);
1104 }));
1054 }));
1105
1055
@@ -1107,13 +1057,7 @@ impl Application {
1107
1057
1108 let close_errs = self.close().await;
1058 let close_errs = self.close().await;
1109
1059
1110− // restore cursor
1111− use helix_view::graphics::CursorKind;
1112− self.terminal
1113− .backend_mut()
1114− .show_cursor(CursorKind::Block)
1115− .ok();
1116− restore_term()?;
1060+ self.restore_term()?;
1117
1061
1118 for err in close_errs {
1062 for err in close_errs {
1119 self.editor.exit_code = 1;
1063 self.editor.exit_code = 1;
helix-tui/Cargo.toml+1
@@ -22,5 +22,6 @@ unicode-segmentation = "1.10"
22crossterm = { version = "0.26", optional = true }
22crossterm = { version = "0.26", optional = true }
23termini = "0.1"
23termini = "0.1"
24serde = { version = "1", "optional" = true, features = ["derive"]}
24serde = { version = "1", "optional" = true, features = ["derive"]}
25+log = "~0.4"
25helix-view = { version = "0.6", path = "../helix-view", features = ["term"] }
26helix-view = { version = "0.6", path = "../helix-view", features = ["term"] }
26helix-core = { version = "0.6", path = "../helix-core" }
27helix-core = { version = "0.6", path = "../helix-core" }
helix-tui/src/backend/crossterm.rs+69−1
@@ -1,6 +1,11 @@
1−use crate::{backend::Backend, buffer::Cell};
1+use crate::{backend::Backend, buffer::Cell, terminal::Config};
2use crossterm::{
2use crossterm::{
3 cursor::{Hide, MoveTo, SetCursorStyle, Show},
3 cursor::{Hide, MoveTo, SetCursorStyle, Show},
4+ event::{
5+ DisableBracketedPaste, DisableFocusChange, DisableMouseCapture, EnableBracketedPaste,
6+ EnableFocusChange, EnableMouseCapture, KeyboardEnhancementFlags,
7+ PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags,
8+ },
4 execute, queue,
9 execute, queue,
5 style::{
10 style::{
6 Attribute as CAttribute, Color as CColor, Print, SetAttribute, SetBackgroundColor,
11 Attribute as CAttribute, Color as CColor, Print, SetAttribute, SetBackgroundColor,
@@ -83,6 +88,69 @@ impl<W> Backend for CrosstermBackend<W>
83where
88where
84 W: Write,
89 W: Write,
85{
90{
91+ fn claim(&mut self, config: Config) -> io::Result<()> {
92+ terminal::enable_raw_mode()?;
93+ execute!(
94+ self.buffer,
95+ terminal::EnterAlternateScreen,
96+ EnableBracketedPaste,
97+ EnableFocusChange
98+ )?;
99+ execute!(self.buffer, terminal::Clear(terminal::ClearType::All))?;
100+ if config.enable_mouse_capture {
101+ execute!(self.buffer, EnableMouseCapture)?;
102+ }
103+ if matches!(terminal::supports_keyboard_enhancement(), Ok(true)) {
104+ log::debug!("The enhanced keyboard protocol is supported on this terminal");
105+ execute!(
106+ self.buffer,
107+ PushKeyboardEnhancementFlags(
108+ KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
109+ | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS
110+ )
111+ )?;
112+ } else {
113+ log::debug!("The enhanced keyboard protocol is not supported on this terminal");
114+ }
115+ Ok(())
116+ }
117+
118+ fn restore(&mut self, config: Config) -> io::Result<()> {
119+ // reset cursor shape
120+ write!(self.buffer, "\x1B[0 q")?;
121+ if config.enable_mouse_capture {
122+ execute!(self.buffer, DisableMouseCapture)?;
123+ }
124+ if matches!(terminal::supports_keyboard_enhancement(), Ok(true)) {
125+ execute!(self.buffer, PopKeyboardEnhancementFlags)?;
126+ }
127+ execute!(
128+ self.buffer,
129+ DisableBracketedPaste,
130+ DisableFocusChange,
131+ terminal::LeaveAlternateScreen
132+ )?;
133+ terminal::disable_raw_mode()
134+ }
135+
136+ fn force_restore() -> io::Result<()> {
137+ let mut stdout = io::stdout();
138+
139+ // reset cursor shape
140+ write!(stdout, "\x1B[0 q")?;
141+ // Ignore errors on disabling, this might trigger on windows if we call
142+ // disable without calling enable previously
143+ let _ = execute!(stdout, DisableMouseCapture);
144+ let _ = execute!(stdout, PopKeyboardEnhancementFlags);
145+ execute!(
146+ stdout,
147+ DisableBracketedPaste,
148+ DisableFocusChange,
149+ terminal::LeaveAlternateScreen
150+ )?;
151+ terminal::disable_raw_mode()
152+ }
153+
86 fn draw<'a, I>(&mut self, content: I) -> io::Result<()>
154 fn draw<'a, I>(&mut self, content: I) -> io::Result<()>
87 where
155 where
88 I: Iterator<Item = (u16, u16, &'a Cell)>,
156 I: Iterator<Item = (u16, u16, &'a Cell)>,
helix-tui/src/backend/mod.rs+4−1
@@ -1,6 +1,6 @@
1use std::io;
1use std::io;
2
2
3−use crate::buffer::Cell;
3+use crate::{buffer::Cell, terminal::Config};
4
4
5use helix_view::graphics::{CursorKind, Rect};
5use helix_view::graphics::{CursorKind, Rect};
6
6
@@ -13,6 +13,9 @@ mod test;
13pub use self::test::TestBackend;
13pub use self::test::TestBackend;
14
14
15pub trait Backend {
15pub trait Backend {
16+ fn claim(&mut self, config: Config) -> Result<(), io::Error>;
17+ fn restore(&mut self, config: Config) -> Result<(), io::Error>;
18+ fn force_restore() -> Result<(), io::Error>;
16 fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
19 fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
17 where
20 where
18 I: Iterator<Item = (u16, u16, &'a Cell)>;
21 I: Iterator<Item = (u16, u16, &'a Cell)>;
helix-tui/src/backend/test.rs+13
@@ -1,6 +1,7 @@
1use crate::{
1use crate::{
2 backend::Backend,
2 backend::Backend,
3 buffer::{Buffer, Cell},
3 buffer::{Buffer, Cell},
4+ terminal::Config,
4};
5};
5use helix_core::unicode::width::UnicodeWidthStr;
6use helix_core::unicode::width::UnicodeWidthStr;
6use helix_view::graphics::{CursorKind, Rect};
7use helix_view::graphics::{CursorKind, Rect};
@@ -106,6 +107,18 @@ impl TestBackend {
106}
107}
107
108
108impl Backend for TestBackend {
109impl Backend for TestBackend {
110+ fn claim(&mut self, _config: Config) -> Result<(), io::Error> {
111+ Ok(())
112+ }
113+
114+ fn restore(&mut self, _config: Config) -> Result<(), io::Error> {
115+ Ok(())
116+ }
117+
118+ fn force_restore() -> Result<(), io::Error> {
119+ Ok(())
120+ }
121+
109 fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
122 fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
110 where
123 where
111 I: Iterator<Item = (u16, u16, &'a Cell)>,
124 I: Iterator<Item = (u16, u16, &'a Cell)>,
helix-tui/src/terminal.rs+22
@@ -1,4 +1,5 @@
1use crate::{backend::Backend, buffer::Buffer};
1use crate::{backend::Backend, buffer::Buffer};
2+use helix_view::editor::Config as EditorConfig;
2use helix_view::graphics::{CursorKind, Rect};
3use helix_view::graphics::{CursorKind, Rect};
3use std::io;
4use std::io;
4
5
@@ -16,6 +17,19 @@ pub struct Viewport {
16 resize_behavior: ResizeBehavior,
17 resize_behavior: ResizeBehavior,
17}
18}
18
19
20+#[derive(Debug)]
21+pub struct Config {
22+ pub enable_mouse_capture: bool,
23+}
24+
25+impl From<EditorConfig> for Config {
26+ fn from(config: EditorConfig) -> Self {
27+ Self {
28+ enable_mouse_capture: config.mouse,
29+ }
30+ }
31+}
32+
19impl Viewport {
33impl Viewport {
20 /// UNSTABLE
34 /// UNSTABLE
21 pub fn fixed(area: Rect) -> Viewport {
35 pub fn fixed(area: Rect) -> Viewport {
@@ -98,6 +112,14 @@ where
98 })
112 })
99 }
113 }
100
114
115+ pub fn claim(&mut self, config: Config) -> io::Result<()> {
116+ self.backend.claim(config)
117+ }
118+
119+ pub fn restore(&mut self, config: Config) -> io::Result<()> {
120+ self.backend.restore(config)
121+ }
122+
101 // /// Get a Frame object which provides a consistent view into the terminal state for rendering.
123 // /// Get a Frame object which provides a consistent view into the terminal state for rendering.
102 // pub fn get_frame(&mut self) -> Frame<B> {
124 // pub fn get_frame(&mut self) -> Frame<B> {
103 // Frame {
125 // Frame {