A simple CPU rendered GUI IDE experience.
slightly hacky go to line command
| -rw-r--r-- | src/commands.rs | 77 | ||||
| -rw-r--r-- | src/edi.rs | 127 | ||||
| -rw-r--r-- | src/edi/st.rs | 9 | ||||
| -rw-r--r-- | src/main.rs | 2 | ||||
| -rw-r--r-- | src/menu/generic.rs | 44 | ||||
| -rw-r--r-- | src/rnd.rs | 2 | ||||
| -rw-r--r-- | src/text.rs | 9 |
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(()); } @@ -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) @@ -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 |