A simple CPU rendered GUI IDE experience.
add commands
| -rw-r--r-- | src/bar.rs | 10 | ||||
| -rw-r--r-- | src/commands.rs | 159 | ||||
| -rw-r--r-- | src/complete.rs (renamed from src/com.rs) | 0 | ||||
| -rw-r--r-- | src/edi.rs | 124 | ||||
| -rw-r--r-- | src/edi/st.rs | 10 | ||||
| -rw-r--r-- | src/lsp.rs | 13 | ||||
| -rw-r--r-- | src/main.rs | 5 | ||||
| -rw-r--r-- | src/menu.rs | 6 | ||||
| -rw-r--r-- | src/rnd.rs | 34 | ||||
| -rw-r--r-- | src/text.rs | 32 |
10 files changed, 349 insertions, 44 deletions
@@ -97,6 +97,16 @@ impl Bar { } }); } + State::Command(x) => { + chain(['>'], x.tedit.rope.chars()).zip(row).for_each( + |(x, y)| { + *y = Cell { + letter: Some(x), + style: Style { flags: 0, ..y.style }, + } + }, + ); + } State::Symbols(Rq { result: Some(Symbols { tedit, .. }), request, diff --git a/src/commands.rs b/src/commands.rs new file mode 100644 index 0000000..c21acb1 --- /dev/null +++ b/src/commands.rs @@ -0,0 +1,159 @@ +use std::iter::repeat; +use std::path::Path; + +use Default::default; +use dsb::Cell; +use dsb::cell::Style; + +use crate::FG; +use crate::menu::{back, charc, filter, next, score}; +use crate::text::{TextArea, col, color_}; + +macro_rules! commands { + ($(#[doc = $d: literal] $t:tt $identifier: ident: $c:literal),+ $(,)?) => { + #[derive(Copy, Clone, PartialEq, Eq)] + pub enum Cmd { + $(#[doc = $d] $identifier),+ + } + impl Cmd { + pub const ALL: [Cmd; { [$($c),+].len() }] = [$(Self::$identifier,)+]; + pub fn name(self) -> &'static str { + match self { + $(Self::$identifier => $c,)+ + } + } + pub fn desc(self) -> &'static str { + match self { + $(Self::$identifier => $d,)+ + } + } + pub fn needs_lsp(self) -> bool { + match self { + $(Self::$identifier => stringify!($t) == "@",)+ + } + } + } + }; +} +commands!( + /// move item at cursor down + @ RAMoveID: "move-item-down", + /// move item at cursor up + @ RAMoveIU: "move-item-up", + /// restart rust analyzer + @ RARestart: "ra-restart", + /// go to parent module + @ RAParent: "parent", + +); + +#[derive(Debug, Default)] +pub struct Commands { + pub tedit: TextArea, + pub selection: usize, + pub vo: usize, +} + +const N: usize = 30; +impl Commands { + fn f(&self) -> String { + self.tedit.rope.to_string() + } + pub fn next(mut self) -> Self { + let n = filter_c(&self.f()).count(); + // coz its bottom up + back::<N>(n, &mut self.selection, &mut self.vo); + self + } + + pub fn sel(&self) -> Cmd { + let f = self.f(); + score_c(filter_c(&f), &f)[self.selection].1 + } + + pub fn back(mut self) -> Self { + let n = filter_c(&self.f()).count(); + next::<N>(n, &mut self.selection, &mut self.vo); + self + } + pub fn cells(&self, c: usize, ws: &Path) -> Vec<Cell> { + let f = self.f(); + let mut out = vec![]; + let v = score_c(filter_c(&f), &f); + let vlen = v.len(); + let i = v.into_iter().zip(0..vlen).skip(self.vo).take(N).rev(); + + i.for_each(|((_, x, indices), i)| { + r(x, ws, c, i == self.selection, &indices, &mut out) + }); + + out + } +} +fn score_c( + x: impl Iterator<Item = Cmd>, + filter: &'_ str, +) -> Vec<(u32, Cmd, Vec<u32>)> { + score(x, filter) +} + +fn filter_c(f: &'_ str) -> impl Iterator<Item = Cmd> { + filter(Cmd::ALL.into_iter(), f) +} +impl crate::menu::Key<'static> for Cmd { + fn key(&self) -> impl Into<std::borrow::Cow<'static, str>> { + self.name() + } +} +#[implicit_fn::implicit_fn] +fn r( + x: Cmd, + _workspace: &Path, + c: usize, + selected: bool, + indices: &[u32], + to: &mut Vec<Cell>, +) { + let bg = if selected { col!("#262d3b") } else { col!("#1c212b") }; + + let ds: Style = Style::new(FG, bg); + let d: Cell = Cell { letter: None, style: ds }; + let mut b = vec![d; c]; + let (bgt, col, ty) = (col!("#FFFFFF"), col!("#ACACAC"), ""); + b.iter_mut().zip(ty.chars()).for_each(|(x, c)| { + *x = (Style::new(col, bgt) | Style::BOLD).basic(c) + }); + let i = &mut b[..]; + let qualifier = x.desc().chars(); + let _left = i.len() as i32 + - (charc(&x.name()) as i32 + qualifier.clone().count() as i32) + - 3; + + i.iter_mut() + .zip( + x.name() + .chars() + .chain([' ']) + .map(|x| ds.basic(x)) + .zip(0..) + .chain( + qualifier + .map(|x| { + Style { + bg, + fg: color_("#858685"), + ..default() + } + .basic(x) + }) + .zip(repeat(u32::MAX)), + ), + ) + .for_each(|(a, (b, i))| { + *a = b; + if indices.contains(&i) { + a.style |= (Style::BOLD, color_("#ffcc66")); + } + }); + to.extend(b); +} diff --git a/src/com.rs b/src/complete.rs index fb326b0..fb326b0 100644 --- a/src/com.rs +++ b/src/complete.rs @@ -8,12 +8,16 @@ use std::sync::Arc; use std::time::SystemTime; use Default::default; +use anyhow::anyhow; use implicit_fn::implicit_fn; use lsp_server::{Connection, Request as LRq, ResponseError}; use lsp_types::request::*; use lsp_types::*; use regex::Regex; use ropey::Rope; +use rust_analyzer::lsp::ext::{ + MoveItemDirection, MoveItemParams, ParentModule, ReloadWorkspace, +}; use rust_fsm::StateMachine; use serde_derive::{Deserialize, Serialize}; use tokio::sync::oneshot::Sender; @@ -27,7 +31,8 @@ pub mod st; use st::*; use crate::bar::Bar; -use crate::com::Complete; +use crate::commands::Cmd; +use crate::complete::Complete; use crate::hov::{self, Hovr}; use crate::lsp::{ self, Anonymize, Client, Map_, PathURI, RedrawAfter, RequestError, Rq, @@ -799,23 +804,8 @@ impl Editor { self.hist.lc = text.cursor.clone(); } Some(Do::GoToDefinition) => { - if let Some(LocationLink { - ref target_uri, - target_range, - .. - }) = self.requests.def.result - { - self.open( - &target_uri.to_file_path().unwrap(), - w.clone(), - ) - .unwrap(); - - self.text.cursor.just( - self.text.l_position(target_range.start).unwrap(), - &self.text.rope, - ); - self.text.scroll_to_cursor(); + if let Some(x) = self.requests.def.result.clone() { + self.open_loclink(&x, w.clone()); } } Some(Do::InsertCursorAtMouse) => { @@ -831,6 +821,7 @@ impl Editor { _ => unreachable!(), } } + pub fn nav_back(&mut self) { self.chist.back().map(|x| { self.text.cursor.just( @@ -960,6 +951,73 @@ impl Editor { (), )); }, + Some(Do::ProcessCommand(x)) => 'out: { + _ = try bikeshed anyhow::Result<()> { + let z = x.sel(); + if z.needs_lsp() + && let Some((l, o)) = lsp!(self + p) + { + match z { + Cmd::RAMoveIU | Cmd::RAMoveID => { + let r = self + .text + .to_l_position( + *self.text.cursor.first(), + ) + .unwrap(); + let mut x = l.runtime.block_on(l.request::<rust_analyzer::lsp::ext::MoveItem>(&MoveItemParams { + direction: if let Cmd::RAMoveIU = z { MoveItemDirection::Up } else { MoveItemDirection::Down }, + text_document: o.tid(), + range: Range { start : r, end : r}, + })?.0)?; + + x.sort_tedits(); + for t in x { + self.text.apply_snippet_tedit(&t)?; + } + } + Cmd::RARestart => { + _ = l.request::<ReloadWorkspace>(&())?.0; + } + Cmd::RAParent => { + let Some(GotoDefinitionResponse::Link( + [ref x], + )) = l.runtime.block_on( + l.request::<ParentModule>( + &TextDocumentPositionParams { + text_document: o.tid(), + position: self + .text + .to_l_position( + *self + .text + .cursor + .first(), + ) + .unwrap(), + }, + )? + .0, + )? + else { + self.bar.last_action = + "no such parent".into(); + break 'out; + }; + self.open_loclink(x, window.clone()); + } + } + break 'out; + } + + // match x.sel() { + // Cmd::RAMoveID => {} + // Cmd::RAMoveIU => todo!(), + // Cmd::RARestart => todo!(), + // Cmd::RAParent => todo!(), + // } + }; + } Some(Do::SymbolsHandleKey) => { if let Some(lsp) = lsp!(self) { let State::Symbols(Rq { result: Some(x), request }) = @@ -1666,16 +1724,24 @@ impl Editor { |TextDocumentEdit { mut edits, text_document, .. }| { edits.sort_tedits(); if text_document.uri != f.tid().uri { - let mut f = OpenOptions::new() - .write(true) + let f = OpenOptions::new() .read(true) + .create(true) + .write(true) .open(text_document.uri.path()) .unwrap(); - let mut r = Rope::from_reader(&mut f).unwrap(); + let mut r = Rope::from_reader(f).unwrap(); for edit in &edits { TextArea::apply_snippet_tedit_raw(edit, &mut r); } - r.write_to(f).unwrap(); + r.write_to( + OpenOptions::new() + .write(true) + .truncate(true) + .open(text_document.uri.path()) + .unwrap(), + ) + .unwrap(); } else { for edit in &edits { self.text.apply_snippet_tedit(edit).unwrap(); @@ -1836,6 +1902,20 @@ impl Editor { } Ok(()) } + + fn open_loclink( + &mut self, + LocationLink { target_uri, target_range, .. }: &LocationLink, + w: Arc<Window>, + ) { + self.open(&target_uri.to_file_path().unwrap(), w.clone()).unwrap(); + + self.text.cursor.just( + self.text.l_position(target_range.start).unwrap(), + &self.text.rope, + ); + self.text.scroll_to_cursor(); + } } use NamedKey::*; diff --git a/src/edi/st.rs b/src/edi/st.rs index 4c3bab3..8844425 100644 --- a/src/edi/st.rs +++ b/src/edi/st.rs @@ -7,6 +7,8 @@ use regex::Regex; use winit::event::MouseButton; use winit::keyboard::{Key, NamedKey, SmolStr}; +use crate::commands::Commands; +use crate::edi::handle2; use crate::lsp::{AQErr, Rq, RqS}; use crate::sym::{Symbols, SymbolsList}; use crate::text::TextArea; @@ -48,6 +50,7 @@ Default => { K(Key::Character(x) if x == "0" && ctrl()) => _ [MatchingBrace], K(Key::Character(x) if x == "`" && ctrl()) => _ [SpawnTerminal], K(Key::Character(y) if y == "/" && ctrl()) => Default [Comment(State => State::Default)], + K(Key::Character(x) if x == "p" && ctrl()) => Command(Commands => Commands::default()), K(Key::Named(F1)) => Procure((default(), InputRequest::RenameSymbol)), K(Key::Named(k @ (ArrowUp | ArrowDown)) if alt()) => _ [InsertCursor(Direction => { if k == ArrowUp {Direction::Above} else { Direction::Below } @@ -68,6 +71,13 @@ Default => { K(_) => _ [Edit], M(_) => _, }, +Command(_) => K(Key::Named(Escape)) => Default, +Command(t) => K(Key::Named(Enter)) => Default [ProcessCommand(Commands => t)], +Command(t) => K(Key::Named(Tab) if shift()) => Command(t.back()), +Command(t) => K(Key::Named(Tab)) => Command(t.next()), +Command(mut t) => K(k) => Command({ handle2(&k, &mut t.tedit, None); t }), +Command(t) => C(_) => _, +Command(t) => K(_) => _, Symbols(Rq { result: Some(_x), request: _rq }) => { K(Key::Named(Tab) if shift()) => _ [SymbolsSelectNext], K(Key::Named(ArrowDown)) => _ [SymbolsSelectNext], @@ -21,6 +21,7 @@ use lsp_server::{ ErrorCode, Message, Notification as N, Request as LRq, Response as Re, ResponseError, }; +use rust_analyzer::lsp::ext::*; use lsp_types::notification::*; use lsp_types::request::*; use lsp_types::*; @@ -449,10 +450,10 @@ impl Client { >, > { self.request::<lsp_request!("workspace/symbol")>( - &WorkspaceSymbolParams { + &lsp_types::WorkspaceSymbolParams { query: f, - search_scope: Some(WorkspaceSymbolSearchScope::Workspace), - search_kind: Some(WorkspaceSymbolSearchKind::AllSymbols), + search_scope: Some(lsp_types::WorkspaceSymbolSearchScope::Workspace), + search_kind: Some(lsp_types::WorkspaceSymbolSearchKind::AllSymbols), ..Default::default() }, ) @@ -464,8 +465,8 @@ impl Client { f: &Path, t: &'a mut TextArea, ) { - if let Ok(Some([x])) = self.runtime.block_on( - self.request::<lsp_request!("experimental/matchingBrace")>( + if let Ok([x]) = self.runtime.block_on( + self.request::<MatchingBrace>( &MatchingBraceParams { text_document: f.tid(), positions: vec![ @@ -593,7 +594,7 @@ impl Client { let r = self .runtime .block_on( - self.request::<lsp_request!("experimental/onEnter")>( + self.request::<OnEnter>( &TextDocumentPositionParams { text_document: f.tid(), position: t.to_l_position(*c).unwrap(), diff --git a/src/main.rs b/src/main.rs index fe94363..07fd6ca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -78,7 +78,8 @@ use crate::edi::st::*; use crate::lsp::RqS; use crate::text::{TextArea, col, is_word}; mod bar; -mod com; +mod commands; +mod complete; pub mod hov; mod lsp; pub mod menu; @@ -476,7 +477,7 @@ rust_fsm::state_machine! { Complete(_x) => K(_) => _ [Request(CompletionContext { trigger_kind: CompletionTriggerKind::TRIGGER_FOR_INCOMPLETE_COMPLETIONS, trigger_character:None })], } -use com::Complete; +use complete::Complete; impl Default for CompletionState { fn default() -> Self { Self::None diff --git a/src/menu.rs b/src/menu.rs index 51fe19f..f900189 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -91,3 +91,9 @@ pub trait Key<'a> { pub fn charc(c: &str) -> usize { c.chars().count() } + +impl<'a> crate::menu::Key<'a> for &'a str { + fn key(&self) -> impl Into<std::borrow::Cow<'a, str>> { + *self + } +} @@ -21,7 +21,7 @@ use crate::edi::{Editor, lsp_m}; use crate::lsp::Rq; use crate::text::{CoerceOption, RopeExt, col, color_}; use crate::{ - BG, BORDER, CompletionAction, CompletionState, FG, FONT, com, filter, + BG, BORDER, CompletionAction, CompletionState, FG, FONT, complete, filter, lsp, sig, }; @@ -669,6 +669,36 @@ pub fn render( BORDER, ); } + State::Command(x) => 'out: { + let ws = ed.workspace.as_deref().unwrap(); + let c = x.cells(50, ws); + // let (_x, _y) = text.cursor_visual(); + let _x = 0; + let _y = r - 1; + let Ok((is_above, left, top, w, mut h)) = place_around( + (_x, _y), + i.copy(), + &c, + 50, + ppem, + ls, + 0., + 0., + 0., + ) else { + println!("ra?"); + break 'out; + }; + i.r#box( + ( + left.saturating_sub(1) as _, + top.saturating_sub(1) as _, + ), + w as _, + h as _, + BORDER, + ); + } State::Symbols(Rq { result: Some(x), .. }) => 'out: { let ws = ed.workspace.as_deref().unwrap(); let c = x.cells(50, ws); @@ -705,7 +735,7 @@ pub fn render( CompletionState::Complete(Rq { result: Some(ref x), .. }) => { - let c = com::s(x, 40, &filter(&text)); + let c = complete::s(x, 40, &filter(&text)); if c.len() == 0 { ed.requests .complete diff --git a/src/text.rs b/src/text.rs index 0873802..72ec25b 100644 --- a/src/text.rs +++ b/src/text.rs @@ -479,36 +479,44 @@ impl TextArea { Ok(()) } pub fn apply_snippet_tedit_raw( - SnippetTextEdit { text_edit: x, insert_text_format, .. }: &SnippetTextEdit, + SnippetTextEdit { range,new_text, insert_text_format, .. }: &SnippetTextEdit, text: &'_ mut Rope, ) -> Option<()> { match insert_text_format { Some(lsp_types::InsertTextFormat::SNIPPET) => { - let begin = text.l_position(x.range.start)?; - let end = text.l_position(x.range.end)?; + let begin = text.l_position(range.start)?; + let end = text.l_position(range.end)?; text.try_remove(begin..end).ok()?; let (_, tex) = - crate::sni::Snippet::parse(&x.new_text, begin)?; + crate::sni::Snippet::parse(&new_text, begin)?; text.try_insert(begin, &tex).ok()?; } _ => { - let begin = text.l_position(x.range.start)?; - let end = text.l_position(x.range.end)?; + let begin = text.l_position(range.start)?; + let end = text.l_position(range.end)?; text.try_remove(begin..end).ok()?; - text.try_insert(begin, &x.new_text).ok()?; + text.try_insert(begin, &new_text).ok()?; } } Some(()) } pub fn apply_snippet_tedit( &mut self, - SnippetTextEdit { text_edit, insert_text_format, .. }: &SnippetTextEdit, + SnippetTextEdit { range,new_text, insert_text_format, .. }: &SnippetTextEdit, ) -> anyhow::Result<()> { match insert_text_format { - Some(lsp_types::InsertTextFormat::SNIPPET) => - self.apply_snippet(&text_edit).unwrap(), + Some(lsp_types::InsertTextFormat::SNIPPET) => self + .apply_snippet(&TextEdit { + range: range.clone(), + new_text: new_text.clone(), + }) + .unwrap(), _ => { - self.apply_adjusting(text_edit).unwrap(); + self.apply_adjusting(&TextEdit { + range: range.clone(), + new_text: new_text.clone(), + }) + .unwrap(); } } Ok(()) @@ -1700,7 +1708,7 @@ impl SortTedits for [TextEdit] { } impl SortTedits for [SnippetTextEdit] { fn sort_tedits(&mut self) { - self.as_mut().sort_by_key(|t| Reverse(t.text_edit.range.start)); + self.as_mut().sort_by_key(|t| Reverse(t.range.start)); } } |