A simple CPU rendered GUI IDE experience.
slightly hacky go to line command
bendn 6 weeks ago
parent 24b93ef · commit f24813c
-rw-r--r--src/commands.rs77
-rw-r--r--src/edi.rs127
-rw-r--r--src/edi/st.rs9
-rw-r--r--src/main.rs2
-rw-r--r--src/menu/generic.rs44
-rw-r--r--src/rnd.rs2
-rw-r--r--src/text.rs9
7 files changed, 191 insertions, 79 deletions
diff --git a/src/commands.rs b/src/commands.rs
index ab398dc..316462e 100644
--- a/src/commands.rs
+++ b/src/commands.rs
@@ -1,3 +1,4 @@
+use std::borrow::Cow;
use std::iter::repeat;
use std::path::Path;
use std::process::Stdio;
@@ -15,30 +16,44 @@ use crate::FG;
use crate::edi::{Editor, lsp_m};
use crate::lsp::{PathURI, Rq};
use crate::menu::charc;
-use crate::menu::generic::{GenericMenu, MenuData};
+use crate::menu::generic::{CorA, GenericMenu, MenuData};
use crate::text::{RopeExt, SortTedits, col, color_};
+macro_rules! repl {
+ ($x:ty, $($with:tt)+) => {
+ $($with)+
+ };
+}
macro_rules! commands {
- ($(#[doc = $d: literal] $t:tt $identifier: ident: $c:literal),+ $(,)?) => {
- #[derive(Copy, Clone, PartialEq, Eq)]
+ ($(#[doc = $d: literal] $t:tt $identifier: ident$(($($thing:ty),+))?: $c:literal),+ $(,)?) => {
+ #[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Cmd {
- $(#[doc = $d] $identifier),+
+ $(#[doc = $d] $identifier $( ( $(Option<$thing>,)+ ) )?
+
+ ),+
}
impl Cmd {
- pub const ALL: [Cmd; { [$($c),+].len() }] = [$(Self::$identifier,)+];
+ pub const ALL: [Cmd; { [$($c),+].len() }] = [$(Self::$identifier
+ $( ( $(None::<$thing>,)+) )?
+ ,)+];
pub fn name(self) -> &'static str {
match self {
- $(Self::$identifier => $c,)+
+ $(Self::$identifier $( ( $(repl!($thing, _),)+ ) )? => $c,)+
}
}
+ // pub fn wants(self) -> &'static [wants] {
+ // match self {
+ // $(Self::$identifier => &[$($(wants::$thing as u8,)+)?],)+
+ // }
+ // }
pub fn desc(self) -> &'static str {
match self {
- $(Self::$identifier => $d,)+
+ $(Self::$identifier $( ( $(repl!($thing, _),)+ ) )? => $d,)+
}
}
pub fn needs_lsp(self) -> bool {
match self {
- $(Self::$identifier => stringify!($t) == "@",)+
+ $(Self::$identifier $( ( $(repl!($thing, _),)+ ) )? => stringify!($t) == "@",)+
}
}
}
@@ -67,6 +82,8 @@ commands!(
@ RAOpenCargoToml: "open-cargo-toml",
/// Runs the test at the cursor
@ RARunTest: "run-test",
+ /// GoTo line,
+ | GoTo(u32): "g",
);
pub enum Cmds {}
@@ -74,10 +91,45 @@ impl MenuData for Cmds {
const HEIGHT: usize = 30;
type Data = ();
type Element<'a> = Cmd;
+ type E = &'static str;
fn gn((): &()) -> impl Iterator<Item = Cmd> {
Cmd::ALL.into_iter()
}
+ fn should_complete<'a>(m: &GenericMenu<Self>) -> bool {
+ !Cmd::ALL.iter().any(|x| m.tedit.to_string().starts_with(x.name()))
+ }
+ fn map<'a>(
+ m: &GenericMenu<Self>,
+ x: Self::Element<'a>,
+ ) -> Result<Self::Element<'a>, Self::E> {
+ if let Cmd::GoTo(_) = x {
+ if !m.tedit.to_string().starts_with(Cmd::GoTo(None).name()) {
+ return Ok(Cmd::GoTo(None));
+ }
+ if let Some((_, x)) = m.tedit.to_string().split_once(" ")
+ // && x.chars().all(|x| x.is_numeric())
+ && let Ok(n) = x.parse()
+ {
+ Ok(Cmd::GoTo(Some(n)))
+ } else {
+ Err("supply number")
+ }
+ } else {
+ Ok(x)
+ }
+ }
+ fn complete_or_accept<'a>(x: Self::Element<'a>) -> CorA {
+ if let Cmd::GoTo(None) = x { CorA::Complete } else { CorA::Accept }
+ }
+ fn f(m: &GenericMenu<Self>) -> String {
+ m.tedit
+ .to_string()
+ .split_once(" ")
+ .map(|x| x.0)
+ .unwrap_or(&m.tedit.to_string())
+ .into()
+ }
fn r(
_: &Self::Data,
x: Cmd,
@@ -144,6 +196,15 @@ impl Editor {
z: Cmd,
w: Arc<winit::window::Window>,
) -> anyhow::Result<()> {
+ match z {
+ Cmd::GoTo(Some(x)) =>
+ if let Ok(x) = self.text.try_line_to_char(x as _) {
+ self.text.cursor.just(x, &self.text.rope);
+ self.text.scroll_to_cursor_centering();
+ },
+ x if x.needs_lsp() => {}
+ x => unimplemented!("{x:?}"),
+ }
if !z.needs_lsp() {
return Ok(());
}
diff --git a/src/edi.rs b/src/edi.rs
index 8421d96..42803fb 100644
--- a/src/edi.rs
+++ b/src/edi.rs
@@ -29,11 +29,13 @@ pub mod st;
use st::*;
use crate::bar::Bar;
+use crate::commands::Cmds;
use crate::complete::Complete;
use crate::hov::{self, Hovr};
use crate::lsp::{
self, Anonymize, Client, Map_, PathURI, RedrawAfter, RequestError, Rq,
};
+use crate::menu::generic::MenuData;
use crate::meta::META;
use crate::runnables::Runnables;
use crate::sym::{Symbols, SymbolsList, SymbolsType};
@@ -989,10 +991,30 @@ impl Editor {
(),
));
},
- Some(Do::ProcessCommand(x)) => {
- let z = x.sel();
- if let Err(e) = self.handle_command(z, window.clone()) {
- self.bar.last_action = format!("{e}");
+ Some(Do::ProcessCommand(mut x, z)) =>
+ match Cmds::complete_or_accept(z) {
+ crate::menu::generic::CorA::Complete => {
+ x.tedit.rope =
+ Rope::from_str(&format!("{} ", z.name()));
+ x.tedit.cursor.end(&x.tedit.rope);
+ self.state = State::Command(x);
+ }
+ crate::menu::generic::CorA::Accept => {
+ if let Err(e) =
+ self.handle_command(z, window.clone())
+ {
+ self.bar.last_action = format!("{e}");
+ }
+ }
+ },
+ Some(Do::CmdTyped) => {
+ let State::Command(x) = &self.state else {
+ unreachable!()
+ };
+ if let Some(Ok(crate::commands::Cmd::GoTo(Some(x)))) =
+ x.sel()
+ {
+ self.text.scroll_to_ln_centering(x as _);
}
}
Some(Do::SymbolsHandleKey) => {
@@ -1040,12 +1062,14 @@ impl Editor {
unreachable!()
};
x.next();
- match x.sel().at {
- sym::GoTo::R(x) => {
- let x = self.text.l_range(x).unwrap();
- self.text.vo = self.text.char_to_line(x.start);
+ if let Some(Ok(x)) = x.sel() {
+ match x.at {
+ sym::GoTo::R(x) => {
+ let x = self.text.l_range(x).unwrap();
+ self.text.vo = self.text.char_to_line(x.start);
+ }
+ _ => {}
}
- _ => {}
}
}
Some(Do::SymbolsSelectPrev) => {
@@ -1055,12 +1079,14 @@ impl Editor {
unreachable!()
};
x.back();
- match x.sel().at {
- sym::GoTo::R(x) => {
- let x = self.text.l_range(x).unwrap();
- self.text.vo = self.text.char_to_line(x.start);
+ if let Some(Ok(x)) = x.sel() {
+ match x.at {
+ sym::GoTo::R(x) => {
+ let x = self.text.l_range(x).unwrap();
+ self.text.vo = self.text.char_to_line(x.start);
+ }
+ _ => {}
}
- _ => {}
}
}
Some(Do::SymbolsSelect) => {
@@ -1069,41 +1095,43 @@ impl Editor {
else {
unreachable!()
};
- let x = x.sel();
- if let Err(e) = try bikeshed anyhow::Result<()> {
- let r = match x.at {
- sym::GoTo::Loc(x) => {
- let x = x.clone();
- let f = x
- .uri
- .to_file_path()
- .map_err(|()| anyhow!("dammit"))?
- .canonicalize()?;
- self.state = State::Default;
- self.requests.complete = CompletionState::None;
- if Some(&f) != self.origin.as_ref() {
- self.open(&f, window.clone())?;
+ if let Some(Ok(x)) = x.sel()
+ && let Err(e) = try bikeshed anyhow::Result<()> {
+ let r = match x.at {
+ sym::GoTo::Loc(x) => {
+ let x = x.clone();
+ let f = x
+ .uri
+ .to_file_path()
+ .map_err(|()| anyhow!("dammit"))?
+ .canonicalize()?;
+ self.state = State::Default;
+ self.requests.complete =
+ CompletionState::None;
+ if Some(&f) != self.origin.as_ref() {
+ self.open(&f, window.clone())?;
+ }
+ x.range
}
- x.range
+ sym::GoTo::R(range) => range,
+ };
+ let p = self
+ .text
+ .l_position(r.start)
+ .ok_or(anyhow!("rah"))?;
+ if p != 0 {
+ self.text.cursor.just(p, &self.text.rope);
}
- sym::GoTo::R(range) => range,
- };
- let p = self
- .text
- .l_position(r.start)
- .ok_or(anyhow!("rah"))?;
- if p != 0 {
- self.text.cursor.just(p, &self.text.rope);
+ self.text.scroll_to_cursor_centering();
}
- self.text.scroll_to_cursor_centering();
- } {
+ {
log::error!("alas! {e}");
}
}
Some(Do::RenameSymbol(to)) => {
if let Some((lsp, f)) = lsp!(self + p) {
- let t = lsp
- .request::<lsp_request!("textDocument/rename")>(
+ let x = lsp
+ .request_immediate::<lsp_request!("textDocument/rename")>(
&RenameParams {
text_document_position:
TextDocumentPositionParams {
@@ -1121,21 +1149,8 @@ impl Editor {
new_name: to,
work_done_progress_params: default(),
},
- )
- .unwrap()
- .0;
- let mut t = Box::pin(t);
- let mut ctx = std::task::Context::from_waker(
- std::task::Waker::noop(),
- );
- let x = loop {
- match Future::poll(t.as_mut(), &mut ctx) {
- std::task::Poll::Ready(x) => break x,
- std::task::Poll::Pending => {
- std::hint::spin_loop();
- }
- }
- };
+ );
+
match x {
Ok(Some(x)) => self.apply_wsedit(x, &f.to_owned()),
Err(RequestError::Failure(
diff --git a/src/edi/st.rs b/src/edi/st.rs
index 5c423e9..11e3cca 100644
--- a/src/edi/st.rs
+++ b/src/edi/st.rs
@@ -1,5 +1,4 @@
#![allow(dead_code, unused)]
-
use Default::default;
use NamedKey::*;
use lsp_types::*;
@@ -11,6 +10,7 @@ use winit::keyboard::{Key, NamedKey, SmolStr};
use crate::commands::Commands;
use crate::edi::handle2;
use crate::lsp::{AQErr, Rq, RqS};
+use crate::menu::generic::{GenericMenu, MenuData};
use crate::sym::{Symbols, SymbolsList};
use crate::text::TextArea;
use crate::{
@@ -73,12 +73,13 @@ Default => {
M(_) => _,
},
Command(_) => K(Key::Named(Escape)) => Default,
-Command(t) => K(Key::Named(Enter)) => Default [ProcessCommand(Commands => t)],
+Command(t) => K(Key::Named(Enter) if let Some(Ok(x)) = t.sel()) => Default [ProcessCommand((Commands, crate::commands::Cmd) => (t, x))],
+Command(t) => K(Key::Named(Enter)) => _,
Command(mut t) => K(Key::Named(Tab) if shift()) => Command({ t.back();t }),
Command(mut t) => K(Key::Named(Tab)) => Command({ t.next(); t }),
Command(mut t) => K(k) => Command({ if let Some(_) = handle2(&k, &mut t.tedit, None) {
t.selection = 0; t.vo = 0;
-}; t }),
+}; t }) [CmdTyped],
Command(t) => C(_) => _,
Command(t) => K(_) => _,
Runnables(_x) => {
@@ -88,7 +89,7 @@ Runnables(RqS::<crate::runnables::Runnables, rust_analyzer::lsp::ext::Runnables>
K(Key::Named(Tab) if shift()) => Runnables({ x.next(); Rq { result: Some(x), request }}),
K(Key::Named(ArrowDown)) => Runnables({ x.next(); Rq { result: Some(x), request }}),
K(Key::Named(ArrowUp | Tab)) => Runnables({ x.back(); Rq { result: Some(x), request }}),
- K(Key::Named(Enter)) => Default [Run(Runnable => x.sel().clone())],
+ K(Key::Named(Enter) if let Some(Ok(x_)) = x.clone().sel()) => Default [Run(Runnable => x_.clone())],
K(k) => Runnables({
if let Some(_) = handle2(&k, &mut x.tedit, None) {
x.selection = 0; x.vo = 0;
diff --git a/src/main.rs b/src/main.rs
index 9325aca..c48eaca 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,4 +1,6 @@
#![feature(
+ inherent_associated_types,
+ never_type,
random,
btree_set_entry,
associated_type_defaults,
diff --git a/src/menu/generic.rs b/src/menu/generic.rs
index ba48d76..b9bf5bf 100644
--- a/src/menu/generic.rs
+++ b/src/menu/generic.rs
@@ -1,3 +1,4 @@
+use std::borrow::Cow;
use std::fmt::Debug;
use std::path::Path;
@@ -46,11 +47,28 @@ impl<T: MenuData<Data: Debug>> Debug for GenericMenu<T> {
.finish()
}
}
-pub trait MenuData {
+pub enum CorA {
+ Complete,
+ Accept,
+}
+pub trait MenuData: Sized {
const HEIGHT: usize = 30;
type Data;
type Element<'a>: Key<'a>;
+ type E = !;
+ fn complete_or_accept<'a>(x: Self::Element<'a>) -> CorA {
+ CorA::Accept
+ }
+ fn map<'a>(
+ _m: &GenericMenu<Self>,
+ x: Self::Element<'a>,
+ ) -> Result<Self::Element<'a>, Self::E> {
+ Ok(x)
+ }
+ fn should_complete<'a>(_m: &GenericMenu<Self>) -> bool {
+ true
+ }
fn gn<'a>(
x: &'a Self::Data,
) -> impl Iterator<Item = Self::Element<'a>>;
@@ -75,11 +93,22 @@ pub trait MenuData {
) -> Vec<(u32, Self::Element<'a>, Vec<u32>)> {
score(x, f)
}
+
+ fn f(m: &GenericMenu<Self>) -> String {
+ m.tedit.rope.to_string()
+ }
}
impl<T: MenuData> GenericMenu<T> {
- fn f(&self) -> String {
- self.tedit.rope.to_string()
+ pub type I = T;
+ pub fn should_render(&self) -> bool {
+ T::should_complete(self)
+ }
+ // pub fn valid(&self) -> Result<(), Cow<'static, str>> {
+ // T::valid(self)
+ // }
+ pub fn f(&self) -> String {
+ T::f(self)
}
pub fn next(&mut self)
where
@@ -90,11 +119,14 @@ impl<T: MenuData> GenericMenu<T> {
back::<{ T::HEIGHT }>(n, &mut self.selection, &mut self.vo);
}
- pub fn sel(&self) -> T::Element<'_> {
+ pub fn sel(
+ &self,
+ ) -> Option<Result<<T as MenuData>::Element<'_>, T::E>> {
let f = self.f();
+
T::score_c(T::filter_c(&self.data, &f), &f)
- .swap_remove(self.selection)
- .1
+ .try_remove(self.selection)
+ .map(|(_, x, _)| T::map(&self, x))
}
pub fn back(&mut self)
diff --git a/src/rnd.rs b/src/rnd.rs
index 4453769..4905cf2 100644
--- a/src/rnd.rs
+++ b/src/rnd.rs
@@ -707,7 +707,7 @@ pub fn render(
BORDER,
);
}
- State::Command(x) => {
+ State::Command(x) if x.should_render() => {
let ws = ed.workspace.as_deref().unwrap();
let c = x.cells(50, ws);
drawb(&c, 50);
diff --git a/src/text.rs b/src/text.rs
index 2f4d30a..2fa0c26 100644
--- a/src/text.rs
+++ b/src/text.rs
@@ -728,15 +728,16 @@ impl TextArea {
}
}
}
-
#[lower::apply(saturating)]
- pub fn scroll_to_cursor_centering(&mut self) {
- let (_, y) = self.primary_cursor();
-
+ pub fn scroll_to_ln_centering(&mut self, y: usize) {
if !(self.vo..self.vo + self.r).contains(&y) {
self.vo = y - (self.r / 2);
}
}
+ pub fn scroll_to_cursor_centering(&mut self) {
+ let (_, y) = self.primary_cursor();
+ self.scroll_to_ln_centering(y);
+ }
#[cold]
pub fn tree_sit<'c>(&self, path: Option<&Path>, cell: &mut Output) {
let language = path