A simple CPU rendered GUI IDE experience.
-rw-r--r--src/commands.rs4
-rw-r--r--src/edi.rs1503
-rw-r--r--src/edi/input_handlers.rs59
-rw-r--r--src/edi/input_handlers/click.rs86
-rw-r--r--src/edi/input_handlers/cursor.rs245
-rw-r--r--src/edi/input_handlers/keyboard.rs888
-rw-r--r--src/edi/lsp_impl.rs220
-rw-r--r--src/rnd.rs12
-rw-r--r--src/text.rs19
9 files changed, 1539 insertions, 1497 deletions
diff --git a/src/commands.rs b/src/commands.rs
index fc37f45..64a382c 100644
--- a/src/commands.rs
+++ b/src/commands.rs
@@ -12,7 +12,7 @@ use rootcause::{bail, report};
use rust_analyzer::lsp::ext::*;
use crate::FG;
-use crate::edi::{Editor, lsp_m};
+use crate::edi::{Editor, lsp};
use crate::gotolist::{At, GoToList};
use crate::lsp::{PathURI, Rq, tdpp};
use crate::menu::charc;
@@ -273,7 +273,7 @@ impl Editor {
z: Cmd,
w: Arc<dyn winit::window::Window>,
) -> rootcause::Result<()> {
- let Some((l, o)) = lsp_m!(self + p) else {
+ let Some((l, o)) = lsp!(self + p) else {
bail!("no lsp");
};
match z {
diff --git a/src/edi.rs b/src/edi.rs
index b9cad5b..d025911 100644
--- a/src/edi.rs
+++ b/src/edi.rs
@@ -1,142 +1,52 @@
-use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt::Debug;
use std::mem::take;
-use std::ops::ControlFlow;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time::SystemTime;
use Default::default;
use bind::Bind;
-use implicit_fn::implicit_fn;
-use lsp_server::{Connection, Request as LRq, ResponseError};
+use lsp_server::Connection;
use lsp_types::request::*;
use lsp_types::*;
use regex::Regex;
use rootcause::report;
use ropey::Rope;
-use rust_analyzer::lsp::ext::OnTypeFormatting;
-use rust_fsm::StateMachine;
use serde_derive::{Deserialize, Serialize};
use tokio::sync::oneshot::Sender;
-use tokio::task::spawn_blocking;
-use tokio_util::task::AbortOnDropHandle as DropH;
-use winit::event::{KeyEvent, MouseButton};
-use winit::keyboard::{Key, NamedKey};
+use winit::keyboard::NamedKey;
use winit::window::Window;
+mod input_handlers;
+pub use input_handlers::handle2;
+
+mod lsp_impl;
pub mod st;
mod wsedit;
+pub use lsp_impl::Requests;
use st::*;
use crate::bar::Bar;
use crate::commands::Cmds;
-use crate::complete::Complete;
use crate::error::WDebug;
use crate::gotolist::{At, GoTo};
use crate::hov::{self, Hovr};
use crate::lsp::{
- self, Anonymize, Client, Map_, PathURI, RequestError, Rq, tdpp,
+ Anonymize, Client, Map_, PathURI, RequestError, Rq, tdpp,
};
use crate::menu::generic::MenuData;
use crate::meta::META;
-use crate::runnables::Runnables;
use crate::sym::{Symbols, SymbolsList, SymbolsType};
use crate::text::cursor::{Ronge, ceach};
use crate::text::hist::{ClickHistory, Hist};
use crate::text::{self, Mapping, RopeExt, SortTedits, TextArea};
use crate::{
- BoolRequest, CDo, CompletionAction, CompletionState, act, alt, ctrl,
- filter, hash, shift, sig, sym, trm,
+ BoolRequest, CDo, CompletionAction, CompletionState, alt, ctrl,
+ filter, hash, shift, sym, trm,
};
-#[allow(dead_code)]
-pub fn serialize_tokens<S: serde::Serializer>(
- s: &Rq<
- Box<[SemanticToken]>,
- Box<[SemanticToken]>,
- (),
- RequestError<SemanticTokensFullRequest>,
- >,
- ser: S,
-) -> Result<S::Ok, S::Error> {
- SemanticToken::serialize_tokens_opt(
- &s.result.clone().map(|x| x.to_vec()),
- ser,
- )
-}
-#[allow(dead_code)]
-pub fn deserialize_tokens<'de, D: serde::Deserializer<'de>>(
- ser: D,
-) -> Result<
- Rq<
- Box<[SemanticToken]>,
- Box<[SemanticToken]>,
- (),
- RequestError<SemanticTokensFullRequest>,
- >,
- D::Error,
-> {
- SemanticToken::deserialize_tokens_opt(ser)
- .map(|x| Rq { result: x.map(Into::into), request: None })
-}
-#[derive(Default, Debug, Serialize, Deserialize)]
-pub struct Requests {
- pub hovering:
- Rq<Hovr, Option<Hovr>, (usize, usize), RequestError<HoverRequest>>,
- pub document_highlights: Rq<
- Vec<DocumentHighlight>,
- Vec<DocumentHighlight>,
- (),
- RequestError<DocumentHighlightRequest>,
- >,
- pub complete: CompletionState,
- pub sig_help: Rq<
- (SignatureHelp, usize, Option<usize>),
- Option<SignatureHelp>,
- (),
- RequestError<SignatureHelpRequest>,
- >, // vo, lines
- // #[serde(serialize_with = "serialize_tokens")]
- // #[serde(deserialize_with = "deserialize_tokens")]
- #[serde(skip)]
- pub semantic_tokens: Rq<
- Box<[SemanticToken]>,
- Box<[SemanticToken]>,
- (),
- RequestError<SemanticTokensFullRequest>,
- >,
- pub diag: Rq<
- String,
- Option<String>,
- (),
- RequestError<DocumentDiagnosticRequest>,
- >,
- #[serde(skip)]
- pub inlay: Rq<
- Vec<InlayHint>,
- Vec<InlayHint>,
- (),
- RequestError<lsp_request!("textDocument/inlayHint")>,
- >,
- pub def: Rq<
- LocationLink,
- Option<GotoDefinitionResponse>,
- (usize, usize),
- RequestError<lsp_request!("textDocument/definition")>,
- >,
- #[serde(skip)]
- pub document_symbols: Rq<
- Option<Vec<DocumentSymbol>>,
- Option<DocumentSymbolResponse>,
- (),
- RequestError<lsp_request!("textDocument/documentSymbol")>,
- >,
- #[serde(skip)]
- pub git_diff: Rq<imara_diff::Diff, imara_diff::Diff, (), ()>,
-}
impl Debug for Editor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Editor")
@@ -185,13 +95,13 @@ macro_rules! lsp {
$self.lsp.as_ref().map(|(x, ..)| *x)
};
($self:ident + p) => {
- $crate::edi::lsp_m!($self).zip($self.origin.as_deref())
+ $crate::edi::lsp!($self).zip($self.origin.as_deref())
};
}
-pub(crate) use lsp as lsp_m;
+pub(crate) use lsp;
macro_rules! inlay {
($self:ident) => {
- $crate::edi::lsp_m!($self + p).map(|(lsp, path)| {
+ $crate::edi::lsp!($self + p).map(|(lsp, path)| {
$self
.requests
.inlay
@@ -208,12 +118,12 @@ macro_rules! change {
change!(@$self, Some($w))
};
(just $self:ident) => {
- lsp_m!($self + p).map(|(x, origin)| {
+ lsp!($self + p).map(|(x, origin)| {
x.edit(&origin, $self.text.rope.to_string()).unwrap();
})
};
(@$self:ident, $w:expr) => {
- lsp_m!($self + p).map(|(x, origin)| {
+ lsp!($self + p).map(|(x, origin)| {
x.edit(&origin, $self.text.rope.to_string()).unwrap();
x.rq_semantic_tokens(
&mut $self.requests.semantic_tokens,
@@ -336,7 +246,7 @@ impl Editor {
rust_analyzer::bin::run_server(b)
})
.unwrap();
- let (c, t2, changed) = lsp::run(
+ let (c, t2, changed) = crate::lsp::run(
(a.sender, a.receiver),
// lsp_server::stdio::stdio_transport(
// BufReader::new(c.stdout.take().unwrap()),
@@ -442,469 +352,6 @@ impl Editor {
std::fs::write(self.origin.as_ref().unwrap(), &t).unwrap();
self.mtime = Self::modify(self.origin.as_deref());
}
- pub fn poll(&mut self) {
- let Some((l, ..)) = self.lsp else { return };
- 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));
- }
- rq => log::debug!("discarding request {rq:?}"),
- }
- }
- self.requests.inlay.poll(|x, p| {
- x.ok().or(p.1).inspect(|x| {
- self.text.set_inlay(x);
- })
- });
- self.requests.document_highlights.poll(|x, _| x.ok());
- self.requests.diag.poll(|x, _| x.ok().flatten());
- if let CompletionState::Complete(rq) = &mut self.requests.complete
- {
- rq.poll(|f, (c, _)| {
- f.ok().flatten().map(|x| Complete {
- r: x,
- start: c,
- selection: 0,
- vo: 0,
- })
- });
- };
- match &mut self.state {
- State::Symbols(x) => {
- x.poll(|x, (_, p)| {
- let Some(p) = p else { unreachable!() };
- x.ok().flatten().map(|r| sym::Symbols {
- data: (r, p.data.1, p.data.2, p.data.3, p.data.4),
- selection: 0,
- vo: 0,
- ..p
- })
- });
- }
- State::CodeAction(x) => {
- if x.poll(|x, _| {
- let lems: Vec<CodeAction> = x
- .ok()??
- .into_iter()
- .map(|x| match x {
- CodeActionOrCommand::CodeAction(x) => x,
- _ => panic!("alas we dont like these"),
- })
- .collect();
- if lems.is_empty() {
- self.bar.last_action =
- "no code actions available".into();
- None
- } else {
- self.bar.last_action =
- format!("{} code actions", lems.len());
- Some(act::CodeActions::new(lems))
- }
- }) && x.result.is_none()
- {
- self.state = State::Default;
- }
- }
- State::Runnables(x) => {
- x.poll(|x, ((), old)| {
- Some(Runnables {
- data: x.ok()?,
- ..old.unwrap_or_default()
- })
- });
- }
- State::GoToL(z) => match &mut z.data.1 {
- Some(crate::gotolist::O::References(y)) => {
- y.poll(|x, _| {
- x.ok().flatten().map(|x| {
- z.data.0 = x.iter().map(GoTo::from).collect()
- })
- });
- }
- Some(crate::gotolist::O::Impl(y)) => {
- y.poll(|x, _| {
- x.ok().map(|x| {
- x.and_then(|x| try {
- z.data.0 = match x {
- GotoDefinitionResponse::Scalar(
- location,
- ) => vec![GoTo::from(
- location,
- )],
- GotoDefinitionResponse::Array(
- locations,
- ) => locations
- .into_iter()
- .map(GoTo::from)
- .collect(),
- GotoDefinitionResponse::Link(
- location_links,
- ) => location_links
- .into_iter()
- .map(|LocationLink {target_uri, target_range, .. }| {
- GoTo::from(
- Location {
- uri: target_uri,
- range: target_range,
- }
- )
- })
- .collect(),
- };
- });
- })
- });
- }
- _ => {}
- },
- _ => {}
- }
- self.requests.def.poll(|x, _| {
- x.ok().flatten().and_then(|x| match &x {
- GotoDefinitionResponse::Link([x, ..]) => Some(x.clone()),
- _ => None,
- })
- });
- self.requests
- .semantic_tokens
- .poll(|x, _| x.ok().inspect(|x| self.text.set_toks(&x)));
- self.requests.sig_help.poll(|x, ((), y)| {
- x.ok().flatten().map(|x| {
- if let Some((old_sig, vo, max)) = y
- && &sig::active(&old_sig) == &sig::active(&x)
- {
- (x, vo, max)
- } else {
- (x, 0, None)
- }
- })
- });
- self.requests.hovering.poll(|x, _| x.ok().flatten());
- self.requests.git_diff.poll(|x, _| x.ok());
- self.requests.document_symbols.poll(|x, _| {
- x.ok().flatten().map(|x| match x {
- DocumentSymbolResponse::Flat(_) => None,
- DocumentSymbolResponse::Nested(x) => Some(x),
- })
- });
- }
- #[implicit_fn]
- pub fn cursor_moved(
- &mut self,
- cursor_position: (usize, usize),
- w: Arc<dyn Window>,
- c: usize,
- ) {
- match self.state.consume(Action::C(cursor_position)).unwrap() {
- Some(Do::ExtendSelectionToMouse) => {
- let p = self.text.mapped_index_at(cursor_position);
- self.text
- .cursor
- .first_mut()
- .extend_selection_to(p, &self.text.rope);
- w.request_redraw();
- }
- Some(Do::StartSelection) => {
- let x = self.text.mapped_index_at(cursor_position);
- self.text.cursor.first_mut().position = x;
- self.text.cursor.first_mut().sel = Some((x..x).into());
- self.hist.lc = self.text.cursor.clone();
- }
- Some(Do::Hover)
- if let Some(hover) =
- self.text.visual_index_at(cursor_position)
- && let Some((cl, o)) = lsp!(self + p) =>
- 'out: {
- let l = &mut self.requests.hovering.result;
- if let Some(Hovr {
- span: Some([(_x, _y), (_x2, _)]), ..
- }) = &*l
- && let Some(_y) = _y.checked_sub(self.text.vo)
- && let Some(_x) = _x.checked_sub(self.text.ho)
- && let Some(_x2) = _x2.checked_sub(self.text.ho)
- && cursor_position.1 == _y
- && (_x..=_x2).contains(
- &&(cursor_position.0
- - self.text.line_number_offset()
- - 1),
- )
- {
- break 'out;
- } else {
- // println!("span no longer below cursor; cancel hover {_x}..{_x2} {}", cursor_position.0 - text.line_number_offset() - 1);
- *l = None;
- w.request_redraw();
- }
- let text = self.text.clone();
- let mut rang = None;
- let z = match hover {
- Mapping::Char(_, _, i) => TextDocumentPositionParams {
- position: text.to_l_position(i).unwrap(),
- text_document: o.tid(),
- },
- Mapping::Fake(mark, relpos, abspos, _) => {
- let Some(ref loc) = mark.data[relpos as usize].1
- else {
- break 'out;
- };
- let (x, y) = text.xy(abspos as _).unwrap();
- let Some(mut begin) = text.reverse_source_map(y)
- else {
- break 'out;
- };
- let start =
- begin.nth(x.saturating_sub(1)).unwrap() + 1;
- let left = mark.data[..relpos as usize]
- .iter()
- .rev()
- .take_while(_.1.as_ref() == Some(loc))
- .count();
- let start = start + relpos as usize - left;
- let length = mark.data[relpos as usize..]
- .iter()
- .take_while(_.1.as_ref() == Some(loc))
- .count()
- + left;
- rang = Some([(start, y), (start + length, y)]);
- TextDocumentPositionParams {
- text_document: TextDocumentIdentifier {
- uri: loc.uri.clone(),
- },
- position: loc.range.start,
- }
- }
- };
- if ctrl() {
- if self
- .requests
- .def
- .request
- .as_ref()
- .is_none_or(|&(_, x)| x != cursor_position)
- {
- let handle =
- cl.runtime.spawn(
- cl.request::<lsp_request!(
- "textDocument/definition"
- )>(
- &GotoDefinitionParams {
- text_document_position_params: z
- .clone(),
- work_done_progress_params: default(
- ),
- partial_result_params: default(),
- },
- )
- .unwrap()
- .0,
- );
- self.requests.def.request =
- Some((DropH::new(handle), cursor_position));
- } else if self
- .requests
- .def
- .result
- .as_ref()
- .is_some_and(|em| {
- let z = em.origin_selection_range.unwrap();
- (z.start.character..z.end.character).contains(
- &((cursor_position.0
- - text.line_number_offset()
- - 1)
- as _),
- )
- })
- {
- self.requests.def.result = None;
- }
- } else {
- self.requests.def.result = None;
- }
- if let Some((_, c)) = self.requests.hovering.request
- && c == cursor_position
- {
- break 'out;
- }
- // if !running.insert(hover) {return}
- let (rx, _) = cl
- .request::<HoverRequest>(&HoverParams {
- text_document_position_params: z,
- work_done_progress_params: default(),
- })
- .unwrap();
- // println!("rq hov of {hover:?} (cur {})", requests.hovering.request.is_some());
- let handle: tokio::task::JoinHandle<
- Result<Option<Hovr>, _>,
- > = cl.runtime.spawn(async move {
- let Some(x) = rx.await? else {
- return Ok(None::<Hovr>);
- };
- let (w, cells) = spawn_blocking(move || {
- let x = match &x.contents {
- lsp_types::HoverContents::Scalar(
- marked_string,
- ) => match marked_string {
- MarkedString::LanguageString(x) =>
- Cow::Borrowed(&*x.value),
- MarkedString::String(x) =>
- Cow::Borrowed(&**x),
- },
- lsp_types::HoverContents::Array(
- marked_strings,
- ) => Cow::Owned(
- marked_strings
- .iter()
- .map(|x| match x {
- MarkedString::LanguageString(
- x,
- ) => &*x.value,
- MarkedString::String(x) => &*x,
- })
- .collect::<String>(),
- ),
- lsp_types::HoverContents::Markup(
- markup_content,
- ) => Cow::Borrowed(&*markup_content.value),
- };
- let x = hov::p(&x).unwrap();
- let m = hov::l(&x)
- .into_iter()
- .max()
- .map(_ + 2)
- .unwrap_or(usize::MAX)
- .min(c - 10);
- (m, hov::markdown2(m, &x))
- })
- .await
- .unwrap();
- let span = rang.or_else(|| {
- x.range.and_then(|range| try {
- let (startx, starty) =
- text.l_pos_to_char(range.start)?;
- let (endx, endy) =
- text.l_pos_to_char(range.end)?;
- let x1 = text
- .reverse_source_map(starty)?
- .nth(startx)?;
- let x2 = text
- .reverse_source_map(endy)?
- .nth(endx)?;
- [
- (x1, range.start.line as _),
- (x2, range.start.line as _),
- ]
- })
- });
- Ok(Some(
- hov::Hovr {
- span,
- item: text::CellBuffer {
- c: w,
- vo: 0,
- cells: cells.into(),
- },
- range: x.range,
- // range: x.range.and_then(|x| text.l_range(x)),
- }
- .into(),
- ))
- });
- self.requests.hovering.request =
- (DropH::new(handle), cursor_position).into();
- // requests.hovering.result = None;
- // lsp!().map(|(cl, o)| {
- // let window = window.clone();
- // });
- // });
- }
- Some(Do::Hover) => {
- self.requests.def.result = None;
- self.requests.hovering.result = None;
- w.request_redraw();
- }
- None => {}
- x => unreachable!("{x:?}"),
- }
- }
- pub fn click(
- &mut self,
- bt: MouseButton,
- cursor_position: (usize, usize),
- w: Arc<dyn Window>,
- ) {
- let text = &mut self.text;
- _ = self
- .requests
- .complete
- .consume(CompletionAction::Click)
- .unwrap();
- match self.state.consume(Action::M(bt)).unwrap() {
- Some(Do::MoveCursor) => {
- text.cursor.just(
- text.mapped_index_at(cursor_position),
- &text.rope,
- );
- if let Some((lsp, path)) = lsp!(self + p) {
- if self.requests.sig_help.result.is_some() {
- self.requests.sig_help.request(lsp.runtime.spawn(
- lsp.request_sig_help(
- path,
- text.primary_cursor(),
- ),
- ));
- }
- self.requests.document_highlights.request(
- lsp.runtime.spawn(
- lsp.document_highlights(
- path,
- text.to_l_position(
- text.cursor.first().position,
- )
- .unwrap(),
- ),
- ),
- );
- }
- self.hist.lc = text.cursor.clone();
- self.chist.push(text.primary_cursor());
- text.cursor.first().setc(&text.rope);
- }
- Some(Do::NavForward) => self.nav_forward(),
- Some(Do::NavBack) => self.nav_back(),
- Some(Do::ExtendSelectionToMouse) => {
- let p = text.mapped_index_at(cursor_position);
- text.cursor.first_mut().extend_selection_to(p, &text.rope);
- }
- Some(Do::StartSelection) => {
- let p = text.mapped_index_at(cursor_position);
-
- let x = *text.cursor.first();
- text.cursor.first_mut().sel = Some((x..x).into());
- text.cursor.first_mut().extend_selection_to(p, &text.rope);
- self.hist.lc = text.cursor.clone();
- }
- Some(Do::GoToDefinition) => {
- if let Some(x) = self.requests.def.result.clone()
- && let Err(e) = self.go(&x, w.clone())
- {
- log::error!("gtd: {e}");
- }
- }
- Some(Do::InsertCursorAtMouse) => {
- text.cursor.add(
- text.mapped_index_at(cursor_position),
- &text.rope,
- );
- self.hist.lc = text.cursor.clone();
- self.chist.push(text.primary_cursor());
- text.cursor.first().setc(&text.rope);
- }
- None => {}
- _ => unreachable!(),
- }
- }
pub fn nav_back(&mut self) {
self.chist.back().map(|x| {
@@ -943,872 +390,7 @@ impl Editor {
}
inlay!(self);
}
- 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)
- || (!super::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(())
- }
pub fn paste(&mut self) {
self.hist.push_if_changed(&mut self.text);
let r = clipp::paste();
@@ -2012,59 +594,6 @@ impl Editor {
}
use NamedKey::*;
-pub fn handle2<'a>(
- key: &'a Key,
- text: &mut TextArea,
- l: Option<(&Client, &Path)>,
-) -> Option<&'a str> {
- use Key::*;
-
- match key {
- Character(" ") => text.insert(" "),
- Named(Backspace) if ctrl() => text.backspace_word(),
- Named(Backspace) => text.backspace(),
- Named(Home) if ctrl() => {
- text.cursor.just(0, &text.rope);
- text.vo = 0;
- }
- Named(End) if ctrl() => {
- text.cursor.just(text.rope.len_chars(), &text.rope);
- text.vo = text.l().saturating_sub(text.r);
- }
- Character("e") if alt() => text.end(),
- Character("q") if alt() => text.home(),
- Named(Home) => text.home(),
- Named(End) => text.end(),
- Named(Tab) => text.tab(),
- Named(Delete) => {
- text.right();
- text.backspace()
- }
- Character("a") if alt() && ctrl() => text.word_left(),
- Character("d") if alt() && ctrl() => text.word_right(),
- Character("a") if alt() => text.left(),
- Character("w") if alt() => text.up(),
- Character("s") if alt() => text.down(),
- Character("d") if alt() => text.right(),
- Named(ArrowLeft) if ctrl() => text.word_left(),
- Named(ArrowRight) if ctrl() => text.word_right(),
- Named(ArrowLeft) => text.left(),
- Named(ArrowRight) => text.right(),
- Named(ArrowUp) => text.up(),
- Named(ArrowDown) => text.down(),
- Named(PageDown) => text.page_down(),
- Named(PageUp) => text.page_up(),
- Named(Enter) if let Some((l, p)) = l => l.enter(p, text).unwrap(),
- Named(Enter) => text.enter(),
- Character(x) => {
- text.insert(&x);
- return Some(x);
- }
- _ => {}
- };
- None
-}
-
impl State {
fn search(&mut self) -> (&mut Regex, &mut usize, &mut usize) {
let State::Search(x, y, z) = self else { panic!() };
diff --git a/src/edi/input_handlers.rs b/src/edi/input_handlers.rs
new file mode 100644
index 0000000..ac4ca4c
--- /dev/null
+++ b/src/edi/input_handlers.rs
@@ -0,0 +1,59 @@
+use std::path::Path;
+
+use winit::keyboard::Key;
+mod click;
+mod cursor;
+mod keyboard;
+use crate::edi::*;
+pub fn handle2<'a>(
+ key: &'a Key,
+ text: &mut TextArea,
+ l: Option<(&Client, &Path)>,
+) -> Option<&'a str> {
+ use Key::*;
+
+ match key {
+ Character(" ") => text.insert(" "),
+ Named(Backspace) if ctrl() => text.backspace_word(),
+ Named(Backspace) => text.backspace(),
+ Named(Home) if ctrl() => {
+ text.cursor.just(0, &text.rope);
+ text.vo = 0;
+ }
+ Named(End) if ctrl() => {
+ text.cursor.just(text.rope.len_chars(), &text.rope);
+ text.vo = text.l().saturating_sub(text.r);
+ }
+ Character("e") if alt() => text.end(),
+ Character("q") if alt() => text.home(),
+ Named(Home) => text.home(),
+ Named(End) => text.end(),
+ Named(Tab) => text.tab(),
+ Named(Delete) => {
+ text.right();
+ text.backspace()
+ }
+ Character("a") if alt() && ctrl() => text.word_left(),
+ Character("d") if alt() && ctrl() => text.word_right(),
+ Character("a") if alt() => text.left(),
+ Character("w") if alt() => text.up(),
+ Character("s") if alt() => text.down(),
+ Character("d") if alt() => text.right(),
+ Named(ArrowLeft) if ctrl() => text.word_left(),
+ Named(ArrowRight) if ctrl() => text.word_right(),
+ Named(ArrowLeft) => text.left(),
+ Named(ArrowRight) => text.right(),
+ Named(ArrowUp) => text.up(),
+ Named(ArrowDown) => text.down(),
+ Named(PageDown) => text.page_down(),
+ Named(PageUp) => text.page_up(),
+ Named(Enter) if let Some((l, p)) = l => l.enter(p, text).unwrap(),
+ Named(Enter) => text.enter(),
+ Character(x) => {
+ text.insert(&x);
+ return Some(x);
+ }
+ _ => {}
+ };
+ None
+}
diff --git a/src/edi/input_handlers/click.rs b/src/edi/input_handlers/click.rs
new file mode 100644
index 0000000..0998d46
--- /dev/null
+++ b/src/edi/input_handlers/click.rs
@@ -0,0 +1,86 @@
+use std::sync::Arc;
+
+use rust_fsm::StateMachine;
+use winit::event::MouseButton;
+use winit::window::Window;
+
+use crate::edi::*;
+impl Editor {
+ pub fn click(
+ &mut self,
+ bt: MouseButton,
+ cursor_position: (usize, usize),
+ w: Arc<dyn Window>,
+ ) {
+ let text = &mut self.text;
+ _ = self
+ .requests
+ .complete
+ .consume(CompletionAction::Click)
+ .unwrap();
+ match self.state.consume(Action::M(bt)).unwrap() {
+ Some(Do::MoveCursor) => {
+ text.cursor.just(
+ text.mapped_index_at(cursor_position),
+ &text.rope,
+ );
+ if let Some((lsp, path)) = lsp!(self + p) {
+ if self.requests.sig_help.result.is_some() {
+ self.requests.sig_help.request(lsp.runtime.spawn(
+ lsp.request_sig_help(
+ path,
+ text.primary_cursor(),
+ ),
+ ));
+ }
+ self.requests.document_highlights.request(
+ lsp.runtime.spawn(
+ lsp.document_highlights(
+ path,
+ text.to_l_position(
+ text.cursor.first().position,
+ )
+ .unwrap(),
+ ),
+ ),
+ );
+ }
+ self.hist.lc = text.cursor.clone();
+ self.chist.push(text.primary_cursor());
+ text.cursor.first().setc(&text.rope);
+ }
+ Some(Do::NavForward) => self.nav_forward(),
+ Some(Do::NavBack) => self.nav_back(),
+ Some(Do::ExtendSelectionToMouse) => {
+ let p = text.mapped_index_at(cursor_position);
+ text.cursor.first_mut().extend_selection_to(p, &text.rope);
+ }
+ Some(Do::StartSelection) => {
+ let p = text.mapped_index_at(cursor_position);
+
+ let x = *text.cursor.first();
+ text.cursor.first_mut().sel = Some((x..x).into());
+ text.cursor.first_mut().extend_selection_to(p, &text.rope);
+ self.hist.lc = text.cursor.clone();
+ }
+ Some(Do::GoToDefinition) => {
+ if let Some(x) = self.requests.def.result.clone()
+ && let Err(e) = self.go(&x, w.clone())
+ {
+ log::error!("gtd: {e}");
+ }
+ }
+ Some(Do::InsertCursorAtMouse) => {
+ text.cursor.add(
+ text.mapped_index_at(cursor_position),
+ &text.rope,
+ );
+ self.hist.lc = text.cursor.clone();
+ self.chist.push(text.primary_cursor());
+ text.cursor.first().setc(&text.rope);
+ }
+ None => {}
+ _ => unreachable!(),
+ }
+ }
+}
diff --git a/src/edi/input_handlers/cursor.rs b/src/edi/input_handlers/cursor.rs
new file mode 100644
index 0000000..bb283af
--- /dev/null
+++ b/src/edi/input_handlers/cursor.rs
@@ -0,0 +1,245 @@
+use std::borrow::Cow;
+use std::sync::Arc;
+
+use Default::default;
+use implicit_fn::implicit_fn;
+use lsp_types::request::*;
+use lsp_types::*;
+use rust_fsm::StateMachine;
+use tokio::task::spawn_blocking;
+use tokio_util::task::AbortOnDropHandle as DropH;
+use winit::window::Window;
+
+use crate::edi::*;
+impl Editor {
+ #[implicit_fn]
+ pub fn cursor_moved(
+ &mut self,
+ cursor_position: (usize, usize),
+ w: Arc<dyn Window>,
+ c: usize,
+ ) {
+ match self.state.consume(Action::C(cursor_position)).unwrap() {
+ Some(Do::ExtendSelectionToMouse) => {
+ let p = self.text.mapped_index_at(cursor_position);
+ self.text
+ .cursor
+ .first_mut()
+ .extend_selection_to(p, &self.text.rope);
+ w.request_redraw();
+ }
+ Some(Do::StartSelection) => {
+ let x = self.text.mapped_index_at(cursor_position);
+ self.text.cursor.first_mut().position = x;
+ self.text.cursor.first_mut().sel = Some((x..x).into());
+ self.hist.lc = self.text.cursor.clone();
+ }
+ Some(Do::Hover)
+ if let Some(hover) = self
+ .text
+ .visual_index_at(cursor_position)
+ .map(Mapping::own)
+ && let Some(_) = lsp!(self) =>
+ 'out: {
+ let l = &mut self.requests.hovering.result;
+ if let Some(Hovr {
+ span: Some([(_x, _y), (_x2, _)]), ..
+ }) = &*l
+ && let Some(_y) = _y.checked_sub(self.text.vo)
+ && let Some(_x) = _x.checked_sub(self.text.ho)
+ && let Some(_x2) = _x2.checked_sub(self.text.ho)
+ && cursor_position.1 == _y
+ && (_x..=_x2).contains(
+ &&(cursor_position.0
+ - self.text.line_number_offset()
+ - 1),
+ )
+ {
+ break 'out;
+ } else {
+ // println!("span no longer below cursor; cancel hover {_x}..{_x2} {}", cursor_position.0 - text.line_number_offset() - 1);
+ *l = None;
+ w.request_redraw();
+ }
+ self.rq_hover(hover, cursor_position, c);
+ }
+ Some(Do::Hover) => {
+ self.requests.def.result = None;
+ self.requests.hovering.result = None;
+ w.request_redraw();
+ }
+ None => {}
+ x => unreachable!("{x:?}"),
+ }
+ }
+ #[implicit_fn]
+ pub fn rq_hover(
+ &mut self,
+ hover: Mapping<'_>,
+ cursor_position: (usize, usize),
+ c: usize,
+ ) {
+ let (lsp, o) = lsp!(self + p).unwrap();
+ let text = self.text.clone();
+ let mut rang = None;
+ let z = match hover {
+ Mapping::Char(_, _, i) => TextDocumentPositionParams {
+ position: text.to_l_position(i).unwrap(),
+ text_document: o.tid(),
+ },
+ Mapping::Fake(mark, relpos, abspos, _) => {
+ let Some(ref loc) = mark.data[relpos as usize].1 else {
+ return;
+ };
+ let (x, y) = text.xy(abspos as _).unwrap();
+ let Some(mut begin) = text.reverse_source_map(y) else {
+ return;
+ };
+ let start = begin.nth(x.saturating_sub(1)).unwrap() + 1;
+ let left = mark.data[..relpos as usize]
+ .iter()
+ .rev()
+ .take_while(_.1.as_ref() == Some(loc))
+ .count();
+ let start = start + relpos as usize - left;
+ let length = mark.data[relpos as usize..]
+ .iter()
+ .take_while(_.1.as_ref() == Some(loc))
+ .count()
+ + left;
+ rang = Some([(start, y), (start + length, y)]);
+ TextDocumentPositionParams {
+ text_document: TextDocumentIdentifier {
+ uri: loc.uri.clone(),
+ },
+ position: loc.range.start,
+ }
+ }
+ };
+ if ctrl() {
+ if self
+ .requests
+ .def
+ .request
+ .as_ref()
+ .is_none_or(|&(_, x)| x != cursor_position)
+ {
+ let handle = lsp.runtime.spawn(
+ lsp.request::<lsp_request!("textDocument/definition")>(
+ &GotoDefinitionParams {
+ text_document_position_params: z.clone(),
+ work_done_progress_params: default(),
+ partial_result_params: default(),
+ },
+ )
+ .unwrap()
+ .0,
+ );
+ self.requests.def.request =
+ Some((DropH::new(handle), cursor_position));
+ } else if self.requests.def.result.as_ref().is_some_and(|em| {
+ let z = em.origin_selection_range.unwrap();
+ (z.start.character..z.end.character).contains(
+ &((cursor_position.0 - text.line_number_offset() - 1)
+ as _),
+ )
+ }) {
+ self.requests.def.result = None;
+ }
+ } else {
+ self.requests.def.result = None;
+ }
+ if let Some((_, c)) = self.requests.hovering.request
+ && c == cursor_position
+ {
+ return;
+ }
+ // if !running.insert(hover) {return}
+ let (rx, _) = lsp
+ .request::<HoverRequest>(&HoverParams {
+ text_document_position_params: z,
+ work_done_progress_params: default(),
+ })
+ .unwrap();
+ // println!("rq hov of {hover:?} (cur {})", requests.hovering.request.is_some());
+ let handle: tokio::task::JoinHandle<Result<Option<Hovr>, _>> =
+ lsp.runtime.spawn(async move {
+ let Some(x) = rx.await? else {
+ return Ok(None::<Hovr>);
+ };
+ let (w, cells) = spawn_blocking(move || {
+ let x = match &x.contents {
+ lsp_types::HoverContents::Scalar(
+ marked_string,
+ ) => match marked_string {
+ MarkedString::LanguageString(x) =>
+ Cow::Borrowed(&*x.value),
+ MarkedString::String(x) => Cow::Borrowed(&**x),
+ },
+ lsp_types::HoverContents::Array(
+ marked_strings,
+ ) => Cow::Owned(
+ marked_strings
+ .iter()
+ .map(|x| match x {
+ MarkedString::LanguageString(x) =>
+ &*x.value,
+ MarkedString::String(x) => &*x,
+ })
+ .collect::<String>(),
+ ),
+ lsp_types::HoverContents::Markup(
+ markup_content,
+ ) => Cow::Borrowed(&*markup_content.value),
+ };
+ let x = hov::p(&x).unwrap();
+ let m = hov::l(&x)
+ .into_iter()
+ .max()
+ .map(_ + 2)
+ .unwrap_or(usize::MAX)
+ .min(c - 10);
+ (m, hov::markdown2(m, &x))
+ })
+ .await
+ .unwrap();
+ let span = rang.or_else(|| {
+ x.range.and_then(|range| try {
+ let (startx, starty) =
+ text.l_pos_to_char(range.start)?;
+ let (endx, endy) =
+ text.l_pos_to_char(range.end)?;
+ let x1 = text
+ .reverse_source_map(starty)?
+ .nth(startx)?;
+ let x2 =
+ text.reverse_source_map(endy)?.nth(endx)?;
+ [
+ (x1, range.start.line as _),
+ (x2, range.start.line as _),
+ ]
+ })
+ });
+ Ok(Some(
+ hov::Hovr {
+ span,
+ item: text::CellBuffer {
+ c: w,
+ vo: 0,
+ cells: cells.into(),
+ },
+ range: x.range,
+ // range: x.range.and_then(|x| text.l_range(x)),
+ }
+ .into(),
+ ))
+ });
+ self.requests.hovering.request =
+ (DropH::new(handle), cursor_position).into();
+ // requests.hovering.result = None;
+ // lsp!().map(|(cl, o)| {
+ // let window = window.clone();
+ // });
+ // });
+ }
+}
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(())
+ }
+}
diff --git a/src/edi/lsp_impl.rs b/src/edi/lsp_impl.rs
new file mode 100644
index 0000000..c28670e
--- /dev/null
+++ b/src/edi/lsp_impl.rs
@@ -0,0 +1,220 @@
+use lsp_server::Request as LRq;
+use lsp_types::request::*;
+use lsp_types::*;
+use serde::{Deserialize, Serialize};
+use ttools::{Tupl, With};
+
+use crate::complete::Complete;
+use crate::edi::st::*;
+use crate::hov::Hovr;
+use crate::lsp::{RequestError, Rq};
+use crate::runnables::Runnables;
+use crate::sym::GoTo;
+use crate::{CompletionState, act, sig, sym};
+
+#[derive(Default, Debug, Serialize, Deserialize)]
+pub struct Requests {
+ pub hovering:
+ Rq<Hovr, Option<Hovr>, (usize, usize), RequestError<HoverRequest>>,
+ pub document_highlights: Rq<
+ Vec<DocumentHighlight>,
+ Vec<DocumentHighlight>,
+ (),
+ RequestError<DocumentHighlightRequest>,
+ >,
+ pub complete: CompletionState,
+ pub sig_help: Rq<
+ (SignatureHelp, usize, Option<usize>),
+ Option<SignatureHelp>,
+ (),
+ RequestError<SignatureHelpRequest>,
+ >, // vo, lines
+ // #[serde(serialize_with = "serialize_tokens")]
+ // #[serde(deserialize_with = "deserialize_tokens")]
+ #[serde(skip)]
+ pub semantic_tokens: Rq<
+ Box<[SemanticToken]>,
+ Box<[SemanticToken]>,
+ (),
+ RequestError<SemanticTokensFullRequest>,
+ >,
+ pub diag: Rq<
+ String,
+ Option<String>,
+ (),
+ RequestError<DocumentDiagnosticRequest>,
+ >,
+ #[serde(skip)]
+ pub inlay: Rq<
+ Vec<InlayHint>,
+ Vec<InlayHint>,
+ (),
+ RequestError<lsp_request!("textDocument/inlayHint")>,
+ >,
+ pub def: Rq<
+ LocationLink,
+ Option<GotoDefinitionResponse>,
+ (usize, usize),
+ RequestError<lsp_request!("textDocument/definition")>,
+ >,
+ #[serde(skip)]
+ pub document_symbols: Rq<
+ Option<Vec<DocumentSymbol>>,
+ Option<DocumentSymbolResponse>,
+ (),
+ RequestError<lsp_request!("textDocument/documentSymbol")>,
+ >,
+ #[serde(skip)]
+ pub git_diff: Rq<imara_diff::Diff, imara_diff::Diff, (), ()>,
+}
+impl crate::edi::Editor {
+ pub fn poll(&mut self) {
+ let Some((l, ..)) = self.lsp else { return };
+ 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));
+ }
+ rq => log::debug!("discarding request {rq:?}"),
+ }
+ }
+ self.requests.inlay.poll(|x, p| {
+ x.ok().or(p.1).inspect(|x| {
+ self.text.set_inlay(x);
+ })
+ });
+ self.requests.document_highlights.poll(|x, _| x.ok());
+ self.requests.diag.poll(|x, _| x.ok().flatten());
+ if let CompletionState::Complete(rq) = &mut self.requests.complete
+ {
+ rq.poll(|f, (c, _)| {
+ f.ok().flatten().map(|x| Complete {
+ r: x,
+ start: c,
+ selection: 0,
+ vo: 0,
+ })
+ });
+ };
+ match &mut self.state {
+ State::Symbols(x) => {
+ x.poll(|x, (_, p)| {
+ let Some(p) = p else { unreachable!() };
+
+ x.ok().flatten().map(|r| sym::Symbols {
+ data: r.with(p.data.drop::<1>()),
+ selection: 0,
+ vo: 0,
+ ..p
+ })
+ });
+ }
+ State::CodeAction(x) => {
+ if x.poll(|x, _| {
+ let lems: Vec<CodeAction> = x
+ .ok()??
+ .into_iter()
+ .map(|x| match x {
+ CodeActionOrCommand::CodeAction(x) => x,
+ _ => panic!("alas we dont like these"),
+ })
+ .collect();
+ if lems.is_empty() {
+ self.bar.last_action =
+ "no code actions available".into();
+ None
+ } else {
+ self.bar.last_action =
+ format!("{} code actions", lems.len());
+ Some(act::CodeActions::new(lems))
+ }
+ }) && x.result.is_none()
+ {
+ self.state = State::Default;
+ }
+ }
+ State::Runnables(x) => {
+ x.poll(|x, ((), old)| {
+ Some(Runnables {
+ data: x.ok()?,
+ ..old.unwrap_or_default()
+ })
+ });
+ }
+ State::GoToL(z) => match &mut z.data.1 {
+ Some(crate::gotolist::O::References(y)) => {
+ y.poll(|x, _| {
+ x.ok().flatten().map(|x| {
+ z.data.0 = x.iter().map(GoTo::from).collect()
+ })
+ });
+ }
+ Some(crate::gotolist::O::Impl(y)) => {
+ y.poll(|x, _| {
+ x.ok().map(|x| {
+ x.and_then(|x| try {
+ z.data.0 = match x {
+ GotoDefinitionResponse::Scalar(
+ location,
+ ) => vec![GoTo::from(
+ location,
+ )],
+ GotoDefinitionResponse::Array(
+ locations,
+ ) => locations
+ .into_iter()
+ .map(GoTo::from)
+ .collect(),
+ GotoDefinitionResponse::Link(
+ location_links,
+ ) => location_links
+ .into_iter()
+ .map(|LocationLink {target_uri, target_range, .. }| {
+ GoTo::from(
+ Location {
+ uri: target_uri,
+ range: target_range,
+ }
+ )
+ })
+ .collect(),
+ };
+ });
+ })
+ });
+ }
+ _ => {}
+ },
+ _ => {}
+ }
+ self.requests.def.poll(|x, _| {
+ x.ok().flatten().and_then(|x| match &x {
+ GotoDefinitionResponse::Link([x, ..]) => Some(x.clone()),
+ _ => None,
+ })
+ });
+ self.requests
+ .semantic_tokens
+ .poll(|x, _| x.ok().inspect(|x| self.text.set_toks(&x)));
+ self.requests.sig_help.poll(|x, ((), y)| {
+ x.ok().flatten().map(|x| {
+ if let Some((old_sig, vo, max)) = y
+ && &sig::active(&old_sig) == &sig::active(&x)
+ {
+ (x, vo, max)
+ } else {
+ (x, 0, None)
+ }
+ })
+ });
+ self.requests.hovering.poll(|x, _| x.ok().flatten());
+ self.requests.git_diff.poll(|x, _| x.ok());
+ self.requests.document_symbols.poll(|x, _| {
+ x.ok().flatten().map(|x| match x {
+ DocumentSymbolResponse::Flat(_) => None,
+ DocumentSymbolResponse::Nested(x) => Some(x),
+ })
+ });
+ }
+}
diff --git a/src/rnd.rs b/src/rnd.rs
index aee37c6..8970d81 100644
--- a/src/rnd.rs
+++ b/src/rnd.rs
@@ -17,7 +17,7 @@ use winit::dpi::{PhysicalPosition, PhysicalSize};
use winit::window::{ImeRequestData, Window};
use crate::edi::st::State;
-use crate::edi::{Editor, lsp_m};
+use crate::edi::{Editor, lsp};
use crate::gotolist::{At, GoTo};
use crate::lsp::Rq;
use crate::sym::{UsedSI};
@@ -183,7 +183,7 @@ pub fn render(
});
// x.range;
}
- if let Some((lsp, p)) = lsp_m!(ed + p) && let uri = Url::from_file_path(p).unwrap() && let Some(diag) = lsp.diagnostics.get(&uri, &lsp.diagnostics.guard()) {
+ if let Some((lsp, p)) = lsp!(ed + p) && let uri = Url::from_file_path(p).unwrap() && let Some(diag) = lsp.diagnostics.get(&uri, &lsp.diagnostics.guard()) {
#[derive(Copy, Clone, Debug)]
enum EType {
Hint, Info, Error,Warning,Related(DiagnosticSeverity),
@@ -266,7 +266,7 @@ pub fn render(
}
},
ed.origin.as_deref(),
- match lsp_m!(ed) {
+ match lsp!(ed) {
Some(lsp::Client { initialized: Some(lsp_types::InitializeResult {
capabilities: ServerCapabilities {
semantic_tokens_provider:
@@ -298,7 +298,7 @@ pub fn render(
.unwrap_or("new buffer"),
&ed.state,
&text,
- lsp_m!(ed),
+ lsp!(ed),
);
unsafe {
dsb::render(
@@ -491,7 +491,7 @@ pub fn render(
// chain([Cell::default()], x.to_string().chars().map(Cell::basic)).chain([Cell::default()]).collect();
for (x, rel, mut cell) in text
- .colored_lines(r, lsp_m!(ed).and_then(|x| x.legend()))
+ .colored_lines(r, lsp!(ed).and_then(|x| x.legend()))
{
if rel == 0 {
let rem = cells.len() % c;
@@ -544,7 +544,7 @@ pub fn render(
// dbg !(x);
}
let mut pass = true;
- if let Some((lsp, p)) = lsp_m!(ed + p)
+ if let Some((lsp, p)) = lsp!(ed + p)
&& let Some(diag) = lsp.diagnostics.get(
&Url::from_file_path(p).unwrap(),
&lsp.diagnostics.guard(),
diff --git a/src/text.rs b/src/text.rs
index 2e1de2b..786d850 100644
--- a/src/text.rs
+++ b/src/text.rs
@@ -1,3 +1,4 @@
+use std::borrow::Cow;
use std::cmp::{Reverse, min};
use std::collections::BTreeSet;
use std::fmt::Debug;
@@ -315,7 +316,10 @@ impl TextArea {
{
for (i, (c, _)) in x.data.iter().enumerate() {
yield Mapping::Fake(
- x, i as u32, x.position, *c,
+ Cow::Borrowed(x),
+ i as u32,
+ x.position,
+ *c,
);
}
}
@@ -1622,7 +1626,7 @@ pub(crate) use col;
#[derive(Debug, PartialEq, Clone)]
pub enum Mapping<'a> {
Fake(
- &'a Marking<Box<[(char, Option<Location>)]>>,
+ Cow<'a, Marking<Box<[(char, Option<Location>)]>>>,
/// Label relative
u32,
/// True position
@@ -1632,6 +1636,17 @@ pub enum Mapping<'a> {
Char(char, usize /* line rel */, usize /* true position */),
}
impl Mapping<'_> {
+ pub fn own(self) -> Mapping<'static> {
+ match self {
+ Self::Fake(mark, a, b, c) => Mapping::Fake(
+ Cow::Owned(mark.clone().into_owned()),
+ a,
+ b,
+ c,
+ ),
+ Self::Char(x, y, z) => Mapping::Char(x, y, z),
+ }
+ }
fn c(&self) -> char {
let (Mapping::Char(x, ..) | Mapping::Fake(.., x)) = self;
*x