A simple CPU rendered GUI IDE experience.
add open function
| -rw-r--r-- | src/edi.rs | 239 | ||||
| -rw-r--r-- | src/lsp.rs | 14 | ||||
| -rw-r--r-- | src/main.rs | 1 | ||||
| -rw-r--r-- | src/text.rs | 93 |
4 files changed, 228 insertions, 119 deletions
@@ -27,7 +27,7 @@ use crate::bar::Bar; use crate::com::Complete; use crate::hov::{self, Hovr}; use crate::lsp::{self, Client, PathURI, RedrawAfter, RequestError, Rq}; -use crate::text::{self, CoerceOption, Mapping, TextArea}; +use crate::text::{self, CoerceOption, Mapping, SortTedits, TextArea}; use crate::{ BoolRequest, CDo, ClickHistory, CompletionAction, CompletionState, Hist, act, alt, ctrl, filter, shift, sig, sym, trm, @@ -36,7 +36,7 @@ use crate::{ pub struct Editor { pub files: HashMap<PathBuf, Editor>, pub text: TextArea, - pub origin: Option<PathBuf>, + pub origin: Option<PathBuf>, // ie active pub state: State, pub bar: Bar, pub workspace: Option<PathBuf>, @@ -232,24 +232,28 @@ impl Editor { // } pub fn save(&mut self) { - let t = self.text.rope.to_string(); - std::fs::write(self.origin.as_ref().unwrap(), &t).unwrap(); self.bar.last_action = "saved".into(); lsp!(self + p).map(|(l, o)| { let v = l.runtime.block_on(l.format(o)).unwrap(); for v in v.coerce() { - self.text.apply_adjusting(&v); + if let Err(()) = self.text.apply_adjusting(&v) { + eprintln!("unhappy fmt") + } } + self.text.cursor = + self.text.cursor.min(self.text.rope.len_chars()); change!(self); self.hist.push(&self.text); l.notify::<lsp_notification!("textDocument/didSave")>( &DidSaveTextDocumentParams { text_document: o.tid(), - text: Some(t), + text: Some(self.text.rope.to_string()), }, ) .unwrap(); }); + let t = self.text.rope.to_string(); + std::fs::write(self.origin.as_ref().unwrap(), &t).unwrap(); self.mtime = Self::modify(self.origin.as_deref()); } pub fn poll(&mut self) { @@ -764,77 +768,58 @@ impl Editor { } Some(Do::SymbolsSelect) => { let State::Symbols(Rq { result: Some(x), .. }) = - &mut self.state + &self.state else { unreachable!() }; - let x = x.sel(); // TODO dedup - let _: anyhow::Result<()> = try bikeshed _ { + let x = x.sel().clone(); + if let Err(e) = try bikeshed anyhow::Result<()> { let f = x .location .uri .to_file_path() .map_err(|()| anyhow::anyhow!("dammit"))? .canonicalize()?; - self.origin = Some(f.clone()); - let r = self.text.r; - self.text = default(); - self.text.r = r; - let new = std::fs::read_to_string(f) - .map_err(anyhow::Error::from)?; - self.text.insert(&new)?; - self.text.cursor = self.text - .l_position(x.location.range.start) - .ok_or(anyhow::anyhow!("dangit"))?; - self.text.scroll_to_cursor_centering(); - self.hist = Hist { - history: vec![], - redo_history: vec![], - last: self.text.clone(), - last_edit: Instant::now(), - changed: false, - }; + if Some(&f) != self.origin.as_ref() { + self.open(&f, window)?; + } + self.state = State::Default; self.complete = CompletionState::None; - self.mtime = Self::modify(self.origin.as_deref()); - lsp!(self + p).map(|(x, origin)| { - ( - self.def, - self.semantic_tokens, - self.inlay, - self.sig_help, - self.complete, - self.hovering, - ) = ( - default(), - default(), - default(), - default(), - default(), - default(), - ); - x.open(&origin, new).unwrap(); - x.rq_semantic_tokens( - &mut self.semantic_tokens, - origin, - Some(window.clone()), - ) - .unwrap(); - }); - self.state = State::Default; - self.bar.last_action = "open".to_string(); - }; + let p = self.text + .l_position(x.location.range.start).ok_or(anyhow::anyhow!("rah"))?; + self.text.cursor = p; + self.text.scroll_to_cursor_centering(); + } { + log::error!("alas! {e}"); + } } Some(Do::CodeAction) => { if let Some((lsp, f)) = lsp!(self + p) { - let r = lsp.request::<lsp_request!("textDocument/codeAction")>(&CodeActionParams { - text_document: f.tid(), range: self.text.to_l_range(self.text.cursor..self.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| { - // self.text.l_range(x.range).unwrap().contains(&self.text.cursor) - // }).cloned().collect()) } else { vec![] }, - ..default() - }, work_done_progress_params: default(), partial_result_params: default() }).unwrap(); + let r = lsp + .request::<lsp_request!("textDocument/codeAction")>( + &CodeActionParams { + text_document: f.tid(), + range: self + .text + .to_l_range( + self.text.cursor..self.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| { + // self.text.l_range(x.range).unwrap().contains(&self.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(async { r.0.await })); @@ -871,7 +856,8 @@ impl Editor { .0, ) .unwrap(); - let mut f_ = |edits: &[SnippetTextEdit]| { + let mut f_ = |edits: &mut [SnippetTextEdit]| { + edits.sort_tedits(); // let mut first = false; for edit in edits { self.text.apply_snippet_tedit(edit).unwrap(); @@ -886,7 +872,7 @@ impl Editor { if x.text_document.uri != f.tid().uri { continue; } - f_(&x.edits); + f_(&mut { x }.edits); }, Some(WorkspaceEdit { document_changes: @@ -905,7 +891,7 @@ impl Editor { if text_document.uri != f.tid().uri { continue; } - f_(&edits); + f_(&mut { edits }); } x => log::error!("didnt apply {x:?}"), }; @@ -1097,12 +1083,15 @@ impl Editor { s + ed.new_text.chars().count(); } } - for additional in - additional_text_edits.into_iter().flatten() + if let Some(mut additional_tedits) = + additional_text_edits { - self.text - .apply_adjusting(&additional) - .unwrap(); + additional_tedits.sort_tedits(); + for additional in additional_tedits { + self.text + .apply_adjusting(&additional) + .unwrap(); + } } if self.hist.record(&self.text) { change!(self); @@ -1180,48 +1169,7 @@ impl Editor { change!(self); } Some(Do::OpenFile(x)) => { - let _ = try { - self.origin = Some(PathBuf::from(&x).canonicalize()?); - self.text = TextArea::default(); - let new = std::fs::read_to_string(x)?; - self.text.insert(&new); - self.text.cursor = 0; - self.hist = Hist { - history: vec![], - redo_history: vec![], - last: self.text.clone(), - last_edit: Instant::now(), - changed: false, - }; - self.complete = CompletionState::None; - self.mtime = Self::modify(self.origin.as_deref()); - - lsp!(self + p).map(|(x, origin)| { - ( - self.def, - self.semantic_tokens, - self.inlay, - self.sig_help, - self.complete, - self.hovering, - ) = ( - default(), - default(), - default(), - default(), - default(), - default(), - ); - x.open(&origin, new).unwrap(); - x.rq_semantic_tokens( - &mut self.semantic_tokens, - origin, - Some(window.clone()), - ) - .unwrap(); - }); - self.bar.last_action = "open".to_string(); - }; + _ = self.open(Path::new(&x), window); } Some( Do::MoveCursor | Do::ExtendSelectionToMouse | Do::Hover, @@ -1274,6 +1222,71 @@ impl Editor { } ControlFlow::Continue(()) } + + pub fn open( + &mut self, + x: &Path, + w: &mut Arc<Window>, + ) -> anyhow::Result<()> { + self.origin = Some(x.canonicalize()?.to_path_buf()); + let r = self.text.r; + self.text = TextArea::default(); + let new = std::fs::read_to_string(x)?; + self.text.insert(&new)?; + self.hist = Hist { + history: vec![], + redo_history: vec![], + last: self.text.clone(), + last_edit: Instant::now(), + changed: false, + }; + + ( + self.text.r, + self.text.cursor, + self.text.vo, + self.chist, + self.state, + self.complete, + self.mtime, + self.bar.last_action, + ) = ( + r, + 0, + 0, + default(), + State::Default, + CompletionState::None, + Self::modify(self.origin.as_deref()), + "open".to_string(), + ); + + lsp!(self + p).map(|(x, origin)| { + ( + self.def, + self.semantic_tokens, + self.inlay, + self.sig_help, + self.complete, + self.hovering, + ) = ( + default(), + default(), + default(), + default(), + default(), + default(), + ); + x.open(&origin, new).unwrap(); + x.rq_semantic_tokens( + &mut self.semantic_tokens, + origin, + Some(w.clone()), + ) + .unwrap(); + }); + Ok(()) + } } use NamedKey::*; @@ -470,7 +470,7 @@ impl Client { f: &Path, ) -> impl Future< Output = Result< - <Formatting as Request>::Result, + Option<Vec<TextEdit>>, RequestError<Formatting>, >, > { @@ -489,7 +489,9 @@ impl Client { }, ) .unwrap() - .0 + .0.map(|x| { + x.map(|x|x.map(|mut x| { x.sort_tedits(); x })) + }) } pub fn rq_semantic_tokens( &'static self, @@ -549,10 +551,12 @@ impl Client { .unwrap(); match r { None => t.enter(), - Some(r) => + Some(mut r) => { + r.sort_tedits(); for f in r { t.apply_snippet_tedit(&f).unwrap(); - }, + } + } } } } @@ -1045,7 +1049,7 @@ impl<T, U, F: FnMut(T) -> U, Fu: Future<Output = T>> Map_<T, U, F> for Fu { } use tokio::task; -use crate::text::{CoerceOption, TextArea}; +use crate::text::{CoerceOption, SortTedits, TextArea}; #[derive(Debug)] pub enum OnceOff<T> { Waiting(task::JoinHandle<Result<T, oneshot::error::RecvError>>), diff --git a/src/main.rs b/src/main.rs index 699d6f4..0557ca2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,6 +35,7 @@ #![allow(incomplete_features, irrefutable_let_patterns)] mod act; mod edi; +// mod new; mod rnd; mod sym; mod trm; diff --git a/src/text.rs b/src/text.rs index c5e7b5b..56e6b36 100644 --- a/src/text.rs +++ b/src/text.rs @@ -1,4 +1,4 @@ -use std::cmp::min; +use std::cmp::{Reverse, min}; use std::fmt::{Debug, Display}; use std::ops::{Deref, Not as _, Range, RangeBounds}; use std::path::Path; @@ -489,6 +489,7 @@ impl TextArea { self.insert_at(begin, &x.new_text).map_err(|_| ())?; Ok((begin, end)) } + pub fn apply_adjusting(&mut self, x: &TextEdit) -> Result<(), ()> { let (b, e) = self.apply(&x)?; if e < self.cursor { @@ -1855,3 +1856,93 @@ fn apply() { .unwrap(); assert_eq!(t.cursor, 8); } +#[test] +fn apply2() { + let mut t = TextArea::default(); + + t.insert( + "impl Editor { // 0 + pub fn open(f: &Path) { // 1 +// 2 + let new = std::fs::read_to_string(f) // 3 + .map_err(anyhow::Error::from)?; // 4 + } +}", + ) + .unwrap(); + + use lsp_types::Range; + let mut th = [ + TextEdit { + range: Range { + start: Position { line: 1, character: 0 }, + end: Position { line: 1, character: 4 }, + }, + new_text: "".into(), + }, + TextEdit { + range: Range { + start: Position { line: 2, character: 0 }, + end: Position { line: 3, character: 1 }, + }, + new_text: "".into(), + }, + TextEdit { + range: Range { + start: Position { line: 3, character: 9 }, + end: Position { line: 3, character: 9 }, + }, + new_text: "let new =\n".into(), + }, + TextEdit { + range: Range { + start: Position { line: 3, character: 20 }, + end: Position { line: 3, character: 29 }, + }, + new_text: "".into(), + }, + TextEdit { + range: Range { + start: Position { line: 3, character: 56 }, + end: Position { line: 4, character: 24 }, + }, + new_text: "".into(), + }, + TextEdit { + range: Range { + start: Position { line: 6, character: 1 }, + end: Position { line: 6, character: 1 }, + }, + new_text: "\n".into(), + }, + ]; + th.sort_tedits(); + for th in th { + t.apply(&th).unwrap(); + println!("=>\n{}", t.rope); + } + assert_eq!( + t.rope.to_string(), + "impl Editor { // 0 + pub fn open(f: &Path) { // 1 + let new = + std::fs::read_to_string(f).map_err(anyhow::Error::from)?; // 4 + } +} +" + ); +} + +pub trait SortTedits { + fn sort_tedits(&mut self); +} +impl SortTedits for [TextEdit] { + fn sort_tedits(&mut self) { + self.as_mut().sort_by_key(|t| Reverse(t.range.start)); + } +} +impl SortTedits for [SnippetTextEdit] { + fn sort_tedits(&mut self) { + self.as_mut().sort_by_key(|t| Reverse(t.text_edit.range.start)); + } +} |