From 64b5b23315f12125a2c5b2f810fe5ac285bdfa79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Mon, 19 Oct 2020 17:18:03 +0900 Subject: [PATCH] Move theme from view to editor, support multiple views in editor. --- helix-core/src/state.rs | 2 +- helix-term/src/application.rs | 30 ++++++++++++++++-------------- helix-view/src/editor.rs | 9 ++++++++- helix-view/src/lib.rs | 1 + helix-view/src/view.rs | 16 +++++++--------- 5 files changed, 33 insertions(+), 25 deletions(-) diff --git a/helix-core/src/state.rs b/helix-core/src/state.rs index 1b0a67aef..35e20aef5 100644 --- a/helix-core/src/state.rs +++ b/helix-core/src/state.rs @@ -23,7 +23,7 @@ pub struct State { pub restore_cursor: bool, - // + // TODO: move these to a Document wrapper? pub syntax: Option, /// Pending changes since last history commit. pub changes: ChangeSet, diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 9c90b6f38..d65e7e2e3 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -1,6 +1,6 @@ use clap::ArgMatches as Args; use helix_core::{indent::TAB_WIDTH, state::Mode, syntax::HighlightEvent, Position, Range, State}; -use helix_view::{commands, keymap, prompt::Prompt, Editor, View}; +use helix_view::{commands, keymap, prompt::Prompt, Editor, Theme, View}; use std::{ borrow::Cow, @@ -15,8 +15,7 @@ use anyhow::Error; use crossterm::{ cursor, - cursor::position, - event::{self, read, Event, EventStream, KeyCode, KeyEvent}, + event::{read, Event, EventStream, KeyCode, KeyEvent}, execute, queue, terminal::{self, disable_raw_mode, enable_raw_mode}, }; @@ -75,19 +74,18 @@ impl Renderer { self.cache = Surface::empty(area); } - pub fn render_view(&mut self, view: &mut View, viewport: Rect) { - self.render_buffer(view, viewport); - self.render_statusline(view); + pub fn render_view(&mut self, view: &mut View, viewport: Rect, theme: &Theme) { + self.render_buffer(view, viewport, theme); + self.render_statusline(view, theme); } // TODO: ideally not &mut View but highlights require it because of cursor cache - pub fn render_buffer(&mut self, view: &mut View, viewport: Rect) { + pub fn render_buffer(&mut self, view: &mut View, viewport: Rect, theme: &Theme) { let area = Rect::new(0, 0, self.size.0, self.size.1); self.surface.reset(); // reset is faster than allocating new empty surface // clear with background color - self.surface - .set_style(area, view.theme.get("ui.background")); + self.surface.set_style(area, theme.get("ui.background")); // TODO: inefficient, should feed chunks.iter() to tree_sitter.parse_with(|offset, pos|) let source_code = view.state.doc().to_string(); @@ -150,7 +148,7 @@ impl Renderer { use helix_core::graphemes::{grapheme_width, RopeGraphemes}; let style = match spans.first() { - Some(span) => view.theme.get(view.theme.scopes()[span.0].as_str()), + Some(span) => theme.get(theme.scopes()[span.0].as_str()), None => Style::default().fg(Color::Rgb(164, 160, 232)), // lavender }; @@ -214,7 +212,7 @@ impl Renderer { } } } - let style: Style = view.theme.get("ui.linenr"); + let style: Style = theme.get("ui.linenr"); let last_line = view.last_line(); for (i, line) in (view.first_line..last_line).enumerate() { self.surface @@ -222,7 +220,7 @@ impl Renderer { } } - pub fn render_statusline(&mut self, view: &View) { + pub fn render_statusline(&mut self, view: &View, theme: &Theme) { let mode = match view.state.mode() { Mode::Insert => "INS", Mode::Normal => "NOR", @@ -231,7 +229,7 @@ impl Renderer { // statusline self.surface.set_style( Rect::new(0, self.size.1 - 2, self.size.0, 1), - view.theme.get("ui.statusline"), + theme.get("ui.statusline"), ); self.surface .set_string(1, self.size.1 - 2, mode, self.text_color); @@ -354,8 +352,11 @@ impl Application { fn render(&mut self) { let viewport = Rect::new(OFFSET, 0, self.terminal.size.0, self.terminal.size.1 - 2); // - 2 for statusline and prompt + // SAFETY: we cheat around the view_mut() borrow because it doesn't allow us to also borrow + // theme. Theme is immutable mutating view won't disrupt theme_ref. + let theme_ref = unsafe { &*(&self.editor.theme as *const Theme) }; if let Some(view) = self.editor.view_mut() { - self.terminal.render_view(view, viewport); + self.terminal.render_view(view, viewport, theme_ref); if let Some(prompt) = &self.prompt { if prompt.should_close { self.prompt = None; @@ -389,6 +390,7 @@ impl Application { self.terminal.resize(width, height); // TODO: simplistic ensure cursor in view for now + // TODO: loop over views if let Some(view) = self.editor.view_mut() { view.size = self.terminal.size; view.ensure_cursor_in_view() diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 08fd1f0c1..61abd4829 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -1,4 +1,6 @@ +use crate::theme::Theme; use crate::View; +use helix_core::State; use std::path::PathBuf; @@ -8,20 +10,25 @@ pub struct Editor { pub views: Vec, pub focus: usize, pub should_close: bool, + pub theme: Theme, // TODO: share one instance } impl Editor { pub fn new() -> Self { + let theme = Theme::default(); + Self { views: Vec::new(), focus: 0, should_close: false, + theme, } } pub fn open(&mut self, path: PathBuf, size: (u16, u16)) -> Result<(), Error> { let pos = self.views.len(); - self.views.push(View::open(path, size)?); + let state = State::load(path, self.theme.scopes())?; + self.views.push(View::new(state, size)?); self.focus = pos; Ok(()) } diff --git a/helix-view/src/lib.rs b/helix-view/src/lib.rs index 8ea634af7..9abe8a1aa 100644 --- a/helix-view/src/lib.rs +++ b/helix-view/src/lib.rs @@ -6,4 +6,5 @@ pub mod theme; pub mod view; pub use editor::Editor; +pub use theme::Theme; pub use view::View; diff --git a/helix-view/src/view.rs b/helix-view/src/view.rs index 2b68dbc3f..d2a7d5562 100644 --- a/helix-view/src/view.rs +++ b/helix-view/src/view.rs @@ -1,8 +1,7 @@ use anyhow::Error; -use std::{borrow::Cow, path::PathBuf}; +use std::borrow::Cow; -use crate::theme::Theme; use helix_core::{ graphemes::{grapheme_width, RopeGraphemes}, indent::TAB_WIDTH, @@ -12,24 +11,23 @@ use tui::layout::Rect; pub const PADDING: usize = 5; +// TODO: view should be View { doc: Document(state, history,..) } +// since we can have multiple views into the same file pub struct View { pub state: State, - pub history: History, pub first_line: usize, pub size: (u16, u16), - pub theme: Theme, // TODO: share one instance + + // TODO: Doc<> fields + pub history: History, } impl View { - pub fn open(path: PathBuf, size: (u16, u16)) -> Result { - let theme = Theme::default(); - let state = State::load(path, theme.scopes())?; - + pub fn new(state: State, size: (u16, u16)) -> Result { let view = Self { state, first_line: 0, size, - theme, history: History::default(), };