A simple CPU rendered GUI IDE experience.
goto bookmarks
bendn 4 weeks ago
parent 6231c7e · commit 3e28328
-rw-r--r--Cargo.toml2
-rw-r--r--src/commands.rs26
-rw-r--r--src/edi.rs112
-rw-r--r--src/git.rs2
-rw-r--r--src/gotolist.rs92
-rw-r--r--src/main.rs2
-rw-r--r--src/rnd.rs11
-rw-r--r--src/sym.rs71
-rw-r--r--src/text/bookmark.rs1
9 files changed, 196 insertions, 123 deletions
diff --git a/Cargo.toml b/Cargo.toml
index c977688..f94dd64 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -70,6 +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"
+bind = { version = "0.1.0", path = "../bind" }
[profile.dev.package]
rust-analyzer.opt-level = 3
fimg.opt-level = 3
diff --git a/src/commands.rs b/src/commands.rs
index a0b28f8..d32f3c9 100644
--- a/src/commands.rs
+++ b/src/commands.rs
@@ -13,12 +13,12 @@ use rust_analyzer::lsp::ext::*;
use crate::FG;
use crate::edi::{Editor, lsp_m};
+use crate::gotolist::{At, GoToList};
use crate::lsp::{PathURI, Rq};
use crate::menu::charc;
use crate::menu::generic::{CorA, GenericMenu, MenuData};
-use crate::text::{
- Bookmark, RopeExt, SortTedits, TextArea, col, color_,
-};
+use crate::sym::GoTo;
+use crate::text::{Bookmark, RopeExt, SortTedits, TextArea, col, color_};
macro_rules! repl {
($x:ty, $($with:tt)+) => {
@@ -117,6 +117,8 @@ commands!(
| DefineBookmark(String): "bookmark",
/// 󰃆 Remove bookmark
| RemoveBookmark: "remove-bookmark",
+ /// Global bookmark menu
+ | Bookmarks: "goto-bookmarks",
);
pub enum Cmds {}
@@ -240,6 +242,24 @@ impl Editor {
.rev()
.for_each(|x| drop(self.text.bookmarks.remove(x)));
}
+ Cmd::Bookmarks => {
+ let src = self
+ .files
+ .iter()
+ .chain(self.origin.as_ref().zip(Some(&*self)))
+ .flat_map(|(f, x)| {
+ x.text.bookmarks.iter().zip(repeat(f))
+ })
+ .map(|(b, path)| GoTo {
+ path: path.clone().into(),
+ at: At::P(b.position),
+ })
+ .collect::<Vec<_>>();
+ self.state = crate::edi::st::State::GoToL(GoToList {
+ data: (src, Some(crate::gotolist::O::Bmk)),
+ ..default()
+ });
+ }
z if z.needs_lsp() => return self.handle_lsp_command(z, w),
x => unimplemented!("{x:?}"),
}
diff --git a/src/edi.rs b/src/edi.rs
index 4c1150e..1562df1 100644
--- a/src/edi.rs
+++ b/src/edi.rs
@@ -8,6 +8,7 @@ use std::sync::Arc;
use std::time::SystemTime;
use Default::default;
+use bind::Bind;
use implicit_fn::implicit_fn;
use lsp_server::{Connection, Request as LRq, ResponseError};
use lsp_types::request::*;
@@ -34,6 +35,7 @@ use crate::bar::Bar;
use crate::commands::Cmds;
use crate::complete::Complete;
use crate::error::WDebug;
+use crate::gotolist::{At, GoTo};
use crate::hov::{self, Hovr};
use crate::lsp::{
self, Anonymize, Client, Map_, PathURI, RequestError, Rq, tdpp,
@@ -244,13 +246,13 @@ macro_rules! change {
}
pub(crate) use change;
-fn rooter(x: &Path) -> Option<PathBuf> {
- for f in std::fs::read_dir(&x).unwrap().filter_map(Result::ok) {
- if f.file_name() == "Cargo.toml" {
+fn rooter(x: &Path, search: &str) -> Option<PathBuf> {
+ for f in std::fs::read_dir(&x).ok()?.filter_map(Result::ok) {
+ if f.file_name() == search {
return Some(f.path().with_file_name("").to_path_buf());
}
}
- x.parent().and_then(rooter)
+ x.parent().and_then(rooter.rbind(search))
}
impl Editor {
@@ -269,7 +271,7 @@ impl Editor {
me.workspace = o
.as_ref()
.and_then(|x| x.parent())
- .and_then(|x| rooter(&x))
+ .and_then(|x| rooter(&x, "Cargo.toml"))
.and_then(|x| x.canonicalize().ok());
let mut loaded_state = false;
if let Some(ws) = me.workspace.as_deref()
@@ -466,7 +468,7 @@ impl Editor {
x.poll(|x, (_, p)| {
let Some(p) = p else { unreachable!() };
x.ok().flatten().map(|r| sym::Symbols {
- data: (r, p.data.1, p.data.2, p.data.3),
+ data: (r, p.data.1, p.data.2, p.data.3, p.data.4),
selection: 0,
vo: 0,
..p
@@ -513,36 +515,25 @@ impl Editor {
z.data.0 = match x {
GotoDefinitionResponse::Scalar(
location,
- ) => vec![(
- location
- .uri
- .to_file_path()
- .ok()?,
- location.range,
+ ) => vec![GoTo::from(
+ location,
)],
GotoDefinitionResponse::Array(
locations,
) => locations
.into_iter()
- .filter_map(|x| try {
- (
- x.uri
- .to_file_path()
- .ok()?,
- x.range,
- )
- })
+ .map(GoTo::from)
.collect(),
GotoDefinitionResponse::Link(
location_links,
) => location_links
.into_iter()
- .filter_map(|x| try {
- (
- x.target_uri
- .to_file_path()
- .ok()?,
- x.target_range,
+ .map(|LocationLink {target_uri, target_range, .. }| {
+ GoTo::from(
+ Location {
+ uri: target_uri,
+ range: target_range,
+ }
)
})
.collect(),
@@ -1018,7 +1009,7 @@ impl Editor {
}
}
Some(Do::Symbols) =>
- if let Some(lsp) = lsp!(self) {
+ if let Some((lsp, o)) = lsp!(self + p) {
let mut q = Rq::new(
lsp.runtime.spawn(
lsp.workspace_symbols("".into())
@@ -1033,6 +1024,7 @@ impl Editor {
q.result = Some(Symbols::new(
self.tree.as_deref().unwrap(),
self.text.bookmarks.clone(),
+ o.into(),
));
self.state = State::Symbols(q);
},
@@ -1131,15 +1123,16 @@ impl Editor {
unreachable!()
};
x.next();
- if let Some(Ok(x)) = x.sel() {
+ if let Some(Ok(x)) = x.sel()
+ && Some(&*x.at.path) == self.origin.as_deref()
+ {
match x.at {
- sym::GoTo::R(x) => {
+ sym::GoTo { path: _, at: At::R(x) } => {
let x = self.text.l_range(x).unwrap();
self.text.vo = self.text.char_to_line(x.start);
}
- sym::GoTo::P(None, x) =>
+ sym::GoTo { path: _, at: At::P(x) } =>
self.text.vo = self.text.char_to_line(x),
- _ => {}
}
}
}
@@ -1150,15 +1143,16 @@ impl Editor {
unreachable!()
};
x.back();
- if let Some(Ok(x)) = x.sel() {
- match x.at {
- sym::GoTo::R(x) => {
+ if let Some(Ok(x)) = x.sel()
+ && Some(&*x.at.path) == self.origin.as_deref()
+ {
+ match x.at.at {
+ At::R(x) => {
let x = self.text.l_range(x).unwrap();
self.text.vo = self.text.char_to_line(x.start);
}
- sym::GoTo::P(None, x) =>
+ At::P(x) =>
self.text.vo = self.text.char_to_line(x),
- _ => {}
}
}
}
@@ -1166,44 +1160,34 @@ impl Editor {
{
if let Some(Ok(x)) = x.sel()
&& let Err(e) = try bikeshed rootcause::Result<()> {
- let r = match x.at {
- sym::GoTo::Loc(x) => {
- let x = x.clone();
- let f = x
- .uri
- .to_file_path()
- .map_err(|()| {
- report!(
- "provided uri not path"
- )
- .context(x.uri)
- })?
- .canonicalize()?;
+ match x.at.at {
+ At::R(r) => {
+ let f = x.at.path.canonicalize()?;
self.state = State::Default;
self.requests.complete =
CompletionState::None;
if Some(&f) != self.origin.as_ref() {
self.open(&f, window.clone())?;
}
- x.range
+ let p = self.text.l_position(r.start).ok_or(
+ report!("provided range out of bound")
+ .context_custom::<WDebug, _>(r),
+ )?;
+ if p != 0 {
+ self.text
+ .cursor
+ .just(p, &self.text.rope);
+ }
+ self.text.scroll_to_cursor_centering();
}
- sym::GoTo::P(_u, x) => {
+ At::P(x) => {
self.text
.cursor
.just(x, &self.text.rope);
self.text.scroll_to_cursor_centering();
break 'out;
}
- sym::GoTo::R(range) => range,
};
- let p = self.text.l_position(r.start).ok_or(
- report!("provided range out of bound")
- .context_custom::<WDebug, _>(r),
- )?;
- if p != 0 {
- self.text.cursor.just(p, &self.text.rope);
- }
- self.text.scroll_to_cursor_centering();
}
{
log::error!("alas! {e}");
@@ -1820,8 +1804,8 @@ impl Editor {
}
}
Some(Do::GTLSelect(x)) => {
- if let Some(Ok((p, r))) = x.sel()
- && Some(p) == self.origin.as_deref()
+ 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();
self.text.vo = self.text.char_to_line(x.start);
@@ -1832,8 +1816,8 @@ impl Editor {
let State::GoToL(x) = &mut self.state else {
unreachable!()
};
- if let Some(Ok((p, r))) = x.sel()
- && Some(p) == self.origin.as_deref()
+ 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();
self.text.scroll_to_ln_centering(r.start.line as _);
diff --git a/src/git.rs b/src/git.rs
index e0d0628..2f4da65 100644
--- a/src/git.rs
+++ b/src/git.rs
@@ -5,7 +5,7 @@ use imara_diff::InternedInput;
use ropey::Rope;
pub fn load(p: &Path, ws: &Path) -> Result<Vec<u8>, git2::Error> {
- let r = Repository::open(ws).unwrap();
+ let r = Repository::open(ws)?;
let o =
r.head()?.peel_to_commit()?.tree()?.get_path(p)?.to_object(&r)?;
let blob = o
diff --git a/src/gotolist.rs b/src/gotolist.rs
index 5a38fb9..8461021 100644
--- a/src/gotolist.rs
+++ b/src/gotolist.rs
@@ -1,9 +1,10 @@
-use std::path::{Path, PathBuf};
+use std::borrow::Cow;
+use std::path::Path;
use dsb::Cell;
use dsb::cell::Style;
-use lsp_types::Range;
use lsp_types::request::GotoImplementation;
+use lsp_types::{Location, Range};
use crate::FG;
use crate::lsp::RqS;
@@ -15,23 +16,25 @@ pub enum GTL {}
#[derive(Debug)]
pub enum O {
Impl(RqS<(), GotoImplementation>),
+ Bmk,
}
-impl<'a> Key<'a> for (&'a Path, Range) {
+impl<'a> Key<'a> for GoTo<'a> {
fn key(&self) -> impl Into<std::borrow::Cow<'a, str>> {
- self.0.display().to_string()
+ self.path.display().to_string()
+ // self.display().to_string()
}
}
impl MenuData for GTL {
- type Data = (Vec<(PathBuf, Range)>, Option<O>);
+ type Data = (Vec<GoTo<'static>>, Option<O>);
- type Element<'a> = (&'a Path, Range);
+ type Element<'a> = GoTo<'a>;
type E = !;
fn gn<'a>(
x: &'a Self::Data,
) -> impl Iterator<Item = Self::Element<'a>> {
- x.0.iter().map(|(x, y)| (&**x, *y))
+ x.0.iter().map(GoTo::asref)
}
fn r(
@@ -53,20 +56,25 @@ impl MenuData for GTL {
into.iter_mut()
// .skip(1)
.zip(
- elem.0
+ elem.path
.strip_prefix(workspace)
- .unwrap_or(&elem.0)
+ .unwrap_or(&elem.path)
.display()
.to_string()
.chars()
.chain(
- format!(
- " 󱊀 {}:{} - {}:{}",
- elem.1.start.line,
- elem.1.start.character,
- elem.1.end.line,
- elem.1.end.character
- )
+ match elem.at {
+ At::R(r) => format!(
+ " 󱊀 {}:{} - {}:{}",
+ r.start.line,
+ r.start.character,
+ r.end.line,
+ r.end.character
+ ),
+ At::P(at) => {
+ format!(" 󱊀 {at}")
+ }
+ }
.chars(),
),
)
@@ -74,3 +82,55 @@ impl MenuData for GTL {
to.extend(into);
}
}
+
+#[derive(Debug, Eq, PartialEq, Clone)]
+pub struct GoTo<'a> {
+ pub path: Cow<'a, Path>,
+ pub at: At,
+}
+
+impl From<Location> for GoTo<'static> {
+ fn from(Location { uri, range }: Location) -> Self {
+ Self {
+ path: Cow::Owned(uri.to_file_path().unwrap()),
+ at: At::R(range),
+ }
+ }
+}
+impl From<&Location> for GoTo<'static> {
+ fn from(Location { uri, range }: &Location) -> Self {
+ Self {
+ path: Cow::Owned(uri.to_file_path().unwrap()),
+ at: At::R(*range),
+ }
+ }
+}
+
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+pub enum At {
+ R(Range),
+ P(usize),
+}
+
+impl From<usize> for At {
+ fn from(v: usize) -> Self {
+ Self::P(v)
+ }
+}
+
+impl From<Range> for At {
+ fn from(v: Range) -> Self {
+ Self::R(v)
+ }
+}
+impl GoTo<'static> {
+ fn asref<'n>(&'n self) -> GoTo<'n> {
+ GoTo {
+ at: self.at,
+ path: match &self.path {
+ Cow::Borrowed(x) => Cow::Borrowed(x),
+ Cow::Owned(x) => Cow::Borrowed(&**x),
+ },
+ }
+ }
+}
diff --git a/src/main.rs b/src/main.rs
index 3eae1e0..6c92fe4 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -141,7 +141,7 @@ pub(crate) fn entry(event_loop: EventLoop) {
};
assert_eq!(unsafe { atexit(cleanup) }, 0);
unsafe { signal(libc::SIGINT, sigint as *const () as usize) };
- let ed = unsafe { __ED.assume_init_mut() };
+ let ed: &'static mut Editor = unsafe { __ED.assume_init_mut() };
let ppem = 20.0;
let ls = 20.0;
// let ed = Box::leak(Box::new(ed));
diff --git a/src/rnd.rs b/src/rnd.rs
index 231eaf0..aee37c6 100644
--- a/src/rnd.rs
+++ b/src/rnd.rs
@@ -18,8 +18,9 @@ use winit::window::{ImeRequestData, Window};
use crate::edi::st::State;
use crate::edi::{Editor, lsp_m};
+use crate::gotolist::{At, GoTo};
use crate::lsp::Rq;
-use crate::sym::{GoTo, UsedSI};
+use crate::sym::{UsedSI};
use crate::text::{CoerceOption, RopeExt, col, color_};
use crate::{
BG, BORDER, CompletionAction, CompletionState, FG, FONT, complete,
@@ -103,16 +104,16 @@ pub fn render(
(1, 0),
|_text, mut f, y| {
if let State::GoToL(menu) = &ed.state
- && let Some(Ok((p, r))) = menu.sel()
- && Some(p) == ed.origin.as_deref()
- {
+ && 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 _)) {
f.style.fg = col!("#FFCC66");
}
}
if let State::Symbols(Rq { result: Some(menu), .. }) =
&ed.state
- && let Some(Ok(UsedSI { at: GoTo::R(x), .. })) =
+ && let Some(Ok(UsedSI { at: GoTo{ at: At::R(x),..}, .. })) =
menu.sel()
{
if (x.start.line..=x.end.line).contains(&(y as _)) {
diff --git a/src/sym.rs b/src/sym.rs
index 8e2a420..5d472e9 100644
--- a/src/sym.rs
+++ b/src/sym.rs
@@ -9,18 +9,25 @@ use itern::Iter3;
use lsp_types::*;
use crate::FG;
+use crate::gotolist::At;
+pub use crate::gotolist::GoTo;
use crate::menu::generic::{GenericMenu, MenuData};
use crate::menu::{Key, charc};
use crate::text::{Bookmarks, col, color_, set_a};
pub enum Symb {}
impl MenuData for Symb {
- type Data =
- (SymbolsList, Vec<SymbolInformation>, Bookmarks, SymbolsType);
+ type Data = (
+ SymbolsList,
+ Vec<SymbolInformation>,
+ Bookmarks,
+ SymbolsType,
+ PathBuf, // origin
+ );
type Element<'a> = UsedSI<'a>;
fn gn(
- (r, tree, bmks, _): &Self::Data,
+ (r, tree, bmks, _, origin): &Self::Data,
) -> impl Iterator<Item = Self::Element<'_>> {
match r {
SymbolsList::Document(DocumentSymbolResponse::Flat(x)) =>
@@ -33,7 +40,10 @@ impl MenuData for Symb {
name: &bmk.text,
kind: SymbolKind::BOOKMARK,
tags: None,
- at: GoTo::P(None, bmk.position),
+ at: GoTo {
+ path: origin.into(),
+ at: At::P(bmk.position),
+ },
right: None,
}
}
@@ -44,7 +54,7 @@ impl MenuData for Symb {
q.push_back(x);
while let Some(x) = q.pop_front() {
q.extend(x.children.iter().flatten());
- yield x.into();
+ yield (x, origin).into();
}
},
)),
@@ -56,7 +66,7 @@ impl MenuData for Symb {
}
fn r(
- &(.., sty): &Self::Data,
+ &(.., sty, _): &Self::Data,
x: Self::Element<'_>,
workspace: &Path,
c: usize,
@@ -69,13 +79,7 @@ impl MenuData for Symb {
}
pub type Symbols = GenericMenu<Symb>;
-#[derive(Debug, Eq, PartialEq, Clone, Copy)]
-pub enum GoTo<'a> {
- Loc(&'a Location),
- R(Range),
- P(Option<&'a Url>, usize),
-}
-#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+#[derive(Debug, Eq, PartialEq, Clone)]
pub struct UsedSI<'a> {
pub name: &'a str,
pub kind: SymbolKind,
@@ -98,28 +102,31 @@ impl<'a> From<&'a SymbolInformation> for UsedSI<'a> {
name: &name,
kind: *kind,
tags: tags.as_deref(),
- at: GoTo::Loc(location),
+ at: GoTo::from(location),
right: container_name.as_deref(),
}
}
}
-impl<'a> From<&'a DocumentSymbol> for UsedSI<'a> {
+impl<'a> From<(&'a DocumentSymbol, &'a PathBuf)> for UsedSI<'a> {
fn from(
- DocumentSymbol {
- name,
- detail,
- kind,
- tags,
- range,
- selection_range: _,
- ..
- }: &'a DocumentSymbol,
+ (
+ DocumentSymbol {
+ name,
+ detail,
+ kind,
+ tags,
+ range,
+ selection_range: _,
+ ..
+ },
+ path,
+ ): (&'a DocumentSymbol, &'a PathBuf),
) -> Self {
UsedSI {
name: &name,
kind: *kind,
tags: tags.as_deref(),
- at: GoTo::R(*range),
+ at: GoTo { path: path.into(), at: At::R(*range) },
right: detail.as_deref(),
}
}
@@ -143,7 +150,7 @@ impl Default for SymbolsList {
}
impl GenericMenu<Symb> {
- pub fn new(tree: &[PathBuf], bmk: Bookmarks) -> Self {
+ pub fn new(tree: &[PathBuf], bmk: Bookmarks, orig: PathBuf) -> Self {
let tree = tree
.iter()
.map(|x| SymbolInformation {
@@ -162,7 +169,7 @@ impl GenericMenu<Symb> {
tags: None,
})
.collect();
- Self { data: (default(), tree, bmk, default()), ..default() }
+ Self { data: (default(), tree, bmk, default(), orig), ..default() }
}
}
@@ -230,14 +237,12 @@ fn r<'a>(
let left = i.len() as i32
- (charc(&x.name) as i32 + qualifier.clone().count() as i32)
- 3;
- let loc = match x.at {
- GoTo::Loc(Location { uri, .. }) | GoTo::P(Some(uri), _) =>
- Some(uri.to_file_path().unwrap()),
- _ => None,
- };
+ let loc = x.at.path;
let locs = if sty == SymbolsType::Workspace {
loc.as_ref()
- .and_then(|x| x.strip_prefix(workspace).unwrap_or(&x).to_str())
+ .strip_prefix(workspace)
+ .unwrap_or(&*loc)
+ .to_str()
.unwrap_or("")
} else {
""
diff --git a/src/text/bookmark.rs b/src/text/bookmark.rs
index ebda796..4080d21 100644
--- a/src/text/bookmark.rs
+++ b/src/text/bookmark.rs
@@ -30,4 +30,5 @@ impl Bookmarks {
lem.position = f(lem.position);
}
}
+ // pub fn to_gtl_d(&self) -> Vec<(PathBuf, Range)> {}
}