A simple CPU rendered GUI IDE experience.
add open function
bendn 7 weeks ago
parent ff01ca8 · commit bdd03a4
-rw-r--r--src/edi.rs239
-rw-r--r--src/lsp.rs14
-rw-r--r--src/main.rs1
-rw-r--r--src/text.rs93
4 files changed, 228 insertions, 119 deletions
diff --git a/src/edi.rs b/src/edi.rs
index 5b8bd15..f6970a8 100644
--- a/src/edi.rs
+++ b/src/edi.rs
@@ -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::*;
diff --git a/src/lsp.rs b/src/lsp.rs
index 8708afc..b80a37e 100644
--- a/src/lsp.rs
+++ b/src/lsp.rs
@@ -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));
+ }
+}