A simple CPU rendered GUI IDE experience.
-rw-r--r--Cargo.toml1
-rw-r--r--src/act.rs201
-rw-r--r--src/lsp.rs7
-rw-r--r--src/main.rs83
-rw-r--r--src/text.rs2
5 files changed, 233 insertions, 61 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 27dd9d1..2dda033 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -56,6 +56,7 @@ tokio-util = { version = "0.7.17", features = ["rt"] }
scopeguard = "1.2.0"
arc-swap = "1.7.1"
atools = "0.1.10"
+swizzle = "0.1.0"
[profile.dev.package]
rust-analyzer.opt-level = 3
diff --git a/src/act.rs b/src/act.rs
index f28ca29..fc4a221 100644
--- a/src/act.rs
+++ b/src/act.rs
@@ -1,58 +1,185 @@
use dsb::Cell;
use dsb::cell::Style;
-use lsp_types::CodeAction;
+use itertools::Itertools;
+use lsp_types::{CodeAction, CodeActionKind};
+
+#[derive(Debug, Clone)]
+enum N<T> {
+ One(T),
+ Many(Vec<T>, Entry, Vec<String>),
+}
+#[derive(Debug, Clone, Copy)]
+enum Entry {
+ Inside(usize),
+ Outside(usize),
+}
#[derive(Debug, Clone)]
pub struct CodeActions {
- pub inner: Vec<CodeAction>,
+ pub inner: N<Vec<CodeAction>>,
pub selection: usize,
pub vo: usize,
}
use crate::FG;
-use crate::text::col;
+use crate::text::{col, set_a};
const N: usize = 13;
impl CodeActions {
- pub fn next(&mut self) {
- let n = self.inner.len();
- self.selection += 1;
- if self.selection == n {
- self.vo = 0;
- self.selection = 0;
+ /// there is a clear most intuitive way to do this, but. it is hard.
+ pub fn new(x: Vec<CodeAction>) -> Self {
+ let has_groups = x.iter().any(|x| x.group.is_some());
+ let inner = if has_groups {
+ let lem: Vec<Vec<CodeAction>> = x
+ .into_iter()
+ .chunk_by(|x| x.group.clone().unwrap_or("0".into()))
+ .into_iter()
+ .map(|x| x.1.collect::<Vec<_>>())
+ .collect();
+ let g = lem
+ .iter()
+ .map(|x| x[0].group.clone().unwrap_or("misc".into()))
+ .collect::<Vec<_>>();
+ N::Many(lem, Entry::Outside(0), g)
+ } else {
+ N::One(x)
+ };
+ Self { inner, selection: 0, vo: 0 }
+ }
+
+ pub fn down(&mut self) {
+ let mut adj = |y: &mut usize, max| {
+ *y += 1;
+ if *y == max {
+ self.vo = 0;
+ *y = 0;
+ }
+ if *y >= self.vo + 13 {
+ self.vo += 1;
+ }
+ };
+ match &mut self.inner {
+ N::Many(x, Entry::Outside(y), so) => {
+ let n = x.len();
+ adj(y, n);
+ }
+ N::Many(x, Entry::Inside(g_sel), _) => {
+ let z = &x[*g_sel];
+ let n = z.len();
+
+ // TODO: think about this
+ adj(&mut self.selection, n);
+ }
+ N::One(x) => {
+ let n = x.len();
+ adj(&mut self.selection, n);
+ }
+ };
+ }
+ pub fn innr(&self) -> Option<&[CodeAction]> {
+ match &self.inner {
+ N::One(x) => Some(x),
+ N::Many(x, Entry::Inside(y), _) => Some(&x[*y]),
+ N::Many(_, Entry::Outside(_), _) => None,
}
- if self.selection >= self.vo + 13 {
- self.vo += 1;
+ }
+ pub fn left(&mut self) {
+ match &mut self.inner {
+ N::Many(items, x @ Entry::Inside(_), items1) => {
+ let Entry::Inside(y) = x else { unreachable!() };
+ *x = Entry::Outside(*y);
+ }
+ _ => {}
}
}
-
- pub fn sel(&self) -> &CodeAction {
- &self.inner[self.selection]
+ pub fn right(&mut self) -> Option<&CodeAction> {
+ match &mut self.inner {
+ N::One(x) => Some(&x[self.selection]),
+ N::Many(y, Entry::Inside(x), _) =>
+ Some(&y[*x][self.selection]),
+ N::Many(_, y, _) => {
+ let x =
+ if let Entry::Outside(x) = y { *x } else { panic!() };
+ *y = Entry::Inside(x);
+ None
+ }
+ }
}
#[lower::apply(saturating)]
- pub fn back(&mut self) {
- let n = self.inner.len();
- if self.selection == 0 {
- self.vo = n - N;
- self.selection = n - 1;
+ pub fn up(&mut self) {
+ if let Some(x) = self.innr() {
+ let n = x.len();
+ if self.selection == 0 {
+ self.vo = n - N;
+ self.selection = n - 1;
+ } else {
+ self.selection -= 1;
+ if self.selection < self.vo {
+ self.vo -= 1;
+ }
+ }
} else {
- self.selection -= 1;
- if self.selection < self.vo {
- self.vo -= 1;
+ match &mut self.inner {
+ N::Many(_, Entry::Outside(y), z) => {
+ let n = z.len();
+ if *y == 0 {
+ self.vo = n - N;
+ *y = n - 1;
+ } else {
+ *y = *y - 1;
+ if *y < self.vo {
+ self.vo -= 1;
+ }
+ }
+ }
+ _ => unreachable!(),
}
}
}
pub fn maxc(&self) -> usize {
- self.inner
- .iter()
- .map(|x| x.title.chars().count())
- .max()
- .unwrap_or(0)
+ match &self.inner {
+ N::One(x) => x
+ .iter()
+ .map(|x| x.title.chars().count() + 2)
+ .max()
+ .unwrap_or(0),
+ N::Many(x, _, g) => x
+ .iter()
+ .flatten()
+ .map(|x| &x.title)
+ .chain(g)
+ .map(|x| x.chars().count() + 2)
+ .max()
+ .unwrap_or(0),
+ }
}
pub fn write(&self, c: usize) -> Vec<Cell> {
let mut into = vec![];
- for (el, i) in self.inner.iter().zip(0..) {
- write(el, c, self.selection == i, &mut into);
+ if let Some(x) = self.innr() {
+ for (el, i) in x.iter().zip(0..).skip(self.vo).take(13) {
+ write(el, c, self.selection == i, &mut into);
+ }
+ } else if let N::Many(_, Entry::Outside(n), z) = &self.inner {
+ for (el, i) in z.iter().skip(self.vo).zip(0..).take(13) {
+ let bg = if *n == i {
+ col!("#262d3b")
+ } else {
+ col!("#1c212b")
+ };
+
+ let mut to = vec![
+ Cell {
+ style: Style { bg, color: FG, flags: 0 },
+ ..Default::default()
+ };
+ c
+ ];
+ to.iter_mut()
+ .zip(el.chars())
+ .for_each(|(a, b)| a.letter = Some(b));
+ into.extend(to);
+ // write(el, c, self.selection == i, &mut into);
+ }
}
into
}
@@ -66,7 +193,23 @@ fn write(x: &CodeAction, c: usize, selected: bool, to: &mut Vec<Cell>) {
};
c
];
+
+ let t = match &x.kind {
+ Some(x) if x == &CodeActionKind::QUICKFIX => '󰁨',
+ Some(x)
+ if x == &CodeActionKind::REFACTOR
+ || x == &CodeActionKind::REFACTOR_EXTRACT
+ || x == &CodeActionKind::REFACTOR_INLINE
+ || x == &CodeActionKind::REFACTOR_REWRITE =>
+ '󰷥',
+ Some(x) if x == &CodeActionKind::SOURCE => '󱇧',
+ _ => '', /* 󱢇☭ */
+ };
+ into[0].style.color = col!("#E5C07B");
+ into[0].style.bg = set_a(into[0].style.color, 0.5);
+ into[0].letter = Some(t);
into.iter_mut()
+ .skip(1)
.zip(x.title.chars())
.for_each(|(a, b)| a.letter = Some(b));
to.extend(into);
diff --git a/src/lsp.rs b/src/lsp.rs
index 2af09ac..323ebac 100644
--- a/src/lsp.rs
+++ b/src/lsp.rs
@@ -316,6 +316,7 @@ impl Client {
},
Err(e) => return Err(e.into()),
};
+ // dbg!(&x);
match x.clone() {
DocumentDiagnosticReportResult::Report(
DocumentDiagnosticReport::Full(
@@ -483,7 +484,10 @@ pub fn run(
data_support: Some(true),
resolve_support: Some(CodeActionCapabilityResolveSupport { properties: vec!["edit".to_string()] }),
code_action_literal_support: Some(CodeActionLiteralSupport { code_action_kind: CodeActionKindLiteralSupport { value_set: [
- "", "Empty", "QuickFix", "Refactor", "RefactorExtract", "RefactorInline", "RefactorRewrite", "Source", "SourceOrganizeImports", "quickfix", "refactor", "refactor.extract", "refactor.inline", "refactor.rewrite", "source", "source.organizeImports"
+ "", "Empty", "QuickFix", "Refactor",
+ "RefactorExtract", "RefactorInline", "RefactorRewrite", "Source",
+ "SourceOrganizeImports", "quickfix", "refactor", "refactor.extract",
+ "refactor.inline", "refactor.rewrite", "source", "source.organizeImports"
].map(String::from).into()} }),
..default()
}
@@ -648,6 +652,7 @@ pub fn run(
"rangeExclusiveHints": { "enable": true },
"closureCaptureHints": { "enable": true },
},
+ "assist": { "preferSelf": true },
"checkOnSave": true,
"diagnostics": { "enable": true },
"semanticHighlighting": {
diff --git a/src/main.rs b/src/main.rs
index 962ccde..f1cca39 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,6 +1,7 @@
// this looks pretty good though
#![feature(tuple_trait, unboxed_closures, fn_traits)]
#![feature(
+ current_thread_id,
vec_try_remove,
iter_next_chunk,
iter_array_chunks,
@@ -33,7 +34,6 @@
)]
#![allow(incomplete_features, redundant_semicolons)]
use std::borrow::Cow;
-use std::collections::HashMap;
use std::iter::once;
mod act;
use std::num::NonZeroU32;
@@ -84,7 +84,8 @@ mod sni;
mod text;
mod winit_app;
fn main() {
- let x = 4;
+ let _x = 4;
+ // let x = HashMap::new();
unsafe { std::env::set_var("CARGO_UNSTABLE_RUSTC_UNICODE", "true") };
env_logger::init();
// lsp::x();
@@ -211,6 +212,8 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
.and_then(|x| x.canonicalize().ok());
let c = workspace.as_ref().zip(origin.clone()).map(
|(workspace, origin)| {
+ let dh = std::panic::take_hook();
+ let main = std::thread::current_id();
// let mut c = Command::new("rust-analyzer")
// .stdin(Stdio::piped())
// .stdout(Stdio::piped())
@@ -221,9 +224,15 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
std::thread::Builder::new()
.name("Rust Analyzer".into())
.stack_size(1024 * 1024 * 8)
- .spawn(|| {
- std::panic::set_hook(Box::new(|info| {
- println!("RA panic @ {}", info.location().unwrap());
+ .spawn(move || {
+ let ra = std::thread::current_id();
+ std::panic::set_hook(Box::new(move |info| {
+ // iz
+ if std::thread::current_id() == main {
+ dh(info);
+ } else if std::thread::current_id() == ra || std::thread::current().name().is_some_and(|x| x.starts_with("RA")) {
+ println!("RA panic @ {}", info.location().unwrap());
+ }
}));
rust_analyzer::bin::run_server(b)
})
@@ -380,8 +389,8 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
for rq in l.req_rx.try_iter() {
match rq {
LRq { method: "workspace/diagnostic/refresh", .. } => {
- let x = l.pull_diag(o.into(), diag.result.clone());
- diag.request(l.runtime.spawn(x));
+ // let x = l.pull_diag(o.into(), diag.result.clone());
+ // diag.request(l.runtime.spawn(x));
},
rq =>
log::debug!("discarding request {rq:?}"),
@@ -398,11 +407,17 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
};
if let State::CodeAction(x) = &mut state {
x.poll(|x, _| {
- Some(act::CodeActions {selection:0, vo:0, inner: x.ok()??.into_iter().map(|x| match x {
+ let lems: Vec<CodeAction> = x.ok()??.into_iter().map(|x| match x {
CodeActionOrCommand::CodeAction(x) => x,
_ => panic!("alas we dont like these"),
- }).collect(),})
- },&l.runtime);
+ }).collect();
+ if lems.is_empty() {
+ bar.last_action = "no code actions available".into();
+ None
+ } else {
+ Some(act::CodeActions::new(lems))
+ }
+ },&l.runtime);
}
def.poll(|x, _|
x.ok().flatten().and_then(|x| match &x {
@@ -704,7 +719,7 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
match diag.data.as_ref().unwrap_or_default().get("rendered") {
Some(x) if let Some(x) = x.as_str() => {
let mut t = pattypan::term::Terminal::new((95, (r.saturating_sub(5)) as _), false);
- for b in x.replace('\n', "\r\n").bytes(){ t.rx(b,std::fs::File::open("/dev/null").unwrap().as_fd()); }
+ for b in x.replace('\n', "\r\n").replace("⸬", ":").replace("/home/os", "").bytes(){ t.rx(b,std::fs::File::open("/dev/null").unwrap().as_fd()); }
let y_lim = t.cells.rows().position(|x| x.iter().all(_.letter.is_none())).unwrap_or(20);
let c =t.cells.c() as usize;
let Some(x_lim) = t.cells.rows().map(_.iter().rev().take_while(_.letter.is_none()).count()).map(|x|
@@ -756,14 +771,12 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
State::CodeAction(Rq{ result :Some(x), ..}) => 'out: {
let m = x.maxc();
let c = x.write(m);
- dbg!(&c);
let (_x, _y) = text.cursor_visual();
let _x = _x + text.line_number_offset()+1;
let Some(_y) = _y.checked_sub(text.vo) else {
println!("rah");
break 'out };
let Ok((is_above,left, top, w, mut h)) = place_around((_x, _y), &mut fonts, i.as_mut(), &c, m, ppem, ls, 0., 0., 0.)else { println!("ra?"); break 'out};
- dbg!(c);
i.r#box((left .saturating_sub(1) as _, top.saturating_sub(1) as _), w as _,h as _, BORDER);
},
_ =>{},
@@ -1039,6 +1052,7 @@ hovering.request = (DropH::new(handle), cursor_position).into();
if let Some((lsp, path)) = lsp!() {
sig_help.request(lsp.runtime.spawn(window.redraw_after(lsp.request_sig_help(path, text.cursor()))));
}
+ hist.last.cursor = text.cursor;
text.setc();
}
Some(Do::ExtendSelectionToMouse) => {
@@ -1145,8 +1159,14 @@ hovering.request = (DropH::new(handle), cursor_position).into();
Some(Do::CodeAction) => {
if let Some((lsp, f)) = lsp!() {
let r = lsp.request::<lsp_request!("textDocument/codeAction")>(&CodeActionParams {
- text_document: f.tid(), range: text.to_l_range(text.beginning_of_line(text.cursor).unwrap()
- ..text.eol(text.cursor)).unwrap(), context: CodeActionContext { trigger_kind: Some(CodeActionTriggerKind::INVOKED), ..default() }, work_done_progress_params: default(), partial_result_params: default() }).unwrap();
+ text_document: f.tid(), range: text.to_l_range(text.cursor..text.cursor).unwrap(), context: CodeActionContext {
+ trigger_kind: Some(CodeActionTriggerKind::INVOKED),
+ // diagnostics: if let Some((lsp, p)) = lsp!() && let uri = Url::from_file_path(p).unwrap() && let Some(diag) = lsp.diagnostics.get(&uri, &lsp.diagnostics.guard()) { dbg!(diag.iter().filter(|x| {
+
+ // text.l_range(x.range).unwrap().contains(&text.cursor)
+ // }).cloned().collect()) } else { vec![] },
+ ..default()
+ }, work_done_progress_params: default(), partial_result_params: default() }).unwrap();
let mut r2 = Rq::default();
r2.request(lsp.runtime.spawn(
@@ -1158,29 +1178,32 @@ hovering.request = (DropH::new(handle), cursor_position).into();
);
}
}
- Some(Do::CASelect(act)) if let Some((lsp,f)) = lsp!() => {
+ Some(Do::CASelectLeft) => {
+ let State::CodeAction(Rq{ result: Some(c), .. }) = &mut state else { panic!()};
+ c.left();
+ }
+ Some(Do::CASelectRight) =>'out: {
+ let Some((lsp,f)) = lsp!() else {unreachable!()};
+ let State::CodeAction(Rq{ result: Some(c), .. }) = &mut state else { panic!()};
+ let Some(act) = c.right() else { break 'out };
+ let act = act.clone();
+ state = State::Default;
+ hist.last.cursor = text.cursor;
hist.test_push(&text);
- let act = act.sel();
let act = lsp.runtime.block_on(
lsp.request::<CodeActionResolveRequest>(&act).unwrap().0
).unwrap();
let mut f_ = |edits: &[SnippetTextEdit]|{
- let mut first = false;
+ // let mut first = false;
for SnippetTextEdit { text_edit, insert_text_format ,..}in edits {
match insert_text_format {
Some(InsertTextFormat::SNIPPET) => {
text.apply_snippet(&text_edit).unwrap()
},
_ => {
- if first {
- let (b, e) = text.apply(&text_edit).unwrap();
- log::error!("dont account for this case yet");
- } else {
- text.apply_adjusting(text_edit).unwrap();
- }
+ text.apply_adjusting(text_edit).unwrap();
}
}
- first = false;
}
};
match act.edit {
@@ -1219,14 +1242,13 @@ hovering.request = (DropH::new(handle), cursor_position).into();
change!();
hist.record(&text);
}
- Some(Do::CASelect(_)) => {}
Some(Do::CASelectNext) => {
let State::CodeAction(Rq{ result: Some(c), .. }) = &mut state else { panic!()};
- c.next();
+ c.down();
}
Some(Do::CASelectPrev) => {
let State::CodeAction(Rq{ result: Some(c), .. }) = &mut state else { panic!()};
- c.back();
+ c.up();
}
Some(Do::Reinsert | Do::GoToDefinition) => panic!(),
Some(Do::Save) => match &origin {
@@ -1577,11 +1599,12 @@ Default => {
K(_) => _ [Edit],
M(_) => _,
},
-CodeAction(Rq<act::CodeActions, Option<CodeActionResponse>,()> => Rq { result : Some(act), request: None, }) => {
+CodeAction(Rq<act::CodeActions, Option<CodeActionResponse>,()> => Rq { result : Some(_x), request: None, }) => {
K(Key::Named(Tab) if shift()) => _ [CASelectPrev],
K(Key::Named(ArrowDown | Tab)) => _ [CASelectNext],
K(Key::Named(ArrowUp)) => _ [CASelectPrev],
- K(Key::Named(Enter)) => Default [CASelect(act::CodeActions => act)],
+ K(Key::Named(Enter | ArrowRight)) => _ [CASelectRight],
+ K(Key::Named(ArrowLeft)) => _ [CASelectLeft],
},
CodeAction(Rq<act::CodeActions, Option<CodeActionResponse>,(), RequestError<lsp_request!("textDocument/codeAction")>> => rq) => {
K(Key::Named(Escape)) => Default,
diff --git a/src/text.rs b/src/text.rs
index 80d30b2..0c963fc 100644
--- a/src/text.rs
+++ b/src/text.rs
@@ -487,7 +487,7 @@ impl TextArea {
self.vo +=
x.new_text.chars().filter(|&x| x == '\n').count();
}
- let removed = dbg!(e - b);
+ let removed = e - b;
self.cursor += x.new_text.chars().count();
self.cursor -= removed; // compensate
// text.cursor += additional.new_text.chars().count(); // compensate