A simple CPU rendered GUI IDE experience.
-rw-r--r--Cargo.toml3
-rw-r--r--src/commands.rs11
-rw-r--r--src/edi.rs2
-rw-r--r--src/edi/input_handlers/keyboard.rs5
-rw-r--r--src/edi/lsp_impl.rs16
-rw-r--r--src/gotolist.rs39
-rw-r--r--src/rnd.rs2
-rw-r--r--src/sni.rs23
-rw-r--r--src/sym.rs15
-rw-r--r--src/text.rs25
-rw-r--r--src/text/bookmark.rs10
-rw-r--r--src/text/cursor.rs9
-rw-r--r--src/text/manipulations.rs78
-rw-r--r--src/text/semantic_tokens.rs7
14 files changed, 173 insertions, 72 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 16e34f3..90cb9c6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -70,7 +70,8 @@ kitty-rc = { version = "0.4.2", git = "https://github.com/bend-n/kitty-rc-rs" }
smol_str = "0.3.6"
futures = "0.3.32"
rootcause = "0.12.1"
-ttools = "0.1.0"
+ttools = { git = "https://git.bendn.org/ttools" }
+# ttools = { path = "../ttools/" }
bind = { package = "ftools", git = "https://git.bendn.org/bind" }
[profile.dev.package]
rust-analyzer.opt-level = 3
diff --git a/src/commands.rs b/src/commands.rs
index 64a382c..edcf6d6 100644
--- a/src/commands.rs
+++ b/src/commands.rs
@@ -252,9 +252,14 @@ impl Editor {
.flat_map(|(f, x)| {
x.text.bookmarks.iter().zip(repeat(f))
})
- .map(|(b, path)| GoTo {
- path: path.clone().into(),
- at: At::P(b.position),
+ .map(|(b, path)| {
+ (
+ GoTo {
+ path: path.clone().into(),
+ at: At::P(b.position),
+ },
+ Some(b.text.clone()),
+ )
})
.collect::<Vec<_>>();
self.state = crate::edi::st::State::GoToL(GoToList {
diff --git a/src/edi.rs b/src/edi.rs
index d025911..5c54c29 100644
--- a/src/edi.rs
+++ b/src/edi.rs
@@ -8,12 +8,10 @@ use std::time::SystemTime;
use Default::default;
use bind::Bind;
use lsp_server::Connection;
-use lsp_types::request::*;
use lsp_types::*;
use regex::Regex;
use rootcause::report;
use ropey::Rope;
-use serde_derive::{Deserialize, Serialize};
use tokio::sync::oneshot::Sender;
use winit::keyboard::NamedKey;
use winit::window::Window;
diff --git a/src/edi/input_handlers/keyboard.rs b/src/edi/input_handlers/keyboard.rs
index eb624ac..351e323 100644
--- a/src/edi/input_handlers/keyboard.rs
+++ b/src/edi/input_handlers/keyboard.rs
@@ -864,7 +864,7 @@ impl Editor {
}
}
Some(Do::GTLSelect(x)) =>
- if let Some(Ok(g)) = x.sel()
+ if let Some(Ok((g, _))) = x.sel()
&& let Err(e) = self.go(g, window.clone())
{
eprintln!("go-to-list select fail: {e}");
@@ -873,7 +873,8 @@ impl Editor {
let State::GoToL(x) = &mut self.state else {
unreachable!()
};
- if let Some(Ok(GoTo { path: p, at: At::R(r) })) = x.sel()
+ if let Some(Ok((GoTo { path: p, at: At::R(r) }, _))) =
+ x.sel()
&& Some(&*p) == self.origin.as_deref()
{
// let x = self.text.l_range(r).unwrap();
diff --git a/src/edi/lsp_impl.rs b/src/edi/lsp_impl.rs
index c28670e..f7e617b 100644
--- a/src/edi/lsp_impl.rs
+++ b/src/edi/lsp_impl.rs
@@ -1,3 +1,5 @@
+use std::iter::repeat;
+
use lsp_server::Request as LRq;
use lsp_types::request::*;
use lsp_types::*;
@@ -146,7 +148,11 @@ impl crate::edi::Editor {
Some(crate::gotolist::O::References(y)) => {
y.poll(|x, _| {
x.ok().flatten().map(|x| {
- z.data.0 = x.iter().map(GoTo::from).collect()
+ z.data.0 = x
+ .iter()
+ .map(GoTo::from)
+ .zip(repeat(None))
+ .collect()
})
});
}
@@ -157,14 +163,14 @@ impl crate::edi::Editor {
z.data.0 = match x {
GotoDefinitionResponse::Scalar(
location,
- ) => vec![GoTo::from(
+ ) => vec![(GoTo::from(
location,
- )],
+ ),None)],
GotoDefinitionResponse::Array(
locations,
) => locations
.into_iter()
- .map(GoTo::from)
+ .map(GoTo::from).zip(repeat(None))
.collect(),
GotoDefinitionResponse::Link(
location_links,
@@ -177,7 +183,7 @@ impl crate::edi::Editor {
range: target_range,
}
)
- })
+ }).zip(repeat(None))
.collect(),
};
});
diff --git a/src/gotolist.rs b/src/gotolist.rs
index 3fee279..450e486 100644
--- a/src/gotolist.rs
+++ b/src/gotolist.rs
@@ -10,6 +10,7 @@ use crate::FG;
use crate::lsp::RqS;
use crate::menu::Key;
use crate::menu::generic::{GenericMenu, MenuData};
+use crate::rnd::simplify_path;
use crate::text::col;
pub type GoToList = GenericMenu<GTL>;
pub enum GTL {}
@@ -19,28 +20,34 @@ pub enum O {
References(RqS<(), lsp_types::request::References>),
Bmk,
}
-impl<'a> Key<'a> for GoTo<'a> {
+impl<'a> Key<'a> for (GoTo<'a>, Option<&'a str>) {
fn key(&self) -> impl Into<std::borrow::Cow<'a, str>> {
- self.path.display().to_string()
+ self.0.path.display().to_string()
// self.display().to_string()
}
}
impl MenuData for GTL {
- type Data = (Vec<GoTo<'static>>, Option<O>);
+ type Data = (Vec<(GoTo<'static>, Option<String>)>, Option<O>);
- type Element<'a> = GoTo<'a>;
+ type Element<'a> = (GoTo<'a>, Option<&'a str>);
type E = !;
fn gn<'a>(
x: &'a Self::Data,
) -> impl Iterator<Item = Self::Element<'a>> {
- x.0.iter().map(GoTo::asref)
+ use ttools::*;
+ x.0.iter()
+ // .map(|x| {
+ // x
+ // })
+ .map_at::<0>(GoTo::asref)
+ .map_at::<1>(Option::as_deref)
}
fn r(
_data: &'_ Self::Data,
- elem: Self::Element<'_>,
+ (elem, desc): Self::Element<'_>,
workspace: &std::path::Path,
columns: usize,
selected: bool,
@@ -57,12 +64,20 @@ impl MenuData for GTL {
into.iter_mut()
// .skip(1)
.zip(
- elem.path
- .strip_prefix(workspace)
- .unwrap_or(&elem.path)
- .display()
- .to_string()
- .chars()
+ desc.map(str::chars)
+ .into_iter()
+ .flatten()
+ .chain(
+ simplify_path(
+ &elem
+ .path
+ .strip_prefix(workspace)
+ .unwrap_or(&elem.path)
+ .display()
+ .to_string(),
+ )
+ .chars(),
+ )
.chain(
match elem.at {
At::R(r) => format!(
diff --git a/src/rnd.rs b/src/rnd.rs
index 8970d81..7c78ddd 100644
--- a/src/rnd.rs
+++ b/src/rnd.rs
@@ -104,7 +104,7 @@ pub fn render(
(1, 0),
|_text, mut f, y| {
if let State::GoToL(menu) = &ed.state
- && let Some(Ok( GoTo{ at: At::R(r),path})) = menu.sel()
+ && let Some(Ok(( GoTo{ at: At::R(r),path}, _))) = menu.sel()
&& Some(&*path) == ed.origin.as_deref()
{
if (r.start.line..=r.end.line).contains(&(y as _)) {
diff --git a/src/sni.rs b/src/sni.rs
index 10fe392..319ccaa 100644
--- a/src/sni.rs
+++ b/src/sni.rs
@@ -24,6 +24,7 @@ pub enum StopP {
Just(usize),
Range(Range<usize>),
}
+
impl StopP {
pub fn r(&self) -> Range<usize> {
match self {
@@ -31,15 +32,6 @@ impl StopP {
Self::Range(range) => range.clone(),
}
}
- pub fn manipulate(&mut self, mut f: impl FnMut(usize) -> usize) {
- match self {
- Self::Just(x) => *x = f(*x),
- Self::Range(range) => {
- range.start = f(range.start);
- range.end = f(range.end);
- }
- }
- }
}
impl Snippet {
@@ -55,15 +47,18 @@ impl Snippet {
for value in value.into_iter() {
Self::apply(value, &mut stops, &mut cursor, &mut o);
}
+ use ttools::*;
stops.sort_by_key(|x| x.0);
- stops.iter_mut().for_each(|x| x.1.manipulate(|x| x + at));
+ stops.iter_mut().for_all::<1>(|x| {
+ *x = x
+ .clone()
+ .manipulate(|x| crate::text::Manip::Moved(x + at))
+ .unwrap()
+ });
let last = stops.try_remove(0);
Ok((Snippet { last: last.map(|x| x.1), stops, index: 0 }, o))
}
- pub fn manipulate(&mut self, f: impl FnMut(usize) -> usize + Clone) {
- self.stops.iter_mut().for_each(|x| x.1.manipulate(f.clone()));
- self.last.as_mut().map(|x| x.manipulate(f));
- }
+
pub fn next(&mut self) -> Option<StopP> {
self.stops.get(self.index).map(|x| {
self.index += 1;
diff --git a/src/sym.rs b/src/sym.rs
index 5d472e9..74d7a6c 100644
--- a/src/sym.rs
+++ b/src/sym.rs
@@ -13,6 +13,7 @@ use crate::gotolist::At;
pub use crate::gotolist::GoTo;
use crate::menu::generic::{GenericMenu, MenuData};
use crate::menu::{Key, charc};
+use crate::rnd::simplify_path;
use crate::text::{Bookmarks, col, color_, set_a};
pub enum Symb {}
impl MenuData for Symb {
@@ -239,13 +240,15 @@ fn r<'a>(
- 3;
let loc = x.at.path;
let locs = if sty == SymbolsType::Workspace {
- loc.as_ref()
- .strip_prefix(workspace)
- .unwrap_or(&*loc)
- .to_str()
- .unwrap_or("")
+ simplify_path(
+ loc.as_ref()
+ .strip_prefix(workspace)
+ .unwrap_or(&*loc)
+ .to_str()
+ .unwrap_or(""),
+ )
} else {
- ""
+ "".into()
};
let loc = locs.chars().rev().collect::<Vec<_>>().into_iter();
let q = if left < charc(&locs) as i32 {
diff --git a/src/text.rs b/src/text.rs
index 786d850..e481d4f 100644
--- a/src/text.rs
+++ b/src/text.rs
@@ -19,6 +19,7 @@ use lsp_types::{
DocumentSymbol, Location, Position, SemanticTokensLegend,
SnippetTextEdit, TextEdit,
};
+pub use manipulations::Manip;
use rootcause::option_ext::OptionExt;
use rootcause::prelude::{IteratorExt, ResultExt};
use rootcause::report;
@@ -36,9 +37,11 @@ pub mod theme_treesitter;
use hist::Changes;
mod bookmark;
pub use bookmark::*;
+mod manipulations;
use crate::sni::{Snippet, StopP};
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 {
panic!()
@@ -393,17 +396,25 @@ impl TextArea {
});
self.rope.try_remove(r.clone())?;
let manip = |x| {
- // if your region gets removed, what happens to your tabstop? big question.
if r.contains(&x) {
- // for now, simply move it.
- r.start
+ Manip::Removed(r.start)
} else {
- if x >= r.end { x - r.len() } else { x }
+ if x >= r.end {
+ Manip::Moved(x - r.len())
+ } else {
+ Manip::Unmoved(x)
+ }
}
};
self.tabstops.as_mut().map(|x| x.manipulate(manip));
self.cursor.manipulate(manip);
self.bookmarks.manipulate(manip);
+ self.inlays
+ .extract_if(
+ Marking::idx(r.start as _)..Marking::idx(r.end as _),
+ |_| true,
+ )
+ .for_each(drop);
for pos in self
.inlays
.range(Marking::idx(r.end as _)..)
@@ -433,7 +444,11 @@ impl TextArea {
.inner
.push(Action::Inserted { at: c, insert: with.to_string() });
let manip = |x| {
- if x < c { x } else { x + with.chars().count() }
+ if x < c {
+ Manip::Unmoved(x)
+ } else {
+ Manip::Moved(x + with.chars().count())
+ }
};
self.tabstops.as_mut().map(|x| x.manipulate(manip));
self.cursor.manipulate(manip);
diff --git a/src/text/bookmark.rs b/src/text/bookmark.rs
index 4080d21..0efd8b0 100644
--- a/src/text/bookmark.rs
+++ b/src/text/bookmark.rs
@@ -9,7 +9,7 @@ pub struct Bookmark {
}
#[derive(Clone, Serialize, Deserialize, Default, Debug)]
-pub struct Bookmarks(Vec<Bookmark>);
+pub struct Bookmarks(pub Vec<Bookmark>);
impl DerefMut for Bookmarks {
fn deref_mut(&mut self) -> &mut Self::Target {
@@ -24,11 +24,3 @@ impl Deref for Bookmarks {
&self.0
}
}
-impl Bookmarks {
- pub fn manipulate(&mut self, mut f: impl FnMut(usize) -> usize) {
- for lem in &mut self.0 {
- lem.position = f(lem.position);
- }
- }
- // pub fn to_gtl_d(&self) -> Vec<(PathBuf, Range)> {}
-}
diff --git a/src/text/cursor.rs b/src/text/cursor.rs
index 07fe471..f8e4cc9 100644
--- a/src/text/cursor.rs
+++ b/src/text/cursor.rs
@@ -440,15 +440,6 @@ impl Cursors {
pub fn each_ref(&self, f: impl FnMut(Cursor)) {
self.inner.iter().copied().rev().for_each(f);
}
- pub fn manipulate(&mut self, mut f: impl FnMut(usize) -> usize) {
- self.each(|lem| {
- lem.position = f(lem.position);
- if let Some(sel) = &mut lem.sel {
- sel.start = f(sel.start);
- sel.end = f(sel.end);
- }
- });
- }
pub fn left(&mut self, r: &Rope) {
self.each(|cursor| cursor.left(r));
self.coalesce();
diff --git a/src/text/manipulations.rs b/src/text/manipulations.rs
new file mode 100644
index 0000000..c1be175
--- /dev/null
+++ b/src/text/manipulations.rs
@@ -0,0 +1,78 @@
+use std::mem::take;
+
+use crate::sni::{Snippet, StopP};
+use crate::text::cursor::Cursors;
+#[derive(Clone, Debug)]
+pub enum Manip {
+ Removed(usize /* usize */),
+ Moved(usize),
+ Unmoved(usize),
+}
+use Manip::*;
+use ttools::IteratorOfTuplesWithF;
+impl Manip {
+ pub fn kept(self) -> Option<usize> {
+ match self {
+ Self::Removed(..) => None,
+ Self::Moved(x) => Some(x),
+ Self::Unmoved(x) => Some(x),
+ }
+ }
+ pub fn unwrap(self) -> usize {
+ let (Removed(x) | Moved(x) | Unmoved(x)) = self;
+ x
+ }
+}
+macro_rules! manipulator {
+ () => {impl FnMut(usize) -> Manip + Clone};
+}
+impl Snippet {
+ pub fn manipulate(&mut self, f: manipulator!()) {
+ self.stops = std::mem::take(&mut self.stops)
+ .into_iter()
+ .filter_map_at::<1>(|x| x.manipulate(f.clone()))
+ .collect();
+ self.last = self.last.take().and_then(|x| x.manipulate(f));
+ }
+}
+
+impl StopP {
+ pub fn manipulate(self, mut f: manipulator!()) -> Option<Self> {
+ match self {
+ Self::Just(x) => f(x).kept().map(Self::Just),
+ Self::Range(range) => match (f(range.start), f(range.end)) {
+ (Removed(..), Removed(..)) => None,
+ (
+ Moved(x) | Unmoved(x) | Removed(x),
+ Moved(y) | Unmoved(y) | Removed(y),
+ ) => Some(Self::Range(x..y)),
+ },
+ }
+ }
+}
+impl Cursors {
+ pub fn manipulate(&mut self, mut f: manipulator!()) {
+ self.each(|lem| {
+ lem.position = f(lem.position).unwrap();
+ if let Some(sel) = &mut lem.sel {
+ sel.start = f(sel.start).unwrap();
+ sel.end = f(sel.end).unwrap();
+ }
+ });
+ }
+}
+
+impl super::Bookmarks {
+ pub fn manipulate(&mut self, mut f: manipulator!()) {
+ self.0 = take(&mut self.0)
+ .into_iter()
+ .filter_map(|mut y| {
+ f(y.position).kept().map(|x| {
+ y.position = x;
+ y
+ })
+ })
+ .collect();
+ }
+ // pub fn to_gtl_d(&self) -> Vec<(PathBuf, Range)> {}
+}
diff --git a/src/text/semantic_tokens.rs b/src/text/semantic_tokens.rs
index 1fa0e70..96eeb01 100644
--- a/src/text/semantic_tokens.rs
+++ b/src/text/semantic_tokens.rs
@@ -8,6 +8,7 @@ pub(crate) use theme::theme;
use theme::{COLORS, MCOLORS, MODIFIED, MSTYLE, NAMES, STYLES};
use crate::text::TextArea;
+use crate::text::manipulations::Manip;
#[derive(
Copy, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize,
@@ -18,9 +19,9 @@ pub struct TokenD {
pub modifiers: u32,
}
impl TokenD {
- pub fn manip(&mut self, mut f: impl FnMut(usize) -> usize) {
- self.range.0 = f(self.range.0 as _) as _;
- self.range.1 = f(self.range.1 as _) as _;
+ pub fn manip(&mut self, mut f: impl FnMut(usize) -> Manip) {
+ self.range.0 = f(self.range.0 as _).unwrap() as _;
+ self.range.1 = f(self.range.1 as _).unwrap() as _;
}
pub fn style(self, leg: &SemanticTokensLegend) -> Style {
let mut sty = Style::new(crate::FG, crate::BG);