From 638a9a112b90d006914ec9184fd9e63b76f5ef7c Mon Sep 17 00:00:00 2001 From: Borodinov Ilya Date: Wed, 15 May 2024 22:14:17 +0300 Subject: [PATCH] treesitter revamp :) --- Cargo.lock | 226 +++++++++++++--------- Cargo.toml | 11 +- crates/ming/src/action.rs | 32 +-- crates/ming/src/color.rs | 17 +- crates/ming/src/geometry.rs | 8 + crates/ming/src/text_system.rs | 70 ++++++- crates/ming_macros/src/register_action.rs | 16 +- flake.nix | 2 +- src/buffer.rs | 27 +-- src/editor.rs | 97 ++++++++-- src/editor/element.rs | 91 +++++---- src/main.rs | 13 +- themes/catppuccin/mocha-teal.kdl | 24 +++ themes/catppuccin/nite.tera | 40 ++++ 14 files changed, 467 insertions(+), 207 deletions(-) create mode 100644 themes/catppuccin/mocha-teal.kdl create mode 100644 themes/catppuccin/nite.tera diff --git a/Cargo.lock b/Cargo.lock index 32b7451..18831d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -306,27 +306,12 @@ dependencies = [ "rustc-demangle", ] -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - [[package]] name = "bit-set" version = "0.5.3" @@ -432,6 +417,15 @@ dependencies = [ "piper", ] +[[package]] +name = "brownstone" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5839ee4f953e811bfdcf223f509cb2c6a3e1447959b0bff459405575bc17f22" +dependencies = [ + "arrayvec", +] + [[package]] name = "bstr" version = "1.9.1" @@ -1641,6 +1635,12 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" +[[package]] +name = "indent_write" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cfe9645a18782869361d9c8732246be7b410ad4e919d3609ebabdac00ba12c3" + [[package]] name = "indexmap" version = "2.2.6" @@ -1740,6 +1740,12 @@ dependencies = [ "libc", ] +[[package]] +name = "joinery" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72167d68f5fce3b8655487b8038691a3c9984ee769590f93f2a631f4ad64e4f5" + [[package]] name = "jpeg-decoder" version = "0.1.22" @@ -1758,6 +1764,34 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kaydle" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2de193d14f7d6f3288f5ee1c38b0d7869e95c322fc5e54d1c385765172db53" +dependencies = [ + "kaydle-primitives", + "lazy_format", + "nom", + "nom-supreme", + "serde", + "serde-mobile", + "thiserror", +] + +[[package]] +name = "kaydle-primitives" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f31f657f34304c4853f1e3a3f3c9a556d0462d7004460791728713d61d88272" +dependencies = [ + "arrayvec", + "memchr", + "nom", + "nom-supreme", + "serde", +] + [[package]] name = "khronos-egl" version = "5.0.0" @@ -1784,6 +1818,12 @@ version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10257499f089cd156ad82d0a9cd57d9501fa2c989068992a97eb3c27836f206b" +[[package]] +name = "lazy_format" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05662be9cd63006934464f935195ae936460edb75de7b9a07e0509795afbdc3" + [[package]] name = "lazy_static" version = "1.4.0" @@ -1849,18 +1889,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "line-wrap" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd1bc4d24ad230d21fb898d1116b1801d7adfc449d42026475862ab48b11e70e" - -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - [[package]] name = "linkme" version = "0.3.26" @@ -2052,6 +2080,12 @@ dependencies = [ "syn 2.0.63", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.3.7" @@ -2133,9 +2167,12 @@ version = "0.1.0" dependencies = [ "anyhow", "funnylog", + "kaydle", "log", "ming", - "syntect", + "serde", + "tree-sitter-highlight", + "tree-sitter-rust", "widestring", ] @@ -2152,6 +2189,29 @@ dependencies = [ "memoffset", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nom-supreme" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bd3ae6c901f1959588759ff51c95d24b491ecb9ff91aa9c2ef4acc5b1dcab27" +dependencies = [ + "brownstone", + "indent_write", + "joinery", + "memchr", + "nom", +] + [[package]] name = "num" version = "0.4.3" @@ -2324,28 +2384,6 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" -[[package]] -name = "onig" -version = "6.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c4b31c8722ad9171c6d77d3557db078cab2bd50afcc9d09c8b315c59df8ca4f" -dependencies = [ - "bitflags 1.3.2", - "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 = "oo7" version = "0.3.2" @@ -2554,20 +2592,6 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" -[[package]] -name = "plist" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9d34169e64b3c7a80c8621a48adaf44e0cf62c78a9b25dd9dd35f1881a17cf9" -dependencies = [ - "base64 0.21.7", - "indexmap", - "line-wrap", - "quick-xml", - "serde", - "time", -] - [[package]] name = "png" version = "0.16.8" @@ -3062,6 +3086,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-mobile" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c32e12ac49f4f1fde3b44b9eccfcc6cb70bbb2622dac40698b34c3d607cf21a4" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" version = "1.0.201" @@ -3352,28 +3385,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "syntect" -version = "5.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "874dcfa363995604333cf947ae9f751ca3af4522c60886774c4963943b4746b1" -dependencies = [ - "bincode", - "bitflags 1.3.2", - "flate2", - "fnv", - "once_cell", - "onig", - "plist", - "regex-syntax", - "serde", - "serde_derive", - "serde_json", - "thiserror", - "walkdir", - "yaml-rust", -] - [[package]] name = "sys-locale" version = "0.3.1" @@ -3640,6 +3651,38 @@ dependencies = [ "tracing", ] +[[package]] +name = "tree-sitter" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df7cc499ceadd4dcdf7ec6d4cbc34ece92c3fa07821e287aedecd4416c516dca" +dependencies = [ + "cc", + "regex", +] + +[[package]] +name = "tree-sitter-highlight" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaca0fe34fa96eec6aaa8e63308dbe1bafe65a6317487c287f93938959b21907" +dependencies = [ + "lazy_static", + "regex", + "thiserror", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-rust" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "277690f420bf90741dea984f3da038ace46c4fe6047cba57a66822226cde1c93" +dependencies = [ + "cc", + "tree-sitter", +] + [[package]] name = "ttf-parser" version = "0.20.0" @@ -3759,7 +3802,7 @@ version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c704361d822337cfc00387672c7b59eaa72a1f0744f62b2a68aa228a0c6927d" dependencies = [ - "base64 0.22.1", + "base64", "data-url", "flate2", "imagesize", @@ -4285,15 +4328,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] - [[package]] name = "yazi" version = "0.1.6" diff --git a/Cargo.toml b/Cargo.toml index ae058ff..65f962a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,8 +8,11 @@ funnylog.workspace = true ming.workspace = true anyhow.workspace = true widestring = "1.1.0" -syntect = "5.2.0" log.workspace = true +tree-sitter-highlight = "0.22.6" +tree-sitter-rust = "0.21.2" +serde = { workspace = true, features = ["derive"] } +kaydle = "0.2.0" [workspace] members = [ @@ -57,13 +60,7 @@ schemars = "0.8" serde = { version = "1.0", features = ["derive", "rc"] } serde_derive = { version = "1.0", features = ["deserialize_in_place"] } serde_json = { version = "1.0", features = ["preserve_order", "raw_value"] } -serde_json_lenient = { version = "0.1", features = [ - "preserve_order", - "raw_value", -] } -serde_repr = "0.1" smallvec = { version = "1.6", features = ["union"] } -sha2 = "0.10" thiserror = "1" time = { version = "0.3", features = [ "macros", diff --git a/crates/ming/src/action.rs b/crates/ming/src/action.rs index cf0ad7e..0f5eb72 100644 --- a/crates/ming/src/action.rs +++ b/crates/ming/src/action.rs @@ -29,12 +29,12 @@ use std::any::{Any, TypeId}; /// macro, which only generates the code needed to register your action before `main`. /// /// ``` -/// #[derive(gpui::private::serde::Deserialize, std::cmp::PartialEq, std::clone::Clone)] +/// #[derive(ming::private::serde::Deserialize, std::cmp::PartialEq, std::clone::Clone)] /// pub struct Paste { /// pub content: SharedString, /// } /// -/// impl gpui::Action for Paste { +/// impl ming::Action for Paste { /// ///... /// } /// register_action!(Paste); @@ -184,18 +184,18 @@ macro_rules! actions { $( #[doc = "The `"] #[doc = stringify!($name)] - #[doc = "` action, see [`gpui::actions!`]"] - #[derive(::std::cmp::PartialEq, ::std::clone::Clone, ::std::default::Default, ::std::fmt::Debug, gpui::private::serde_derive::Deserialize)] - #[serde(crate = "gpui::private::serde")] + #[doc = "` action, see [`ming::actions!`]"] + #[derive(::std::cmp::PartialEq, ::std::clone::Clone, ::std::default::Default, ::std::fmt::Debug, ming::private::serde_derive::Deserialize)] + #[serde(crate = "ming::private::serde")] pub struct $name; - gpui::__impl_action!($namespace, $name, - fn build(_: gpui::private::serde_json::Value) -> gpui::Result<::std::boxed::Box> { + ming::__impl_action!($namespace, $name, + fn build(_: ming::private::serde_json::Value) -> ming::Result<::std::boxed::Box> { Ok(Box::new(Self)) } ); - gpui::register_action!($name); + ming::register_action!($name); )* }; } @@ -205,13 +205,13 @@ macro_rules! actions { macro_rules! impl_actions { ($namespace:path, [ $($name:ident),* $(,)? ]) => { $( - gpui::__impl_action!($namespace, $name, - fn build(value: gpui::private::serde_json::Value) -> gpui::Result<::std::boxed::Box> { - Ok(std::boxed::Box::new(gpui::private::serde_json::from_value::(value)?)) + ming::__impl_action!($namespace, $name, + fn build(value: ming::private::serde_json::Value) -> ming::Result<::std::boxed::Box> { + Ok(std::boxed::Box::new(ming::private::serde_json::from_value::(value)?)) } ); - gpui::register_action!($name); + ming::register_action!($name); )* }; } @@ -220,7 +220,7 @@ macro_rules! impl_actions { #[macro_export] macro_rules! __impl_action { ($namespace:path, $name:ident, $build:item) => { - impl gpui::Action for $name { + impl ming::Action for $name { fn name(&self) -> &'static str { concat!( @@ -243,14 +243,14 @@ macro_rules! __impl_action { $build - fn partial_eq(&self, action: &dyn gpui::Action) -> bool { + fn partial_eq(&self, action: &dyn ming::Action) -> bool { action .as_any() .downcast_ref::() .map_or(false, |a| self == a) } - fn boxed_clone(&self) -> std::boxed::Box { + fn boxed_clone(&self) -> std::boxed::Box { ::std::boxed::Box::new(self.clone()) } @@ -262,7 +262,7 @@ macro_rules! __impl_action { } mod no_action { - use crate as gpui; + use crate as ming; actions!(zed, [NoAction]); } diff --git a/crates/ming/src/color.rs b/crates/ming/src/color.rs index 7246af4..5767b5c 100644 --- a/crates/ming/src/color.rs +++ b/crates/ming/src/color.rs @@ -78,6 +78,21 @@ impl<'de> Visitor<'de> for RgbaVisitor { fn visit_str(self, value: &str) -> Result { Rgba::try_from(value).map_err(E::custom) } + + fn visit_u32(self, hex: u32) -> Result + where + E: de::Error, + { + let r = ((hex >> 24) & 0xFF) as f32 / 255.0; + let g = ((hex >> 16) & 0xFF) as f32 / 255.0; + let b = ((hex >> 8) & 0xFF) as f32 / 255.0; + let mut a = (hex & 0xFF) as f32 / 255.0; + if a == 0. { + a = 1.; + } + + Ok(Rgba { r, g, b, a }) + } } impl<'de> Deserialize<'de> for Rgba { @@ -174,7 +189,7 @@ impl TryFrom<&'_ str> for Rgba { } /// An HSLA color -#[derive(Default, Copy, Clone, Debug)] +#[derive(Default, Copy, Clone, Debug, serde::Serialize)] #[repr(C)] pub struct Hsla { /// Hue, in a range from 0 to 1 diff --git a/crates/ming/src/geometry.rs b/crates/ming/src/geometry.rs index 75c0fb1..31c509a 100644 --- a/crates/ming/src/geometry.rs +++ b/crates/ming/src/geometry.rs @@ -110,6 +110,14 @@ impl Point { Self { x, y } } + /// Splats a `value` across both `x` and `y` + 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. /// /// This method allows for converting a `Point` to a `Point` by specifying a closure diff --git a/crates/ming/src/text_system.rs b/crates/ming/src/text_system.rs index a030316..decab5b 100644 --- a/crates/ming/src/text_system.rs +++ b/crates/ming/src/text_system.rs @@ -25,6 +25,7 @@ use std::{ fmt::{Debug, Display, Formatter}, hash::{Hash, Hasher}, ops::{Deref, DerefMut, Range}, + str::FromStr, sync::Arc, }; @@ -593,8 +594,75 @@ impl FontWeight { pub const BLACK: FontWeight = FontWeight(900.0); } +impl FromStr for FontWeight { + type Err = (); + + fn from_str(s: &str) -> std::prelude::v1::Result { + Ok(match s { + "thin" => Self::THIN, + "extra_light" => Self::EXTRA_LIGHT, + "light" => Self::LIGHT, + "normal" => Self::NORMAL, + "medium" => Self::MEDIUM, + "semibold" => Self::SEMIBOLD, + "bold" => Self::BOLD, + "extra_bold" => Self::EXTRA_BOLD, + "black" => Self::BLACK, + _ => return Err(()), + }) + } +} + +impl<'de> serde::Deserialize<'de> for FontWeight { + fn deserialize(deserializer: D) -> std::prelude::v1::Result + where + D: serde::Deserializer<'de>, + { + struct Visitor; + + impl<'de> serde::de::Visitor<'de> for Visitor { + type Value = FontWeight; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "a font weight value (e.g. 700 or 'bold')") + } + + fn visit_f32(self, v: f32) -> std::prelude::v1::Result + where + E: serde::de::Error, + { + Ok(FontWeight(v)) + } + + fn visit_f64(self, v: f64) -> std::prelude::v1::Result + where + E: serde::de::Error, + { + Ok(FontWeight(v as f32)) + } + + fn visit_i64(self, v: i64) -> std::prelude::v1::Result + where + E: serde::de::Error, + { + Ok(FontWeight(v as f32)) + } + + fn visit_str(self, v: &str) -> std::prelude::v1::Result + where + E: serde::de::Error, + { + v.parse() + .map_err(|()| E::custom("expected a font weight value (e.g. 'normal' or 'bold')")) + } + } + } +} + /// Allows italic or oblique faces to be selected. -#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash, Default)] +#[derive( + Clone, Copy, Eq, PartialEq, Debug, Hash, Default, serde::Serialize, serde::Deserialize, +)] pub enum FontStyle { /// A face that is neither italic not obliqued. #[default] diff --git a/crates/ming_macros/src/register_action.rs b/crates/ming_macros/src/register_action.rs index 7ec1d6d..e6a097e 100644 --- a/crates/ming_macros/src/register_action.rs +++ b/crates/ming_macros/src/register_action.rs @@ -17,7 +17,7 @@ pub(crate) fn register_action(type_name: &Ident) -> proc_macro2::TokenStream { format_ident!("__GPUI_ACTIONS_{}", type_name.to_string().to_uppercase()); let action_builder_fn_name = format_ident!( - "__gpui_actions_builder_{}", + "__ming_actions_builder_{}", type_name.to_string().to_lowercase() ); @@ -29,17 +29,17 @@ pub(crate) fn register_action(type_name: &Ident) -> proc_macro2::TokenStream { fn __autogenerated() { /// This is an auto generated function, do not use. #[doc(hidden)] - fn #action_builder_fn_name() -> gpui::ActionData { - gpui::ActionData { - name: <#type_name as gpui::Action>::debug_name(), + fn #action_builder_fn_name() -> ming::ActionData { + ming::ActionData { + name: <#type_name as ming::Action>::debug_name(), type_id: ::std::any::TypeId::of::<#type_name>(), - build: <#type_name as gpui::Action>::build, + build: <#type_name as ming::Action>::build, } } #[doc(hidden)] - #[gpui::private::linkme::distributed_slice(gpui::__GPUI_ACTIONS)] - #[linkme(crate = gpui::private::linkme)] - static #static_slice_name: gpui::MacroActionBuilder = #action_builder_fn_name; + #[ming::private::linkme::distributed_slice(ming::__GPUI_ACTIONS)] + #[linkme(crate = ming::private::linkme)] + static #static_slice_name: ming::MacroActionBuilder = #action_builder_fn_name; } } diff --git a/flake.nix b/flake.nix index 64814c0..02b0979 100644 --- a/flake.nix +++ b/flake.nix @@ -68,7 +68,7 @@ scripts = { fmt.exec = "${config.treefmt.build.wrapper}/bin/treefmt ."; - nite.exec = "RUST_LOG=info,nite=trace,ming=trace cargo run -- $@"; + nite.exec = "RUST_LOG=error,nite=trace,ming=trace cargo run -- $@"; }; }; }; diff --git a/src/buffer.rs b/src/buffer.rs index 8fa27f0..9acd986 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -1,10 +1,5 @@ use std::{ops::Range, path::PathBuf}; -use syntect::{ - highlighting::{ - Color, FontStyle as SFontStyle, HighlightState, Highlighter, RangedHighlightIterator, Theme, - }, - parsing::{ParseState, ScopeStack, SyntaxSet}, -}; +use tree_sitter_highlight::{HighlightConfiguration, Highlighter}; use widestring::Utf16String; use super::*; @@ -19,7 +14,6 @@ pub struct Buffer { impl Buffer { pub fn read(path: PathBuf) -> anyhow::Result { let text = std::fs::read_to_string(&path)?; - log::trace!("read file: {text}"); Ok(Self { text: text.into(), path: Some(path), @@ -40,9 +34,8 @@ impl Buffer { pub fn styled( &self, default_style: &TextStyle, - theme: &Theme, + theme: &crate::editor::Theme, syntax_set: &SyntaxSet, - scrolled: usize, ) -> Vec { let text = self.text.to_string(); @@ -51,7 +44,19 @@ impl Buffer { .as_ref() .and_then(|path| path.extension()) .and_then(|extension| { - syntax_set.find_syntax_by_extension(extension.to_str().expect("non-utf8 file ext")) + Some( + match extension.to_str().expect("non-utf8 file ext") { + "rs" => HighlightConfiguration::new( + tree_sitter_rust::language(), + "rust", + tree_sitter_rust::HIGHLIGHTS_QUERY, + tree_sitter_rust::INJECTIONS_QUERY, + "", + ), + _ => return None, + } + .unwrap(), + ) }) { let mut parse = ParseState::new(syntax); @@ -59,7 +64,6 @@ impl Buffer { let mut hl = HighlightState::new(&hler, ScopeStack::new()); text.lines() - .skip(scrolled) .flat_map(|line| { parse.parse_line(line, &syntax_set).map(|parsed| { StyledText::new(Arc::from(line)).with_highlights( @@ -94,7 +98,6 @@ impl Buffer { .collect() } else { text.lines() - .skip(scrolled) .map(|line| StyledText::new(Arc::from(line))) .collect() } diff --git a/src/editor.rs b/src/editor.rs index c69bbb9..2cce0f5 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -1,6 +1,6 @@ -use std::path::PathBuf; +use std::{collections::HashMap, path::PathBuf}; -use syntect::{highlighting::ThemeSet, parsing::SyntaxSet}; +use serde::{Deserialize, Serialize}; use super::*; @@ -8,15 +8,50 @@ mod element; mod input; pub struct EditorSettings { - theme_set: ThemeSet, - syntax_set: SyntaxSet, + style: EditorStyle, +} + +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +pub struct Highlight { + pub color: Option, + #[serde(default)] + pub style: Option, + #[serde(default)] + pub weight: Option, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Theme { + pub name: String, + pub background_color: Rgba, + pub text_color: Rgba, + pub highlights: HashMap, +} + +impl Theme { + pub fn load(text: &str) -> Result { + kaydle::serde::from_str(text) + } +} + +#[derive(Clone)] +pub struct EditorStyle { + pub font_family: SharedString, + pub font_size: AbsoluteLength, + pub theme: Arc, } impl EditorSettings { pub fn load_defaults() -> Self { Self { - theme_set: ThemeSet::load_defaults(), - syntax_set: SyntaxSet::load_defaults_nonewlines(), + style: EditorStyle { + font_size: AbsoluteLength::Pixels(px(14.0)), + font_family: SharedString::from_static("ComicShannsMono Nerd Font Mono"), + theme: Arc::new( + Theme::load(include_str!("../themes/catppuccin/mocha-teal.kdl")) + .expect("mocha theme failed to parse (how?)"), + ), + }, } } } @@ -25,12 +60,15 @@ pub struct Editor { focus_handle: FocusHandle, buf: Model, settings: Model, + cursor: Point, + logger: Logger, } impl Editor { pub fn make( cx: &mut ViewContext, settings: Model, + logger: Logger, ) -> View { cx.new_view(|cx| Self { buf: cx.new_model(|_cx| { @@ -42,27 +80,38 @@ impl Editor { }), focus_handle: cx.focus_handle(), settings, + logger, + cursor: Point::default(), }) } } impl Render for Editor { fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { - element::EditorElement::new( - cx.view(), - element::EditorStyle { - font_size: AbsoluteLength::Pixels(px(14.0)), - font_family: SharedString::from_static("ComicShannsMono Nerd Font Mono"), - hl_theme: self - .settings - .read(cx) - .theme_set - .themes - .get("base16-mocha.dark") - .expect("mocha.dark wasn't available even if it's told it's there") - .clone(), - }, - ) + cx.focus(&self.focus_handle); + + div() + .key_context("Editor") + .on_action(cx.listener(|me, event: &action::CursorDown, cx| { + me.cursor.y = me.cursor.y.saturating_add(1).min( + me.buf + .read(cx) + .text + .chars() + .filter(|char| *char == '\n') + .count(), + ); + funnylog::trace!(me.logger, ?me.cursor, "down"); + })) + .on_action(cx.listener(|me, event: &action::CursorUp, cx| { + me.cursor.y = me.cursor.y.saturating_sub(1); + funnylog::trace!(me.logger, ?me.cursor, "up"); + })) + .child(element::EditorElement::new( + cx.view(), + self.settings.read(cx).style.clone(), + self.cursor, + )) } } @@ -71,3 +120,9 @@ impl FocusableView for Editor { self.focus_handle.clone() } } + +pub mod action { + use ming::actions; + + actions!(nite, [CursorDown, CursorUp]); +} diff --git a/src/editor/element.rs b/src/editor/element.rs index d69d4a0..274a38f 100644 --- a/src/editor/element.rs +++ b/src/editor/element.rs @@ -1,29 +1,20 @@ -use std::{cell::Cell, rc::Rc}; - -use syntect::highlighting::Theme; - -use self::buffer::hsla_from_syntect; +use crate::buffer::hsla_from_syntect; use super::*; -pub struct EditorStyle { - pub font_family: SharedString, - pub font_size: AbsoluteLength, - pub hl_theme: Theme, -} pub struct EditorElement { editor: View, style: EditorStyle, - scrolled: Rc>, + cursor: Point, } impl EditorElement { - pub fn new(viewref: &View, style: EditorStyle) -> Self { + pub fn new(viewref: &View, style: EditorStyle, cursor: Point) -> Self { Self { editor: viewref.clone(), style, - scrolled: Rc::new(Cell::new(0)), + cursor, } } } @@ -61,9 +52,7 @@ impl Element for EditorElement { font_family: self.style.font_family.clone(), ..Default::default() }, - &self.style.hl_theme, - &editor.settings.read(cx).syntax_set, - self.scrolled.get(), + &self.style.theme, ); let children = styled @@ -77,6 +66,14 @@ impl Element for EditorElement { display: Display::Flex, flex_direction: FlexDirection::Column, align_content: Some(AlignContent::FlexStart), + overflow: Point::all(Overflow::Scroll), + size: Size::full(), + background: self + .style + .theme + .settings + .background + .map(|bg| Fill::Color(hsla_from_syntect(bg))), ..Default::default() }, children.iter().copied(), @@ -98,13 +95,35 @@ impl Element for EditorElement { request_layout: &mut Self::RequestLayoutState, cx: &mut WindowContext, ) -> Self::PrepaintState { - request_layout - .styled - .iter_mut() - .for_each(|(id, styled, bounds)| { - *bounds = cx.layout_bounds(*id); - styled.prepaint(None, *bounds, &mut (), cx) + if self.cursor.y > 0 { + let offset = Point::new( + px(0.), + request_layout.styled.iter_mut().take(self.cursor.y).fold( + px(0.), + |px, (id, _, bounds)| { + *bounds = cx.layout_bounds(*id); + px + bounds.size.height + }, + ), + ); + + cx.with_element_offset(offset, |cx| { + request_layout + .styled + .iter_mut() + .skip(self.cursor.y) + .for_each(|(_id, styled, bounds)| styled.prepaint(None, *bounds, &mut (), cx)); }); + } else { + request_layout + .styled + .iter_mut() + .skip(self.cursor.y) + .for_each(|(id, styled, bounds)| { + *bounds = cx.layout_bounds(*id); + styled.prepaint(None, *bounds, &mut (), cx) + }); + } EditorLayout {} } @@ -126,41 +145,37 @@ impl Element for EditorElement { ElementInputHandler::new(bounds, self.editor.clone()), ); - let scrolled = self.scrolled.clone(); + let view = self.editor.clone(); let font_size = self.style.font_size.to_pixels(px(4.)); cx.on_mouse_event(move |scroll: &ScrollWheelEvent, _dispatch, cx| { - match scroll.delta { - ScrollDelta::Lines(lines) => scrolled.set({ + log::trace!("{scroll:#?}"); + view.update(cx, |editor, cx| match scroll.delta { + ScrollDelta::Lines(lines) => { let lines_y = lines.y.ceil() as i32; - scrolled.get().saturating_add_signed(lines_y as isize) - }), - ScrollDelta::Pixels(pix) => scrolled.set({ + editor.cursor.y = editor.cursor.y.saturating_add_signed(lines_y as isize) + } + ScrollDelta::Pixels(pix) => { let px_y = (pix.y.ceil().0 / font_size.0) as i32; - scrolled.get().saturating_add_signed(px_y as isize) - }), - }; + editor.cursor.y = editor.cursor.y.saturating_add_signed(px_y as isize) + } + }); }); cx.with_text_style( Some(TextStyleRefinement { color: self .style - .hl_theme + .theme .settings .foreground .map(hsla_from_syntect), - background_color: self - .style - .hl_theme - .settings - .background - .map(hsla_from_syntect), ..Default::default() }), |cx| { request_layout .styled .iter_mut() + .skip(self.cursor.y) .for_each(|(id, styled, bounds)| { styled.paint(None, *bounds, &mut (), &mut (), cx); }); diff --git a/src/main.rs b/src/main.rs index 134fe8c..680c6ad 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use funnylog::Drain; +use funnylog::{filter::LevelFilter, span, Drain, Level}; use ming::*; mod buffer; @@ -41,10 +41,14 @@ fn main() { .env_filter("RUST_LOG") .ignore_error(), ); - funnylog::stdlog::setup(drain.clone()).unwrap(); + funnylog::stdlog::setup_with_level(drain.clone(), LevelFilter(Some(Level::Trace))).unwrap(); let logger = Logger::new(drain); App::new().run(|cx| { + cx.bind_keys([ + KeyBinding::new("up", editor::action::CursorUp, Some("Editor")), + KeyBinding::new("down", editor::action::CursorDown, Some("Editor")), + ]); cx.open_window( WindowOptions { ..Default::default() @@ -54,10 +58,7 @@ fn main() { let settings = cx.new_model(|_cx| editor::EditorSettings::load_defaults()); cx.new_view(|cx| Nite { - editor: editor::Editor::make( - cx, - settings, - ), + editor: editor::Editor::make(cx, settings, logger.branch(span!("editor"))), logger, }) }, diff --git a/themes/catppuccin/mocha-teal.kdl b/themes/catppuccin/mocha-teal.kdl new file mode 100644 index 0000000..e67fdac --- /dev/null +++ b/themes/catppuccin/mocha-teal.kdl @@ -0,0 +1,24 @@ + + + +name "Catppuccin Mocha" +background_color "#1e1e2e" +text_color "#cdd6f4" +highlights { + attribute "#f9e2af" + boolean "#fab387" + comment "#7f849c" + comment.doc "#7f849c" + constant "#fab387" + constructor "#89b4fa" + embedded "#eba0ac" + emphasis "#f38ba8" + emphasis.strong "#f38ba8" font_weight=700 + enum "#94e2d5" font_weight=700 + function "#89b4fa" + hint "#94e2d5" + keyword "#cba6f7" + link_text "#89b4fa" + link_uri "#89b4fa" + number "#fab387" +} \ No newline at end of file diff --git a/themes/catppuccin/nite.tera b/themes/catppuccin/nite.tera new file mode 100644 index 0000000..237331a --- /dev/null +++ b/themes/catppuccin/nite.tera @@ -0,0 +1,40 @@ +--- +whiskers: + version: 2.0.2 + matrix: + - variant: ["", "-no-italics"] + - flavor + - accent + filename: "{{flavor.identifier}}-{{accent}}{{variant}}.kdl" +--- + +{%set c = flavor.colors-%} +{%set accent = c[accent]-%} + +{%if variant == "-no-italics"%} + {%set italic = "font_style=italic"-%} +{%else%} + {%set italic = ""-%} +{%endif%} + +name "Catppuccin {{flavor.name}} {%- if variant == "-no-italics" %} (no italics) {%- endif -%}" +background_color "#{{c.base.hex}}" +text_color "#{{c.text.hex}}" +highlights { + attribute "#{{c.yellow.hex}}" + boolean "#{{c.peach.hex}}" + comment "#{{c.overlay1.hex}}" {{italic}} + comment.doc "#{{c.overlay1.hex}}" {{italic}} + constant "#{{c.peach.hex}}" + constructor "#{{c.blue.hex}}" + embedded "#{{c.maroon.hex}}" + emphasis "#{{c.red.hex}}" {{italic}} + emphasis.strong "#{{c.red.hex}}" font_weight=700 + enum "#{{c.teal.hex}}" font_weight=700 + function "#{{c.blue.hex}}" {{italic}} + hint "#{{c.teal.hex}}" {{italic}} + keyword "#{{c.mauve.hex}}" + link_text "#{{c.blue.hex}}" + link_uri "#{{c.blue.hex}}" + number "#{{c.peach.hex}}" +}