mirror of
https://github.com/helix-editor/helix.git
synced 2024-11-24 18:36:18 +04:00
Split parts of helix-term into helix-view.
It still largely depends on term for some types but I plan to change that later.
This commit is contained in:
parent
48330ddb5f
commit
935cfeae57
12
Cargo.lock
generated
12
Cargo.lock
generated
@ -410,12 +410,22 @@ dependencies = [
|
|||||||
"argh",
|
"argh",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
"helix-core",
|
"helix-core",
|
||||||
"helix-syntax",
|
"helix-view",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
"smol",
|
"smol",
|
||||||
"tui",
|
"tui",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "helix-view"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"crossterm",
|
||||||
|
"helix-core",
|
||||||
|
"tui",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.1.15"
|
version = "0.1.15"
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"helix-core",
|
"helix-core",
|
||||||
|
"helix-view",
|
||||||
"helix-term",
|
"helix-term",
|
||||||
"helix-syntax",
|
"helix-syntax",
|
||||||
]
|
]
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
# Helix
|
||||||
|
|
||||||
|
| Crate | Description |
|
||||||
|
| ----------- | ----------- |
|
||||||
|
| helix-core | Core editing primitives, functional. |
|
||||||
|
| helix-syntax | Tree-sitter grammars |
|
||||||
|
| helix-view | UI abstractions for use in backends, imperative shell. |
|
||||||
|
| helix-term | Terminal UI |
|
||||||
|
|
||||||
- server-client architecture via gRPC, UI separate from core
|
- server-client architecture via gRPC, UI separate from core
|
||||||
- multi cursor based editing and slicing
|
- multi cursor based editing and slicing
|
||||||
- WASM based plugins (builtin LSP & fuzzy file finder)
|
- WASM based plugins (builtin LSP & fuzzy file finder)
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
|
pub mod config;
|
||||||
pub mod graphemes;
|
pub mod graphemes;
|
||||||
|
pub mod macros;
|
||||||
mod position;
|
mod position;
|
||||||
mod selection;
|
mod selection;
|
||||||
pub mod state;
|
pub mod state;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#[macro_export]
|
||||||
macro_rules! hashmap {
|
macro_rules! hashmap {
|
||||||
(@single $($x:tt)*) => (());
|
(@single $($x:tt)*) => (());
|
||||||
(@count $($rest:expr),*) => (<[()]>::len(&[$(hashmap!(@single $rest)),*]));
|
(@count $($rest:expr),*) => (<[()]>::len(&[$(hashmap!(@single $rest)),*]));
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum Mode {
|
pub enum Mode {
|
||||||
Normal,
|
Normal,
|
||||||
Insert,
|
Insert,
|
||||||
|
@ -12,12 +12,12 @@ path = "src/main.rs"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
helix-core = { path = "../helix-core" }
|
helix-core = { path = "../helix-core" }
|
||||||
helix-syntax = { path = "../helix-syntax" }
|
helix-view = { path = "../helix-view", features = ["term"]}
|
||||||
|
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
argh = "0.1.3"
|
argh = "0.1.3"
|
||||||
crossterm = { version = "0.17", features = ["event-stream"] }
|
|
||||||
|
|
||||||
smol = "1"
|
smol = "1"
|
||||||
num_cpus = "1.13.0"
|
num_cpus = "1.13.0"
|
||||||
tui = { version = "0.10.0", default-features = false, features = ["crossterm"] }
|
tui = { version = "0.10.0", default-features = false, features = ["crossterm"] }
|
||||||
|
crossterm = { version = "0.17", features = ["event-stream"] }
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
use crate::{commands, keymap, theme::Theme, Args};
|
use crate::Args;
|
||||||
use helix_core::{
|
use helix_core::{
|
||||||
state::coords_at_pos,
|
state::coords_at_pos,
|
||||||
state::Mode,
|
state::Mode,
|
||||||
syntax::{HighlightConfiguration, HighlightEvent, Highlighter},
|
syntax::{HighlightConfiguration, HighlightEvent, Highlighter},
|
||||||
State,
|
State,
|
||||||
};
|
};
|
||||||
|
use helix_view::{commands, keymap, View};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
io::{self, stdout, Write},
|
io::{self, stdout, Write},
|
||||||
@ -31,19 +32,12 @@
|
|||||||
|
|
||||||
static EX: smol::Executor = smol::Executor::new();
|
static EX: smol::Executor = smol::Executor::new();
|
||||||
|
|
||||||
pub struct View {
|
|
||||||
pub state: State,
|
|
||||||
pub first_line: u16,
|
|
||||||
pub size: (u16, u16),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Editor {
|
pub struct Editor {
|
||||||
terminal: Terminal,
|
terminal: Terminal,
|
||||||
view: Option<View>,
|
view: Option<View>,
|
||||||
size: (u16, u16),
|
size: (u16, u16),
|
||||||
surface: Surface,
|
surface: Surface,
|
||||||
cache: Surface,
|
cache: Surface,
|
||||||
theme: Theme,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Editor {
|
impl Editor {
|
||||||
@ -53,7 +47,6 @@ pub fn new(mut args: Args) -> Result<Self, Error> {
|
|||||||
let mut terminal = Terminal::new(backend)?;
|
let mut terminal = Terminal::new(backend)?;
|
||||||
let size = terminal::size().unwrap();
|
let size = terminal::size().unwrap();
|
||||||
let area = Rect::new(0, 0, size.0, size.1);
|
let area = Rect::new(0, 0, size.0, size.1);
|
||||||
let theme = Theme::default();
|
|
||||||
|
|
||||||
let mut editor = Editor {
|
let mut editor = Editor {
|
||||||
terminal,
|
terminal,
|
||||||
@ -61,7 +54,6 @@ pub fn new(mut args: Args) -> Result<Self, Error> {
|
|||||||
size,
|
size,
|
||||||
surface: Surface::empty(area),
|
surface: Surface::empty(area),
|
||||||
cache: Surface::empty(area),
|
cache: Surface::empty(area),
|
||||||
theme,
|
|
||||||
// TODO; move to state
|
// TODO; move to state
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -73,20 +65,7 @@ pub fn new(mut args: Args) -> Result<Self, Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn open(&mut self, path: PathBuf) -> Result<(), Error> {
|
pub fn open(&mut self, path: PathBuf) -> Result<(), Error> {
|
||||||
let mut state = State::load(path)?;
|
self.view = Some(View::open(path, self.size)?);
|
||||||
state
|
|
||||||
.syntax
|
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.configure(self.theme.scopes());
|
|
||||||
|
|
||||||
let view = View {
|
|
||||||
state,
|
|
||||||
first_line: 0,
|
|
||||||
size: self.size,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.view = Some(view);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +81,7 @@ fn render(&mut self) {
|
|||||||
|
|
||||||
// clear with background color
|
// clear with background color
|
||||||
self.surface
|
self.surface
|
||||||
.set_style(area, self.theme.get("ui.background"));
|
.set_style(area, view.theme.get("ui.background").into());
|
||||||
|
|
||||||
let offset = 5 + 1; // 5 linenr + 1 gutter
|
let offset = 5 + 1; // 5 linenr + 1 gutter
|
||||||
let viewport = Rect::new(offset, 0, self.size.0, self.size.1 - 1); // - 1 for statusline
|
let viewport = Rect::new(offset, 0, self.size.0, self.size.1 - 1); // - 1 for statusline
|
||||||
@ -161,7 +140,9 @@ fn render(&mut self) {
|
|||||||
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) => self.theme.get(self.theme.scopes()[span.0].as_str()),
|
Some(span) => {
|
||||||
|
view.theme.get(view.theme.scopes()[span.0].as_str()).into()
|
||||||
|
}
|
||||||
None => Style::default().fg(Color::Rgb(164, 160, 232)), // lavender
|
None => Style::default().fg(Color::Rgb(164, 160, 232)), // lavender
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -202,7 +183,7 @@ fn render(&mut self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut line = 0;
|
let mut line = 0;
|
||||||
let style = self.theme.get("ui.linenr");
|
let style: Style = view.theme.get("ui.linenr").into();
|
||||||
for i in view.first_line..(last_line as u16) {
|
for i in view.first_line..(last_line as u16) {
|
||||||
self.surface
|
self.surface
|
||||||
.set_stringn(0, line, format!("{:>5}", i + 1), 5, style); // lavender
|
.set_stringn(0, line, format!("{:>5}", i + 1), 5, style); // lavender
|
||||||
@ -241,7 +222,7 @@ fn render(&mut self) {
|
|||||||
};
|
};
|
||||||
self.surface.set_style(
|
self.surface.set_style(
|
||||||
Rect::new(0, self.size.1 - 1, self.size.0, 1),
|
Rect::new(0, self.size.1 - 1, self.size.0, 1),
|
||||||
self.theme.get("ui.statusline"),
|
view.theme.get("ui.statusline").into(),
|
||||||
);
|
);
|
||||||
// TODO: unfocused one with different color
|
// TODO: unfocused one with different color
|
||||||
let text_color = Style::default().fg(Color::Rgb(219, 191, 239)); // lilac
|
let text_color = Style::default().fg(Color::Rgb(219, 191, 239)); // lilac
|
||||||
@ -296,7 +277,9 @@ pub async fn event_loop(&mut self) {
|
|||||||
self.cache = Surface::empty(area);
|
self.cache = Surface::empty(area);
|
||||||
|
|
||||||
// TODO: simplistic ensure cursor in view for now
|
// TODO: simplistic ensure cursor in view for now
|
||||||
self.ensure_cursor_in_view();
|
if let Some(view) = &mut self.view {
|
||||||
|
view.ensure_cursor_in_view()
|
||||||
|
};
|
||||||
|
|
||||||
self.render();
|
self.render();
|
||||||
}
|
}
|
||||||
@ -333,18 +316,19 @@ pub async fn event_loop(&mut self) {
|
|||||||
_ => (), // skip
|
_ => (), // skip
|
||||||
}
|
}
|
||||||
// TODO: simplistic ensure cursor in view for now
|
// TODO: simplistic ensure cursor in view for now
|
||||||
self.ensure_cursor_in_view();
|
view.ensure_cursor_in_view();
|
||||||
|
|
||||||
self.render();
|
self.render();
|
||||||
}
|
}
|
||||||
Mode::Normal => {
|
Mode::Normal => {
|
||||||
// TODO: handle modes and sequences (`gg`)
|
// TODO: handle modes and sequences (`gg`)
|
||||||
if let Some(command) = keymap.get(&event) {
|
let keys = vec![event];
|
||||||
|
if let Some(command) = keymap[&Mode::Normal].get(&keys) {
|
||||||
// TODO: handle count other than 1
|
// TODO: handle count other than 1
|
||||||
command(view, 1);
|
command(view, 1);
|
||||||
|
|
||||||
// TODO: simplistic ensure cursor in view for now
|
// TODO: simplistic ensure cursor in view for now
|
||||||
self.ensure_cursor_in_view();
|
view.ensure_cursor_in_view();
|
||||||
|
|
||||||
self.render();
|
self.render();
|
||||||
}
|
}
|
||||||
@ -361,26 +345,6 @@ pub async fn event_loop(&mut self) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ensure_cursor_in_view(&mut self) {
|
|
||||||
if let Some(view) = &mut self.view {
|
|
||||||
let cursor = view.state.selection().cursor();
|
|
||||||
let line = view.state.doc().char_to_line(cursor) as u16;
|
|
||||||
let document_end = view.first_line + self.size.1.saturating_sub(1) - 1;
|
|
||||||
|
|
||||||
let padding = 5u16;
|
|
||||||
|
|
||||||
// TODO: side scroll
|
|
||||||
|
|
||||||
if line > document_end.saturating_sub(padding) {
|
|
||||||
// scroll down
|
|
||||||
view.first_line += line - (document_end.saturating_sub(padding));
|
|
||||||
} else if line < view.first_line + padding {
|
|
||||||
// scroll up
|
|
||||||
view.first_line = line.saturating_sub(padding);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn run(&mut self) -> Result<(), Error> {
|
pub async fn run(&mut self) -> Result<(), Error> {
|
||||||
enable_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
|
|
||||||
|
@ -1,11 +1,6 @@
|
|||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
#[macro_use]
|
|
||||||
mod macros;
|
|
||||||
|
|
||||||
mod commands;
|
|
||||||
mod editor;
|
mod editor;
|
||||||
mod keymap;
|
|
||||||
mod theme;
|
|
||||||
|
|
||||||
use editor::Editor;
|
use editor::Editor;
|
||||||
|
|
||||||
|
18
helix-view/Cargo.toml
Normal file
18
helix-view/Cargo.toml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
[package]
|
||||||
|
name = "helix-view"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Blaž Hrastnik <blaz@mxxn.io>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[features]
|
||||||
|
term = ["tui", "crossterm"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0.32"
|
||||||
|
helix-core = { path = "../helix-core" }
|
||||||
|
|
||||||
|
# Conversion traits
|
||||||
|
tui = { version = "0.10.0", default-features = false, features = ["crossterm"], optional = true}
|
||||||
|
crossterm = { version = "0.17", features = ["event-stream"], optional = true}
|
@ -1,10 +1,10 @@
|
|||||||
use helix_core::{
|
use helix_core::{
|
||||||
graphemes,
|
graphemes,
|
||||||
state::{Direction, Granularity, Mode, State},
|
state::{Direction, Granularity, Mode, State},
|
||||||
ChangeSet, Range, Selection, Tendril, Transaction,
|
Range, Selection, Tendril, Transaction,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::editor::View;
|
use crate::view::View;
|
||||||
|
|
||||||
/// A command is a function that takes the current state and a count, and does a side-effect on the
|
/// A command is a function that takes the current state and a count, and does a side-effect on the
|
||||||
/// state (usually by creating and applying a transaction).
|
/// state (usually by creating and applying a transaction).
|
@ -1,9 +1,5 @@
|
|||||||
use crate::commands::{self, Command};
|
use crate::commands::{self, Command};
|
||||||
use crossterm::{
|
use helix_core::{hashmap, state};
|
||||||
event::{KeyCode, KeyEvent as Key, KeyModifiers as Modifiers},
|
|
||||||
execute,
|
|
||||||
style::Print,
|
|
||||||
};
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
// Kakoune-inspired:
|
// Kakoune-inspired:
|
||||||
@ -79,56 +75,57 @@
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
type Keymap = HashMap<Key, Command>;
|
#[cfg(feature = "term")]
|
||||||
|
pub use crossterm::event::{KeyCode, KeyEvent as Key, KeyModifiers as Modifiers};
|
||||||
|
|
||||||
pub fn default() -> Keymap {
|
// TODO: could be trie based
|
||||||
|
type Keymap = HashMap<Vec<Key>, Command>;
|
||||||
|
type Keymaps = HashMap<state::Mode, Keymap>;
|
||||||
|
|
||||||
|
pub fn default() -> Keymaps {
|
||||||
hashmap!(
|
hashmap!(
|
||||||
Key {
|
state::Mode::Normal =>
|
||||||
code: KeyCode::Char('h'),
|
hashmap!(
|
||||||
modifiers: Modifiers::NONE
|
vec![Key {
|
||||||
} => commands::move_char_left as Command,
|
code: KeyCode::Char('h'),
|
||||||
Key {
|
modifiers: Modifiers::NONE
|
||||||
code: KeyCode::Char('j'),
|
}] => commands::move_char_left as Command,
|
||||||
modifiers: Modifiers::NONE
|
vec![Key {
|
||||||
} => commands::move_line_down as Command,
|
code: KeyCode::Char('j'),
|
||||||
Key {
|
modifiers: Modifiers::NONE
|
||||||
code: KeyCode::Char('k'),
|
}] => commands::move_line_down as Command,
|
||||||
modifiers: Modifiers::NONE
|
vec![Key {
|
||||||
} => commands::move_line_up as Command,
|
code: KeyCode::Char('k'),
|
||||||
Key {
|
modifiers: Modifiers::NONE
|
||||||
code: KeyCode::Char('l'),
|
}] => commands::move_line_up as Command,
|
||||||
modifiers: Modifiers::NONE
|
vec![Key {
|
||||||
} => commands::move_char_right as Command,
|
code: KeyCode::Char('l'),
|
||||||
Key {
|
modifiers: Modifiers::NONE
|
||||||
code: KeyCode::Char('i'),
|
}] => commands::move_char_right as Command,
|
||||||
modifiers: Modifiers::NONE
|
vec![Key {
|
||||||
} => commands::insert_mode as Command,
|
code: KeyCode::Char('i'),
|
||||||
Key {
|
modifiers: Modifiers::NONE
|
||||||
code: KeyCode::Char('I'),
|
}] => commands::insert_mode as Command,
|
||||||
modifiers: Modifiers::SHIFT,
|
vec![Key {
|
||||||
} => commands::prepend_to_line as Command,
|
code: KeyCode::Char('I'),
|
||||||
Key {
|
modifiers: Modifiers::SHIFT,
|
||||||
code: KeyCode::Char('a'),
|
}] => commands::prepend_to_line as Command,
|
||||||
modifiers: Modifiers::NONE
|
vec![Key {
|
||||||
} => commands::append_mode as Command,
|
code: KeyCode::Char('a'),
|
||||||
Key {
|
modifiers: Modifiers::NONE
|
||||||
code: KeyCode::Char('A'),
|
}] => commands::append_mode as Command,
|
||||||
modifiers: Modifiers::SHIFT,
|
vec![Key {
|
||||||
} => commands::append_to_line as Command,
|
code: KeyCode::Char('A'),
|
||||||
Key {
|
modifiers: Modifiers::SHIFT,
|
||||||
code: KeyCode::Char('o'),
|
}] => commands::append_to_line as Command,
|
||||||
modifiers: Modifiers::NONE
|
vec![Key {
|
||||||
} => commands::open_below as Command,
|
code: KeyCode::Char('o'),
|
||||||
Key {
|
modifiers: Modifiers::NONE
|
||||||
code: KeyCode::Esc,
|
}] => commands::open_below as Command,
|
||||||
modifiers: Modifiers::NONE
|
vec![Key {
|
||||||
} => commands::normal_mode as Command,
|
code: KeyCode::Esc,
|
||||||
|
modifiers: Modifiers::NONE
|
||||||
|
}] => commands::normal_mode as Command,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
// hashmap!(
|
|
||||||
// Key {
|
|
||||||
// code: KeyCode::Esc,
|
|
||||||
// modifiers: Modifiers::NONE
|
|
||||||
// } => commands::normal_mode as Command,
|
|
||||||
// )
|
|
||||||
}
|
}
|
6
helix-view/src/lib.rs
Normal file
6
helix-view/src/lib.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
pub mod commands;
|
||||||
|
pub mod keymap;
|
||||||
|
pub mod theme;
|
||||||
|
pub mod view;
|
||||||
|
|
||||||
|
pub use view::View;
|
@ -1,5 +1,86 @@
|
|||||||
|
use helix_core::hashmap;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use tui::style::{Color, Style};
|
|
||||||
|
#[cfg(feature = "term")]
|
||||||
|
pub use tui::style::{Color, Style};
|
||||||
|
|
||||||
|
// #[derive(Clone, Copy, PartialEq, Eq, Default, Hash)]
|
||||||
|
// pub struct Color {
|
||||||
|
// pub r: u8,
|
||||||
|
// pub g: u8,
|
||||||
|
// pub b: u8,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl Color {
|
||||||
|
// pub fn new(r: u8, g: u8, b: u8) -> Self {
|
||||||
|
// Self { r, g, b }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[cfg(feature = "term")]
|
||||||
|
// impl Into<tui::style::Color> for Color {
|
||||||
|
// fn into(self) -> tui::style::Color {
|
||||||
|
// tui::style::Color::Rgb(self.r, self.g, self.b)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl std::str::FromStr for Color {
|
||||||
|
// type Err = ();
|
||||||
|
|
||||||
|
// /// Tries to parse a string (`'#FFFFFF'` or `'FFFFFF'`) into RGB.
|
||||||
|
// fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||||
|
// let input = input.trim();
|
||||||
|
// let input = match (input.chars().next(), input.len()) {
|
||||||
|
// (Some('#'), 7) => &input[1..],
|
||||||
|
// (_, 6) => input,
|
||||||
|
// _ => return Err(()),
|
||||||
|
// };
|
||||||
|
|
||||||
|
// u32::from_str_radix(&input, 16)
|
||||||
|
// .map(|s| Color {
|
||||||
|
// r: ((s >> 16) & 0xFF) as u8,
|
||||||
|
// g: ((s >> 8) & 0xFF) as u8,
|
||||||
|
// b: (s & 0xFF) as u8,
|
||||||
|
// })
|
||||||
|
// .map_err(|_| ())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[derive(Clone, Copy, PartialEq, Eq, Default, Hash)]
|
||||||
|
// pub struct Style {
|
||||||
|
// pub fg: Option<Color>,
|
||||||
|
// pub bg: Option<Color>,
|
||||||
|
// // TODO: modifiers (bold, underline, italic, etc)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl Style {
|
||||||
|
// pub fn fg(mut self, fg: Color) -> Self {
|
||||||
|
// self.fg = Some(fg);
|
||||||
|
// self
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn bg(mut self, bg: Color) -> Self {
|
||||||
|
// self.bg = Some(bg);
|
||||||
|
// self
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[cfg(feature = "term")]
|
||||||
|
// impl Into<tui::style::Style> for Style {
|
||||||
|
// fn into(self) -> tui::style::Style {
|
||||||
|
// let style = tui::style::Style::default();
|
||||||
|
|
||||||
|
// if let Some(fg) = self.fg {
|
||||||
|
// style.fg(fg.into());
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if let Some(bg) = self.bg {
|
||||||
|
// style.bg(bg.into());
|
||||||
|
// }
|
||||||
|
|
||||||
|
// style
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
/// Color theme for syntax highlighting.
|
/// Color theme for syntax highlighting.
|
||||||
pub struct Theme {
|
pub struct Theme {
|
48
helix-view/src/view.rs
Normal file
48
helix-view/src/view.rs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
use anyhow::Error;
|
||||||
|
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use crate::theme::Theme;
|
||||||
|
use helix_core::State;
|
||||||
|
|
||||||
|
pub struct View {
|
||||||
|
pub state: State,
|
||||||
|
pub first_line: u16,
|
||||||
|
pub size: (u16, u16),
|
||||||
|
pub theme: Theme, // TODO: share one instance
|
||||||
|
}
|
||||||
|
|
||||||
|
impl View {
|
||||||
|
pub fn open(path: PathBuf, size: (u16, u16)) -> Result<View, Error> {
|
||||||
|
let mut state = State::load(path)?;
|
||||||
|
let theme = Theme::default();
|
||||||
|
state.syntax.as_mut().unwrap().configure(theme.scopes());
|
||||||
|
|
||||||
|
let view = View {
|
||||||
|
state,
|
||||||
|
first_line: 0,
|
||||||
|
size, // TODO: pass in from term
|
||||||
|
theme,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(view)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ensure_cursor_in_view(&mut self) {
|
||||||
|
let cursor = self.state.selection().cursor();
|
||||||
|
let line = self.state.doc().char_to_line(cursor) as u16;
|
||||||
|
let document_end = self.first_line + self.size.1.saturating_sub(1) - 1;
|
||||||
|
|
||||||
|
let padding = 5u16;
|
||||||
|
|
||||||
|
// TODO: side scroll
|
||||||
|
|
||||||
|
if line > document_end.saturating_sub(padding) {
|
||||||
|
// scroll down
|
||||||
|
self.first_line += line - (document_end.saturating_sub(padding));
|
||||||
|
} else if line < self.first_line + padding {
|
||||||
|
// scroll up
|
||||||
|
self.first_line = line.saturating_sub(padding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user