diff --git a/Cargo.lock b/Cargo.lock index 82525bd..7f12809 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1649,22 +1649,6 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "inkjet" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdd9fd670f1a42725a90e7a97f5e6a777fc56f9031c1ee0ebcea8f91a6bb9c2f" -dependencies = [ - "anyhow", - "cc", - "once_cell", - "serde", - "thiserror", - "toml", - "tree-sitter", - "tree-sitter-highlight", -] - [[package]] name = "inout" version = "0.1.3" @@ -2203,12 +2187,13 @@ dependencies = [ "anyhow", "careless", "funnylog", - "inkjet", "kdl", "log", "miette", "ming", + "pepegsitter", "serde", + "tree-sitter-highlight", "widestring", ] @@ -2560,6 +2545,17 @@ dependencies = [ "hmac", ] +[[package]] +name = "pepegsitter" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "215b00db1f5bfe7f8f1a3edb0e1ac9c4a42ffc4e50afd929c1e6a6f36d620cca" +dependencies = [ + "cc", + "tree-sitter", + "tree-sitter-highlight", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -2718,7 +2714,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ - "toml_edit 0.21.1", + "toml_edit", ] [[package]] @@ -3160,15 +3156,6 @@ dependencies = [ "syn 2.0.63", ] -[[package]] -name = "serde_spanned" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" -dependencies = [ - "serde", -] - [[package]] name = "sha1" version = "0.10.6" @@ -3676,26 +3663,11 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.22.12", -] - [[package]] name = "toml_datetime" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" -dependencies = [ - "serde", -] [[package]] name = "toml_edit" @@ -3705,20 +3677,7 @@ checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ "indexmap", "toml_datetime", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.22.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "winnow 0.6.8", + "winnow", ] [[package]] @@ -4351,15 +4310,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winnow" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" -dependencies = [ - "memchr", -] - [[package]] name = "wio" version = "0.2.2" diff --git a/Cargo.toml b/Cargo.toml index 376f0c2..ec76005 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,10 +10,11 @@ anyhow.workspace = true widestring = "1.1.0" log.workspace = true serde = { workspace = true, features = ["derive"] } -inkjet = { version = "0.10.5", features = ["language-rust", "language-toml", "language-zig"], default-features = false } careless = { git = "https://codeberg.org/minky/careless", version = "0.1.0" } kdl = { version = "4.6.0", default-features = false } miette = { workspace = true, features = ["fancy"] } +pepegsitter = { version = "0.2.2", features = ["tree-sitter-highlight"] } +tree-sitter-highlight = "0.20.1" [workspace] members = [ diff --git a/crates/ming/src/app.rs b/crates/ming/src/app.rs index bb83a3f..f6e24c1 100644 --- a/crates/ming/src/app.rs +++ b/crates/ming/src/app.rs @@ -11,17 +11,21 @@ use std::{ use anyhow::{anyhow, Result}; use derive_more::{Deref, DerefMut}; -use futures::{channel::oneshot, future::{LocalBoxFuture, FutureExt}, Future}; +use futures::{ + channel::oneshot, + future::{FutureExt, LocalBoxFuture}, + Future, +}; use slotmap::SlotMap; use time::UtcOffset; pub use async_context::*; use collections::{FxHashMap, FxHashSet, VecDeque}; pub use entity_map::*; -use util::http::{self, HttpClient}; pub use model_context::*; #[cfg(any(test, feature = "test-support"))] pub use test_context::*; +use util::http::{self, HttpClient}; use util::ResultExt; use crate::{ diff --git a/crates/ming/src/elements/img.rs b/crates/ming/src/elements/img.rs index 9eb6a9d..71fae59 100644 --- a/crates/ming/src/elements/img.rs +++ b/crates/ming/src/elements/img.rs @@ -13,8 +13,8 @@ use image::{ImageBuffer, ImageError}; #[cfg(target_os = "macos")] use media::core_video::CVImageBuffer; -use util::http; use thiserror::Error; +use util::http; use util::ResultExt; /// A source of image content. diff --git a/crates/ming/src/elements/list.rs b/crates/ming/src/elements/list.rs index 82c7199..207c47a 100644 --- a/crates/ming/src/elements/list.rs +++ b/crates/ming/src/elements/list.rs @@ -14,8 +14,8 @@ use crate::{ use collections::VecDeque; use refineable::Refineable as _; use std::{cell::RefCell, ops::Range, rc::Rc}; -use util::sum_tree::{self, Bias, SumTree}; use taffy::style::Overflow; +use util::sum_tree::{self, Bias, SumTree}; /// Construct a new list element pub fn list(state: ListState) -> List { diff --git a/crates/ming/src/executor.rs b/crates/ming/src/executor.rs index 94f5c56..31b0a78 100644 --- a/crates/ming/src/executor.rs +++ b/crates/ming/src/executor.rs @@ -1,7 +1,8 @@ use crate::{AppContext, PlatformDispatcher}; +use futures::FutureExt; use std::{ - future::Future, fmt::Debug, + future::Future, marker::PhantomData, mem, num::NonZeroUsize, @@ -14,7 +15,6 @@ use std::{ task::{Context, Poll}, time::Duration, }; -use futures::FutureExt; use tokio::sync::mpsc; use util::TryFutureExt; use waker_fn::waker_fn; diff --git a/crates/ming/src/geometry.rs b/crates/ming/src/geometry.rs index 31c509a..f96dc83 100644 --- a/crates/ming/src/geometry.rs +++ b/crates/ming/src/geometry.rs @@ -111,11 +111,11 @@ impl Point { } /// Splats a `value` across both `x` and `y` - pub const fn all(value: T) -> Self where T: Copy { - Self { - x: value, - y: value - } + pub const fn all(value: T) -> Self + where + T: Copy, + { + Self { x: value, y: value } } /// Transforms the point to a `Point` by applying the given function to both coordinates. diff --git a/crates/ming/src/lib.rs b/crates/ming/src/lib.rs index 6f33623..c31525d 100644 --- a/crates/ming/src/lib.rs +++ b/crates/ming/src/lib.rs @@ -125,18 +125,17 @@ pub use element::*; pub use elements::*; pub use executor::*; pub use geometry::*; -pub use ming_macros::{register_action, test, IntoElement, Render}; pub use input::*; pub use interactive::*; use key_dispatch::*; pub use keymap::*; +pub use ming_macros::{register_action, test, IntoElement, Render}; pub use platform::*; pub use refineable::*; pub use scene::*; use seal::Sealed; pub use shared_string::*; pub use shared_uri::*; -pub use tokio::time::Sleep as Timer; pub use style::*; pub use styled::*; pub use subscription::*; @@ -145,6 +144,7 @@ pub use taffy::{AvailableSpace, LayoutId}; #[cfg(any(test, feature = "test-support"))] pub use test::*; pub use text_system::*; +pub use tokio::time::Sleep as Timer; pub use util::arc_cow::ArcCow; pub use view::*; pub use window::*; diff --git a/crates/ming/src/platform.rs b/crates/ming/src/platform.rs index 8451358..a8b4a8d 100644 --- a/crates/ming/src/platform.rs +++ b/crates/ming/src/platform.rs @@ -58,10 +58,10 @@ pub(crate) use cosmic_text::*; pub(crate) use linux::*; #[cfg(target_os = "macos")] pub(crate) use mac::*; -pub use util::semver::SemanticVersion; #[cfg(any(test, feature = "test-support"))] pub(crate) use test::*; use time::UtcOffset; +pub use util::semver::SemanticVersion; #[cfg(target_os = "windows")] pub(crate) use windows::*; diff --git a/crates/ming/src/platform/windows/platform.rs b/crates/ming/src/platform/windows/platform.rs index 188b91c..bae380b 100644 --- a/crates/ming/src/platform/windows/platform.rs +++ b/crates/ming/src/platform/windows/platform.rs @@ -17,9 +17,9 @@ use copypasta::{ClipboardContext, ClipboardProvider}; use futures::channel::oneshot::{self, Receiver}; use itertools::Itertools; use parking_lot::RwLock; -use util::semver::SemanticVersion; use smallvec::SmallVec; use time::UtcOffset; +use util::semver::SemanticVersion; use windows::{ core::*, Wdk::System::SystemServices::*, diff --git a/crates/ming/src/text_system.rs b/crates/ming/src/text_system.rs index ba7424a..7d5c902 100644 --- a/crates/ming/src/text_system.rs +++ b/crates/ming/src/text_system.rs @@ -652,8 +652,9 @@ impl<'de> serde::Deserialize<'de> for FontWeight { where E: serde::de::Error, { - v.parse() - .map_err(|()| E::custom("expected a font weight value (e.g. 'normal' or 'bold')")) + v.parse().map_err(|()| { + E::custom("expected a font weight value (e.g. 'normal' or 'bold')") + }) } } diff --git a/crates/ming/src/util.rs b/crates/ming/src/util.rs index 6a074cd..efdec46 100644 --- a/crates/ming/src/util.rs +++ b/crates/ming/src/util.rs @@ -15,9 +15,12 @@ pub trait FluentBuilder { { f(self) } - + /// Imperatively modify self with the given closure. - fn apply(mut self, f: impl FnOnce(&mut Self)) -> Self where Self: Sized { + fn apply(mut self, f: impl FnOnce(&mut Self)) -> Self + where + Self: Sized, + { f(&mut self); self diff --git a/crates/ming_macros/src/test.rs b/crates/ming_macros/src/test.rs index c3a211f..2fe4cc8 100644 --- a/crates/ming_macros/src/test.rs +++ b/crates/ming_macros/src/test.rs @@ -3,17 +3,18 @@ use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, ToTokens}; use std::mem; use syn::{ - parse2, parse_quote, punctuated::Punctuated, spanned::Spanned as _, Expr, - FnArg, ItemFn, Lit, Meta, PatLit as ExprLit, Token, Type, + parse2, parse_quote, punctuated::Punctuated, spanned::Spanned as _, Expr, FnArg, ItemFn, Lit, + Meta, PatLit as ExprLit, Token, Type, }; pub fn test(args: TS, function: TS) -> TS { - test_impl(args.into(), function.into()).map_or_else(|e| e.into_compile_error().into(), |ts| ts.into()) + test_impl(args.into(), function.into()) + .map_or_else(|e| e.into_compile_error().into(), |ts| ts.into()) } fn test_impl(args: TokenStream, function: TokenStream) -> syn::Result { let Meta::List(args) = parse2(args)? else { - return Err(syn::Error::new(Span::call_site(), "invalid attr")) + return Err(syn::Error::new(Span::call_site(), "invalid attr")); }; let mut max_retries = 0; let mut num_iterations = 1; diff --git a/crates/util/src/lib.rs b/crates/util/src/lib.rs index 76ffb5b..58767df 100644 --- a/crates/util/src/lib.rs +++ b/crates/util/src/lib.rs @@ -1,9 +1,9 @@ pub mod arc_cow; -pub mod http; pub mod fs; +pub mod http; pub mod paths; -pub mod serde; pub mod semver; +pub mod serde; pub mod sum_tree; #[cfg(any(test, feature = "test-support"))] pub mod test; diff --git a/src/buffer.rs b/src/buffer.rs index c85e48f..956ceeb 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -1,9 +1,10 @@ use careless::prelude::*; -use inkjet::tree_sitter_highlight::{Error, HighlightEvent, Highlighter}; +use funnylog::warn; use std::{ops::Range, path::PathBuf}; +use tree_sitter_highlight::{Error, HighlightConfiguration, HighlightEvent, Highlighter}; use widestring::Utf16String; -use crate::editor::Theme; +use crate::editor::{Highlight, Theme}; use crate::vim::Mode; use super::*; @@ -15,7 +16,7 @@ pub struct Buffer { pub selected: Option>, pub name: SharedString, pub modified: bool, - pub mode: Mode + pub mode: Mode, } impl Buffer { @@ -28,7 +29,7 @@ impl Buffer { marked: None, selected: None, modified: false, - mode: Mode::default() + mode: Mode::default(), }) } @@ -40,33 +41,35 @@ impl Buffer { selected: None, name: SharedString::from_static(""), modified: false, - mode: Mode::default() + mode: Mode::default(), } } pub fn styled(&self, default_style: &TextStyle, theme: &Theme) -> Vec { let text = self.text.to_string(); - if let Some(language) = self + if let Some(mut language) = self .path .as_ref() .and_then(|path| path.extension()) .and_then(|extension| { - inkjet::Language::from_token(extension.to_str()?).map(|lang| lang.config()) + Language::from_token(extension.to_str()?).map(|lang| lang.config()) }) { + let (indices, highlights): (Vec<&str>, Vec<&Highlight>) = + theme.highlights.iter().map(|(name, hl)| (name.as_str(), hl)).unzip(); + language.configure(&indices); let mut hl = Highlighter::new(); let mut lines = vec![]; for line in text.lines() { // damnation match hl - .highlight(language, line.as_bytes(), None, |lang| { - inkjet::Language::from_token(lang).map(|lang| lang.config()) - }) + .highlight(&language, line.as_bytes(), None, |lang| None) .and_then(|hls| { Ok(StyledText::new(Arc::from(line)).with_runs( - MingHighlighter::new(hls, default_style, theme).try_to_collect()?, + MingHighlighter::new(hls, default_style, &highlights) + .try_to_collect()?, )) }) { Ok(styled) => lines.push(styled), @@ -92,17 +95,17 @@ impl Buffer { pub struct MingHighlighter<'a, I> { iter: I, default_style: &'a TextStyle, - theme: &'a Theme, + highlights: &'a [&'a Highlight], current_hl_idx: Option, current_span: Option>, } impl<'a, I> MingHighlighter<'a, I> { - pub fn new(iter: I, default_style: &'a TextStyle, theme: &'a Theme) -> Self { + pub fn new(iter: I, default_style: &'a TextStyle, highlights: &'a [&'a Highlight]) -> Self { Self { iter, default_style, - theme, + highlights, current_hl_idx: None, current_span: None, } @@ -122,9 +125,8 @@ impl>> Iterator for MingHighlig self.current_span = Some(start..end); self.default_style.to_run(end - start).apply(|run| { - if let Some(hl) = self - .current_hl_idx - .and_then(|idx| self.theme.highlight_indices.get(idx)) + if let Some(hl) = + self.current_hl_idx.and_then(|idx| self.highlights.get(idx)) { hl.apply_to_run(run) } @@ -143,3 +145,24 @@ impl>> Iterator for MingHighlig ) } } + +pub enum Language { + Rust, + Toml, +} + +impl Language { + pub fn from_token(token: &str) -> Option { + Some(match token { + "rs" | "rust" => Self::Rust, + "toml" => Self::Toml, + _ => return None, + }) + } + pub fn config(&self) -> HighlightConfiguration { + match self { + Self::Rust => pepegsitter::rust::highlight(), + Self::Toml => pepegsitter::toml::highlight(), + } + } +} diff --git a/src/editor.rs b/src/editor.rs index 7ad0efb..5f144f1 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -78,7 +78,7 @@ pub struct Theme { pub name: String, pub background_color: Rgba, pub text_color: Rgba, - pub highlight_indices: Vec, + pub highlights: Vec<(String, Highlight)>, } fn rgba_from_kdl(value: &KdlValue) -> miette::Result { @@ -103,18 +103,12 @@ impl Theme { } pub fn from_kdl_document(document: KdlDocument) -> miette::Result { - let highlight_indices = match document.get("highlights").and_then(|node| node.children()) { - Some(doc) => { - let mut indices = vec![]; - - for name in inkjet::constants::HIGHLIGHT_NAMES { - let Some(node) = doc.get(*name) else { continue }; - - indices.push(Highlight::from_kdl_node(node)?); - } - - indices - } + let highlights = match document.get("highlights").and_then(|node| node.children()) { + Some(doc) => doc + .nodes() + .iter() + .map(|node| Highlight::from_kdl_node(node).map(|hl| (node.name().to_string(), hl))) + .try_to_collect()?, None => Default::default(), }; @@ -133,7 +127,7 @@ impl Theme { .get_arg("text_color") .context("no background_color attr") .and_then(rgba_from_kdl)?, - highlight_indices, + highlights, }) } } @@ -198,7 +192,6 @@ impl Editor { let buf = self.buf.read(cx); div() - .debug() .flex() .flex_row() .justify_between() @@ -233,16 +226,15 @@ impl Editor { impl Render for Editor { fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { cx.focus(&self.focus_handle); - let styled = { - // -1 read... - let style = &self.settings.read(cx).style; - div() - .font_family(style.font_family.clone()) - .text_color(style.theme.text_color) - }; + div() + .map(|div| { + let style = &self.settings.read(cx).style; - styled.key_context("Editor") + div.font_family(style.font_family.clone()) + .text_color(style.theme.text_color) + }) + .key_context("Editor") .on_action(cx.listener(|me, event: &action::CursorDown, cx| { me.cursor.y = me.cursor.y.saturating_add(1).min( me.buf diff --git a/src/editor/element.rs b/src/editor/element.rs index 8fbdcd9..dd5acfe 100644 --- a/src/editor/element.rs +++ b/src/editor/element.rs @@ -1,4 +1,4 @@ -use std::fmt::write; +use std::{any::TypeId, fmt::write}; use super::*; @@ -122,6 +122,7 @@ impl Element for EditorElement { ) { let focus_handle = self.editor.focus_handle(cx); let key_context = self.editor.read(cx).key_context(cx); + cx.set_focus_handle(&focus_handle); cx.set_key_context(key_context); cx.handle_input( @@ -129,68 +130,124 @@ impl Element for EditorElement { ElementInputHandler::new(bounds, self.editor.clone()), ); - let view = self.editor.clone(); - let font_size = self.style.font_size.to_pixels(px(4.)); - let hb = prepaint.hb.clone(); + let styled_bounds: Arc<[Bounds]> = Arc::from( + request_layout + .styled + .iter() + .map(|(_, _, b)| *b) + .collect::>>(), + ); - let styled_bounds = request_layout - .styled - .iter() - .map(|(_, _, b)| *b) - .collect::>(); + cx.on_action(TypeId::of::(), { + let view = self.editor.clone(); + let styled_bounds = styled_bounds.clone(); - cx.on_mouse_event(move |scroll: &ScrollWheelEvent, dispatch, cx| { - if !dispatch.bubble() || !hb.is_hovered(cx) { - return; - } - - view.update(cx, |editor, cx| { - let prev_editor_scroll = editor.scroll; - let change = match scroll.delta { - ScrollDelta::Lines(pnt) => { - let mut lines = pnt.y.abs(); - let mut do_scroll = px(0.); - - for bounds in styled_bounds.iter() { - if lines <= 1. { - do_scroll += px(bounds.size.height.0 * lines); - - break; - } - - do_scroll += bounds.size.height; - lines -= 1.; - } - - point(px(0.), do_scroll * pnt.y.signum()) + move |_event, _dispatch, cx| { + view.update(cx, |editor, cx| { + if editor.cursor.y == 0 { + return; } - ScrollDelta::Pixels(pix) => pix, - }; - editor.scroll = (editor.scroll + change).clamp( - &point(bounds.size.width, bounds.size.height).negate(), - &Point::default(), - ); + editor.cursor.y = editor.cursor.y.saturating_sub(1); - let prev_ruler = editor.ruler; - let next_ruler = (editor.scroll.y.0 / bounds.size.height.0 * 100.).abs().floor() as u8; + let line_bounds = styled_bounds + .iter() + .take(editor.cursor.y) + .fold(px(0.), |px, bounds| px + bounds.size.height); - if prev_ruler != next_ruler { - editor.ruler = next_ruler; - cx.notify(); - } - - log::trace!( - "[{prev_editor_scroll:#?} -> {:#?}] [{prev_ruler}% -> {next_ruler}%] {scroll:#?}", - editor.scroll - ); + if line_bounds > bounds.size.height { + editor.scroll.y = -line_bounds; + } + }); cx.stop_propagation(); + } + }); - if !change.is_zero() { - cx.refresh(); + cx.on_action(TypeId::of::(), { + let view = self.editor.clone(); + let styled_bounds = styled_bounds.clone(); + + move |_event, _dispatch, cx| { + view.update(cx, |editor, cx| { + if editor.cursor.y == editor.buf.read(cx).text.chars().filter(|char| *char == '\n').count() { + return; + } + + editor.cursor.y += 1; + + let line_bounds = styled_bounds + .iter() + .take(editor.cursor.y) + .fold(px(0.), |px, bounds| px + bounds.size.height); + + if line_bounds > bounds.size.height { + editor.scroll.y = -line_bounds; + } + }); + + cx.stop_propagation(); + } + }); + + cx.on_mouse_event({ + let view = self.editor.clone(); + let hb = prepaint.hb.clone(); + let styled_bounds = styled_bounds.clone(); + + move |scroll: &ScrollWheelEvent, dispatch, cx| { + if !dispatch.bubble() || !hb.is_hovered(cx) { + return; } - }); + + view.update(cx, |editor, cx| { + let prev_editor_scroll = editor.scroll; + let change = match scroll.delta { + ScrollDelta::Lines(pnt) => { + let mut lines = pnt.y.abs(); + let mut do_scroll = px(0.); + + for bounds in styled_bounds.iter() { + if lines <= 1. { + do_scroll += px(bounds.size.height.0 * lines); + + break; + } + + do_scroll += bounds.size.height; + lines -= 1.; + } + + point(px(0.), do_scroll * pnt.y.signum()) + } + ScrollDelta::Pixels(pix) => pix, + }; + + editor.scroll = (editor.scroll + change).clamp( + &point(bounds.size.width, bounds.size.height).negate(), + &Point::default(), + ); + + let prev_ruler = editor.ruler; + let next_ruler = (editor.scroll.y.0 / bounds.size.height.0 * 100.).abs().floor() as u8; + + if prev_ruler != next_ruler { + editor.ruler = next_ruler; + cx.notify(); + } + + log::trace!( + "[{prev_editor_scroll:#?} -> {:#?}] [{prev_ruler}% -> {next_ruler}%] {scroll:#?}", + editor.scroll + ); + + cx.stop_propagation(); + + if !change.is_zero() { + cx.refresh(); + } + }); + } }); cx.with_text_style( diff --git a/src/vim.rs b/src/vim.rs index 52066d9..f91d813 100644 --- a/src/vim.rs +++ b/src/vim.rs @@ -3,5 +3,5 @@ pub enum Mode { #[default] Normal, Insert, - Visual + Visual, }