on master help sign in
Move terminal out of compositor
264a455Blaž Hrastnik
helix-term/src/application.rs+59−16
@@ -10,11 +10,13 @@ use helix_view::{
10 align_view,
10 align_view,
11 document::DocumentSavedEventResult,
11 document::DocumentSavedEventResult,
12 editor::{ConfigEvent, EditorEvent},
12 editor::{ConfigEvent, EditorEvent},
13+ graphics::Rect,
13 theme,
14 theme,
14 tree::Layout,
15 tree::Layout,
15 Align, Editor,
16 Align, Editor,
16};
17};
17use serde_json::json;
18use serde_json::json;
19+use tui::backend::Backend;
18
20
19use crate::{
21use crate::{
20 args::Args,
22 args::Args,
@@ -53,8 +55,18 @@ type Signals = futures_util::stream::Empty<()>;
53
55
54const LSP_DEADLINE: Duration = Duration::from_millis(16);
56const LSP_DEADLINE: Duration = Duration::from_millis(16);
55
57
58+#[cfg(not(feature = "integration"))]
59+use tui::backend::CrosstermBackend;
60+
61+#[cfg(not(feature = "integration"))]
62+type Terminal = tui::terminal::Terminal<CrosstermBackend<std::io::Stdout>>;
63+
64+#[cfg(feature = "integration")]
65+type Terminal = tui::terminal::Terminal<TestBackend>;
66+
56pub struct Application {
67pub struct Application {
57 compositor: Compositor,
68 compositor: Compositor,
69+ terminal: Terminal,
58 pub editor: Editor,
70 pub editor: Editor,
59
71
60 config: Arc<ArcSwap<Config>>,
72 config: Arc<ArcSwap<Config>>,
@@ -143,10 +155,18 @@ impl Application {
143
155
144 let syn_loader = std::sync::Arc::new(syntax::Loader::new(syn_loader_conf));
156 let syn_loader = std::sync::Arc::new(syntax::Loader::new(syn_loader_conf));
145
157
158+ #[cfg(not(feature = "integration"))]
159+ let backend = CrosstermBackend::new(stdout());
160+
161+ #[cfg(feature = "integration")]
162+ let backend = TestBackend::new(120, 150);
163+
164+ let terminal = Terminal::new(backend)?;
165+ let area = terminal.size().expect("couldn't get terminal size");
146− let mut compositor = Compositor::new().context("build compositor")?;
166+ let mut compositor = Compositor::new(area);
147 let config = Arc::new(ArcSwap::from_pointee(config));
167 let config = Arc::new(ArcSwap::from_pointee(config));
148 let mut editor = Editor::new(
168 let mut editor = Editor::new(
149− compositor.size(),
169+ area,
150 theme_loader.clone(),
170 theme_loader.clone(),
151 syn_loader.clone(),
171 syn_loader.clone(),
152 Box::new(Map::new(Arc::clone(&config), |config: &Config| {
172 Box::new(Map::new(Arc::clone(&config), |config: &Config| {
@@ -233,6 +253,7 @@ impl Application {
233
253
234 let app = Self {
254 let app = Self {
235 compositor,
255 compositor,
256+ terminal,
236 editor,
257 editor,
237
258
238 config,
259 config,
@@ -254,15 +275,26 @@ impl Application {
254
275
255 #[cfg(not(feature = "integration"))]
276 #[cfg(not(feature = "integration"))]
256 fn render(&mut self) {
277 fn render(&mut self) {
257− let compositor = &mut self.compositor;
258−
259 let mut cx = crate::compositor::Context {
278 let mut cx = crate::compositor::Context {
260 editor: &mut self.editor,
279 editor: &mut self.editor,
261 jobs: &mut self.jobs,
280 jobs: &mut self.jobs,
262 scroll: None,
281 scroll: None,
263 };
282 };
264
283
284+ let area = self
285+ .terminal
286+ .autoresize()
287+ .expect("Unable to determine terminal size");
288+
289+ // TODO: need to recalculate view tree if necessary
290+
291+ let surface = self.terminal.current_buffer_mut();
292+
265− compositor.render(&mut cx);
293+ self.compositor.render(area, surface, &mut cx);
294+
295+ let (pos, kind) = self.compositor.cursor(area, &self.editor);
296+ let pos = pos.map(|pos| (pos.col as u16, pos.row as u16));
297+ self.terminal.draw(pos, kind).unwrap();
266 }
298 }
267
299
268 pub async fn event_loop<S>(&mut self, input_stream: &mut S)
300 pub async fn event_loop<S>(&mut self, input_stream: &mut S)
@@ -392,17 +424,24 @@ impl Application {
392
424
393 #[cfg(not(windows))]
425 #[cfg(not(windows))]
394 pub async fn handle_signals(&mut self, signal: i32) {
426 pub async fn handle_signals(&mut self, signal: i32) {
395− use helix_view::graphics::Rect;
396 match signal {
427 match signal {
397 signal::SIGTSTP => {
428 signal::SIGTSTP => {
429+ // restore cursor
430+ use helix_view::graphics::CursorKind;
431+ self.terminal
432+ .backend_mut()
433+ .show_cursor(CursorKind::Block)
434+ .ok();
398 restore_term().unwrap();
435 restore_term().unwrap();
399 low_level::emulate_default_handler(signal::SIGTSTP).unwrap();
436 low_level::emulate_default_handler(signal::SIGTSTP).unwrap();
400 }
437 }
401 signal::SIGCONT => {
438 signal::SIGCONT => {
402 self.claim_term().await.unwrap();
439 self.claim_term().await.unwrap();
403 // redraw the terminal
440 // redraw the terminal
404− let Rect { width, height, .. } = self.compositor.size();
441+ let area = self.terminal.size().expect("couldn't get terminal size");
405− self.compositor.resize(width, height);
442+ self.compositor.resize(area);
443+ self.terminal.clear().expect("couldn't clear terminal");
444+
406 self.render();
445 self.render();
407 }
446 }
408 signal::SIGUSR1 => {
447 signal::SIGUSR1 => {
@@ -539,7 +578,14 @@ impl Application {
539 // Handle key events
578 // Handle key events
540 let should_redraw = match event.unwrap() {
579 let should_redraw = match event.unwrap() {
541 CrosstermEvent::Resize(width, height) => {
580 CrosstermEvent::Resize(width, height) => {
581+ self.terminal
542− self.compositor.resize(width, height);
582+ .resize(Rect::new(0, 0, width, height))
583+ .expect("Unable to resize terminal");
584+
585+ let area = self.terminal.size().expect("couldn't get terminal size");
586+
587+ self.compositor.resize(area);
588+
543 self.compositor
589 self.compositor
544 .handle_event(&Event::Resize(width, height), &mut cx)
590 .handle_event(&Event::Resize(width, height), &mut cx)
545 }
591 }
@@ -923,10 +969,9 @@ impl Application {
923
969
924 async fn claim_term(&mut self) -> Result<(), Error> {
970 async fn claim_term(&mut self) -> Result<(), Error> {
925 use helix_view::graphics::CursorKind;
971 use helix_view::graphics::CursorKind;
926− use tui::backend::Backend;
927 terminal::enable_raw_mode()?;
972 terminal::enable_raw_mode()?;
928− if self.compositor.terminal.cursor_kind() == CursorKind::Hidden {
973+ if self.terminal.cursor_kind() == CursorKind::Hidden {
929− self.compositor.terminal.backend_mut().hide_cursor().ok();
974+ self.terminal.backend_mut().hide_cursor().ok();
930 }
975 }
931 let mut stdout = stdout();
976 let mut stdout = stdout();
932 execute!(
977 execute!(
@@ -962,14 +1007,12 @@ impl Application {
962
1007
963 let close_errs = self.close().await;
1008 let close_errs = self.close().await;
964
1009
1010+ // restore cursor
965 use helix_view::graphics::CursorKind;
1011 use helix_view::graphics::CursorKind;
966− use tui::backend::Backend;
967− self.compositor
968− .terminal
1012+ self.terminal
969 .backend_mut()
1013 .backend_mut()
970 .show_cursor(CursorKind::Block)
1014 .show_cursor(CursorKind::Block)
971 .ok();
1015 .ok();
972−
973 restore_term()?;
1016 restore_term()?;
974
1017
975 for err in close_errs {
1018 for err in close_errs {
helix-term/src/compositor.rs+6−49
@@ -75,56 +75,28 @@ pub trait Component: Any + AnyComponent {
75 }
75 }
76}
76}
77
77
78−use anyhow::Context as AnyhowContext;
79−
80−#[cfg(not(feature = "integration"))]
81−use tui::backend::CrosstermBackend;
82−
83−#[cfg(not(feature = "integration"))]
84−use std::io::stdout;
85−
86−#[cfg(not(feature = "integration"))]
87−type Terminal = tui::terminal::Terminal<CrosstermBackend<std::io::Stdout>>;
88−
89−#[cfg(feature = "integration")]
90−type Terminal = tui::terminal::Terminal<TestBackend>;
91−
92pub struct Compositor {
78pub struct Compositor {
93 layers: Vec<Box<dyn Component>>,
79 layers: Vec<Box<dyn Component>>,
94− pub terminal: Terminal,
95 area: Rect,
80 area: Rect,
96
81
97 pub(crate) last_picker: Option<Box<dyn Component>>,
82 pub(crate) last_picker: Option<Box<dyn Component>>,
98}
83}
99
84
100impl Compositor {
85impl Compositor {
101− pub fn new() -> anyhow::Result<Self> {
86+ pub fn new(area: Rect) -> Self {
102− #[cfg(not(feature = "integration"))]
103− let backend = CrosstermBackend::new(stdout());
104−
105− #[cfg(feature = "integration")]
106− let backend = TestBackend::new(120, 150);
107−
108− let terminal = Terminal::new(backend).context("build terminal")?;
109− let area = terminal.size().expect("couldn't get terminal size");
110− Ok(Self {
87+ Self {
111 layers: Vec::new(),
88 layers: Vec::new(),
112 area,
89 area,
113− terminal,
114 last_picker: None,
90 last_picker: None,
115− })
91+ }
116 }
92 }
117
93
118 pub fn size(&self) -> Rect {
94 pub fn size(&self) -> Rect {
119 self.area
95 self.area
120 }
96 }
121
97
122− pub fn resize(&mut self, width: u16, height: u16) {
98+ pub fn resize(&mut self, area: Rect) {
123− self.terminal
99+ self.area = area;
124− .resize(Rect::new(0, 0, width, height))
125− .expect("Unable to resize terminal");
126−
127− self.area = self.terminal.size().expect("couldn't get terminal size");
128 }
100 }
129
101
130 pub fn push(&mut self, mut layer: Box<dyn Component>) {
102 pub fn push(&mut self, mut layer: Box<dyn Component>) {
@@ -192,25 +164,10 @@ impl Compositor {
192 consumed
164 consumed
193 }
165 }
194
166
195− pub fn render(&mut self, cx: &mut Context) {
167+ pub fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) {
196− self.terminal
197− .autoresize()
198− .expect("Unable to determine terminal size");
199−
200− // TODO: need to recalculate view tree if necessary
201−
202− let surface = self.terminal.current_buffer_mut();
203−
204− let area = *surface.area();
205−
206 for layer in &mut self.layers {
168 for layer in &mut self.layers {
207 layer.render(area, surface, cx);
169 layer.render(area, surface, cx);
208 }
170 }
209−
210− let (pos, kind) = self.cursor(area, cx.editor);
211− let pos = pos.map(|pos| (pos.col as u16, pos.row as u16));
212−
213− self.terminal.draw(pos, kind).unwrap();
214 }
171 }
215
172
216 pub fn cursor(&self, area: Rect, editor: &Editor) -> (Option<Position>, CursorKind) {
173 pub fn cursor(&self, area: Rect, editor: &Editor) -> (Option<Position>, CursorKind) {