it fucken works!!

This commit is contained in:
Borodinov Ilya 2024-05-15 15:22:28 +03:00
parent d7fe624e8b
commit 4382205e84
Signed by: noth
GPG key ID: 75503B2EF596D1BD
13 changed files with 413 additions and 51 deletions

116
Cargo.lock generated
View file

@ -306,12 +306,27 @@ 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"
@ -1834,6 +1849,18 @@ 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"
@ -1998,6 +2025,7 @@ dependencies = [
"serde_json",
"slotmap",
"smallvec",
"smol",
"taffy",
"thiserror",
"time",
@ -2105,7 +2133,9 @@ version = "0.1.0"
dependencies = [
"anyhow",
"funnylog",
"log",
"ming",
"syntect",
"widestring",
]
@ -2294,6 +2324,28 @@ 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"
@ -2502,6 +2554,20 @@ 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"
@ -3164,6 +3230,23 @@ dependencies = [
"wayland-backend",
]
[[package]]
name = "smol"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e635339259e51ef85ac7aa29a1cd991b957047507288697a690e80ab97d07cad"
dependencies = [
"async-channel 2.3.0",
"async-executor",
"async-fs",
"async-io",
"async-lock",
"async-net",
"async-process",
"blocking",
"futures-lite 2.3.0",
]
[[package]]
name = "socket2"
version = "0.5.7"
@ -3269,6 +3352,28 @@ 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"
@ -3654,7 +3759,7 @@ version = "0.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c704361d822337cfc00387672c7b59eaa72a1f0744f62b2a68aa228a0c6927d"
dependencies = [
"base64",
"base64 0.22.1",
"data-url",
"flate2",
"imagesize",
@ -4180,6 +4285,15 @@ 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"

View file

@ -8,6 +8,8 @@ funnylog.workspace = true
ming.workspace = true
anyhow.workspace = true
widestring = "1.1.0"
syntect = "5.2.0"
log.workspace = true
[workspace]
members = [

View file

@ -61,6 +61,7 @@ time.workspace = true
util.workspace = true
uuid.workspace = true
waker-fn = "1.1.0"
smol = "2.0.0"
[dev-dependencies]
backtrace = "0.3"

View file

@ -143,7 +143,13 @@ impl StyledText {
}
}
/// todo!()
/// Get a reference to the text inside this [`StyledText`]
pub fn text(&self) -> &str {
&self.text
}
/// Access the text layout.
/// This method is not intended to be used outside ming.
pub fn layout(&self) -> &TextLayout {
&self.layout
}

View file

@ -9,13 +9,9 @@ use util::arc_cow::ArcCow;
pub struct SharedString(ArcCow<'static, str>);
impl SharedString {
/// Create a new shared string from anything that can be turned into an [`ArcCow`], including:
/// - `&'static `[`str`]
/// - [`String`]
/// - [`Arc`]`<`[`str`]`>`
/// - a reference to any of the above
pub fn new(ac: impl Into<ArcCow<'static, str>>) -> Self {
Self(ac.into())
/// Create a new empty [`SharedString`].
pub fn new() -> Self {
Self(ArcCow::Borrowed(""))
}
/// Create a new shared string from a `&'static `[`str`].

View file

@ -102,7 +102,7 @@ impl<T: 'static> futures::Stream for Observation<T> {
/// observe returns a stream of the change events from the given `View` or `Model`
pub fn observe<T: 'static>(entity: &impl Entity<T>, cx: &mut TestAppContext) -> Observation<()> {
let (tx, rx) = smol::channel::unbounded();
let (tx, rx) = channel::unbounded();
let _subscription = cx.update(|cx| {
cx.observe(entity, move |_, _| {
let _ = smol::block_on(tx.send(()));

View file

@ -2,10 +2,7 @@
use std::time::Duration;
#[cfg(any(test, feature = "test-support"))]
use futures::Future;
#[cfg(any(test, feature = "test-support"))]
use smol::future::FutureExt;
use futures::{Future, FutureExt};
pub use util::*;

View file

@ -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=info,nite=trace,ming=trace cargo run -- $@";
};
};
};

View file

@ -1,4 +1,10 @@
use std::{ops::Range, path::PathBuf};
use syntect::{
highlighting::{
Color, FontStyle as SFontStyle, HighlightState, Highlighter, RangedHighlightIterator, Theme,
},
parsing::{ParseState, ScopeStack, SyntaxSet},
};
use widestring::Utf16String;
use super::*;
@ -6,18 +12,19 @@ use super::*;
pub struct Buffer {
pub text: Utf16String,
pub path: Option<PathBuf>,
pub marked: Option<Range<usize>>
pub marked: Option<Range<usize>>,
pub selected: Option<Range<usize>>,
}
impl Buffer {
pub fn make_scratch<V>(cx: &mut ViewContext<V>) -> Model<Self> {
cx.new_model(|_cx| Self::scratch())
}
pub fn read(path: PathBuf) -> anyhow::Result<Self> {
let text = std::fs::read_to_string(&path)?;
log::trace!("read file: {text}");
Ok(Self {
text: std::fs::read_to_string(&path)?,
text: text.into(),
path: Some(path),
marked: None,
selected: None,
})
}
@ -25,6 +32,81 @@ impl Buffer {
Self {
text: Utf16String::new(),
path: None,
marked: None,
selected: None,
}
}
pub fn styled(
&self,
default_style: &TextStyle,
theme: &Theme,
syntax_set: &SyntaxSet,
scrolled: usize,
) -> Vec<StyledText> {
let text = self.text.to_string();
if let Some(syntax) = self
.path
.as_ref()
.and_then(|path| path.extension())
.and_then(|extension| {
syntax_set.find_syntax_by_extension(extension.to_str().expect("non-utf8 file ext"))
})
{
let mut parse = ParseState::new(syntax);
let hler = Highlighter::new(theme);
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(
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()
},
)
},
),
)
})
})
.collect()
} else {
text.lines()
.skip(scrolled)
.map(|line| StyledText::new(Arc::from(line)))
.collect()
}
}
}
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()
}

View file

@ -1,25 +1,68 @@
use std::path::PathBuf;
use syntect::{highlighting::ThemeSet, parsing::SyntaxSet};
use super::*;
mod element;
mod input;
pub struct EditorSettings {
theme_set: ThemeSet,
syntax_set: SyntaxSet,
}
impl EditorSettings {
pub fn load_defaults() -> Self {
Self {
theme_set: ThemeSet::load_defaults(),
syntax_set: SyntaxSet::load_defaults_nonewlines(),
}
}
}
pub struct Editor {
focus_handle: FocusHandle,
buf: Model<crate::buffer::Buffer>
buf: Model<crate::buffer::Buffer>,
settings: Model<EditorSettings>,
}
impl Editor {
pub fn make<V: 'static>(cx: &mut ViewContext<V>) -> View<Self> {
pub fn make<V: 'static>(
cx: &mut ViewContext<V>,
settings: Model<EditorSettings>,
) -> View<Self> {
cx.new_view(|cx| Self {
buf: crate::buffer::Buffer::make_scratch(cx),
focus_handle: cx.focus_handle()
buf: cx.new_model(|_cx| {
if let Some(path) = std::env::args().skip(1).next() {
crate::buffer::Buffer::read(PathBuf::from(path)).expect("file failed to read")
} else {
crate::buffer::Buffer::scratch()
}
}),
focus_handle: cx.focus_handle(),
settings,
})
}
}
impl Render for Editor {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
element::EditorElement::new(cx.view())
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(),
},
)
}
}

View file

@ -1,29 +1,29 @@
use std::{cell::Cell, rc::Rc};
use syntect::highlighting::Theme;
use self::buffer::hsla_from_syntect;
use super::*;
pub struct EditorStyle {
pub font_family: SharedString,
pub font_size: AbsoluteLength,
}
impl Default for EditorStyle {
fn default() -> Self {
Self {
font_family: SharedString::from_static("Monospace"),
font_size: AbsoluteLength::Pixels(px(14)),
}
}
pub hl_theme: Theme,
}
pub struct EditorElement {
editor: View<Editor>,
style: EditorStyle,
scrolled: Rc<Cell<usize>>,
}
impl EditorElement {
pub fn new(viewref: &View<Editor>) -> Self {
pub fn new(viewref: &View<Editor>, style: EditorStyle) -> Self {
Self {
editor: viewref.clone(),
style: EditorStyle::default(),
style,
scrolled: Rc::new(Cell::new(0)),
}
}
}
@ -36,10 +36,13 @@ impl IntoElement for EditorElement {
}
}
struct EditorLayout {}
pub struct EditorLayout {}
pub struct EditorRequestLayout {
styled: Vec<(LayoutId, StyledText, Bounds<Pixels>)>,
}
impl Element for EditorElement {
type RequestLayoutState = ();
type RequestLayoutState = EditorRequestLayout;
type PrepaintState = EditorLayout;
fn id(&self) -> Option<ElementId> {
@ -51,6 +54,41 @@ impl Element for EditorElement {
id: Option<&GlobalElementId>,
cx: &mut WindowContext,
) -> (LayoutId, Self::RequestLayoutState) {
let editor = self.editor.read(cx);
let mut styled = editor.buf.read(cx).styled(
&TextStyle {
font_size: self.style.font_size,
font_family: self.style.font_family.clone(),
..Default::default()
},
&self.style.hl_theme,
&editor.settings.read(cx).syntax_set,
self.scrolled.get(),
);
let children = styled
.iter_mut()
.map(|styled| styled.request_layout(None, cx).0)
.collect::<Vec<_>>();
(
cx.request_layout(
Style {
display: Display::Flex,
flex_direction: FlexDirection::Column,
align_content: Some(AlignContent::FlexStart),
..Default::default()
},
children.iter().copied(),
),
EditorRequestLayout {
styled: children
.into_iter()
.zip(styled)
.map(|(id, styled)| (id, styled, Bounds::default()))
.collect(),
},
)
}
fn prepaint(
@ -60,6 +98,14 @@ 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)
});
EditorLayout {}
}
@ -80,14 +126,44 @@ impl Element for EditorElement {
ElementInputHandler::new(bounds, self.editor.clone()),
);
let scrolled = self.scrolled.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({
let lines_y = lines.y.ceil() as i32;
scrolled.get().saturating_add_signed(lines_y as isize)
}),
ScrollDelta::Pixels(pix) => scrolled.set({
let px_y = (pix.y.ceil().0 / font_size.0) as i32;
scrolled.get().saturating_add_signed(px_y as isize)
}),
};
});
cx.with_text_style(
Some(TextStyleRefinement {
font_size: Some(self.style.font_size),
font_family: Some(self.style.font_family),
color: self
.style
.hl_theme
.settings
.foreground
.map(hsla_from_syntect),
background_color: self
.style
.hl_theme
.settings
.background
.map(hsla_from_syntect),
..Default::default()
}),
|cx| {
todo!("draw text??")
request_layout
.styled
.iter_mut()
.for_each(|(id, styled, bounds)| {
styled.paint(None, *bounds, &mut (), &mut (), cx);
});
},
);
}

View file

@ -1,4 +1,4 @@
use widestring::Utf16Str;
use widestring::{Utf16Str, Utf16String};
use super::*;
@ -41,6 +41,30 @@ impl ViewInputHandler for Editor {
element_bounds: Bounds<Pixels>,
cx: &mut ViewContext<Self>,
) -> Option<Bounds<Pixels>> {
None
}
fn selected_text_range(
&mut self,
cx: &mut ViewContext<Self>,
) -> Option<std::ops::Range<usize>> {
self.buf.read(cx).selected.clone()
}
fn replace_text_in_range(
&mut self,
range: Option<std::ops::Range<usize>>,
text: &str,
cx: &mut ViewContext<Self>,
) {
let text = Utf16String::from_str(text);
self.buf.update(cx, |buf, cx| {
if let Some(range) = range {
buf.text.replace_range(range, &text)
} else {
buf.text = text;
}
})
}
fn replace_and_mark_text_in_range(
@ -50,7 +74,13 @@ impl ViewInputHandler for Editor {
new_selected_range: Option<std::ops::Range<usize>>,
cx: &mut ViewContext<Self>,
) {
let new_text = Utf16String::from_str(new_text);
self.buf.update(cx, |buf, cx| {
if let Some(range) = range {
buf.text.replace_range(range, &new_text)
} else {
buf.text = new_text;
}
buf.marked = new_selected_range;
})
}

View file

@ -8,10 +8,16 @@ mod editor;
struct Nite {
editor: View<editor::Editor>,
logger: Logger
logger: Logger,
}
type Logger = funnylog::Logger<Arc<funnylog::IgnoreError<funnylog::filter::FilterDrain<funnylog::terminal::TerminalDrain<std::io::Stderr>>>>>;
type Logger = funnylog::Logger<
Arc<
funnylog::IgnoreError<
funnylog::filter::FilterDrain<funnylog::terminal::TerminalDrain<std::io::Stderr>>,
>,
>,
>;
impl Render for Nite {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
@ -19,8 +25,8 @@ impl Render for Nite {
.flex()
.size(Length::Definite(DefiniteLength::Fraction(1f32)))
.bg(transparent_black())
.justify_center()
.items_center()
.justify_start()
.items_start()
.text_xl()
.text_color(white())
.font_family("ComicShannsMono Nerd Font Mono")
@ -29,8 +35,13 @@ impl Render for Nite {
}
fn main() {
let drain = Arc::new(funnylog::terminal::TerminalConfig::default().to_stderr().env_filter("RUST_LOG").ignore_error());
funnylog::stdlog::setup(drain.clone());
let drain = Arc::new(
funnylog::terminal::TerminalConfig::default()
.to_stderr()
.env_filter("RUST_LOG")
.ignore_error(),
);
funnylog::stdlog::setup(drain.clone()).unwrap();
let logger = Logger::new(drain);
App::new().run(|cx| {
@ -41,9 +52,13 @@ fn main() {
|cx| {
funnylog::info!(logger, "Hello from nite");
let settings = cx.new_model(|_cx| editor::EditorSettings::load_defaults());
cx.new_view(|cx| Nite {
editor: editor::Editor::make(cx),
logger
editor: editor::Editor::make(
cx,
settings,
),
logger,
})
},
);