A simple CPU rendered GUI IDE experience.
Diffstat (limited to 'src/edi/input_handlers/keyboard.rs')
-rw-r--r--src/edi/input_handlers/keyboard.rs888
1 files changed, 888 insertions, 0 deletions
diff --git a/src/edi/input_handlers/keyboard.rs b/src/edi/input_handlers/keyboard.rs
new file mode 100644
index 0000000..eb624ac
--- /dev/null
+++ b/src/edi/input_handlers/keyboard.rs
@@ -0,0 +1,888 @@
+use std::mem::take;
+use std::ops::ControlFlow;
+use std::path::{Path, PathBuf};
+use std::sync::Arc;
+
+use Default::default;
+use lsp_server::ResponseError;
+use lsp_types::request::*;
+use lsp_types::*;
+use regex::Regex;
+use ropey::Rope;
+use rust_analyzer::lsp::ext::OnTypeFormatting;
+use rust_fsm::StateMachine;
+use tokio_util::task::AbortOnDropHandle as DropH;
+use winit::event::KeyEvent;
+use winit::keyboard::Key;
+use winit::window::Window;
+
+use crate::edi::*;
+
+impl Editor {
+ pub fn keyboard(
+ &mut self,
+ event: KeyEvent,
+ window: &mut Arc<dyn Window>,
+ ) -> ControlFlow<()> {
+ let mut o: Option<Do> = self
+ .state
+ .consume(Action::K(event.logical_key.clone()))
+ .unwrap();
+
+ match o {
+ Some(Do::Reinsert) =>
+ o = self
+ .state
+ .consume(Action::K(event.logical_key.clone()))
+ .unwrap(),
+ _ => {}
+ }
+ match o {
+ Some(Do::Escape) => {
+ take(&mut self.requests.complete);
+ take(&mut self.requests.sig_help);
+ self.text.cursor.alone();
+ }
+ Some(Do::Comment(p)) => {
+ ceach!(self.text.cursor, |cursor| {
+ Some(
+ if let Some(x) = cursor.sel
+ && matches!(p, State::Selection)
+ {
+ self.text.comment(x.into());
+ } else {
+ self.text
+ .comment(cursor.position..cursor.position);
+ },
+ )
+ });
+ self.text.cursor.clear_selections();
+ change!(self, window.clone());
+ }
+ Some(Do::SpawnTerminal) => {
+ if let Err(e) = trm::toggle(
+ self.workspace
+ .as_deref()
+ .unwrap_or(Path::new("/home/os/")),
+ ) {
+ log::error!("opening terminal failed {e}");
+ }
+ }
+ Some(Do::MatchingBrace) => {
+ if let Some((l, f)) = lsp!(self + p) {
+ l.matching_brace(f, &mut self.text);
+ }
+ }
+ Some(Do::DeleteBracketPair) => {
+ if let Some((l, f)) = lsp!(self + p) {
+ if let Ok(x) = l.matching_brace_at(
+ f,
+ self.text.cursor.positions(&self.text.rope),
+ ) {
+ use itertools::Itertools;
+ for p in
+ // self.text.cursor.iter()
+ x
+ .iter()
+ .flatten()
+ .flat_map(|(a, b)| {
+ [a, b].map(|c| {
+ self.text
+ .rope
+ .l_position(*c)
+ .unwrap()
+ })
+ })
+ .sorted()
+ .rev()
+ {
+ self.text.remove(p..p + 1).unwrap();
+ }
+ }
+ }
+ }
+ Some(Do::Symbols) =>
+ if let Some((lsp, o)) = lsp!(self + p) {
+ let mut q = Rq::new(
+ lsp.runtime.spawn(
+ lsp.workspace_symbols("".into())
+ .map(|x| x.anonymize())
+ .map(|x| {
+ x.map(|x| {
+ x.map(SymbolsList::Workspace)
+ })
+ }),
+ ),
+ );
+ q.result = Some(Symbols::new(
+ self.tree.as_deref().unwrap(),
+ self.text.bookmarks.clone(),
+ o.into(),
+ ));
+ self.state = State::Symbols(q);
+ },
+ Some(Do::SwitchType) =>
+ if let Some((lsp, p)) = lsp!(self + p) {
+ let State::Symbols(Rq { result: Some(x), request }) =
+ &mut self.state
+ else {
+ unreachable!()
+ };
+ x.data.3 = sym::SymbolsType::Document;
+ let p = p.to_owned();
+ take(&mut x.data.0);
+ *request = Some((
+ DropH::new(lsp.runtime.spawn(async move {
+ lsp.document_symbols(&p)
+ .await
+ .anonymize()
+ .map(|x| x.map(SymbolsList::Document))
+ })),
+ (),
+ ));
+ },
+ Some(Do::ProcessCommand(mut x, z)) =>
+ match Cmds::complete_or_accept(&z) {
+ crate::menu::generic::CorA::Complete => {
+ x.tedit.rope =
+ Rope::from_str(&format!("{} ", z.name()));
+ x.tedit.cursor.end(&x.tedit.rope);
+ self.state = State::Command(x);
+ }
+ crate::menu::generic::CorA::Accept => {
+ if let Err(e) =
+ self.handle_command(z, window.clone())
+ {
+ self.bar.last_action = format!("{e}");
+ }
+ }
+ },
+ Some(Do::CmdTyped) => {
+ let State::Command(x) = &self.state else {
+ unreachable!()
+ };
+ if let Some(Ok(crate::commands::Cmd::GoTo(Some(x)))) =
+ x.sel()
+ {
+ self.text.scroll_to_ln_centering(x as _);
+ }
+ }
+ Some(Do::SymbolsHandleKey) => {
+ if let Some(lsp) = lsp!(self) {
+ let State::Symbols(Rq { result: Some(x), request }) =
+ &mut self.state
+ else {
+ unreachable!()
+ };
+ let ptedit = x.tedit.rope.clone();
+ if handle2(
+ &event.logical_key,
+ &mut x.tedit,
+ lsp!(self + p),
+ )
+ .is_some()
+ || ptedit != x.tedit.rope
+ {
+ if x.data.3 == SymbolsType::Workspace {
+ *request = Some((
+ DropH::new(
+ lsp.runtime.spawn(
+ lsp.workspace_symbols(
+ x.tedit.rope.to_string(),
+ )
+ .map(|x| {
+ x.anonymize().map(|x| {
+ x.map(
+ SymbolsList::Workspace,
+ )
+ })
+ }),
+ ),
+ ),
+ (),
+ ));
+ } else {
+ x.selection = 0;
+ x.vo = 0;
+ }
+ // state = State::Symbols(Rq::new(lsp.runtime.spawn(lsp.symbols("".into()))));
+ }
+ }
+ }
+ Some(Do::SymbolsSelectNext) => {
+ let State::Symbols(Rq { result: Some(x), .. }) =
+ &mut self.state
+ else {
+ unreachable!()
+ };
+ x.next();
+ if let Some(Ok(x)) = x.sel()
+ && Some(&*x.at.path) == self.origin.as_deref()
+ {
+ match x.at {
+ sym::GoTo { path: _, at: At::R(x) } => {
+ let x = self.text.l_range(x).unwrap();
+ self.text.vo = self.text.char_to_line(x.start);
+ }
+ sym::GoTo { path: _, at: At::P(x) } =>
+ self.text.vo = self.text.char_to_line(x),
+ }
+ }
+ }
+ Some(Do::SymbolsSelectPrev) => {
+ let State::Symbols(Rq { result: Some(x), .. }) =
+ &mut self.state
+ else {
+ unreachable!()
+ };
+ x.back();
+ if let Some(Ok(x)) = x.sel()
+ && Some(&*x.at.path) == self.origin.as_deref()
+ {
+ match x.at.at {
+ At::R(x) => {
+ let x = self.text.l_range(x).unwrap();
+ self.text.vo = self.text.char_to_line(x.start);
+ }
+ At::P(x) =>
+ self.text.vo = self.text.char_to_line(x),
+ }
+ }
+ }
+ Some(Do::SymbolsSelect(x)) =>
+ if let Some(Ok(x)) = x.sel()
+ && let Err(e) = self.go(x.at, window.clone())
+ {
+ log::error!("alas! {e}");
+ },
+ Some(Do::RenameSymbol(to)) => {
+ if let Some((lsp, f)) = lsp!(self + p) {
+ let x = lsp
+ .request_immediate::<lsp_request!("textDocument/rename")>(
+ &RenameParams {
+ text_document_position:
+ TextDocumentPositionParams {
+ text_document: f.tid(),
+ position: self
+ .text
+ .to_l_position(
+ self.text
+ .cursor
+ .first()
+ .position,
+ )
+ .unwrap(),
+ },
+ new_name: to,
+ work_done_progress_params: default(),
+ },
+ );
+
+ match x {
+ Ok(Some(x)) =>
+ if let Err(e) =
+ self.apply_wsedit(x, &f.to_owned())
+ {
+ println!(
+ "couldnt apply one or more wsedits: \
+ {e}"
+ );
+ },
+ Err(RequestError::Failure(
+ lsp_server::Response {
+ result: None,
+ error:
+ Some(ResponseError {
+ code: -32602,
+ message,
+ data: None,
+ }),
+ ..
+ },
+ ..,
+ )) => self.bar.last_action = message,
+ _ => {}
+ }
+ }
+ }
+ 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.first().position..self.text.cursor.first().position,
+ )
+ .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.requests.diagnostics.get(&uri, &lsp.requests.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();
+
+ self.state =
+ State::CodeAction(Rq::new(lsp.runtime.spawn(r.0)));
+ }
+ }
+ Some(Do::CASelectLeft) => {
+ let State::CodeAction(Rq { result: Some(c), .. }) =
+ &mut self.state
+ else {
+ panic!()
+ };
+ c.left();
+ }
+ Some(Do::CASelectRight) => 'out: {
+ let Some((lsp, f)) = lsp!(self + p) else {
+ unreachable!()
+ };
+ let State::CodeAction(Rq { result: Some(c), .. }) =
+ &mut self.state
+ else {
+ panic!()
+ };
+ let Some(act) = c.right() else { break 'out };
+ let act = act.clone();
+ self.state = State::Default;
+ self.hist.lc = self.text.cursor.clone();
+ self.hist.test_push(&mut self.text);
+ let act = lsp
+ .request_immediate::<CodeActionResolveRequest>(&act)
+ .unwrap();
+ let f = f.to_owned();
+ if let Some(x) = act.edit
+ && let Err(e) = self.apply_wsedit(x, &f)
+ {
+ log::error!("{e}");
+ }
+ }
+ Some(Do::CASelectNext) => {
+ let State::CodeAction(Rq { result: Some(c), .. }) =
+ &mut self.state
+ else {
+ panic!()
+ };
+ c.down();
+ }
+ Some(Do::CASelectPrev) => {
+ let State::CodeAction(Rq { result: Some(c), .. }) =
+ &mut self.state
+ else {
+ panic!()
+ };
+ c.up();
+ }
+ Some(Do::NavBack) => self.nav_back(),
+ Some(Do::NavForward) => self.nav_forward(),
+ Some(
+ Do::Reinsert
+ | Do::GoToDefinition
+ | Do::MoveCursor
+ | Do::ExtendSelectionToMouse
+ | Do::Hover
+ | Do::InsertCursorAtMouse,
+ ) => panic!(),
+ Some(Do::Save) => match &self.origin {
+ Some(_) => {
+ self.state.consume(Action::Saved).unwrap();
+ self.save();
+ }
+ None => {
+ self.state.consume(Action::RequireFilename).unwrap();
+ }
+ },
+ Some(Do::SaveTo(x)) => {
+ self.origin = Some(PathBuf::try_from(x).unwrap());
+ self.save();
+ }
+ Some(Do::Edit) => {
+ self.text.cursor.clear_selections();
+ self.hist.test_push(&mut self.text);
+ let cb4 = self.text.cursor.first();
+ if let Key::Named(Enter | ArrowUp | ArrowDown | Tab) =
+ event.logical_key
+ && let CompletionState::Complete(..) =
+ self.requests.complete
+ {
+ } else {
+ if let Some(x) = handle2(
+ &event.logical_key,
+ &mut self.text,
+ lsp!(self + p),
+ ) && let Some((l, p)) = lsp!(self + p)
+ && let Some(
+ InitializeResult {
+ capabilities:
+ ServerCapabilities {
+ document_on_type_formatting_provider:
+ Some(DocumentOnTypeFormattingOptions {
+ first_trigger_character,
+ more_trigger_character: Some(t),
+ }),
+ ..
+ },
+ ..
+ },
+ ..,
+ ) = &l.initialized
+ && (first_trigger_character == first_trigger_character
+ || t.iter().any(|y| y == x))
+ && self.text.cursor.inner.len() == 1
+ && change!(just self).is_some()
+ && let Ok(Some(mut x)) = l
+ .request_immediate::<OnTypeFormatting>(
+ &DocumentOnTypeFormattingParams {
+ text_document_position:
+ TextDocumentPositionParams {
+ text_document: p.tid(),
+ position: self
+ .text
+ .to_l_position(
+ *self.text.cursor.first(),
+ )
+ .unwrap(),
+ },
+ ch: x.into(),
+ options: FormattingOptions {
+ tab_size: 4,
+ ..default()
+ },
+ },
+ )
+ {
+ x.sort_tedits();
+ for x in x {
+ self.text.apply_snippet_tedit(&x).unwrap();
+ }
+ }
+ };
+ self.text.scroll_to_cursor();
+ if cb4 != self.text.cursor.first()
+ && let CompletionState::Complete(Rq {
+ result: Some(c),
+ ..
+ }) = &self.requests.complete
+ && let at =
+ self.text.cursor.first().at_(&self.text.rope)
+ && ((self.text.cursor.first() < c.start)
+ || (!crate::is_word(at)
+ && (at != '.' || at != ':')))
+ {
+ self.requests.complete = CompletionState::None;
+ }
+ if self.requests.sig_help.running()
+ && cb4 != self.text.cursor.first()
+ && let Some((lsp, path)) = lsp!(self + p)
+ {
+ self.requests.sig_help.request(
+ lsp.runtime.spawn(
+ lsp.request_sig_help(
+ path,
+ self.text
+ .cursor
+ .first()
+ .cursor(&self.text.rope),
+ ),
+ ),
+ );
+ }
+ if self.hist.record(&self.text) {
+ change!(self, window.clone());
+ }
+ lsp!(self + p).map(|(lsp, o)| {
+ match event.logical_key.as_ref() {
+ Key::Character(y)
+ if let Some(x) = &lsp.initialized
+ && let Some(x) = &x
+ .capabilities
+ .signature_help_provider
+ && let Some(x) = &x.trigger_characters
+ && x.contains(&y.to_string()) =>
+ {
+ self.requests.sig_help.request(
+ lsp.runtime.spawn(
+ lsp.request_sig_help(
+ o,
+ self.text
+ .cursor
+ .first()
+ .cursor(&self.text.rope),
+ ),
+ ),
+ );
+ }
+ _ => {}
+ }
+ match self
+ .requests
+ .complete
+ .consume(CompletionAction::K(
+ event.logical_key.as_ref(),
+ ))
+ .unwrap()
+ {
+ Some(CDo::Request(ctx)) => {
+ let h = DropH::new(
+ lsp.runtime.spawn(
+ lsp.request_complete(
+ o,
+ self.text
+ .cursor
+ .first()
+ .cursor(&self.text.rope),
+ ctx,
+ ),
+ ),
+ );
+ let CompletionState::Complete(Rq {
+ request: x,
+ result: c,
+ }) = &mut self.requests.complete
+ else {
+ panic!()
+ };
+ *x = Some((
+ h,
+ c.as_ref()
+ .map(|x| x.start)
+ .or(x.as_ref().map(|x| x.1))
+ .unwrap_or(*self.text.cursor.first()),
+ ));
+ }
+ Some(CDo::SelectNext) => {
+ let CompletionState::Complete(Rq {
+ result: Some(c),
+ ..
+ }) = &mut self.requests.complete
+ else {
+ panic!()
+ };
+ c.next(&filter(&self.text));
+ }
+ Some(CDo::SelectPrevious) => {
+ let CompletionState::Complete(Rq {
+ result: Some(c),
+ ..
+ }) = &mut self.requests.complete
+ else {
+ panic!()
+ };
+ c.back(&filter(&self.text));
+ }
+ Some(CDo::Finish(x)) => {
+ let sel = x.sel(&filter(&self.text));
+ let sel = lsp.resolve(sel.clone()).unwrap();
+ let CompletionItem {
+ text_edit:
+ Some(CompletionTextEdit::Edit(ed)),
+ additional_text_edits,
+ insert_text_format,
+ ..
+ } = sel.clone()
+ else {
+ panic!()
+ };
+ match insert_text_format {
+ Some(InsertTextFormat::SNIPPET) => {
+ self.text.apply_snippet(&ed).unwrap();
+ }
+ _ => {
+ self.text.apply(&ed).unwrap();
+ // self.text
+ // .cursor
+ // .first_mut()
+ // .position =
+ // s + ed.new_text.chars().count();
+ }
+ }
+ if let Some(mut additional_tedits) =
+ additional_text_edits
+ {
+ additional_tedits.sort_tedits();
+ for additional in additional_tedits {
+ self.text
+ .apply_adjusting(&additional)
+ .unwrap();
+ }
+ }
+ if self.hist.record(&self.text) {
+ change!(self, window.clone());
+ }
+ self.requests.sig_help = Rq::new(
+ lsp.runtime.spawn(
+ lsp.request_sig_help(
+ o,
+ self.text
+ .cursor
+ .first()
+ .cursor(&self.text.rope),
+ ),
+ ),
+ );
+ }
+ None => return,
+ };
+ });
+ }
+ Some(Do::Undo) => {
+ self.hist.test_push(&mut self.text);
+ self.hist.undo(&mut self.text).unwrap();
+ self.bar.last_action = "undid".to_string();
+ change!(self, window.clone());
+ }
+ Some(Do::Redo) => {
+ self.hist.test_push(&mut self.text);
+ self.hist.redo(&mut self.text).unwrap();
+ self.bar.last_action = "redid".to_string();
+ change!(self, window.clone());
+ }
+ Some(Do::Quit) => return ControlFlow::Break(()),
+ Some(Do::SetCursor(x)) => {
+ self.text.cursor.each(|c| {
+ let Some(r) = c.sel else { return };
+ match x {
+ LR::Left => c.position = r.start,
+ LR::Right => c.position = r.end,
+ }
+ });
+ self.text.cursor.clear_selections();
+ }
+ Some(Do::StartSelection) => {
+ let Key::Named(y) = event.logical_key else { panic!() };
+ // let mut z = vec![];
+ self.text.cursor.each(|x| {
+ x.sel = Some(Ronge::from(**x..**x));
+ x.extend_selection(
+ y,
+ // **x..**x,
+ &self.text.rope,
+ &mut self.text.vo,
+ self.text.r,
+ );
+ });
+ // *self.state.sel() = z;
+ }
+ Some(Do::UpdateSelection) => {
+ let Key::Named(y) = event.logical_key else { panic!() };
+ self.text.cursor.each(|x| {
+ x.extend_selection(
+ y,
+ // dbg!(r.clone()),
+ &self.text.rope,
+ &mut self.text.vo,
+ self.text.r,
+ );
+ });
+
+ self.text.scroll_to_cursor();
+ inlay!(self);
+ }
+ Some(Do::Insert(c)) => {
+ // self.text.cursor.inner.clear();
+ self.hist.push_if_changed(&mut self.text);
+ ceach!(self.text.cursor, |cursor| {
+ let Some(r) = cursor.sel else { return };
+ _ = self.text.remove(r.into());
+ // self.text.cursor.first().setc(&self.text.rope);
+ });
+ self.text.insert(&c);
+ self.text.cursor.clear_selections();
+ self.hist.push_if_changed(&mut self.text);
+ change!(self, window.clone());
+ }
+ Some(Do::Delete) => {
+ self.hist.push_if_changed(&mut self.text);
+ ceach!(self.text.cursor, |cursor| {
+ let Some(r) = cursor.sel else { return };
+ _ = self.text.remove(r.into());
+ });
+ self.text.cursor.clear_selections();
+ self.hist.push_if_changed(&mut self.text);
+ change!(self, window.clone());
+ }
+
+ Some(Do::Copy) => {
+ self.hist.push_if_changed(&mut self.text);
+ unsafe { take(&mut META) };
+ let mut clip = String::new();
+ self.text.cursor.each_ref(|x| {
+ if let Some(x) = x.sel {
+ unsafe {
+ META.count += 1;
+ META.splits.push(clip.len());
+ }
+ clip.extend(self.text.rope.slice(x).chars());
+ }
+ });
+ unsafe {
+ META.splits.push(clip.len());
+ META.hash = hash(&clip)
+ };
+ clipp::copy(clip);
+ self.text.cursor.clear_selections();
+ self.hist.push_if_changed(&mut self.text);
+ change!(self, window.clone());
+ }
+ Some(Do::Cut) => {
+ self.hist.push_if_changed(&mut self.text);
+ unsafe { take(&mut META) };
+ let mut clip = String::new();
+ self.text.cursor.each_ref(|x| {
+ if let Some(x) = x.sel {
+ unsafe {
+ META.count += 1;
+ META.splits.push(clip.len());
+ }
+ clip.extend(self.text.rope.slice(x).chars());
+ }
+ });
+ unsafe {
+ META.splits.push(clip.len());
+ META.hash = hash(&clip)
+ };
+ clipp::copy(clip);
+ ceach!(self.text.cursor, |x| {
+ if let Some(x) = x.sel {
+ self.text.remove(x.into()).unwrap();
+ }
+ });
+ self.text.cursor.clear_selections();
+ self.hist.push_if_changed(&mut self.text);
+ change!(self, window.clone());
+ }
+ Some(Do::PasteOver) => {
+ self.hist.push_if_changed(&mut self.text);
+ ceach!(self.text.cursor, |cursor| {
+ let Some(r) = cursor.sel else { return };
+ _ = self.text.remove(r.into());
+ });
+ self.text.cursor.clear_selections();
+ self.paste();
+ // self.hist.push_if_changed(&mut self.text);
+ }
+ Some(Do::Paste) => self.paste(),
+ Some(Do::OpenFile(x)) => {
+ _ = self.open(Path::new(&x), window.clone());
+ }
+ Some(Do::StartSearch(x)) => {
+ let s = Regex::new(&x).unwrap();
+ let n = s
+ .find_iter(&self.text.rope.to_string())
+ .enumerate()
+ .count();
+ s.clone()
+ .find_iter(&self.text.rope.to_string())
+ .enumerate()
+ .find(|(_, x)| x.start() > *self.text.cursor.first())
+ .map(|(x, m)| {
+ self.state = State::Search(s, x, n);
+ self.text.cursor.just(
+ self.text.rope.byte_to_char(m.end()),
+ &self.text.rope,
+ );
+ self.text.scroll_to_cursor_centering();
+ inlay!(self);
+ })
+ .unwrap_or_else(|| {
+ self.bar.last_action = "no matches".into()
+ });
+ }
+ Some(Do::SearchChanged) => {
+ let (re, index, _) = self.state.search();
+ let s = self.text.rope.to_string();
+ let m = re.find_iter(&s).nth(*index).unwrap();
+ self.text.cursor.just(
+ self.text.rope.byte_to_char(m.end()),
+ &self.text.rope,
+ );
+ self.text.scroll_to_cursor_centering();
+ inlay!(self);
+ }
+ Some(Do::Boolean(BoolRequest::ReloadFile, true)) => {
+ self.hist.push_if_changed(&mut self.text);
+ self.text.rope = Rope::from_str(
+ &std::fs::read_to_string(
+ self.origin.as_ref().unwrap(),
+ )
+ .unwrap(),
+ );
+
+ self.text.cursor.first_mut().position = self
+ .text
+ .cursor
+ .first()
+ .position
+ .min(self.text.rope.len_chars());
+ self.mtime = Self::modify(self.origin.as_deref());
+ self.bar.last_action = "reloaded".into();
+ self.hist.push(&mut self.text)
+ }
+ Some(Do::Boolean(BoolRequest::ReloadFile, false)) => {}
+ Some(Do::InsertCursor(dir)) => {
+ let (x, y) = match dir {
+ Direction::Above => self.text.cursor.min(),
+ Direction::Below => self.text.cursor.max(),
+ }
+ .cursor(&self.text.rope);
+ let y = match dir {
+ Direction::Above => y - 1,
+ Direction::Below => y + 1,
+ };
+ let position = self.text.line_to_char(y);
+ self.text.cursor.add(position + x, &self.text.rope);
+ }
+ Some(Do::Run(x)) =>
+ if let Some((l, ws)) =
+ lsp!(self).zip(self.workspace.as_deref())
+ {
+ l.runtime
+ .block_on(crate::runnables::run(x, ws))
+ .unwrap();
+ },
+ Some(Do::GoToImplementations) => {
+ let State::GoToL(x) = &mut self.state else {
+ unreachable!()
+ };
+ if let Some(l) = lsp!(self) {
+ x.data.1 = Some(crate::gotolist::O::Impl(Rq::new(
+ l.runtime.spawn(
+ l.go_to_implementations(tdpp!(self)).unwrap(),
+ ),
+ )));
+ }
+ }
+ Some(Do::GTLSelect(x)) =>
+ if let Some(Ok(g)) = x.sel()
+ && let Err(e) = self.go(g, window.clone())
+ {
+ eprintln!("go-to-list select fail: {e}");
+ },
+ Some(Do::GT) => {
+ let State::GoToL(x) = &mut self.state else {
+ unreachable!()
+ };
+ if let Some(Ok(GoTo { path: p, at: At::R(r) })) = x.sel()
+ && Some(&*p) == self.origin.as_deref()
+ {
+ // let x = self.text.l_range(r).unwrap();
+ self.text.scroll_to_ln_centering(r.start.line as _);
+ // self.text.vo = self.text.char_to_line(x.start);
+ }
+ }
+ None => {}
+ }
+ ControlFlow::Continue(())
+ }
+}