i treesittered

This commit is contained in:
Borodinov Ilya 2024-05-17 22:01:05 +03:00
parent 638a9a112b
commit 7424dc67b7
Signed by: noth
GPG key ID: 75503B2EF596D1BD
9 changed files with 219 additions and 148 deletions

97
Cargo.lock generated
View file

@ -500,6 +500,10 @@ dependencies = [
"wayland-client",
]
[[package]]
name = "careless"
version = "0.1.0"
[[package]]
name = "castaway"
version = "0.1.2"
@ -1651,6 +1655,22 @@ 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"
@ -2166,13 +2186,13 @@ name = "nite"
version = "0.1.0"
dependencies = [
"anyhow",
"careless",
"funnylog",
"inkjet",
"kaydle",
"log",
"ming",
"serde",
"tree-sitter-highlight",
"tree-sitter-rust",
"widestring",
]
@ -2689,7 +2709,7 @@ version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284"
dependencies = [
"toml_edit",
"toml_edit 0.21.1",
]
[[package]]
@ -3140,6 +3160,15 @@ 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"
@ -3592,11 +3621,26 @@ 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"
@ -3606,7 +3650,20 @@ checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1"
dependencies = [
"indexmap",
"toml_datetime",
"winnow",
"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",
]
[[package]]
@ -3653,9 +3710,9 @@ dependencies = [
[[package]]
name = "tree-sitter"
version = "0.22.6"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df7cc499ceadd4dcdf7ec6d4cbc34ece92c3fa07821e287aedecd4416c516dca"
checksum = "e747b1f9b7b931ed39a548c1fae149101497de3c1fc8d9e18c62c1a66c683d3d"
dependencies = [
"cc",
"regex",
@ -3663,26 +3720,15 @@ dependencies = [
[[package]]
name = "tree-sitter-highlight"
version = "0.22.6"
version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eaca0fe34fa96eec6aaa8e63308dbe1bafe65a6317487c287f93938959b21907"
checksum = "042342584c5a7a0b833d9fc4e2bdab3f9868ddc6c4b339a1e01451c6720868bc"
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"
@ -4250,6 +4296,15 @@ 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"
@ -4471,7 +4526,3 @@ dependencies = [
"quote",
"syn 1.0.109",
]
[[patch.unused]]
name = "careless"
version = "0.1.0"

View file

@ -9,10 +9,10 @@ ming.workspace = true
anyhow.workspace = true
widestring = "1.1.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"
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" }
[workspace]
members = [

View file

@ -109,6 +109,7 @@ pub trait IntoElement: Sized {
}
}
impl FluentBuilder for crate::TextRun {}
impl<T: IntoElement> FluentBuilder for T {}
/// An object that can be drawn to the screen. This is the trait that distinguishes `Views` from

View file

@ -656,6 +656,8 @@ impl<'de> serde::Deserialize<'de> for FontWeight {
.map_err(|()| E::custom("expected a font weight value (e.g. 'normal' or 'bold')"))
}
}
deserializer.deserialize_any(Visitor)
}
}

View file

@ -15,6 +15,13 @@ 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 {
f(&mut self);
self
}
/// Conditionally modify self with the given closure.
fn when(self, condition: bool, then: impl FnOnce(Self) -> Self) -> Self

View file

@ -1,7 +1,10 @@
use careless::prelude::*;
use inkjet::tree_sitter_highlight::{Error, HighlightEvent, Highlighter};
use std::{ops::Range, path::PathBuf};
use tree_sitter_highlight::{HighlightConfiguration, Highlighter};
use widestring::Utf16String;
use crate::editor::Theme;
use super::*;
pub struct Buffer {
@ -31,71 +34,43 @@ impl Buffer {
}
}
pub fn styled(
&self,
default_style: &TextStyle,
theme: &crate::editor::Theme,
syntax_set: &SyntaxSet,
) -> Vec<StyledText> {
pub fn styled(&self, default_style: &TextStyle, theme: &Theme) -> Vec<StyledText> {
let text = self.text.to_string();
if let Some(syntax) = self
if let Some(language) = self
.path
.as_ref()
.and_then(|path| path.extension())
.and_then(|extension| {
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(),
)
inkjet::Language::from_token(extension.to_str()?).map(|lang| lang.config())
})
{
let mut parse = ParseState::new(syntax);
let hler = Highlighter::new(theme);
let mut hl = HighlightState::new(&hler, ScopeStack::new());
let mut hl = Highlighter::new();
let mut lines = vec![];
text.lines()
.flat_map(|line| {
parse.parse_line(line, &syntax_set).map(|parsed| {
StyledText::new(Arc::from(line)).with_highlights(
default_style,
RangedHighlightIterator::new(&mut hl, &parsed, line, &hler).map(
move |(style, _text, range)| {
(
range,
HighlightStyle {
color: Some(hsla_from_syntect(style.foreground)),
background_color: None,
font_style: style
.font_style
.contains(SFontStyle::ITALIC)
.then(|| FontStyle::Italic),
underline: style
.font_style
.contains(SFontStyle::UNDERLINE)
.then(|| UnderlineStyle::default()),
font_weight: style
.font_style
.contains(SFontStyle::BOLD)
.then(|| FontWeight::BOLD),
..Default::default()
},
)
},
),
)
for line in text.lines() {
// damnation
match hl
.highlight(language, line.as_bytes(), None, |lang| {
inkjet::Language::from_token(lang).map(|lang| lang.config())
})
})
.collect()
.and_then(|hls| {
Ok(StyledText::new(Arc::from(line)).with_runs(
MingHighlighter::new(hls, default_style, theme).try_to_collect()?,
))
}) {
Ok(styled) => lines.push(styled),
Err(error) => {
log::error!("highlight error: {error}");
lines.push(StyledText::new(Arc::from(line)));
continue;
}
}
}
lines
} else {
text.lines()
.map(|line| StyledText::new(Arc::from(line)))
@ -104,12 +79,57 @@ impl Buffer {
}
}
pub fn hsla_from_syntect(color: Color) -> Hsla {
Rgba {
r: color.r as f32 / 255.0,
g: color.g as f32 / 255.0,
b: color.b as f32 / 255.0,
a: color.a as f32 / 255.0,
}
.into()
pub struct MingHighlighter<'a, I> {
iter: I,
default_style: &'a TextStyle,
theme: &'a Theme,
current_hl_idx: Option<usize>,
current_span: Option<Range<usize>>,
}
impl<'a, I> MingHighlighter<'a, I> {
pub fn new(iter: I, default_style: &'a TextStyle, theme: &'a Theme) -> Self {
Self {
iter,
default_style,
theme,
current_hl_idx: None,
current_span: None,
}
}
}
impl<I: Iterator<Item = Result<HighlightEvent, Error>>> Iterator for MingHighlighter<'_, I> {
type Item = Result<TextRun, Error>;
fn next(&mut self) -> Option<Self::Item> {
use careless::prelude::*;
TryOption::into(
try {
match self.iter.try_next()? {
HighlightEvent::Source { start, end } => {
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))
{
hl.apply_to_run(run)
}
})
}
HighlightEvent::HighlightStart(hl_idx) => {
self.current_hl_idx = Some(hl_idx.0);
self.try_next()?
}
HighlightEvent::HighlightEnd => {
self.current_hl_idx = None;
self.try_next()?
}
}
},
)
}
}

View file

@ -11,7 +11,7 @@ pub struct EditorSettings {
style: EditorStyle,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[derive(Clone, Copy, Debug, Deserialize)]
pub struct Highlight {
pub color: Option<Rgba>,
#[serde(default)]
@ -20,17 +20,41 @@ pub struct Highlight {
pub weight: Option<FontWeight>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
impl Highlight {
pub fn apply_to_run(&self, run: &mut TextRun) {
if let Some(color) = self.color {
run.color = color.into();
}
if let Some(style) = self.style {
run.font.style = style;
}
if let Some(weight) = self.weight {
run.font.weight = weight;
}
}
}
#[derive(Clone, Debug, Deserialize)]
pub struct Theme {
pub name: String,
pub background_color: Rgba,
pub text_color: Rgba,
pub highlights: HashMap<String, Highlight>,
#[serde(skip)]
pub highlight_indices: Vec<Highlight>
}
impl Theme {
pub fn load(text: &str) -> Result<Self, kaydle::serde::de::Error> {
kaydle::serde::from_str(text)
kaydle::serde::from_str(text).map(|mut me: Self| {
me.highlight_indices = inkjet::constants::HIGHLIGHT_NAMES.iter().flat_map(|hl| {
me.highlights.get(*hl).cloned()
}).collect();
me
})
}
}
@ -61,6 +85,7 @@ pub struct Editor {
buf: Model<crate::buffer::Buffer>,
settings: Model<EditorSettings>,
cursor: Point<usize>,
scroll: Point<Pixels>,
logger: Logger,
}
@ -82,6 +107,7 @@ impl Editor {
settings,
logger,
cursor: Point::default(),
scroll: Point::default()
})
}
}
@ -107,10 +133,10 @@ impl Render for Editor {
me.cursor.y = me.cursor.y.saturating_sub(1);
funnylog::trace!(me.logger, ?me.cursor, "up");
}))
.overflow_hidden()
.child(element::EditorElement::new(
cx.view(),
self.settings.read(cx).style.clone(),
self.cursor,
))
}
}

View file

@ -1,20 +1,15 @@
use crate::buffer::hsla_from_syntect;
use super::*;
pub struct EditorElement {
editor: View<Editor>,
style: EditorStyle,
cursor: Point<usize>,
}
impl EditorElement {
pub fn new(viewref: &View<Editor>, style: EditorStyle, cursor: Point<usize>) -> Self {
pub fn new(viewref: &View<Editor>, style: EditorStyle) -> Self {
Self {
editor: viewref.clone(),
style,
cursor,
}
}
}
@ -66,14 +61,9 @@ impl Element for EditorElement {
display: Display::Flex,
flex_direction: FlexDirection::Column,
align_content: Some(AlignContent::FlexStart),
overflow: Point::all(Overflow::Scroll),
overflow: Point::all(Overflow::Hidden),
size: Size::full(),
background: self
.style
.theme
.settings
.background
.map(|bg| Fill::Color(hsla_from_syntect(bg))),
background: Some(Fill::Color(self.style.theme.background_color.into())),
..Default::default()
},
children.iter().copied(),
@ -95,35 +85,12 @@ impl Element for EditorElement {
request_layout: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
) -> Self::PrepaintState {
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 {
cx.with_element_offset(self.editor.read(cx).scroll, |cx| {
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)
});
}
.for_each(|(_id, styled, bounds)| styled.prepaint(None, *bounds, &mut (), cx));
});
EditorLayout {}
}
@ -151,8 +118,7 @@ impl Element for EditorElement {
log::trace!("{scroll:#?}");
view.update(cx, |editor, cx| match scroll.delta {
ScrollDelta::Lines(lines) => {
let lines_y = lines.y.ceil() as i32;
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;
@ -163,22 +129,18 @@ impl Element for EditorElement {
cx.with_text_style(
Some(TextStyleRefinement {
color: self
.style
.theme
.settings
.foreground
.map(hsla_from_syntect),
color: Some(self.style.theme.text_color.into()),
..Default::default()
}),
|cx| {
request_layout
.styled
.iter_mut()
.skip(self.cursor.y)
.for_each(|(id, styled, bounds)| {
styled.paint(None, *bounds, &mut (), &mut (), cx);
});
cx.with_element_offset(self.editor.read(cx).scroll, |cx| {
request_layout
.styled
.iter_mut()
.for_each(|(_id, styled, bounds)| {
styled.paint(None, *bounds, &mut (), &mut (), cx)
});
});
},
);
}

View file

@ -1,7 +1,9 @@
#![feature(try_blocks)]
use std::sync::Arc;
use funnylog::{filter::LevelFilter, span, Drain, Level};
use ming::*;
use ming::{*, prelude::*};
mod buffer;
mod editor;