A simple CPU rendered GUI IDE experience.
| -rw-r--r-- | Cargo.toml | 3 | ||||
| -rw-r--r-- | src/commands.rs | 11 | ||||
| -rw-r--r-- | src/edi.rs | 2 | ||||
| -rw-r--r-- | src/edi/input_handlers/keyboard.rs | 5 | ||||
| -rw-r--r-- | src/edi/lsp_impl.rs | 16 | ||||
| -rw-r--r-- | src/gotolist.rs | 39 | ||||
| -rw-r--r-- | src/rnd.rs | 2 | ||||
| -rw-r--r-- | src/sni.rs | 23 | ||||
| -rw-r--r-- | src/sym.rs | 15 | ||||
| -rw-r--r-- | src/text.rs | 25 | ||||
| -rw-r--r-- | src/text/bookmark.rs | 10 | ||||
| -rw-r--r-- | src/text/cursor.rs | 9 | ||||
| -rw-r--r-- | src/text/manipulations.rs | 78 | ||||
| -rw-r--r-- | src/text/semantic_tokens.rs | 7 |
14 files changed, 173 insertions, 72 deletions
@@ -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 { @@ -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!( @@ -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 _)) { @@ -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; @@ -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); |