A simple CPU rendered GUI IDE experience.
redo history
bendn 7 days ago
parent 4b8582f · commit 2a7c39c
-rw-r--r--Cargo.toml1
-rw-r--r--src/edi.rs104
-rw-r--r--src/edi/st.rs2
-rw-r--r--src/main.rs146
-rw-r--r--src/rnd.rs2
-rw-r--r--src/text.rs112
-rw-r--r--src/text/hist.rs240
-rw-r--r--src/text/semantic_tokens.rs95
-rw-r--r--src/text/semantic_tokens/theme.rs95
-rw-r--r--src/text/theme_treesitter.rs20
10 files changed, 456 insertions, 361 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 758623f..46a3063 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,7 +22,6 @@ run_times = "0.1.0"
array_chunks = "1.0.0"
rust-fsm = { git = "https://git.bendn.org/rust-fsm", features = ["diagram"] }
clipp = "0.1.0"
-diff-match-patch-rs = { git = "https://git.bendn.org/dmp" }
regex = { version = "1.11.3", features = ["unstable", "use_std"] }
tree-house = { version = "0.3.0", features = ["fixtures"] }
diff --git a/src/edi.rs b/src/edi.rs
index b867d32..ab25fec 100644
--- a/src/edi.rs
+++ b/src/edi.rs
@@ -34,12 +34,13 @@ use crate::lsp::{
use crate::meta::META;
use crate::sym::{Symbols, SymbolsType};
use crate::text::cursor::{Ronge, ceach};
+use crate::text::hist::{ClickHistory, Hist};
use crate::text::{
self, CoerceOption, Mapping, RopeExt, SortTedits, TextArea,
};
use crate::{
- BoolRequest, CDo, ClickHistory, CompletionAction, CompletionState,
- Hist, act, alt, ctrl, filter, hash, shift, sig, sym, trm,
+ BoolRequest, CDo, CompletionAction, CompletionState, act, alt, ctrl,
+ filter, hash, shift, sig, sym, trm,
};
#[allow(dead_code)]
pub fn serialize_tokens<S: serde::Serializer>(
@@ -308,7 +309,8 @@ impl Editor {
me.tree = t;
} else {
me.lsp = l;
- me.hist.last = me.text.clone();
+ me.hist.lc = me.text.cursor.clone();
+ me.hist.last = me.text.changes.clone();
me.lsp.as_ref().zip(me.origin.as_deref()).map(
|((c, ..), origin)| {
c.open(
@@ -375,7 +377,7 @@ impl Editor {
// self.text.cursor =
// self.text.cursor.min(self.text.rope.len_chars());
change!(self);
- self.hist.push(&self.text);
+ self.hist.push(&mut self.text);
l.notify::<lsp_notification!("textDocument/didSave")>(
&DidSaveTextDocumentParams {
text_document: o.tid(),
@@ -510,9 +512,9 @@ impl Editor {
}
Some(Do::StartSelection) => {
let x = self.text.mapped_index_at(cursor_position);
- self.hist.last.cursor.first_mut().position = x;
self.text.cursor.first_mut().position = x;
self.text.cursor.first_mut().sel = Some((x..x).into());
+ self.hist.lc = self.text.cursor.clone();
}
Some(Do::Hover)
if let Some(hover) =
@@ -756,38 +758,23 @@ impl Editor {
),
);
}
- self.hist.last.cursor = text.cursor.clone();
+ self.hist.lc = text.cursor.clone();
self.chist.push(text.primary_cursor());
text.cursor.first().setc(&text.rope);
}
- Some(Do::NavForward) => {
- self.chist.forth().map(|x| {
- text.cursor.just(
- text.rope.line_to_char(x.1) + x.0,
- &text.rope,
- );
- text.scroll_to_cursor();
- });
- }
- Some(Do::NavBack) => {
- self.chist.back().map(|x| {
- text.cursor.just(
- text.rope.line_to_char(x.1) + x.0,
- &text.rope,
- );
- text.scroll_to_cursor();
- });
- }
+ Some(Do::NavForward) => self.nav_forward(),
+ Some(Do::NavBack) => self.nav_back(),
Some(Do::ExtendSelectionToMouse) => {
let p = text.mapped_index_at(cursor_position);
text.cursor.first_mut().extend_selection_to(p, &text.rope);
}
Some(Do::StartSelection) => {
let p = text.mapped_index_at(cursor_position);
- self.hist.last.cursor.just(p, &text.rope);
+
let x = *text.cursor.first();
text.cursor.first_mut().sel = Some((x..x).into());
text.cursor.first_mut().extend_selection_to(p, &text.rope);
+ self.hist.lc = text.cursor.clone();
}
Some(Do::GoToDefinition) => {
if let Some(LocationLink {
@@ -814,7 +801,7 @@ impl Editor {
text.mapped_index_at(cursor_position),
&text.rope,
);
- self.hist.last.cursor = text.cursor.clone();
+ self.hist.lc = text.cursor.clone();
self.chist.push(text.primary_cursor());
text.cursor.first().setc(&text.rope);
}
@@ -822,6 +809,24 @@ impl Editor {
_ => unreachable!(),
}
}
+ pub fn nav_back(&mut self) {
+ self.chist.back().map(|x| {
+ self.text.cursor.just(
+ self.text.rope.line_to_char(x.1) + x.0,
+ &self.text.rope,
+ );
+ self.text.scroll_to_cursor();
+ });
+ }
+ pub fn nav_forward(&mut self) {
+ self.chist.forth().map(|x| {
+ self.text.cursor.just(
+ self.text.rope.line_to_char(x.1) + x.0,
+ &self.text.rope,
+ );
+ self.text.scroll_to_cursor();
+ });
+ }
pub fn scroll(&mut self, rows: f32) {
let rows = if alt() { rows * 8. } else { rows * 3. };
let (vo, max) = lower::saturating::math! { if let Some(x)= &mut self.requests.hovering.result && shift() {
@@ -1113,8 +1118,8 @@ impl Editor {
let Some(act) = c.right() else { break 'out };
let act = act.clone();
self.state = State::Default;
- self.hist.last.cursor = self.text.cursor.clone();
- self.hist.test_push(&self.text);
+ self.hist.lc = self.text.cursor.clone();
+ self.hist.test_push(&mut self.text);
let act = lsp
.runtime
.block_on(
@@ -1142,14 +1147,14 @@ impl Editor {
};
c.up();
}
+ Some(Do::NavBack) => self.nav_back(),
+ Some(Do::NavForward) => self.nav_forward(),
Some(
Do::Reinsert
| Do::GoToDefinition
| Do::MoveCursor
| Do::ExtendSelectionToMouse
| Do::Hover
- | Do::NavBack
- | Do::NavForward
| Do::InsertCursorAtMouse,
) => panic!(),
Some(Do::Save) => match &self.origin {
@@ -1167,7 +1172,7 @@ impl Editor {
}
Some(Do::Edit) => {
self.text.cursor.clear_selections();
- self.hist.test_push(&self.text);
+ self.hist.test_push(&mut self.text);
let cb4 = self.text.cursor.first();
if let Key::Named(Enter | ArrowUp | ArrowDown | Tab) =
event.logical_key
@@ -1366,14 +1371,14 @@ impl Editor {
});
}
Some(Do::Undo) => {
- self.hist.test_push(&self.text);
- self.hist.undo(&mut self.text);
+ self.hist.test_push(&mut self.text);
+ self.hist.undo(&mut self.text).unwrap();
self.bar.last_action = "undid".to_string();
change!(self, window.clone());
}
Some(Do::Redo) => {
- self.hist.test_push(&self.text);
- self.hist.redo(&mut self.text);
+ self.hist.test_push(&mut self.text);
+ self.hist.redo(&mut self.text).unwrap();
self.bar.last_action = "redid".to_string();
change!(self, window.clone());
}
@@ -1420,7 +1425,7 @@ impl Editor {
}
Some(Do::Insert(c)) => {
// self.text.cursor.inner.clear();
- self.hist.push_if_changed(&self.text);
+ self.hist.push_if_changed(&mut self.text);
ceach!(self.text.cursor, |cursor| {
let Some(r) = cursor.sel else { return };
_ = self.text.remove(r.into());
@@ -1428,22 +1433,22 @@ impl Editor {
});
self.text.insert(&c);
self.text.cursor.clear_selections();
- self.hist.push_if_changed(&self.text);
+ self.hist.push_if_changed(&mut self.text);
change!(self, window.clone());
}
Some(Do::Delete) => {
- self.hist.push_if_changed(&self.text);
+ self.hist.push_if_changed(&mut self.text);
ceach!(self.text.cursor, |cursor| {
let Some(r) = cursor.sel else { return };
_ = self.text.remove(r.into());
});
self.text.cursor.clear_selections();
- self.hist.push_if_changed(&self.text);
+ self.hist.push_if_changed(&mut self.text);
change!(self, window.clone());
}
Some(Do::Copy) => {
- self.hist.push_if_changed(&self.text);
+ self.hist.push_if_changed(&mut self.text);
unsafe { take(&mut META) };
let mut clip = String::new();
self.text.cursor.each_ref(|x| {
@@ -1461,11 +1466,11 @@ impl Editor {
};
clipp::copy(clip);
self.text.cursor.clear_selections();
- self.hist.push_if_changed(&self.text);
+ self.hist.push_if_changed(&mut self.text);
change!(self, window.clone());
}
Some(Do::Cut) => {
- self.hist.push_if_changed(&self.text);
+ self.hist.push_if_changed(&mut self.text);
unsafe { take(&mut META) };
let mut clip = String::new();
self.text.cursor.each_ref(|x| {
@@ -1488,11 +1493,11 @@ impl Editor {
}
});
self.text.cursor.clear_selections();
- self.hist.push_if_changed(&self.text);
+ self.hist.push_if_changed(&mut self.text);
change!(self, window.clone());
}
Some(Do::Paste) => {
- self.hist.push_if_changed(&self.text);
+ self.hist.push_if_changed(&mut self.text);
let r = clipp::paste();
if unsafe { META.hash == hash(&r) } {
let bounds = unsafe { &*META.splits };
@@ -1525,7 +1530,7 @@ impl Editor {
} else {
self.text.insert(&clipp::paste());
}
- self.hist.push_if_changed(&self.text);
+ self.hist.push_if_changed(&mut self.text);
change!(self, window.clone());
}
Some(Do::OpenFile(x)) => {
@@ -1566,7 +1571,7 @@ impl Editor {
inlay!(self);
}
Some(Do::Boolean(BoolRequest::ReloadFile, true)) => {
- self.hist.push_if_changed(&self.text);
+ self.hist.push_if_changed(&mut self.text);
self.text.rope = Rope::from_str(
&std::fs::read_to_string(
self.origin.as_ref().unwrap(),
@@ -1582,7 +1587,7 @@ impl Editor {
.min(self.text.rope.len_chars());
self.mtime = Self::modify(self.origin.as_deref());
self.bar.last_action = "reloaded".into();
- self.hist.push(&self.text)
+ self.hist.push(&mut self.text)
}
Some(Do::Boolean(BoolRequest::ReloadFile, false)) => {}
Some(Do::InsertCursor(dir)) => {
@@ -1692,7 +1697,7 @@ impl Editor {
self.files = f;
self.bar.last_action = "restored".into();
if self.mtime != Self::modify(self.origin.as_deref()) {
- self.hist.push_if_changed(&self.text);
+ self.hist.push_if_changed(&mut self.text);
self.text.rope = Rope::from_str(
&std::fs::read_to_string(
self.origin.as_ref().unwrap(),
@@ -1709,7 +1714,7 @@ impl Editor {
self.mtime = Self::modify(self.origin.as_deref());
self.bar.last_action = "restored -> reloaded".into();
take(&mut self.requests);
- self.hist.push(&self.text)
+ self.hist.push(&mut self.text)
}
self.lsp = lsp;
@@ -1723,6 +1728,7 @@ impl Editor {
let new = std::fs::read_to_string(&x)?;
take(&mut self.text);
self.text.insert(&new);
+ take(&mut self.text.changes);
self.text.cursor.just(0, &self.text.rope);
self.bar.last_action = "open".into();
self.mtime = Self::modify(self.origin.as_deref());
diff --git a/src/edi/st.rs b/src/edi/st.rs
index 90c16fb..20fcfb3 100644
--- a/src/edi/st.rs
+++ b/src/edi/st.rs
@@ -57,6 +57,8 @@ Default => {
M(MouseButton::Left if alt()) => _ [InsertCursorAtMouse],
M(MouseButton::Left if ctrl()) => _ [GoToDefinition],
M(MouseButton::Left) => _ [MoveCursor],
+ K(Key::Character(x) if x == "-" && ctrl()) => _ [NavBack],
+ K(Key::Character(x) if x == "=" && ctrl()) => _ [NavBack],
M(MouseButton::Back) => _ [NavBack],
M(MouseButton::Forward) => _ [NavForward],
C(((usize, usize)) => .. if unsafe { CLICKING }) => Selection [StartSelection],
diff --git a/src/main.rs b/src/main.rs
index 7d2080e..a318719 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -53,11 +53,9 @@ use std::hash::Hash;
use std::mem::MaybeUninit;
use std::num::NonZeroU32;
use std::sync::LazyLock;
-use std::time::Instant;
use Default::default;
use NamedKey::*;
-use diff_match_patch_rs::PatchInput;
use dsb::cell::Style;
use dsb::{Cell, F};
use fimg::Image;
@@ -67,7 +65,7 @@ use lsp_types::*;
use rust_fsm::StateMachine;
use serde::{Deserialize, Serialize};
use swash::FontRef;
-use winit::dpi::{LogicalSize, PhysicalSize};
+use winit::dpi::PhysicalSize;
use winit::event::{
ElementState, Event, Ime, MouseButton, MouseScrollDelta, WindowEvent,
};
@@ -79,7 +77,7 @@ use winit::window::Icon;
use crate::edi::Editor;
use crate::edi::st::*;
use crate::lsp::RqS;
-use crate::text::{Diff, TextArea, col, is_word};
+use crate::text::{TextArea, col, is_word};
mod bar;
pub mod com;
pub mod hov;
@@ -97,133 +95,6 @@ fn main() {
// lsp::x();
entry(EventLoop::new().unwrap())
}
-pub fn serialize_debug<S: serde::Serializer, T: Debug>(
- s: &T,
- ser: S,
-) -> Result<S::Ok, S::Error> {
- ser.serialize_str(&format!("{s:?}"))
-}
-pub fn serialize_display<S: serde::Serializer, T: Display>(
- s: &T,
- ser: S,
-) -> Result<S::Ok, S::Error> {
- ser.serialize_str(&format!("{s}"))
-}
-
-#[derive(serde::Deserialize, serde::Serialize, Debug)]
-struct Hist {
- pub history: Vec<Diff>,
- pub redo_history: Vec<Diff>,
- pub last: TextArea,
- #[serde(
- skip_deserializing,
- serialize_with = "serialize_debug",
- default = "Instant::now"
- )]
- pub last_edit: std::time::Instant,
- pub changed: bool,
-}
-impl Default for Hist {
- fn default() -> Self {
- Self {
- history: vec![],
- redo_history: vec![],
- last: TextArea::default(),
- last_edit: Instant::now(),
- changed: false,
- }
- }
-}
-#[derive(Debug, Default, Serialize, Deserialize)]
-struct ClickHistory {
- pub his: Vec<(usize, usize)>,
- pub red: Vec<(usize, usize)>,
-}
-
-impl ClickHistory {
- fn push(&mut self, x: (usize, usize)) {
- if self.his.last() != Some(&x) {
- self.his.push(x);
- self.red.clear();
- }
- }
- fn back(&mut self) -> Option<(usize, usize)> {
- self.his.pop().inspect(|x| {
- self.red.push(*x);
- })
- }
- fn forth(&mut self) -> Option<(usize, usize)> {
- self.red.pop().inspect(|x| {
- self.his.push(*x);
- })
- }
-}
-impl Hist {
- fn push(&mut self, x: &TextArea) {
- let d = diff_match_patch_rs::DiffMatchPatch::new();
- self.history.push(Diff {
- forth: d
- .patch_make(PatchInput::new_text_text(
- &x.rope.to_string(),
- &self.last.rope.to_string(),
- ))
- .unwrap(),
- back: d
- .patch_make(PatchInput::new_text_text(
- &self.last.rope.to_string(),
- &x.rope.to_string(),
- ))
- .unwrap(),
- data: [
- (self.last.cursor.clone(), self.last.vo),
- (x.cursor.clone(), x.vo),
- ],
- });
- println!("push {}", self.history.last().unwrap());
- self.redo_history.clear();
- self.last = x.clone();
- self.last_edit = Instant::now();
- self.changed = false;
- }
- fn undo_(&mut self) -> Option<Diff> {
- self.history.pop().inspect(|x| self.redo_history.push(x.clone()))
- }
- fn redo_(&mut self) -> Option<Diff> {
- self.redo_history.pop().inspect(|x| self.history.push(x.clone()))
- }
- pub fn undo(&mut self, t: &mut TextArea) {
- self.push_if_changed(t);
- self.undo_().map(|x| {
- x.apply(t, false);
- self.last = t.clone();
- });
- }
- pub fn redo(&mut self, t: &mut TextArea) {
- self.redo_().map(|x| {
- x.apply(t, true);
- self.last = t.clone();
- });
- }
- pub fn push_if_changed(&mut self, x: &TextArea) {
- if self.changed || x.rope != self.last.rope {
- self.push(x);
- }
- }
- pub fn test_push(&mut self, x: &TextArea) {
- if self.last_edit.elapsed().as_millis() > 500 {
- self.push_if_changed(x);
- }
- }
- pub fn record(&mut self, new: &TextArea) -> bool {
- // self.test_push(x);
- if new.rope != self.last.rope {
- self.last_edit = Instant::now();
- self.changed = true;
- }
- new.rope != self.last.rope
- }
-}
-
static mut MODIFIERS: ModifiersState = ModifiersState::empty();
static mut CLICKING: bool = false;
@@ -633,3 +504,16 @@ pub fn hash(x: &impl Hash) -> u64 {
use std::hash::BuildHasher;
rustc_hash::FxBuildHasher::default().hash_one(x)
}
+
+pub fn serialize_debug<S: serde::Serializer, T: Debug>(
+ s: &T,
+ ser: S,
+) -> Result<S::Ok, S::Error> {
+ ser.serialize_str(&format!("{s:?}"))
+}
+pub fn serialize_display<S: serde::Serializer, T: Display>(
+ s: &T,
+ ser: S,
+) -> Result<S::Ok, S::Error> {
+ ser.serialize_str(&format!("{s}"))
+}
diff --git a/src/rnd.rs b/src/rnd.rs
index c7b7a69..b22f20e 100644
--- a/src/rnd.rs
+++ b/src/rnd.rs
@@ -4,8 +4,8 @@ use std::sync::{Arc, LazyLock};
use std::time::Instant;
use atools::prelude::*;
-use dsb::cell::Style;
use dsb::Cell;
+use dsb::cell::Style;
use fimg::pixels::Blend;
use fimg::{Image, OverlayAt};
use lsp_types::*;
diff --git a/src/text.rs b/src/text.rs
index ddeb205..0ba3e3c 100644
--- a/src/text.rs
+++ b/src/text.rs
@@ -1,6 +1,6 @@
use std::cmp::{Reverse, min};
use std::collections::BTreeSet;
-use std::fmt::{Debug, Display};
+use std::fmt::Debug;
use std::iter::repeat_n;
use std::ops::{Deref, Range, RangeBounds};
use std::path::Path;
@@ -10,7 +10,6 @@ use std::vec::Vec;
use anyhow::anyhow;
use atools::prelude::*;
-use diff_match_patch_rs::{DiffMatchPatch, Patches};
use dsb::Cell;
use dsb::cell::Style;
use helix_core::Syntax;
@@ -27,29 +26,14 @@ use cursor::{Cursor, Cursors, ceach};
pub mod inlay;
use inlay::{Inlay, Marking};
pub mod semantic_tokens;
-use semantic_tokens::{TokenD, theme};
+use semantic_tokens::TokenD;
+pub mod hist;
+pub mod theme_treesitter;
+use hist::Changes;
use crate::lsp::Void;
use crate::sni::{Snippet, StopP};
-
-theme! {
- "attribute" b"#ffd173",
- "comment" b"#5c6773" Style::ITALIC,
- "constant" b"#DFBFFF",
- "function" b"#FFD173" Style::ITALIC,
- "function.macro" b"#fbc351",
- "variable.builtin" b"#FFAD66",
- "keyword" b"#FFAD66" Style::ITALIC | Style::BOLD,
- "number" b"#dfbfff",
- "operator" b"#F29E74",
- "punctuation" b"#cccac2",
- "string" b"#D5FF80",
- "tag" b"#5CCFE6" Style::ITALIC | Style::BOLD,
- "type" b"#73D0FF" Style::ITALIC | Style::BOLD,
- "variable" b"#cccac2",
- "variable.parameter" b"#DFBFFF",
- "namespace" b"#73d0ff",
-}
+use crate::text::hist::Action;
pub const fn color_(x: &str) -> [u8; 3] {
let Some(x): Option<[u8; 7]> = x.as_bytes().try_into().ok() else {
@@ -83,68 +67,6 @@ macro_rules! col {
($(crate::text::col!($x),)+)
}};
}
-#[derive(Debug)]
-struct E;
-impl Display for E {
- fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- Ok(())
- }
-}
-
-fn from_t<'de, D>(de: D) -> Result<Patches<u8>, D::Error>
-where
- D: serde::Deserializer<'de>,
-{
- let dmp = DiffMatchPatch::new();
- let s: &str = serde::de::Deserialize::deserialize(de)?;
- let p =
- dmp.patch_from_text(s).map_err(|_| serde::de::Error::custom(E))?;
- Ok(p)
-}
-fn to_t<S: serde::Serializer>(
- x: &Patches<u8>,
- s: S,
-) -> Result<S::Ok, S::Error> {
- let dmp = DiffMatchPatch::new();
- s.serialize_str(&dmp.patch_to_text(x))
-}
-
-#[derive(
- Clone, Debug, serde_derive::Deserialize, serde_derive::Serialize,
-)]
-pub struct Diff {
- #[serde(deserialize_with = "from_t", serialize_with = "to_t")]
- pub forth: Patches<u8>,
- #[serde(deserialize_with = "from_t", serialize_with = "to_t")]
- pub back: Patches<u8>,
- pub data: [(Cursors, usize); 2],
-}
-
-impl Display for Diff {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- let d = DiffMatchPatch::new();
- writeln!(f, "{}", d.patch_to_text(&self.back))
- }
-}
-impl Diff {
- pub fn apply(self, t: &mut TextArea, redo: bool) {
- let d = DiffMatchPatch::new();
- // println!("{}", d.patch_to_text(&self.changes.0));
- // causes great internal strife atm
- t.rope = Rope::from_str(
- &d.patch_apply(
- &if redo { self.back } else { self.forth },
- &t.rope.to_string(),
- )
- .unwrap()
- .0,
- );
- let (cu, _vo) = self.data[redo as usize].clone();
- t.cursor = cu;
- t.scroll_to_cursor_centering();
- // t.vo = vo;
- }
-}
pub fn deserialize_from_string<'de, D: serde::de::Deserializer<'de>>(
de: D,
@@ -182,7 +104,8 @@ pub struct TextArea {
#[serde(skip)]
pub tabstops: Option<Snippet>,
pub inlays: BTreeSet<Inlay>,
- pub tokens: Vec<TokenD>, // TODO: fixperf
+ pub tokens: Vec<TokenD>,
+ pub changes: Changes,
}
#[derive(Serialize, Deserialize)]
pub struct CellBuffer {
@@ -416,6 +339,15 @@ impl TextArea {
}
pub fn remove(&mut self, r: Range<usize>) -> Result<(), ropey::Error> {
+ let removed = self
+ .rope
+ .get_slice(r.clone())
+ .ok_or(ropey::Error::CharIndexOutOfBounds(4, 4))?
+ .to_string();
+ self.changes.inner.push(Action::Removed {
+ removed: Some(removed),
+ range: r.clone(),
+ });
self.rope.try_remove(r.clone())?;
let manip = |x| {
// if your region gets removed, what happens to your tabstop? big question.
@@ -453,6 +385,9 @@ impl TextArea {
with: &str,
) -> Result<(), ropey::Error> {
self.rope.try_insert(c, with)?;
+ self.changes
+ .inner
+ .push(Action::Inserted { at: c, insert: with.to_string() });
let manip = |x| {
if x < c { x } else { x + with.chars().count() }
};
@@ -1056,7 +991,7 @@ pub fn is_word(r: char) -> bool {
}
pub static LOADER: LazyLock<Loader> = LazyLock::new(|| {
let x = helix_core::config::default_lang_loader();
- x.set_scopes(NAMES.map(|x| x.to_string()).to_vec());
+ x.set_scopes(theme_treesitter::NAMES.map(|x| x.to_string()).to_vec());
// x.languages().for_each(|(_, x)| {
// x.syntax_config(&LOADER).map(|x| {
@@ -1264,7 +1199,10 @@ pub fn hl(
yield (
(x1, y1),
(x2, y2),
- (STYLES[h.idx()], COLORS[h.idx()]),
+ (
+ theme_treesitter::STYLES[h.idx()],
+ theme_treesitter::COLORS[h.idx()],
+ ),
(text.byte_slice(at..end)),
)
}
diff --git a/src/text/hist.rs b/src/text/hist.rs
new file mode 100644
index 0000000..ed0d571
--- /dev/null
+++ b/src/text/hist.rs
@@ -0,0 +1,240 @@
+use core::ops::Range;
+use std::fmt::{Debug, Display};
+use std::mem::take;
+use std::ops::Deref;
+use std::time::Instant;
+
+use Default::default;
+
+use super::cursor::Cursors;
+use crate::text::TextArea;
+
+#[derive(
+ Clone,
+ PartialEq,
+ Eq,
+ serde_derive::Serialize,
+ serde_derive::Deserialize,
+)]
+pub enum Action {
+ Inserted { at: usize, insert: String },
+ Removed { range: Range<usize>, removed: Option<String> },
+}
+impl Debug for Action {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Self::Inserted { at, insert } =>
+ write!(f, "A(@{at} + {insert})"),
+ Self::Removed { range, removed: None } =>
+ write!(f, "A(@{range:?} -)"),
+ Self::Removed { range, removed: Some(rem) } =>
+ write!(f, "A(@{range:?} - {rem})"),
+ }
+ }
+}
+
+#[derive(
+ Default,
+ Clone,
+ Debug,
+ PartialEq,
+ Eq,
+ serde_derive::Serialize,
+ serde_derive::Deserialize,
+)]
+pub struct Changes {
+ pub inner: Vec<Action>,
+}
+
+impl Display for Changes {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{:?}", self.inner)
+ }
+}
+impl Display for Diff {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{:?}", self.0)
+ }
+}
+impl Deref for Changes {
+ type Target = [Action];
+
+ fn deref(&self) -> &Self::Target {
+ &self.inner
+ }
+}
+impl Deref for Diff {
+ type Target = [Action];
+
+ fn deref(&self) -> &Self::Target {
+ &*self.0
+ }
+}
+impl Changes {
+ pub fn rev(&mut self) {
+ self.inner.reverse();
+ self.inner.iter_mut().for_each(|action| {
+ *action = match action {
+ Action::Inserted { at, insert } => {
+ let end = *at + insert.len();
+ Action::Removed { range: *at..end, removed: None }
+ }
+ Action::Removed { range, removed: Some(insert) } =>
+ Action::Inserted {
+ at: range.start,
+ insert: insert.clone(),
+ },
+ _ => unreachable!(),
+ }
+ });
+ }
+ pub fn apply(
+ mut self,
+ t: &mut TextArea,
+ redo: bool,
+ ) -> ropey::Result<()> {
+ if !redo {
+ self.rev();
+ }
+ let pc = take(&mut t.changes);
+ for c in self.inner {
+ match c {
+ Action::Inserted { at, insert } =>
+ t.insert_at(at, &insert),
+ Action::Removed { range, .. } => t.remove(range),
+ }?;
+ }
+ t.changes = pc;
+ Ok(())
+ }
+}
+#[derive(
+ Clone,
+ PartialEq,
+ Debug,
+ serde_derive::Serialize,
+ serde_derive::Deserialize,
+)]
+pub struct Diff(pub Changes, pub [Cursors; 2]);
+impl Diff {
+ pub fn apply(
+ mut self,
+ t: &mut TextArea,
+ redo: bool,
+ ) -> Result<(), ropey::Error> {
+ self.0.apply(t, redo)?;
+ t.cursor = take(&mut self.1[redo as usize]);
+ t.scroll_to_cursor_centering();
+ Ok(())
+ }
+}
+
+#[derive(serde::Deserialize, serde::Serialize, Debug)]
+pub struct Hist {
+ pub history: Vec<Diff>,
+ pub redo_history: Vec<Diff>,
+ pub last: Changes,
+ pub lc: super::Cursors,
+ #[serde(
+ skip_deserializing,
+ serialize_with = "crate::serialize_debug",
+ default = "Instant::now"
+ )]
+ pub last_edit: std::time::Instant,
+ pub changed: bool,
+}
+impl Default for Hist {
+ fn default() -> Self {
+ Self {
+ history: vec![],
+ redo_history: vec![],
+ last: default(),
+ lc: default(),
+ last_edit: Instant::now(),
+ changed: false,
+ }
+ }
+}
+#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
+pub struct ClickHistory {
+ pub his: Vec<(usize, usize)>,
+ pub red: Vec<(usize, usize)>,
+}
+
+impl ClickHistory {
+ pub fn push(&mut self, x: (usize, usize)) {
+ if self.his.last() != Some(&x) {
+ self.his.push(x);
+ self.red.clear();
+ }
+ }
+ pub fn back(&mut self) -> Option<(usize, usize)> {
+ self.his.pop().inspect(|x| {
+ self.red.push(*x);
+ })
+ }
+ pub fn forth(&mut self) -> Option<(usize, usize)> {
+ self.red.pop().inspect(|x| {
+ self.his.push(*x);
+ })
+ }
+}
+impl Hist {
+ pub fn push(&mut self, x: &mut TextArea) {
+ // assert_eq!(x.changes[..self.last.len()], *self.last);
+ // x.changes.inner.drain(..self.last.len());
+ let c = take(&mut x.changes);
+ self.history.push(Diff(c, [take(&mut self.lc), x.cursor.clone()]));
+ self.lc = x.cursor.clone();
+ println!("push {}", self.history.last().unwrap());
+ self.redo_history.clear();
+ take(&mut self.last);
+ self.last_edit = Instant::now();
+ self.changed = false;
+ }
+ fn undo_(&mut self) -> Option<Diff> {
+ self.history.pop().inspect(|x| self.redo_history.push(x.clone()))
+ }
+ fn redo_(&mut self) -> Option<Diff> {
+ self.redo_history.pop().inspect(|x| self.history.push(x.clone()))
+ }
+ pub fn undo(&mut self, t: &mut TextArea) -> ropey::Result<Option<()>> {
+ self.push_if_changed(t);
+ self.undo_()
+ .map(|x| {
+ let r = x.apply(t, false);
+ self.lc = t.cursor.clone();
+ self.last = t.changes.clone();
+ r
+ })
+ .transpose()
+ }
+ pub fn redo(&mut self, t: &mut TextArea) -> ropey::Result<Option<()>> {
+ self.redo_()
+ .map(|x| {
+ let r = x.apply(t, true);
+ self.lc = t.cursor.clone();
+ self.last = t.changes.clone();
+ r
+ })
+ .transpose()
+ }
+ pub fn push_if_changed(&mut self, x: &mut TextArea) {
+ if self.changed || self.last != x.changes {
+ self.push(x);
+ }
+ }
+ pub fn test_push(&mut self, x: &mut TextArea) {
+ if self.last_edit.elapsed().as_millis() > 500 {
+ self.push_if_changed(x);
+ }
+ }
+ pub fn record(&mut self, new: &TextArea) -> bool {
+ (new.changes != self.last)
+ .then(|| {
+ self.last_edit = Instant::now();
+ self.changed = true;
+ })
+ .is_some()
+ }
+}
diff --git a/src/text/semantic_tokens.rs b/src/text/semantic_tokens.rs
index fa964e6..1fa0e70 100644
--- a/src/text/semantic_tokens.rs
+++ b/src/text/semantic_tokens.rs
@@ -3,99 +3,10 @@ use log::error;
use lsp_types::{SemanticToken, SemanticTokensLegend};
use serde_derive::{Deserialize, Serialize};
-macro_rules! modified {
- ($count:literal $($x:literal . $mod:literal $color:literal $($style:expr)?,)+ $(,)?) => {
- pub const MODIFIED: [(&str, &str); $count] = [
- $(($x, $mod),)+
- ];
- pub const MCOLORS: [[u8;3]; MODIFIED.len()] = car::map!([$($color),+], |x| color(x));
- pub const MSTYLE: [u8; MODIFIED.len()] = [$(($($style, )? 0, ).0 ,)+];
- }
-}
-use super::color;
-macro_rules! theme {
- ($($x:literal $color:literal $($style:expr)?),+ $(,)?) => {
- #[rustfmt::skip]
- pub const NAMES: [&str; [$($x),+].len()] = [$($x),+];
- #[rustfmt::skip]
- pub const COLORS: [[u8; 3]; NAMES.len()] = car::map!([$($color),+], |x| color(x));
- pub const STYLES: [u8; NAMES.len()] = [$(
- ($($style, )? 0, ).0
- ),+];
- };
-}
-pub(crate) use theme;
-theme! {
-"constructor" b"#FFAD66",
-"field" b"#cccac2",
+pub mod theme;
+pub(crate) use theme::theme;
+use theme::{COLORS, MCOLORS, MODIFIED, MSTYLE, NAMES, STYLES};
-"comment" b"#5c6773" Style::ITALIC,
-// "decorator" b"#cccac2",
-"function" b"#FFD173" Style::ITALIC,
-"interface" b"#5CCFE6",
-"keyword" b"#FFAD66" Style::ITALIC | Style::BOLD,
-"macro" b"#fbc351" Style::BOLD,
-"method" b"#FFD173" Style::ITALIC,
-// "namespace" b"#cccac2",
-"number" b"#dfbfff",
-"operator" b"#F29E74",
-// "property" b"#cccac2",
-"string" b"#D5FF80",
-// "struct" b"#cccac2",
-// "typeParameter" b"#cccac2",
-"class" b"#73b9ff",
-"enumMember" b"#73b9ff",
-"enum" b"#73b9ff" Style::ITALIC | Style::BOLD,
-"builtinType" b"#73d0ff" Style::ITALIC,
-// "type" b"#73d0ff" Style::ITALIC | Style::BOLD,
-"typeAlias" b"#69caed" Style::ITALIC | Style::BOLD,
-"struct" b"#73d0ff" Style::ITALIC | Style::BOLD,
-"variable" b"#cccac2",
-// "angle" b"#cccac2",
-// "arithmetic" b"#cccac2",
-// "attributeBracket" b"#cccac2",
-"parameter" b"#DFBFFF",
-"namespace" b"#73d0ff",
-// "attributeBracket" b"#cccac2",
-// "attribute" b"#cccac2",
-// "bitwise" b"#cccac2",
-// "boolean" b"#cccac2",
-// "brace" b"#cccac2",
-// "bracket" b"#cccac2",
-// "builtinAttribute" b"#cccac2",
-// "character" b"#cccac2",
-// "colon" b"#cccac2",
-// "comma" b"#cccac2",
-// "comparison" b"#cccac2",
-// "constParameter" b"#cccac2",
-"const" b"#DFBFFF",
-// "deriveHelper" b"#cccac2",
-// "derive" b"#cccac2",
-// "dot" b"#cccac2",
-// "escapeSequence" b"#cccac2",
-// "formatSpecifier" b"#cccac2",
-// "generic" b"#cccac2",
-// "invalidEscapeSequence" b"#cccac2",
-// "label" b"#cccac2",
-// "lifetime" b"#cccac2",
-// "logical" b"#cccac2",
-"macroBang" b"#f28f74",
-// "parenthesis" b"#cccac2",
-// "procMacro" b"#cccac2",
-// "punctuation" b"#cccac2",
-"selfKeyword" b"#FFAD66" Style::ITALIC | Style::BOLD,
-"selfTypeKeyword" b"#FFAD66" Style::ITALIC | Style::BOLD,
-// "semicolon" b"#cccac2",
-// "static" b"#cccac2",
-// "toolModule" b"#cccac2",
-// "union" b"#cccac2",
-// "unresolvedReference" b"#cccac2",
-}
-
-modified! { 2
- "function" . "unsafe" b"#F28779",
- "variable" . "mutable" b"#e6dab6",
-}
use crate::text::TextArea;
#[derive(
diff --git a/src/text/semantic_tokens/theme.rs b/src/text/semantic_tokens/theme.rs
new file mode 100644
index 0000000..6be578c
--- /dev/null
+++ b/src/text/semantic_tokens/theme.rs
@@ -0,0 +1,95 @@
+use dsb::cell::Style;
+
+macro_rules! theme {
+ ($($x:literal $color:literal $($style:expr)?),+ $(,)?) => {
+ #[rustfmt::skip]
+ pub const NAMES: [&str; [$($x),+].len()] = [$($x),+];
+ #[rustfmt::skip]
+ pub const COLORS: [[u8; 3]; NAMES.len()] = car::map!([$($color),+], |x| crate::text::color(x));
+ pub const STYLES: [u8; NAMES.len()] = [$(
+ ($($style, )? 0, ).0
+ ),+];
+ };
+}
+
+macro_rules! modified {
+ ($count:literal $($x:literal . $mod:literal $color:literal $($style:expr)?,)+ $(,)?) => {
+ pub const MODIFIED: [(&str, &str); $count] = [
+ $(($x, $mod),)+
+ ];
+ pub const MCOLORS: [[u8;3]; MODIFIED.len()] = car::map!([$($color),+], |x| crate::text::color(x));
+ pub const MSTYLE: [u8; MODIFIED.len()] = [$(($($style, )? 0, ).0 ,)+];
+ }
+}
+pub(crate) use theme;
+theme! {
+"constructor" b"#FFAD66",
+"field" b"#cccac2",
+
+"comment" b"#5c6773" Style::ITALIC,
+// "decorator" b"#cccac2",
+"function" b"#FFD173" Style::ITALIC,
+"interface" b"#5CCFE6",
+"keyword" b"#FFAD66" Style::ITALIC | Style::BOLD,
+"macro" b"#fbc351" Style::BOLD,
+"method" b"#FFD173" Style::ITALIC,
+// "namespace" b"#cccac2",
+"number" b"#dfbfff",
+"operator" b"#F29E74",
+// "property" b"#cccac2",
+"string" b"#D5FF80",
+// "struct" b"#cccac2",
+// "typeParameter" b"#cccac2",
+"class" b"#73b9ff",
+"enumMember" b"#73b9ff",
+"enum" b"#73b9ff" Style::ITALIC | Style::BOLD,
+"builtinType" b"#73d0ff" Style::ITALIC,
+// "type" b"#73d0ff" Style::ITALIC | Style::BOLD,
+"typeAlias" b"#69caed" Style::ITALIC | Style::BOLD,
+"struct" b"#73d0ff" Style::ITALIC | Style::BOLD,
+"variable" b"#cccac2",
+// "angle" b"#cccac2",
+// "arithmetic" b"#cccac2",
+// "attributeBracket" b"#cccac2",
+"parameter" b"#DFBFFF",
+"namespace" b"#73d0ff",
+// "attributeBracket" b"#cccac2",
+// "attribute" b"#cccac2",
+// "bitwise" b"#cccac2",
+// "boolean" b"#cccac2",
+// "brace" b"#cccac2",
+// "bracket" b"#cccac2",
+// "builtinAttribute" b"#cccac2",
+// "character" b"#cccac2",
+// "colon" b"#cccac2",
+// "comma" b"#cccac2",
+// "comparison" b"#cccac2",
+// "constParameter" b"#cccac2",
+"const" b"#DFBFFF",
+// "deriveHelper" b"#cccac2",
+// "derive" b"#cccac2",
+// "dot" b"#cccac2",
+// "escapeSequence" b"#cccac2",
+// "formatSpecifier" b"#cccac2",
+// "generic" b"#cccac2",
+// "invalidEscapeSequence" b"#cccac2",
+// "label" b"#cccac2",
+// "lifetime" b"#cccac2",
+// "logical" b"#cccac2",
+"macroBang" b"#f28f74",
+// "parenthesis" b"#cccac2",
+// "procMacro" b"#cccac2",
+// "punctuation" b"#cccac2",
+"selfKeyword" b"#FFAD66" Style::ITALIC | Style::BOLD,
+"selfTypeKeyword" b"#FFAD66" Style::ITALIC | Style::BOLD,
+// "semicolon" b"#cccac2",
+// "static" b"#cccac2",
+// "toolModule" b"#cccac2",
+// "union" b"#cccac2",
+// "unresolvedReference" b"#cccac2",
+}
+
+modified! { 2
+ "function" . "unsafe" b"#F28779",
+ "variable" . "mutable" b"#e6dab6",
+}
diff --git a/src/text/theme_treesitter.rs b/src/text/theme_treesitter.rs
new file mode 100644
index 0000000..89813ad
--- /dev/null
+++ b/src/text/theme_treesitter.rs
@@ -0,0 +1,20 @@
+use dsb::cell::Style;
+
+super::semantic_tokens::theme! {
+ "attribute" b"#ffd173",
+ "comment" b"#5c6773" Style::ITALIC,
+ "constant" b"#DFBFFF",
+ "function" b"#FFD173" Style::ITALIC,
+ "function.macro" b"#fbc351",
+ "variable.builtin" b"#FFAD66",
+ "keyword" b"#FFAD66" Style::ITALIC | Style::BOLD,
+ "number" b"#dfbfff",
+ "operator" b"#F29E74",
+ "punctuation" b"#cccac2",
+ "string" b"#D5FF80",
+ "tag" b"#5CCFE6" Style::ITALIC | Style::BOLD,
+ "type" b"#73D0FF" Style::ITALIC | Style::BOLD,
+ "variable" b"#cccac2",
+ "variable.parameter" b"#DFBFFF",
+ "namespace" b"#73d0ff",
+}