emma
/
breed
Archived
forked from mars/breed
1
0
Fork 0

Compare commits

...

9 Commits

6 changed files with 428 additions and 25 deletions

203
Cargo.lock generated
View File

@ -2,6 +2,12 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "arg"
version = "0.4.1"
@ -18,7 +24,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "849efc162c06e51ce911bf4fe702f62a08f3b6ebbbfc5178e86c6bae449c2c60"
dependencies = [
"quote",
"syn",
"syn 1.0.109",
"tabwriter",
]
@ -28,6 +34,15 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]]
name = "bindgen"
version = "0.63.0"
@ -46,7 +61,7 @@ dependencies = [
"regex",
"rustc-hash",
"shlex",
"syn",
"syn 1.0.109",
"which",
]
@ -64,11 +79,19 @@ dependencies = [
"crossterm",
"libc",
"once_cell",
"parking_lot",
"ropey",
"syntect",
"toml",
"yacexits",
]
[[package]]
name = "cc"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "cexpr"
version = "0.6.0"
@ -95,6 +118,15 @@ dependencies = [
"libloading",
]
[[package]]
name = "crc32fast"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossterm"
version = "0.26.1"
@ -126,6 +158,22 @@ version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
[[package]]
name = "flate2"
version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "glob"
version = "0.3.1"
@ -148,6 +196,12 @@ dependencies = [
"hashbrown",
]
[[package]]
name = "itoa"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -207,6 +261,15 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
dependencies = [
"adler",
]
[[package]]
name = "mio"
version = "0.8.6"
@ -235,6 +298,28 @@ version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "onig"
version = "6.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c4b31c8722ad9171c6d77d3557db078cab2bd50afcc9d09c8b315c59df8ca4f"
dependencies = [
"bitflags",
"libc",
"once_cell",
"onig_sys",
]
[[package]]
name = "onig_sys"
version = "69.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b829e3d7e9cc74c7e315ee8edb185bf4190da5acde74afd7fc59c35b1f086e7"
dependencies = [
"cc",
"pkg-config",
]
[[package]]
name = "parking_lot"
version = "0.12.1"
@ -264,6 +349,12 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "pkg-config"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
[[package]]
name = "proc-macro2"
version = "1.0.56"
@ -322,6 +413,21 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "ryu"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
@ -334,6 +440,28 @@ version = "1.0.160"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c"
[[package]]
name = "serde_derive"
version = "1.0.160"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.14",
]
[[package]]
name = "serde_json"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "serde_spanned"
version = "0.6.1"
@ -402,6 +530,38 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcf316d5356ed6847742d036f8a39c3b8435cac10bd528a4bd461928a6ab34d5"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syntect"
version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6c454c27d9d7d9a84c7803aaa3c50cd088d2906fe3c6e42da3209aa623576a8"
dependencies = [
"bincode",
"bitflags",
"flate2",
"fnv",
"lazy_static",
"once_cell",
"onig",
"regex-syntax",
"serde",
"serde_derive",
"serde_json",
"thiserror",
"walkdir",
]
[[package]]
name = "tabwriter"
version = "1.2.1"
@ -411,6 +571,26 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "thiserror"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.14",
]
[[package]]
name = "toml"
version = "0.7.3"
@ -457,6 +637,16 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "walkdir"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
@ -490,6 +680,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"

View File

@ -9,6 +9,12 @@ arg = "0.4.1"
crossterm = "0.26"
libc = "0.2.141"
once_cell = "1.17"
parking_lot = "0.12"
ropey = "1.6"
toml = "0.7"
yacexits = "0.1.5"
[dependencies.syntect]
version = "5"
default-features = false
features = ["default-syntaxes", "parsing", "regex-onig"]

View File

@ -1,3 +1,93 @@
"ui.linenr" = "gray"
"error" = "red"
"error" = "love"
"attribute" = "iris"
"type" = "foam"
# "type.builtin" = ""
"constructor" = "foam"
"comment" = { fg = "muted", modifiers = ["italic"]}
"constant" = "foam"
"constant.builtin" = "rose"
"constant.character" = "gold"
"constant.character.escape" = "pine"
"constant.numeric" = "gold"
"entity.name" = "rose"
"invalid" = "love"
"keyword" = "pine"
"keyword.operator" = "subtle"
"label" = "foam"
"operator" = "subtle"
"punctuation" = "subtle"
"string" = "gold"
# "string.regexp" = ""
# "string.special" = ""
# "string.special.path" = ""
# "string.special.url" = ""
# "string.special.symbol" = ""
"variable" = "text"
"variable.builtin" = "love"
"variable.parameter" = "iris"
"markup.heading.marker" = "muted"
"markup.heading" = { fg = "iris", modifiers = ["bold"] }
"markup.heading.1" = { fg = "iris", modifiers = ["bold"] }
"markup.heading.2" = { fg = "foam", modifiers = ["bold"] }
"markup.heading.3" = { fg = "rose", modifiers = ["bold"] }
"markup.heading.4" = { fg = "gold", modifiers = ["bold"] }
"markup.heading.5" = { fg = "pine", modifiers = ["bold"] }
"markup.heading.6" = { fg = "foam", modifiers = ["bold"] }
# "markup.heading.completion" = ""
# "markup.heading.hover" = ""
"markup.list" = "muted"
# "markup.list.unnumbered" = ""
# "markup.list.numbered" = ""
"markup.bold" = { modifiers = ["bold"] }
"markup.italic" = { modifiers = ["italic"] }
"markup.strikethrough" = { modifiers = ["crossed_out"] }
"markup.link" = "iris"
"markup.link.url" = { fg = "iris", underline = { color = "iris", style = "line" } }
"markup.link.label" = "subtle"
"markup.link.text" = "text"
"markup.quote" = "subtle"
"markup.raw" = "subtle"
# "markup.raw.inline" = {}
# "markup.raw.inline.completion" = {}
# "markup.raw.inline.hover" = {}
# "markup.raw.block" = {}
# "markup.normal" = ""
# "markup.normal.completion" = ""
# "markup.normal.hover" = ""
"diff" = "overlay"
"diff.plus" = "foam"
"diff.minus" = "love"
"diff.delta" = "highlight_high"
# "diff.delta.moved" = ""
[palette]
base = "#191724"
surface = "#1f1d2e"
overlay = "#26233a"
muted = "#6e6a86"
subtle = "#908caa"
text = "#e0def4"
love = "#eb6f92"
love_10 = "#311f30"
gold = "#f6c177"
gold_10 = "#30282c"
rose = "#ebbcba"
rose_10 = "#2f2834"
pine = "#31748f"
pine_10 = "#1a2030"
foam = "#9ccfd8"
foam_10 = "#252937"
iris = "#c4a7e7"
iris_10 = "#2b2539"
highlight_low = "#21202e"
highlight_med = "#403d52"
highlight_high = "#524f67"

View File

@ -18,23 +18,45 @@
*/
use std::io::Write;
use std::ops::Range;
use std::sync::Arc;
use crossterm::{cursor, ExecutableCommand};
use parking_lot::Mutex;
use ropey::Rope;
use syntect::easy::ScopeRangeIterator;
use syntect::parsing::{ParseState, Scope, ScopeStack, SyntaxSet};
use crate::theme::StyleStore;
use crate::theme::{Style, StyleStore};
use crate::{Cursor, Direction};
#[derive(Clone, Debug)]
pub struct Buffer {
styles: Arc<Mutex<StyleStore>>,
pub text: Rope,
syntax_set: Arc<SyntaxSet>,
parser: ParseState,
styled: Vec<Vec<(Style, Range<usize>)>>,
}
impl Buffer {
pub fn from_str(text: &str) -> Self {
Self {
pub fn from_str(styles: Arc<Mutex<StyleStore>>, text: &str) -> Self {
let syntax_set = SyntaxSet::load_defaults_nonewlines();
// TODO load non-Rust syntax highlighting
let syntax_ref = syntax_set.find_syntax_by_extension("rs").unwrap();
let parser = ParseState::new(syntax_ref);
let mut buf = Self {
styles,
text: Rope::from_str(text),
}
syntax_set: Arc::new(SyntaxSet::load_defaults_newlines()),
parser,
styled: Vec::new(),
};
buf.parse();
buf
}
pub fn draw(
@ -63,11 +85,24 @@ impl Buffer {
linenr_style.print_styled(out, &linenr)?;
let lhs = scroll.column;
let width = line.len_chars().saturating_sub(1); // lop off whitespace
let width = line.len_chars();
if lhs < width {
let window = text_width.min(width - lhs);
let rhs = lhs + window;
write!(out, "{}", line.slice(lhs..rhs))?;
let styled = &self.styled[row];
for (style, range) in styled.iter() {
let range = range.start.max(lhs)..range.end.min(rhs);
if range.start < range.end {
// this range is in view so display
let content = line.slice(range.clone());
style.print_styled(out, content)?;
}
if range.end >= rhs {
// past the end of the window so we're done
break;
}
}
}
out.execute(cursor::MoveToNextLine(1))?;
@ -79,11 +114,55 @@ impl Buffer {
pub fn remove(&mut self, cursor: Cursor) {
let index = self.cursor_to_char(cursor);
self.text.remove(index..=index);
self.parse();
}
pub fn insert_char(&mut self, cursor: Cursor, c: char) {
let index = self.cursor_to_char(cursor);
self.text.insert_char(index, c);
self.parse();
}
/// Parses the whole file from scratch.
fn parse(&mut self) {
use std::fmt::Write;
let mut styles = self.styles.lock();
self.styled.clear();
let mut parser = self.parser.clone();
let mut line_buf = String::new();
let mut stack = ScopeStack::new();
for line in self.text.lines() {
// display line to a pre-allocated buffer for fast parsing
line_buf.clear();
write!(line_buf, "{}", line).unwrap();
let end = line_buf.trim_end().len();
line_buf.truncate(end);
// parse the line into a sequence of stack operations
let stack_ops = parser.parse_line(&line_buf, &self.syntax_set).unwrap();
// execute the operations on the stack
let mut line = Vec::new();
let range_iter = ScopeRangeIterator::new(&stack_ops, &line_buf);
for (range, op) in range_iter {
stack.apply(&op).unwrap();
if range.start == range.end {
continue;
}
let mut style = Style::default();
for scope in stack.scopes.clone() {
// TODO docs say that build_string "shouldn't be done frequently"
let scope = scope.build_string();
style.apply(&styles.get_scope(&scope));
}
line.push((style, range));
}
self.styled.push(line);
}
}
pub fn clamped_cursor(&self, cursor: Cursor) -> Cursor {
@ -136,3 +215,16 @@ impl Buffer {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn style_scopes() {
let text = include_str!("buffer.rs");
let mut buffer = Buffer::from_str(Default::default(), text);
buffer.parse();
println!("{:#?}", buffer.styled);
}
}

View File

@ -28,7 +28,8 @@ use std::{
Write,
},
os::fd::FromRawFd,
path::{ Path }
path::{ Path },
sync::Arc,
};
use crossterm::{
@ -36,6 +37,7 @@ use crossterm::{
event::{read, Event, KeyCode, KeyEvent},
terminal, ExecutableCommand, Result,
};
use parking_lot::Mutex;
use yacexits::{exit, EX_DATAERR, EX_UNAVAILABLE};
mod buffer;
@ -101,7 +103,7 @@ pub enum Direction {
}
struct State {
pub style_store: StyleStore,
pub styles: Arc<Mutex<StyleStore>>,
pub buffer: Buffer,
pub cursor: Cursor,
pub file: Option<OsString>,
@ -113,11 +115,13 @@ struct State {
impl State {
pub fn from_str(file_name: Option<OsString>, text: &str) -> Result<Self> {
let styles = Arc::new(Mutex::new(StyleStore::default()));
let buffer = Buffer::from_str(styles.clone(), text);
let (cols, rows) = terminal::size()?;
Ok(Self {
style_store: StyleStore::default(),
buffer: Buffer::from_str(text),
styles,
buffer,
cursor: Cursor::default(),
file: file_name,
mode: Mode::default(),
@ -132,6 +136,7 @@ impl State {
let (cols, rows) = terminal::size()?;
out.execute(terminal::BeginSynchronizedUpdate)?;
out.execute(terminal::Clear(terminal::ClearType::All))?;
let mut styles = self.styles.lock();
// draw status line
let mut set_cursor_pos = None;
@ -147,7 +152,7 @@ impl State {
show_status_bar = true;
}
Mode::Normal(NormalState { error: Some(error) }) => {
let error_style = self.style_store.get_scope("error");
let error_style = styles.get_scope("error");
out.execute(cursor::MoveTo(0, rows - 1))?;
error_style.print_styled(out, error)?;
show_status_bar = true;
@ -157,9 +162,9 @@ impl State {
// draw buffer
let buffer_rows = if show_status_bar { rows - 1 } else { rows };
let lr_width =
self.buffer
.draw(&mut self.style_store, cols, buffer_rows, self.scroll, out)?;
let lr_width = self
.buffer
.draw(&mut styles, cols, buffer_rows, self.scroll, out)?;
// draw cursor
let cursor_pos = set_cursor_pos.unwrap_or_else(|| {

View File

@ -17,8 +17,8 @@
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
use std::collections::HashMap;
use std::io::Write;
use std::{collections::HashMap, fmt::Display};
use crossterm::{style::Color, ExecutableCommand};
use once_cell::sync::Lazy;
@ -124,7 +124,7 @@ impl Style {
pub fn print_styled(
&self,
out: &mut (impl ExecutableCommand + Write),
content: &str,
content: impl Display,
) -> crossterm::Result<()> {
use crossterm::style::{ResetColor, SetBackgroundColor, SetForegroundColor};
@ -142,6 +142,17 @@ impl Style {
Ok(())
}
/// Applies another [Style] on top of this one.
pub fn apply(&mut self, other: &Style) {
if other.fg.is_some() {
self.fg = other.fg;
}
if other.bg.is_some() {
self.bg = other.bg;
}
}
}
#[derive(Clone, Debug, Default)]
@ -186,7 +197,7 @@ impl Theme {
let mut longest_len = 0;
for (target, style) in self.styles.iter() {
if target.len() > longest_len && scope.starts_with(target) {
if target.len() >= longest_len && scope.starts_with(target) {
longest = *style;
longest_len = target.len();
}
@ -243,15 +254,13 @@ impl TryFrom<Value> for Palette {
value
))?;
let default = Self::default();
let mut palette = HashMap::with_capacity(values.len());
let mut default = Self::default();
for (name, value) in values {
let color = default.parse_color(value)?;
palette.insert(name.to_string(), color);
default.palette.insert(name.to_string(), color);
}
Ok(Self { palette })
Ok(default)
}
}
@ -292,6 +301,8 @@ impl Palette {
match name.as_str() {
"fg" => style.fg = Some(self.parse_color(&value)?),
"bg" => style.bg = Some(self.parse_color(&value)?),
"modifiers" => {} // ignore for now
"underline" => {} // ignore for now
_ => return Err(format!("Theme: invalid style attribute: {}", name)),
}
}