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; enum Set { To(T), Reset, Ignore, } use crate::edi::*; use crate::hov::{DiagnosticHovr, Hoverable, Hovring}; use crate::lsp::BehaviourAfter; use crate::rnd::CellBuffer; impl Editor { #[implicit_fn] pub fn cursor_moved( &mut self, cursor_position: (usize, usize), w: Arc, 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(x) = lsp!(self) && x.initialized.is_some() && let Mapping::Char(e, ..) | Mapping::Fake(.., e) = hover && !e.is_whitespace() => 'out: { match self .state .consume(Action::HOnSomething(cursor_position)) .unwrap() { Some(Do::SetHovering) => { let State::Hovering(l) = &mut self.state else { panic!() }; let l2 = &mut l.result; match l2 { Some(hovrables) => { for hoverable in &hovrables.of { if let Hoverable::Lsp(Hovr { span, .. }) | Hoverable::Diagnostic( DiagnosticHovr { span, .. }, ) = &*hoverable && let Some([(_x, _y), (_x2, _)]) = span && 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); *l2 = None; w.request_redraw(); break; } } } None => {} } self.rq_hover(hover, cursor_position, c, w); } _ => {} } // let l = &mut self.requests.hovering.result; } Some(Do::Hover) => { self.state.consume(Action::HOnNothing).unwrap(); w.request_redraw(); } None => {} x => unreachable!("{x:?}"), } } pub fn find_diags( &mut self, cursor_position: (usize, usize), w: &Arc, ) -> Option> { lsp!(let lsp, p = self else None); lsp.diagnostics .get( &Url::from_file_path(p).unwrap(), &lsp.diagnostics.guard(), ) .map(|diag| { let text = &mut self.text; let r = text.r; diag.iter() .filter(|diag| { text.l_range(diag.range).is_some_and(|x| { x.contains( &text.mapped_index_at(cursor_position), ) && (text.vo..text.vo + r) .contains(&(diag.range.start.line as _)) }) }) .cloned() .map(|x| { let span = try { let range = x.range; 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 _), ] }; // println!("{x:?}"); DiagnosticHovr::new(span, x, w, r, text) }) .map(Hoverable::Diagnostic) .collect::>() }) } #[implicit_fn] pub fn rq_hover( &mut self, hover: Mapping<'_>, cursor_position: (usize, usize), c: usize, w: Arc, ) { let (lsp, o) = lsp!(self + p).unwrap(); let text = self.text.clone(); let mut rang = None; let tdpp = 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, } } }; let x = if ctrl() { if self .requests .def .request .as_ref() .is_none_or(|&(_, x)| x != cursor_position) { let handle = lsp.runtime.spawn( lsp.request::( &GotoDefinitionParams { text_document_position_params: tdpp.clone(), work_done_progress_params: default(), partial_result_params: default(), }, ) .unwrap() .0, ); Set::To((handle, cursor_position)) // 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 _), ) }) { Set::Reset } else { Set::Ignore } } else { Set::Reset }; match x { Set::To((x, y)) => self.requests.def.request = Some((DropH::new(x), y)), Set::Reset => self.requests.def.result = None, Set::Ignore => {} } // match self.state.consume(cursor_position) {} // if let Some((_, c)) = hov.request // && c == cursor_position // { // return; // } // if !running.insert(hover) {return} // let (rx, _) =; // println!("rq hov of {hover:?} (cur {})", requests.hovering.request.is_some()); let tdp = tdpp.clone(); let l = self.language; let window = w.clone(); let handle: tokio::task::JoinHandle, _>> = lsp.runtime.spawn(async move { let Some(x) = lsp .request_::( &HoverParams { text_document_position_params: tdp.clone(), work_done_progress_params: default(), }, )? .0 .await? else { return Ok(None::); }; let (width, cells) = spawn_blocking(move || { let mut 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::(), ), lsp_types::HoverContents::Markup( markup_content, ) => Cow::Borrowed(&*markup_content.value), }; if l.is_some_and(|l| { matches!( &LOADER.language(l).config().language_id, "cpp" ) }) { x = format!("```cpp\n{x}\n```").into(); } // println!("{x}"); let x = hov::p(&x).expect("markdown parsing"); let m = hov::l(&x) .into_iter() .max() .map(_ + 2) .unwrap_or(usize::MAX) .min(c - 10); (m, hov::markdown2(m, &x, l)) }) .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 _), ] }) }); let cells = cells.into(); window.request_redraw(); Ok(Some( hov::Hovr { span, tdpp: tdp, item: CellBuffer { c: width, vo: 0, cells: cells }, range: x.range, // range: x.range.and_then(|x| text.l_range(x)), } .into(), )) }); let diags = self.find_diags(cursor_position, &w); println!("requesting hover"); self.state .consume(Action::SetHovering( diags.map(|of| Hovring { of, ..default() }), handle, (cursor_position, tdpp), )) .unwrap(); // self.requests.hovering.request = // (DropH::new(handle), cursor_position).into(); // requests.hovering.result = None; // lsp!().map(|(cl, o)| { // let window = window.clone(); // }); // }); } }