mirror of https://github.com/helix-editor/helix
Move theme from view to editor, support multiple views in editor.
parent
b2b3083a62
commit
64b5b23315
|
@ -23,7 +23,7 @@ pub struct State {
|
||||||
|
|
||||||
pub restore_cursor: bool,
|
pub restore_cursor: bool,
|
||||||
|
|
||||||
//
|
// TODO: move these to a Document wrapper?
|
||||||
pub syntax: Option<Syntax>,
|
pub syntax: Option<Syntax>,
|
||||||
/// Pending changes since last history commit.
|
/// Pending changes since last history commit.
|
||||||
pub changes: ChangeSet,
|
pub changes: ChangeSet,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use clap::ArgMatches as Args;
|
use clap::ArgMatches as Args;
|
||||||
use helix_core::{indent::TAB_WIDTH, state::Mode, syntax::HighlightEvent, Position, Range, State};
|
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::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
|
@ -15,8 +15,7 @@ use anyhow::Error;
|
||||||
|
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
cursor,
|
cursor,
|
||||||
cursor::position,
|
event::{read, Event, EventStream, KeyCode, KeyEvent},
|
||||||
event::{self, read, Event, EventStream, KeyCode, KeyEvent},
|
|
||||||
execute, queue,
|
execute, queue,
|
||||||
terminal::{self, disable_raw_mode, enable_raw_mode},
|
terminal::{self, disable_raw_mode, enable_raw_mode},
|
||||||
};
|
};
|
||||||
|
@ -75,19 +74,18 @@ impl Renderer {
|
||||||
self.cache = Surface::empty(area);
|
self.cache = Surface::empty(area);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_view(&mut self, view: &mut View, viewport: Rect) {
|
pub fn render_view(&mut self, view: &mut View, viewport: Rect, theme: &Theme) {
|
||||||
self.render_buffer(view, viewport);
|
self.render_buffer(view, viewport, theme);
|
||||||
self.render_statusline(view);
|
self.render_statusline(view, theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: ideally not &mut View but highlights require it because of cursor cache
|
// 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);
|
let area = Rect::new(0, 0, self.size.0, self.size.1);
|
||||||
self.surface.reset(); // reset is faster than allocating new empty surface
|
self.surface.reset(); // reset is faster than allocating new empty surface
|
||||||
|
|
||||||
// clear with background color
|
// clear with background color
|
||||||
self.surface
|
self.surface.set_style(area, theme.get("ui.background"));
|
||||||
.set_style(area, view.theme.get("ui.background"));
|
|
||||||
|
|
||||||
// TODO: inefficient, should feed chunks.iter() to tree_sitter.parse_with(|offset, pos|)
|
// TODO: inefficient, should feed chunks.iter() to tree_sitter.parse_with(|offset, pos|)
|
||||||
let source_code = view.state.doc().to_string();
|
let source_code = view.state.doc().to_string();
|
||||||
|
@ -150,7 +148,7 @@ impl Renderer {
|
||||||
use helix_core::graphemes::{grapheme_width, RopeGraphemes};
|
use helix_core::graphemes::{grapheme_width, RopeGraphemes};
|
||||||
|
|
||||||
let style = match spans.first() {
|
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
|
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();
|
let last_line = view.last_line();
|
||||||
for (i, line) in (view.first_line..last_line).enumerate() {
|
for (i, line) in (view.first_line..last_line).enumerate() {
|
||||||
self.surface
|
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() {
|
let mode = match view.state.mode() {
|
||||||
Mode::Insert => "INS",
|
Mode::Insert => "INS",
|
||||||
Mode::Normal => "NOR",
|
Mode::Normal => "NOR",
|
||||||
|
@ -231,7 +229,7 @@ impl Renderer {
|
||||||
// statusline
|
// statusline
|
||||||
self.surface.set_style(
|
self.surface.set_style(
|
||||||
Rect::new(0, self.size.1 - 2, self.size.0, 1),
|
Rect::new(0, self.size.1 - 2, self.size.0, 1),
|
||||||
view.theme.get("ui.statusline"),
|
theme.get("ui.statusline"),
|
||||||
);
|
);
|
||||||
self.surface
|
self.surface
|
||||||
.set_string(1, self.size.1 - 2, mode, self.text_color);
|
.set_string(1, self.size.1 - 2, mode, self.text_color);
|
||||||
|
@ -354,8 +352,11 @@ impl Application {
|
||||||
fn render(&mut self) {
|
fn render(&mut self) {
|
||||||
let viewport = Rect::new(OFFSET, 0, self.terminal.size.0, self.terminal.size.1 - 2); // - 2 for statusline and prompt
|
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() {
|
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 let Some(prompt) = &self.prompt {
|
||||||
if prompt.should_close {
|
if prompt.should_close {
|
||||||
self.prompt = None;
|
self.prompt = None;
|
||||||
|
@ -389,6 +390,7 @@ impl Application {
|
||||||
self.terminal.resize(width, height);
|
self.terminal.resize(width, height);
|
||||||
|
|
||||||
// TODO: simplistic ensure cursor in view for now
|
// TODO: simplistic ensure cursor in view for now
|
||||||
|
// TODO: loop over views
|
||||||
if let Some(view) = self.editor.view_mut() {
|
if let Some(view) = self.editor.view_mut() {
|
||||||
view.size = self.terminal.size;
|
view.size = self.terminal.size;
|
||||||
view.ensure_cursor_in_view()
|
view.ensure_cursor_in_view()
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
use crate::theme::Theme;
|
||||||
use crate::View;
|
use crate::View;
|
||||||
|
use helix_core::State;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
@ -8,20 +10,25 @@ pub struct Editor {
|
||||||
pub views: Vec<View>,
|
pub views: Vec<View>,
|
||||||
pub focus: usize,
|
pub focus: usize,
|
||||||
pub should_close: bool,
|
pub should_close: bool,
|
||||||
|
pub theme: Theme, // TODO: share one instance
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Editor {
|
impl Editor {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
|
let theme = Theme::default();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
views: Vec::new(),
|
views: Vec::new(),
|
||||||
focus: 0,
|
focus: 0,
|
||||||
should_close: false,
|
should_close: false,
|
||||||
|
theme,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open(&mut self, path: PathBuf, size: (u16, u16)) -> Result<(), Error> {
|
pub fn open(&mut self, path: PathBuf, size: (u16, u16)) -> Result<(), Error> {
|
||||||
let pos = self.views.len();
|
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;
|
self.focus = pos;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,4 +6,5 @@ pub mod theme;
|
||||||
pub mod view;
|
pub mod view;
|
||||||
|
|
||||||
pub use editor::Editor;
|
pub use editor::Editor;
|
||||||
|
pub use theme::Theme;
|
||||||
pub use view::View;
|
pub use view::View;
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
|
|
||||||
use std::{borrow::Cow, path::PathBuf};
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use crate::theme::Theme;
|
|
||||||
use helix_core::{
|
use helix_core::{
|
||||||
graphemes::{grapheme_width, RopeGraphemes},
|
graphemes::{grapheme_width, RopeGraphemes},
|
||||||
indent::TAB_WIDTH,
|
indent::TAB_WIDTH,
|
||||||
|
@ -12,24 +11,23 @@ use tui::layout::Rect;
|
||||||
|
|
||||||
pub const PADDING: usize = 5;
|
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 struct View {
|
||||||
pub state: State,
|
pub state: State,
|
||||||
pub history: History,
|
|
||||||
pub first_line: usize,
|
pub first_line: usize,
|
||||||
pub size: (u16, u16),
|
pub size: (u16, u16),
|
||||||
pub theme: Theme, // TODO: share one instance
|
|
||||||
|
// TODO: Doc<> fields
|
||||||
|
pub history: History,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl View {
|
impl View {
|
||||||
pub fn open(path: PathBuf, size: (u16, u16)) -> Result<Self, Error> {
|
pub fn new(state: State, size: (u16, u16)) -> Result<Self, Error> {
|
||||||
let theme = Theme::default();
|
|
||||||
let state = State::load(path, theme.scopes())?;
|
|
||||||
|
|
||||||
let view = Self {
|
let view = Self {
|
||||||
state,
|
state,
|
||||||
first_line: 0,
|
first_line: 0,
|
||||||
size,
|
size,
|
||||||
theme,
|
|
||||||
history: History::default(),
|
history: History::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue