A simple CPU rendered GUI IDE experience.
-rw-r--r--src/bar.rs10
-rw-r--r--src/commands.rs159
-rw-r--r--src/complete.rs (renamed from src/com.rs)0
-rw-r--r--src/edi.rs124
-rw-r--r--src/edi/st.rs10
-rw-r--r--src/lsp.rs13
-rw-r--r--src/main.rs5
-rw-r--r--src/menu.rs6
-rw-r--r--src/rnd.rs34
-rw-r--r--src/text.rs32
10 files changed, 349 insertions, 44 deletions
diff --git a/src/bar.rs b/src/bar.rs
index 93ee057..75d9e5b 100644
--- a/src/bar.rs
+++ b/src/bar.rs
@@ -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
diff --git a/src/edi.rs b/src/edi.rs
index 3408ebf..2d35504 100644
--- a/src/edi.rs
+++ b/src/edi.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],
diff --git a/src/lsp.rs b/src/lsp.rs
index df96a9b..8461050 100644
--- a/src/lsp.rs
+++ b/src/lsp.rs
@@ -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
+ }
+}
diff --git a/src/rnd.rs b/src/rnd.rs
index 3a976f8..7c3138f 100644
--- a/src/rnd.rs
+++ b/src/rnd.rs
@@ -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));
}
}