A simple CPU rendered GUI IDE experience.
separate lsp
bendn 6 weeks ago
parent c5c8b14 · commit e1d4e61
-rw-r--r--Cargo.toml1
-rw-r--r--src/edi.rs40
-rw-r--r--src/edi/st.rs6
-rw-r--r--src/lsp.rs1125
-rw-r--r--src/lsp/client.rs505
-rw-r--r--src/lsp/communication.rs227
-rw-r--r--src/lsp/init_opts.rs362
-rw-r--r--src/lsp/rq.rs171
-rw-r--r--src/main.rs2
9 files changed, 1297 insertions, 1142 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 6de45db..1bad7e5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -69,6 +69,7 @@ rangemap = { version = "1.7.1", features = ["const_fn", "nightly", "serde1"] }
itern = "0.1.1"
kitty-rc = { version = "0.4.2", git = "https://github.com/bend-n/kitty-rc-rs" }
smol_str = "0.3.6"
+futures = "0.3.32"
[profile.dev.package]
rust-analyzer.opt-level = 3
fimg.opt-level = 3
diff --git a/src/edi.rs b/src/edi.rs
index e1e47e0..1f3673d 100644
--- a/src/edi.rs
+++ b/src/edi.rs
@@ -425,14 +425,11 @@ impl Editor {
}
}
let r = &l.runtime;
- self.requests.inlay.poll(
- |x, p| {
- x.ok().or(p.1).inspect(|x| {
- self.text.set_inlay(x);
- })
- },
- r,
- );
+ self.requests.inlay.poll(|x, p| {
+ x.ok().or(p.1).inspect(|x| {
+ self.text.set_inlay(x);
+ })
+ });
self.requests.document_highlights.poll_r(|x, _| x.ok(), r, w);
self.requests.diag.poll_r(|x, _| x.ok().flatten(), r, w);
if let CompletionState::Complete(rq) = &mut self.requests.complete
@@ -1122,12 +1119,7 @@ impl Editor {
}
}
}
- Some(Do::SymbolsSelect) => {
- let State::Symbols(Rq { result: Some(x), .. }) =
- &self.state
- else {
- unreachable!()
- };
+ Some(Do::SymbolsSelect(x)) => {
if let Some(Ok(x)) = x.sel()
&& let Err(e) = try bikeshed anyhow::Result<()> {
let r = match x.at {
@@ -1478,12 +1470,7 @@ impl Editor {
}
Some(CDo::Finish(x)) => {
let sel = x.sel(&filter(&self.text));
- let sel = lsp
- .runtime
- .block_on(
- lsp.resolve(sel.clone()).unwrap(),
- )
- .unwrap();
+ let sel = lsp.resolve(sel.clone()).unwrap();
let CompletionItem {
text_edit:
Some(CompletionTextEdit::Edit(ed)),
@@ -1499,13 +1486,12 @@ impl Editor {
self.text.apply_snippet(&ed).unwrap();
}
_ => {
- let (s, _) =
- self.text.apply(&ed).unwrap();
- self.text
- .cursor
- .first_mut()
- .position =
- s + ed.new_text.chars().count();
+ self.text.apply(&ed).unwrap();
+ // self.text
+ // .cursor
+ // .first_mut()
+ // .position =
+ // s + ed.new_text.chars().count();
}
}
if let Some(mut additional_tedits) =
diff --git a/src/edi/st.rs b/src/edi/st.rs
index 5c7568f..0db1017 100644
--- a/src/edi/st.rs
+++ b/src/edi/st.rs
@@ -104,7 +104,7 @@ Symbols(Rq { result: Some(_x), request: _rq }) => {
K(Key::Named(Tab) if shift()) => _ [SymbolsSelectNext],
K(Key::Named(ArrowDown)) => _ [SymbolsSelectNext],
K(Key::Named(ArrowUp | Tab)) => _ [SymbolsSelectPrev],
- K(Key::Named(Enter)) => _ [SymbolsSelect],
+ K(Key::Named(Enter)) => Default [SymbolsSelect(Symbols => _x)],
K(Key::Named(Escape)) => Default,
},
Symbols(Rq::<Symbols, Option<SymbolsList>, (), AQErr> => _rq) => {
@@ -158,8 +158,8 @@ Procure((t, InputRequest::RenameSymbol)) => K(Key::Named(Enter)) => Default [Ren
Procure((t, a)) => K(k) => Procure((handle(k, t), a)),
Procure((t, a)) => C(_) => Procure((t, a)),
RequestBoolean(t) => {
- K(Key::Character("n")) => Default [Boolean((BoolRequest, bool) => (t, true))],
- K(Key::Character("y")) => Default [Boolean((t, false))],
+ K(Key::Character("y")) => Default [Boolean((BoolRequest, bool) => (t, true))],
+ K(Key::Character("n")) => Default [Boolean((t, false))],
K(Key::Named(Escape)) => Default [Boolean((t, false))],
K(_) => RequestBoolean(t),
C(_) => _,
diff --git a/src/lsp.rs b/src/lsp.rs
index 0a5be2c..332fd23 100644
--- a/src/lsp.rs
+++ b/src/lsp.rs
@@ -1,661 +1,24 @@
-use std::backtrace::Backtrace;
-use std::collections::HashMap;
-use std::fmt::{Debug, Display};
-use std::marker::PhantomData;
-use std::mem::forget;
-use std::path::{Path, PathBuf};
+use std::fmt::Debug;
use std::sync::Arc;
use std::sync::atomic::AtomicI32;
-use std::sync::atomic::Ordering::Relaxed;
use std::task::Poll;
use std::thread::spawn;
use std::time::Instant;
-use Default::default;
-use anyhow::bail;
-use crossbeam::channel::{
- Receiver, RecvError, SendError, Sender, unbounded,
-};
-use log::{debug, error, trace};
-use lsp_server::{
- ErrorCode, Message, Notification as N, Request as LRq, Response as Re,
- ResponseError,
-};
+use crossbeam::channel::{Receiver, Sender, unbounded};
+use lsp_server::Message;
use lsp_types::notification::*;
use lsp_types::request::*;
use lsp_types::*;
-use rust_analyzer::lsp::ext::*;
-use serde::{Deserialize, Serialize};
-use serde_json::json;
use tokio::sync::oneshot;
-use tokio::task;
-use tokio_util::task::AbortOnDropHandle;
use winit::window::Window;
+mod client;
+mod communication;
+pub use client::*;
+mod init_opts;
+mod rq;
+pub use rq::*;
-use crate::text::cursor::ceach;
-use crate::text::{RopeExt, SortTedits, TextArea};
-#[derive(Debug)]
-pub struct Client {
- pub runtime: tokio::runtime::Runtime,
-
- pub tx: Sender<Message>,
- pub id: AtomicI32,
- pub initialized: Option<InitializeResult>,
- // pub pending: HashMap<i32, oneshot::Sender<Re>>,
- pub send_to: Sender<(i32, oneshot::Sender<Re>, BehaviourAfter)>,
- pub progress: &'static papaya::HashMap<
- ProgressToken,
- Option<(WorkDoneProgress, WorkDoneProgressBegin)>,
- >,
- pub diagnostics: &'static papaya::HashMap<Url, Vec<Diagnostic>>,
- #[allow(dead_code)]
- // TODO: handle notifications from the server
- pub not_rx: Receiver<N>,
- pub req_rx: Receiver<LRq>,
-}
-
-impl Drop for Client {
- fn drop(&mut self) {
- panic!("please dont")
- }
-}
-#[derive(Serialize, Deserialize)]
-pub enum RequestError<X> {
- Rx(PhantomData<X>),
- Failure(Re, #[serde(skip)] Option<Backtrace>),
- Cancelled(Re, DiagnosticServerCancellationData),
- Send(Message),
-}
-pub type AQErr = RequestError<LSPError>;
-impl Request for LSPError {
- type Params = ();
- type Result = ();
- const METHOD: &'static str = "<unknown method>";
-}
-#[derive(Debug)]
-pub struct LSPError {}
-pub trait Anonymize<T> {
- fn anonymize(self) -> Result<T, RequestError<LSPError>>;
-}
-impl<T, E> Anonymize<T> for Result<T, RequestError<E>> {
- fn anonymize(self) -> Result<T, RequestError<LSPError>> {
- self.map_err(|e| match e {
- RequestError::Send(x) => RequestError::Send(x),
- RequestError::Rx(_) => RequestError::Rx(PhantomData),
- RequestError::Failure(r, b) => RequestError::Failure(r, b),
- RequestError::Cancelled(r, d) => RequestError::Cancelled(r, d),
- })
- }
-}
-
-// impl<X> Debug for RequestError<X> {}
-impl<X> From<oneshot::error::RecvError> for RequestError<X> {
- fn from(_: oneshot::error::RecvError) -> Self {
- Self::Rx(PhantomData)
- }
-}
-impl<X: Request + Debug> std::error::Error for RequestError<X> {
- fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
- None
- }
-}
-impl<X> From<SendError<Message>> for RequestError<X> {
- fn from(x: SendError<Message>) -> Self {
- Self::Send(x.into_inner())
- }
-}
-impl<X: Request> Display for RequestError<X> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- Self::Send(x) =>
- write!(f, "{} failed; couldnt send {x:?}", X::METHOD),
- Self::Rx(_) =>
- write!(f, "{} failed; couldnt get from thingy", X::METHOD),
- Self::Failure(x, bt) => write!(
- f,
- "{} failed; returned badge :( {x:?} ({bt:?})",
- X::METHOD
- ),
- Self::Cancelled(x, y) =>
- write!(f, "server cancelled {}. {x:?} {y:?}", X::METHOD),
- }
- }
-}
-impl Client {
- pub fn notify<X: Notification>(
- &self,
- y: &X::Params,
- ) -> Result<(), SendError<Message>> {
- self.tx.send(Message::Notification(N {
- method: X::METHOD.into(),
- params: serde_json::to_value(y).unwrap(),
- }))
- }
- pub fn cancel(&self, rid: i32) {
- _ = self.notify::<Cancel>(&CancelParams { id: rid.into() });
- }
- pub fn request_immediate<'me, X: Request>(
- &'me self,
- y: &X::Params,
- ) -> Result<X::Result, RequestError<X>> {
- self.runtime.block_on(self.request_::<X, { Nil }>(y)?.0)
- }
-
- pub fn request<'me, X: Request>(
- &'me self,
- y: &X::Params,
- ) -> Result<
- (
- impl Future<Output = Result<X::Result, RequestError<X>>>
- + use<'me, X>,
- i32,
- ),
- SendError<Message>,
- > {
- self.request_::<X, { Redraw }>(y)
- }
- #[must_use]
- fn request_<'me, X: Request, const THEN: BehaviourAfter>(
- &'me self,
- y: &X::Params,
- ) -> Result<
- (
- impl Future<Output = Result<X::Result, RequestError<X>>>
- + use<'me, X, THEN>,
- i32,
- ),
- SendError<Message>,
- > {
- let id = self.id.fetch_add(1, std::sync::atomic::Ordering::AcqRel);
- self.tx.send(Message::Request(LRq {
- id: id.into(),
- method: X::METHOD.into(),
- params: serde_json::to_value(y).unwrap(),
- }))?;
- let (tx, rx) = oneshot::channel();
- if self.initialized.is_some() {
- debug!("sent request {} ({id})'s handler", X::METHOD);
- self.send_to.send((id, tx, THEN)).expect("oughtnt really fail");
- }
- Ok((
- async move {
- let g = scopeguard::guard((), |()| {
- self.cancel(id);
- });
-
- let mut x = rx.await?;
- forget(g);
- if let Some(ResponseError { code, ref mut data, .. }) =
- x.error
- {
- if code == ErrorCode::ServerCancelled as i32 {
- let e = serde_json::from_value(
- data.take().unwrap_or_default(),
- );
- Err(RequestError::Cancelled(x, e.expect("lsp??")))
- } else {
- Err(RequestError::Failure(
- x,
- Some(Backtrace::capture()),
- ))
- }
- } else {
- Ok(serde_json::from_value::<X::Result>(
- x.result.clone().unwrap_or_default(),
- )
- .unwrap_or_else(|_| {
- panic!(
- "lsp failure for {x:?}\ndidnt follow spec \
- for {}\npossibly spec issue",
- X::METHOD
- )
- }))
- }
- },
- id,
- ))
- }
- pub fn open(
- &self,
- f: &Path,
- text: String,
- ) -> Result<(), SendError<Message>> {
- self.notify::<DidOpenTextDocument>(&DidOpenTextDocumentParams {
- text_document: TextDocumentItem {
- uri: url::Url::from_file_path(f).unwrap(),
- language_id: "rust".into(),
- version: 0,
- text,
- },
- })
- }
- pub fn close(&self, f: &Path) -> Result<(), SendError<Message>> {
- self.notify::<DidCloseTextDocument>(&DidCloseTextDocumentParams {
- text_document: f.tid(),
- })
- }
- pub fn edit(
- &self,
- f: &Path,
- text: String,
- ) -> Result<(), SendError<Message>> {
- static V: AtomicI32 = AtomicI32::new(0);
- self.notify::<lsp_types::notification::DidChangeTextDocument>(
- &DidChangeTextDocumentParams {
- text_document: VersionedTextDocumentIdentifier {
- uri: url::Url::from_file_path(f).unwrap(),
- version: V.fetch_add(1, Relaxed),
- },
- content_changes: vec![TextDocumentContentChangeEvent {
- range: None,
- range_length: None,
- text,
- }],
- },
- )
- }
-
- pub fn resolve(
- &self,
- x: CompletionItem,
- ) -> Result<
- impl Future<
- Output = Result<
- CompletionItem,
- RequestError<ResolveCompletionItem>,
- >,
- >,
- SendError<Message>,
- > {
- self.request::<ResolveCompletionItem>(&x).map(|x| x.0)
- }
-
- pub fn request_complete<'me>(
- &'me self,
- f: &Path,
- (x, y): (usize, usize),
- c: CompletionContext,
- ) -> impl Future<
- Output = Result<
- Option<CompletionResponse>,
- RequestError<Completion>,
- >,
- > + use<'me> {
- let (rx, _) = self
- .request_::<Completion, { Redraw }>(&CompletionParams {
- text_document_position: TextDocumentPositionParams {
- text_document: f.tid(),
- position: Position { line: y as _, character: x as _ },
- },
- work_done_progress_params: default(),
- partial_result_params: default(),
- context: Some(c),
- })
- .unwrap();
- rx
- }
-
- pub fn request_sig_help<'me>(
- &'me self,
- f: &Path,
- (x, y): (usize, usize),
- ) -> impl Future<
- Output = Result<
- Option<SignatureHelp>,
- RequestError<SignatureHelpRequest>,
- >,
- > + use<'me> {
- self.request_::<SignatureHelpRequest, { Redraw }>(&SignatureHelpParams {
- context: None,
- text_document_position_params: TextDocumentPositionParams {
- text_document: f.tid(),
- position: Position { line: y as _, character: x as _ },
- },
- work_done_progress_params: default(),
- })
- .unwrap()
- .0
- }
-
- pub fn _pull_all_diag(
- &self,
- _f: PathBuf,
- ) -> impl Future<Output = anyhow::Result<()>> {
- let r = self
- .request::<lsp_request!("workspace/diagnostic")>(&default())
- .unwrap()
- .0;
- log::info!("pulling diagnostics");
- async move {
- let x = r.await?;
- log::info!("{x:?}");
- // match x {
- // DocumentDiagnosticReportResult::Report(DocumentDiagnosticReport::Full(RelatedFullDocumentDiagnosticReport {
- // related_documents,
- // full_document_diagnostic_report:FullDocumentDiagnosticReport { items,.. },
- // })) => {
- // let l = self.diagnostics.guard();
- // self.diagnostics.insert(f.tid().uri, items, &l);
- // for (uri, rel) in related_documents.into_iter().flatten() {
- // match rel {
- // DocumentDiagnosticReportKind::Full(FullDocumentDiagnosticReport { items, .. }) => {
- // self.diagnostics.insert(uri, items, &l);
- // },
- // DocumentDiagnosticReportKind::Unchanged(_) => {},
- // }
- // }
- // log::info!("pulled diagnostics");
- // },
- // _ => bail!("fuck that"),
- // };
- Ok(())
- }
- }
- pub fn _pull_diag(
- &self,
- f: PathBuf,
- previous: Option<String>,
- ) -> impl Future<Output = anyhow::Result<Option<String>>> {
- let p = DocumentDiagnosticParams {
- text_document: f.tid(),
- identifier: try {
- match self
- .initialized
- .as_ref()?
- .capabilities
- .diagnostic_provider
- .as_ref()?
- {
- DiagnosticServerCapabilities::RegistrationOptions(
- x,
- ) => x.diagnostic_options.identifier.clone()?,
- _ => None?,
- }
- },
- previous_result_id: previous,
- work_done_progress_params: default(),
- partial_result_params: default(),
- };
- let (r, _) = self
- .request::<lsp_request!("textDocument/diagnostic")>(&p)
- .unwrap();
- log::info!("pulling diagnostics");
-
- async move {
- let x = match r.await {
- Ok(x) => x,
- Err(RequestError::Cancelled(_, y)) if y.retrigger_request => {
- self.request::<lsp_request!("textDocument/diagnostic")>(&p,).unwrap().0.await?
- },
- Err(e) => return Err(e.into()),
- };
- // dbg!(&x);
- match x.clone() {
- DocumentDiagnosticReportResult::Report(
- DocumentDiagnosticReport::Full(
- RelatedFullDocumentDiagnosticReport {
- related_documents,
- full_document_diagnostic_report:
- FullDocumentDiagnosticReport {
- items,
- result_id,
- },
- },
- ),
- ) => {
- let l = self.diagnostics.guard();
- self.diagnostics.insert(f.tid().uri, items, &l);
- for (uri, rel) in
- related_documents.into_iter().flatten()
- {
- match rel {
- DocumentDiagnosticReportKind::Full(
- FullDocumentDiagnosticReport {
- items, ..
- },
- ) => {
- self.diagnostics.insert(uri, items, &l);
- }
- DocumentDiagnosticReportKind::Unchanged(_) => {
- }
- }
- }
- log::info!("pulled diagnostics");
- Ok(result_id)
- }
- _ => bail!("fuck that"),
- }
- }
- }
- pub fn document_highlights<'me>(
- &'me self,
- f: &Path,
- cursor: Position,
- ) -> impl Future<
- Output = Result<
- Vec<DocumentHighlight>,
- RequestError<DocumentHighlightRequest>,
- >,
- > + use<'me> {
- let p = DocumentHighlightParams {
- text_document_position_params: TextDocumentPositionParams {
- text_document: f.tid(),
- position: cursor,
- },
- work_done_progress_params: default(),
- partial_result_params: default(),
- };
- self.request_::<lsp_request!("textDocument/documentHighlight"), {Redraw}>(&p)
- .unwrap()
- .0
- .map(|x| x.map(|x| x.unwrap_or_default()))
- }
- pub fn document_symbols(
- &'static self,
- p: &Path,
- ) -> impl Future<
- Output = Result<
- Option<DocumentSymbolResponse>,
- RequestError<lsp_request!("textDocument/documentSymbol")>,
- >,
- > {
- self.request_::<lsp_request!("textDocument/documentSymbol"), { Redraw }>(
- &DocumentSymbolParams {
- text_document: p.tid(),
- work_done_progress_params: default(),
- partial_result_params: default(),
- },
- )
- .unwrap()
- .0
- }
- pub fn workspace_symbols(
- &'static self,
- f: String,
- ) -> impl Future<
- Output = Result<
- Option<WorkspaceSymbolResponse>,
- RequestError<lsp_request!("workspace/symbol")>,
- >,
- > {
- self.request_::<lsp_request!("workspace/symbol"), {Redraw}>(
- &lsp_types::WorkspaceSymbolParams {
- query: f,
- search_scope: Some(
- lsp_types::WorkspaceSymbolSearchScope::Workspace,
- ),
- search_kind: Some(
- lsp_types::WorkspaceSymbolSearchKind::AllSymbols,
- ),
- ..Default::default()
- },
- )
- .unwrap()
- .0
- }
-
- pub fn matching_brace_at(
- &self,
- f: &Path,
- x: Vec<Position>,
- ) -> Result<
- Vec<Option<(Position, Position)>>,
- RequestError<MatchingBrace>,
- > {
- self.request_immediate::<MatchingBrace>(&MatchingBraceParams {
- text_document: f.tid(),
- positions: x,
- })
- }
-
- pub fn matching_brace<'a>(
- &'static self,
- f: &Path,
- t: &'a mut TextArea,
- ) {
- if let Ok(x) =
- self.matching_brace_at(f, t.cursor.positions(&t.rope))
- {
- for (c, p) in t.cursor.inner.iter_mut().zip(x) {
- if let Some(p) = p {
- c.position = t.rope.l_position(p.1).unwrap();
- }
- }
- }
- }
-
- pub fn legend(&self) -> Option<&SemanticTokensLegend> {
- match &self.initialized{Some(lsp_types::InitializeResult {capabilities: ServerCapabilities {semantic_tokens_provider:Some(SemanticTokensServerCapabilities::SemanticTokensOptions(SemanticTokensOptions{legend,..})),..}, ..})=> {Some(legend)},_ => None,}
- }
- pub fn inlay(
- &'static self,
- f: &Path,
- t: &TextArea,
- ) -> impl Future<
- Output = Result<
- Vec<InlayHint>,
- RequestError<lsp_request!("textDocument/inlayHint")>,
- >,
- > + use<> {
- self.request_::<lsp_request!("textDocument/inlayHint"), { Redraw }>(&InlayHintParams {
- work_done_progress_params: default(),
- text_document: f.tid(),
- range: t.to_l_range(lower::saturating::math!{
- t.rope.try_line_to_char(t.vo-t.r).unwrap_or(0)..t.rope.try_line_to_char(t.vo + t.r + t.r).unwrap_or(t.rope.len_chars())
- }).unwrap()
- }).unwrap().0.map(|x| x.map(Option::unwrap_or_default))
- // async {
- // if let Ok(z) = z.await {
- // let mut into = vec![];
- // for lem in z.into_iter(){
- // // if let Some(_) = lem.data {
- // into.push(self.request::<lsp_request!("inlayHint/resolve")>(&lem).unwrap().0.await.unwrap());
- // // }
- // }
- // // std::fs::write("inlay", serde_json::to_string_pretty(&into).unwrap()).unwrap();
- // Ok(into)
- // } else {
- // panic!()
- // }
- // }
- }
- pub fn format(
- &'static self,
- f: &Path,
- ) -> impl Future<
- Output = Result<Option<Vec<TextEdit>>, RequestError<Formatting>>,
- > {
- self.request::<lsp_request!("textDocument/formatting")>(
- &DocumentFormattingParams {
- text_document: f.tid(),
- options: FormattingOptions {
- tab_size: 4,
- insert_spaces: false,
- properties: default(),
- trim_trailing_whitespace: Some(true),
- insert_final_newline: Some(true),
- trim_final_newlines: Some(false),
- },
- work_done_progress_params: default(),
- },
- )
- .unwrap()
- .0
- }
- pub fn rq_semantic_tokens(
- &'static self,
- to: &mut Rq<
- Box<[SemanticToken]>,
- Box<[SemanticToken]>,
- (),
- RequestError<SemanticTokensFullRequest>,
- >,
- f: &Path,
- ) -> anyhow::Result<()> {
- debug!("requested semantic tokens");
-
- let Some(b"rs") = f.extension().map(|x| x.as_encoded_bytes())
- else {
- return Ok(());
- };
- let (rx, _) = self.request_::<SemanticTokensFullRequest, {Redraw}>(
- &SemanticTokensParams {
- work_done_progress_params: default(),
- partial_result_params: default(),
- text_document: f.tid(),
- },
- )?;
- let x = self.runtime.spawn(async move {
- let t = rx.await;
- let y = t?.unwrap();
- debug!("received semantic tokens");
- let r = match y {
- SemanticTokensResult::Partial(_) =>
- panic!("i told the lsp i dont support this"),
- SemanticTokensResult::Tokens(x) =>
- x.data.into_boxed_slice(),
- };
- Ok(r)
- });
- to.request(x);
-
- Ok(())
- }
-
- pub fn enter<'a>(&self, f: &Path, t: &'a mut TextArea) {
- ceach!(t.cursor, |c| {
- let r = self.request_immediate::<OnEnter>(&TextDocumentPositionParams {
- text_document: f.tid(),
- position: t.to_l_position(*c).unwrap(),
- })
- .unwrap();
- match r {
- None => t.enter(),
- Some(mut r) => {
- r.sort_tedits();
- for f in r {
- t.apply_snippet_tedit(&f).unwrap();
- }
- }
- }
- });
- }
- pub fn runnables(
- &'static self,
- t: &Path,
- c: Option<Position>,
- ) -> Result<
- impl Future<Output = Result<Vec<Runnable>, RequestError<Runnables>>>
- + use<>,
- SendError<Message>,
- > {
- self.request::<Runnables>(&RunnablesParams {
- text_document: t.tid(),
- position: c,
- })
- .map(|(x, _)| x)
- }
-}
pub fn run(
(tx, rx): (Sender<Message>, Receiver<Message>),
workspace: WorkspaceFolder,
@@ -683,269 +46,7 @@ pub fn run(
req_rx: _req_rx,
not_rx,
};
- _ = c
- .request::<Initialize>(&InitializeParams {
- process_id: Some(std::process::id()),
-
- capabilities: ClientCapabilities {
- window: Some(WindowClientCapabilities {
- work_done_progress: Some(true),
- ..default()
- }),
- workspace: Some(WorkspaceClientCapabilities {
- symbol: Some(WorkspaceSymbolClientCapabilities {
- symbol_kind: Some(SymbolKindCapability { value_set: Some(SymbolKind::ALL.to_vec()) }),
- tag_support: Some(TagSupport { value_set: SymbolTag::ALL.to_vec() }),
- resolve_support: Some(WorkspaceSymbolResolveSupportCapability {
- properties: vec!["location".into()],
- }),
- ..default()
- }),
- diagnostic: Some(DiagnosticWorkspaceClientCapabilities { refresh_support: Some(true) }),
- inlay_hint: Some(InlayHintWorkspaceClientCapabilities { refresh_support: Some(true) }),
- workspace_edit: Some(
- WorkspaceEditClientCapabilities { document_changes: Some(true),
- resource_operations: Some(vec![ResourceOperationKind::Create, ResourceOperationKind::Rename, ResourceOperationKind::Delete]),
- failure_handling: Some(FailureHandlingKind::Abort), normalizes_line_endings: Some(false),
- change_annotation_support: Some(ChangeAnnotationWorkspaceEditClientCapabilities { groups_on_label: Some(false) }) },
- ),
- ..default()
- }),
- text_document: Some(TextDocumentClientCapabilities {
- on_type_formatting: Some(DocumentOnTypeFormattingClientCapabilities {
- dynamic_registration: Some(false),
- }),
- document_highlight: Some(default()),
- formatting: Some(DynamicRegistrationClientCapabilities { dynamic_registration: Some(false) }),
- inlay_hint: Some(InlayHintClientCapabilities { dynamic_registration: None, resolve_support: Some(InlayHintResolveClientCapabilities {
- properties: vec!["textEdits".into(), "tooltip".into(), "label.tooltip".into(), "label.command".into()], })
- }),
- document_symbol: Some(DocumentSymbolClientCapabilities {
- tag_support: Some(TagSupport { value_set: SymbolTag::ALL.to_vec() }),
- symbol_kind: Some(SymbolKindCapability { value_set: Some(SymbolKind::ALL.to_vec()) }),
- hierarchical_document_symbol_support: Some(true),
- ..default()
- }),
- definition: Some(GotoCapability { link_support: Some(true), ..default() }),
- code_action: Some(
- CodeActionClientCapabilities {
- data_support: Some(true),
- resolve_support: Some(CodeActionCapabilityResolveSupport { properties: vec!["edit".to_string()] }),
- code_action_literal_support: Some(CodeActionLiteralSupport { code_action_kind: CodeActionKindLiteralSupport { value_set: [
- "", "Empty", "QuickFix", "Refactor",
- "RefactorExtract", "RefactorInline", "RefactorRewrite", "Source",
- "SourceOrganizeImports", "quickfix", "refactor", "refactor.extract",
- "refactor.inline", "refactor.rewrite", "source", "source.organizeImports"
- ].map(String::from).into()} }),
- ..default()
- }
- ),
- rename: Some(RenameClientCapabilities { prepare_support: Some(true),
- prepare_support_default_behavior: Some(PrepareSupportDefaultBehavior::IDENTIFIER), honors_change_annotations: Some(false),
- ..default() }),
- hover: Some(HoverClientCapabilities {
- dynamic_registration: None,
- content_format: Some(vec![MarkupKind::PlainText, MarkupKind::Markdown]),
- }),
- diagnostic: Some(DiagnosticClientCapabilities { dynamic_registration: None, related_document_support: Some(true) }),
- publish_diagnostics: Some(PublishDiagnosticsClientCapabilities {
- related_information: Some(true),
- code_description_support: Some(true),
- data_support: Some(true),
- ..default()
- }),
- signature_help: Some(SignatureHelpClientCapabilities {
- dynamic_registration: None, signature_information: Some(SignatureInformationSettings {
- documentation_format: Some(vec![
- MarkupKind::Markdown,
- MarkupKind::PlainText,
- ]),
- parameter_information: Some(ParameterInformationSettings {
- label_offset_support: Some(true) }),
- active_parameter_support: Some(true),
- }), context_support: Some(false) }),
- completion: Some(CompletionClientCapabilities {
- dynamic_registration: Some(false),
- completion_item: Some(CompletionItemCapability {
- snippet_support: Some(true),
- commit_characters_support: Some(true),
- documentation_format: Some(vec![
- MarkupKind::Markdown,
- MarkupKind::PlainText,
- ]),
- deprecated_support: None,
- preselect_support: None,
- tag_support: Some(TagSupport {
- value_set: vec![CompletionItemTag::DEPRECATED],
- }),
-
- resolve_support: Some(CompletionItemCapabilityResolveSupport {
- properties: vec![
- "additionalTextEdits".into(),
- "documentation".into(),
- ],
- }),
- insert_replace_support: Some(false),
- insert_text_mode_support: Some(InsertTextModeSupport {
- value_set: vec![InsertTextMode::AS_IS],
- }),
- label_details_support: Some(true),
- ..default()
- }),
- completion_item_kind: Some(CompletionItemKindCapability {
- value_set: Some(vec![
- CompletionItemKind::TEXT,
- CompletionItemKind::METHOD,
- CompletionItemKind::FUNCTION,
- CompletionItemKind::CONSTRUCTOR,
- CompletionItemKind::FIELD,
- CompletionItemKind::VARIABLE,
- CompletionItemKind::CLASS,
- CompletionItemKind::INTERFACE,
- CompletionItemKind::MODULE,
- CompletionItemKind::PROPERTY,
- CompletionItemKind::UNIT,
- CompletionItemKind::VALUE,
- CompletionItemKind::ENUM,
- CompletionItemKind::KEYWORD,
- CompletionItemKind::SNIPPET,
- CompletionItemKind::COLOR,
- CompletionItemKind::FILE,
- CompletionItemKind::REFERENCE,
- CompletionItemKind::FOLDER,
- CompletionItemKind::ENUM_MEMBER,
- CompletionItemKind::CONSTANT,
- CompletionItemKind::STRUCT,
- CompletionItemKind::EVENT,
- CompletionItemKind::OPERATOR,
- CompletionItemKind::TYPE_PARAMETER,
- ]),
- // value_set: Some(vec![CompletionItemKind::]),
- }),
- context_support: None,
- insert_text_mode: Some(InsertTextMode::AS_IS),
- completion_list: Some(CompletionListCapability { item_defaults: None }),
- }),
- semantic_tokens: Some(SemanticTokensClientCapabilities {
- dynamic_registration: Some(false),
- requests: SemanticTokensClientCapabilitiesRequests {
- range: Some(true),
- full: Some(lsp_types::SemanticTokensFullOptions::Bool(true)),
- },
- token_modifiers: [
- "associated",
- "attribute",
- "callable",
- "constant",
- "consuming",
- "controlFlow",
- "crateRoot",
- "injected",
- "intraDocLink",
- "library",
- "macro",
- "mutable",
- "procMacro",
- "public",
- "reference",
- "trait",
- "unsafe",
- ]
- .map(|x| x.into())
- .to_vec(),
- overlapping_token_support: Some(true),
- multiline_token_support: Some(true),
- server_cancel_support: Some(false),
- augments_syntax_tokens: Some(false),
- ..default()
- }),
- ..default()
- }),
-
- general: Some(GeneralClientCapabilities {
- markdown: Some(MarkdownClientCapabilities {
- version: Some("1.0.0".into()),
- parser: "markdown".into(),
- allowed_tags: Some(vec![]),
- }),
- position_encodings: Some(vec![PositionEncodingKind::UTF8]),
- ..default()
- }),
- experimental: Some(json! {{
- "matchingBrace": true,
- "snippetTextEdit": true,
- "colorDiagnosticOutput": true,
- "codeActionGroup": true,
- "serverStatusNotification": true,
- "hoverActions": true,
- "workspaceSymbolScopeKindFiltering": true,
- "onEnter": true,
- "localDocs": true,
- }}),
- ..default()
- },
- client_info: Some(ClientInfo {
- name: "gracilaria".into(),
- version: Some(env!("CARGO_PKG_VERSION").into()),
- }),
- initialization_options: Some(json! {{
- "cargo": {
- "buildScripts": { "enable": true }
- },
- "procMacro": {
- "enable": true,
- "attributes": { "enable": true }
- },
- "hover": {
- "documentation": {
- "keywords": { "enable": false },
- },
- },
- "inlayHints": {
- "closureReturnTypeHints": { "enable": "with_block" },
- "closingBraceHints": { "minLines": 5 },
- "closureStyle": "rust_analyzer",
- "genericParameterHints": { "type": { "enable": true } },
- "rangeExclusiveHints": { "enable": true },
- "closureCaptureHints": { "enable": true },
- },
- "typing": { "triggerChars": ".=<>{(+" },
- "assist": { "preferSelf": true },
- "checkOnSave": true,
- "diagnostics": { "enable": true },
- "semanticHighlighting": {
- "punctuation": {
- "separate": {
- "macroBang": true
- },
- "specialization": { "enable": true },
- "enable": true
- }
- },
- "workspace": {
- "symbol": {
- "search": { "limit": 1024 }
- }
- },
- "showUnlinkedFileNotification": false,
- "completion": {
- "fullFunctionSignatures": { "enable": true, },
- "autoIter": { "enable": false, },
- "autoImport": { "enable": true, },
- "termSearch": { "enable": true, },
- "autoself": { "enable": true, },
- "privateEditable": { "enable": true },
- },
- "imports": {
- "granularity": "group",
- },
- }}),
- trace: None,
- workspace_folders: Some(vec![workspace]),
-
- ..default()
- })
- .unwrap();
+ _ = c.request::<Initialize>(&init_opts::get(workspace)).unwrap();
let x = serde_json::from_value::<InitializeResult>(
rx.recv().unwrap().response().unwrap().result.unwrap(),
)
@@ -966,104 +67,11 @@ pub fn run(
let progress = c.progress;
let d = c.diagnostics;
log::info!("lsp took {:?} to initialize", now.elapsed());
+
let h = spawn(move || {
- let mut map = HashMap::new();
- let w = window_rx.blocking_recv().unwrap();
- loop {
- crossbeam::select! {
- recv(req_rx) -> x => match x {
- Ok((x, y, and)) => {
- debug!("received request {x}");
- assert!(map.insert(x, (y, Instant::now(), and)).is_none());
- }
- Err(RecvError) => return,
- },
- recv(rx) -> x => match x {
- Ok(Message::Request(rq @ LRq { method: "window/workDoneProgress/create", .. })) => {
- match rq.load::<WorkDoneProgressCreate>() {
- Ok((_, x)) => {
- let g = progress.guard();
- progress.insert(x.token, None, &g);
- },
- Err(lsp_server::ExtractError::MethodMismatch(..)) => {},
- e => {
- error!("{e:?}");
- }
- };
- }
- Ok(Message::Request(x)) => {
- if let Err(e) = _req_tx.send(x) {
- let m = e.to_string();
- error!("couldnt receive request {m}: {:?}", e.into_inner());
- }
- }
- Ok(Message::Response(x)) => {
- if let Some(e) = &x.error {
- if e.code == ErrorCode::RequestCanceled as i32 {}
- else if e.code == ErrorCode::ServerCancelled as i32 {
- if let Some((s, _, t)) = map.remove(&x.id.i32()) {
- log::info!("request {} cancelled", x.id);
- _ = s.send(x);
- if t == Redraw { w.request_redraw() }
- }
- } else {
- if let Some((s, _, t)) = map.remove(&x.id.i32()) {
- _ = s.send(x.clone());
- if t == Redraw { w.request_redraw() }
- trace!("received error from lsp for response {x:?}");
- } else {
- error!("received error from lsp for response {x:?}");
- }
- }
- }
- else if let Some((s, took, t)) = map.remove(&x.id.i32()) {
- log::info!("request {} took {:?}", x.id, took.elapsed());
- match s.send(x) {
- Ok(()) => {}
- Err(e) => {
- error!(
- "unable to respond to {e:?}",
- );
- }
- }
- // w.request_redraw();
- if t == Redraw { w.request_redraw() }
- } else {
- error!("request {x:?} was dropped.")
- }
- }
- Ok(Message::Notification(rq @ N { method: "textDocument/publishDiagnostics", .. })) => {
- debug!("got diagnostics");
- match rq.load::<PublishDiagnostics>() {
- Ok(x) => {
- d.insert(x.uri, x.diagnostics, &d.guard());
- w.request_redraw();
- },
- e => error!("{e:?}"),
- }
- },
- Ok(Message::Notification(x @ N { method: "$/progress", .. })) => {
- let ProgressParams {token,value:ProgressParamsValue::WorkDone(x) } = x.load::<Progress>().unwrap();
- match x.clone() {
- WorkDoneProgress::Begin(y) => {
- progress.update(token, move |_| Some((x.clone(), y.clone())), &progress.guard());
- },
- WorkDoneProgress::Report(_) | WorkDoneProgress::End(_) => {
- progress.update(token, move |v| Some((x.clone(), v.clone().expect("evil lsp").1)), &progress.guard());
- }
- }
- w.request_redraw();
- }
- Ok(Message::Notification(notification)) => {
- debug!("rx {notification:?}");
- not_tx
- .send(notification)
- .expect("why library drop this??? no drop!!");
- }
- Err(RecvError) => return,
- }
- }
- }
+ communication::handler(
+ window_rx, progress, _req_tx, d, not_tx, rx, req_rx,
+ )
});
(c, h, window_tx)
}
@@ -1073,7 +81,6 @@ pub enum BehaviourAfter {
// Poll, ? how impl.
Nil,
}
-pub use BehaviourAfter::*;
// trait RecvEepy<T>: Sized {
// fn recv_eepy(self) -> Result<T, RecvError> {
// self.recv_sleepy(100)
@@ -1135,107 +142,3 @@ impl<T, U, F: FnMut(T) -> U, Fu: Future<Output = T>> Map_<T, U, F> for Fu {
Map(self, f)
}
}
-
-impl<R: Request> Debug for RequestError<R> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- std::fmt::Display::fmt(&self, f)
- }
-}
-
-fn none<T>() -> Option<T> {
- None
-}
-impl<T: Clone, R, D, E> Clone for Rq<T, R, D, E> {
- fn clone(&self) -> Self {
- Self { result: self.result.clone(), request: None }
- }
-}
-#[derive(serde_derive::Serialize, serde_derive::Deserialize)]
-pub struct Rq<T, R, D = (), E = RequestError<R>> {
- #[serde(skip_serializing_if = "Option::is_none", default = "none")]
- pub result: Option<T>,
- #[serde(skip, default = "none")]
- pub request: Option<(AbortOnDropHandle<Result<R, E>>, D)>,
-}
-impl<T: Debug, R, D: Debug, E> Debug for Rq<T, R, D, E> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.debug_struct(&format!("Rq<{}>", std::any::type_name::<R>()))
- .field("result", &self.result)
- .field("request", &self.request)
- .finish()
- }
-}
-
-pub type RqS<T, R: Request, D = ()> = Rq<T, R::Result, D, RequestError<R>>;
-impl<T, R, D, E> Default for Rq<T, R, D, E> {
- fn default() -> Self {
- Self { result: None, request: None }
- }
-}
-impl<T, R, E> Rq<T, R, (), E> {
- pub fn new(f: task::JoinHandle<Result<R, E>>) -> Self {
- Self {
- request: Some((AbortOnDropHandle::new(f), ())),
- result: None,
- }
- }
- pub fn request(&mut self, f: task::JoinHandle<Result<R, E>>) {
- self.request = Some((AbortOnDropHandle::new(f), ()));
- }
-}
-impl<T, R, D, E> Rq<T, R, D, E> {
- pub fn running(&self) -> bool {
- matches!(
- self,
- Self { result: Some(_), .. } | Self { request: Some(_), .. },
- )
- }
- pub fn poll(
- &mut self,
- f: impl FnOnce(Result<R, E>, (D, Option<T>)) -> Option<T>,
- runtime: &tokio::runtime::Runtime,
- ) -> bool {
- if self.request.as_mut().is_some_and(|(x, _)| x.is_finished())
- && let Some((task, d)) = self.request.take()
- {
- let x = match runtime.block_on(task) {
- Ok(x) => x,
- Err(e) => {
- log::error!(
- "unexpected join error from request poll: {e}"
- );
- return false;
- }
- };
- self.result = f(x, (d, self.result.take()));
- true
- } else { false }
- }
-
- pub fn poll_r(
- &mut self,
- f: impl FnOnce(Result<R, E>, (D, Option<T>)) -> Option<T>,
- runtime: &tokio::runtime::Runtime,
- _w: Option<&Arc<dyn Window>>,
- ) -> bool {
- self.poll(
- |x, y| {
- f(x, y).inspect(|_| {
- // w.map(|x| x.request_redraw());
- })
- },
- runtime,
- )
- }
-}
-
-pub trait PathURI {
- fn tid(&self) -> TextDocumentIdentifier;
-}
-impl PathURI for Path {
- fn tid(&self) -> TextDocumentIdentifier {
- TextDocumentIdentifier {
- uri: Url::from_file_path(self).expect("ok"),
- }
- }
-}
diff --git a/src/lsp/client.rs b/src/lsp/client.rs
new file mode 100644
index 0000000..b6b5bea
--- /dev/null
+++ b/src/lsp/client.rs
@@ -0,0 +1,505 @@
+use std::fmt::Debug;
+use std::path::{Path, PathBuf};
+use std::sync::atomic::AtomicI32;
+use std::sync::atomic::Ordering::Relaxed;
+
+use Default::default;
+use anyhow::bail;
+use crossbeam::channel::{Receiver, SendError, Sender};
+use futures::FutureExt;
+use log::debug;
+use lsp_server::{
+ Message, Notification as N, Request as LRq, Response as Re,
+};
+use lsp_types::notification::*;
+use lsp_types::request::*;
+use lsp_types::*;
+use rust_analyzer::lsp::ext::*;
+use tokio::sync::oneshot;
+
+use crate::lsp::BehaviourAfter::{self, *};
+use crate::lsp::{RequestError, Rq};
+use crate::text::cursor::ceach;
+use crate::text::{RopeExt, SortTedits, TextArea};
+
+#[derive(Debug)]
+pub struct Client {
+ pub runtime: tokio::runtime::Runtime,
+
+ pub tx: Sender<Message>,
+ pub id: AtomicI32,
+ pub initialized: Option<InitializeResult>,
+ // pub pending: HashMap<i32, oneshot::Sender<Re>>,
+ pub send_to: Sender<(i32, oneshot::Sender<Re>, BehaviourAfter)>,
+ pub progress: &'static papaya::HashMap<
+ ProgressToken,
+ Option<(WorkDoneProgress, WorkDoneProgressBegin)>,
+ >,
+ pub diagnostics: &'static papaya::HashMap<Url, Vec<Diagnostic>>,
+ #[allow(dead_code)]
+ // TODO: handle notifications from the server
+ pub not_rx: Receiver<N>,
+ pub req_rx: Receiver<LRq>,
+}
+
+impl Drop for Client {
+ fn drop(&mut self) {
+ panic!("please dont");
+ }
+}
+
+impl Client {
+ pub fn open(
+ &self,
+ f: &Path,
+ text: String,
+ ) -> Result<(), SendError<Message>> {
+ self.notify::<DidOpenTextDocument>(&DidOpenTextDocumentParams {
+ text_document: TextDocumentItem {
+ uri: url::Url::from_file_path(f).unwrap(),
+ language_id: "rust".into(),
+ version: 0,
+ text,
+ },
+ })
+ }
+ pub fn close(&self, f: &Path) -> Result<(), SendError<Message>> {
+ self.notify::<DidCloseTextDocument>(&DidCloseTextDocumentParams {
+ text_document: f.tid(),
+ })
+ }
+ pub fn edit(
+ &self,
+ f: &Path,
+ text: String,
+ ) -> Result<(), SendError<Message>> {
+ static V: AtomicI32 = AtomicI32::new(0);
+ self.notify::<lsp_types::notification::DidChangeTextDocument>(
+ &DidChangeTextDocumentParams {
+ text_document: VersionedTextDocumentIdentifier {
+ uri: url::Url::from_file_path(f).unwrap(),
+ version: V.fetch_add(1, Relaxed),
+ },
+ content_changes: vec![TextDocumentContentChangeEvent {
+ range: None,
+ range_length: None,
+ text,
+ }],
+ },
+ )
+ }
+
+ pub fn resolve(
+ &self,
+ x: CompletionItem,
+ ) -> Result<CompletionItem, RequestError<ResolveCompletionItem>> {
+ self.request_immediate::<ResolveCompletionItem>(&x)
+ }
+
+ pub fn request_complete<'me>(
+ &'me self,
+ f: &Path,
+ (x, y): (usize, usize),
+ c: CompletionContext,
+ ) -> impl Future<
+ Output = Result<
+ Option<CompletionResponse>,
+ RequestError<Completion>,
+ >,
+ > + use<'me> {
+ let (rx, _) = self
+ .request_::<Completion, { Redraw }>(&CompletionParams {
+ text_document_position: TextDocumentPositionParams {
+ text_document: f.tid(),
+ position: Position { line: y as _, character: x as _ },
+ },
+ work_done_progress_params: default(),
+ partial_result_params: default(),
+ context: Some(c),
+ })
+ .unwrap();
+ rx
+ }
+
+ pub fn request_sig_help<'me>(
+ &'me self,
+ f: &Path,
+ (x, y): (usize, usize),
+ ) -> impl Future<
+ Output = Result<
+ Option<SignatureHelp>,
+ RequestError<SignatureHelpRequest>,
+ >,
+ > + use<'me> {
+ self.request_::<SignatureHelpRequest, { Redraw }>(
+ &SignatureHelpParams {
+ context: None,
+ text_document_position_params:
+ TextDocumentPositionParams {
+ text_document: f.tid(),
+ position: Position {
+ line: y as _,
+ character: x as _,
+ },
+ },
+ work_done_progress_params: default(),
+ },
+ )
+ .unwrap()
+ .0
+ }
+
+ pub fn _pull_all_diag(
+ &self,
+ _f: PathBuf,
+ ) -> impl Future<Output = anyhow::Result<()>> {
+ let r = self
+ .request::<lsp_request!("workspace/diagnostic")>(&default())
+ .unwrap()
+ .0;
+ log::info!("pulling diagnostics");
+ async move {
+ let x = r.await?;
+ log::info!("{x:?}");
+ // match x {
+ // DocumentDiagnosticReportResult::Report(DocumentDiagnosticReport::Full(RelatedFullDocumentDiagnosticReport {
+ // related_documents,
+ // full_document_diagnostic_report:FullDocumentDiagnosticReport { items,.. },
+ // })) => {
+ // let l = self.diagnostics.guard();
+ // self.diagnostics.insert(f.tid().uri, items, &l);
+ // for (uri, rel) in related_documents.into_iter().flatten() {
+ // match rel {
+ // DocumentDiagnosticReportKind::Full(FullDocumentDiagnosticReport { items, .. }) => {
+ // self.diagnostics.insert(uri, items, &l);
+ // },
+ // DocumentDiagnosticReportKind::Unchanged(_) => {},
+ // }
+ // }
+ // log::info!("pulled diagnostics");
+ // },
+ // _ => bail!("fuck that"),
+ // };
+ Ok(())
+ }
+ }
+ pub fn _pull_diag(
+ &self,
+ f: PathBuf,
+ previous: Option<String>,
+ ) -> impl Future<Output = anyhow::Result<Option<String>>> {
+ let p = DocumentDiagnosticParams {
+ text_document: f.tid(),
+ identifier: try {
+ match self
+ .initialized
+ .as_ref()?
+ .capabilities
+ .diagnostic_provider
+ .as_ref()?
+ {
+ DiagnosticServerCapabilities::RegistrationOptions(
+ x,
+ ) => x.diagnostic_options.identifier.clone()?,
+ _ => None?,
+ }
+ },
+ previous_result_id: previous,
+ work_done_progress_params: default(),
+ partial_result_params: default(),
+ };
+ let (r, _) = self
+ .request::<lsp_request!("textDocument/diagnostic")>(&p)
+ .unwrap();
+ log::info!("pulling diagnostics");
+
+ async move {
+ let x = match r.await {
+ Ok(x) => x,
+ Err(RequestError::Cancelled(_, y)) if y.retrigger_request => {
+ self.request::<lsp_request!("textDocument/diagnostic")>(&p,).unwrap().0.await?
+ },
+ Err(e) => return Err(e.into()),
+ };
+ // dbg!(&x);
+ match x.clone() {
+ DocumentDiagnosticReportResult::Report(
+ DocumentDiagnosticReport::Full(
+ RelatedFullDocumentDiagnosticReport {
+ related_documents,
+ full_document_diagnostic_report:
+ FullDocumentDiagnosticReport {
+ items,
+ result_id,
+ },
+ },
+ ),
+ ) => {
+ let l = self.diagnostics.guard();
+ self.diagnostics.insert(f.tid().uri, items, &l);
+ for (uri, rel) in
+ related_documents.into_iter().flatten()
+ {
+ match rel {
+ DocumentDiagnosticReportKind::Full(
+ FullDocumentDiagnosticReport {
+ items, ..
+ },
+ ) => {
+ self.diagnostics.insert(uri, items, &l);
+ }
+ DocumentDiagnosticReportKind::Unchanged(_) => {
+ }
+ }
+ }
+ log::info!("pulled diagnostics");
+ Ok(result_id)
+ }
+ _ => bail!("fuck that"),
+ }
+ }
+ }
+ pub fn document_highlights<'me>(
+ &'me self,
+ f: &Path,
+ cursor: Position,
+ ) -> impl Future<
+ Output = Result<
+ Vec<DocumentHighlight>,
+ RequestError<DocumentHighlightRequest>,
+ >,
+ > + use<'me> {
+ let p = DocumentHighlightParams {
+ text_document_position_params: TextDocumentPositionParams {
+ text_document: f.tid(),
+ position: cursor,
+ },
+ work_done_progress_params: default(),
+ partial_result_params: default(),
+ };
+ self.request_::<lsp_request!("textDocument/documentHighlight"), {Redraw}>(&p)
+ .unwrap()
+ .0
+ .map(|x| x.map(|x| x.unwrap_or_default()))
+ }
+ pub fn document_symbols(
+ &'static self,
+ p: &Path,
+ ) -> impl Future<
+ Output = Result<
+ Option<DocumentSymbolResponse>,
+ RequestError<lsp_request!("textDocument/documentSymbol")>,
+ >,
+ > {
+ self.request_::<lsp_request!("textDocument/documentSymbol"), { Redraw }>(
+ &DocumentSymbolParams {
+ text_document: p.tid(),
+ work_done_progress_params: default(),
+ partial_result_params: default(),
+ },
+ )
+ .unwrap()
+ .0
+ }
+ pub fn workspace_symbols(
+ &'static self,
+ f: String,
+ ) -> impl Future<
+ Output = Result<
+ Option<WorkspaceSymbolResponse>,
+ RequestError<lsp_request!("workspace/symbol")>,
+ >,
+ > {
+ self.request_::<lsp_request!("workspace/symbol"), { Redraw }>(
+ &lsp_types::WorkspaceSymbolParams {
+ query: f,
+ search_scope: Some(
+ lsp_types::WorkspaceSymbolSearchScope::Workspace,
+ ),
+ search_kind: Some(
+ lsp_types::WorkspaceSymbolSearchKind::AllSymbols,
+ ),
+ ..Default::default()
+ },
+ )
+ .unwrap()
+ .0
+ }
+
+ pub fn matching_brace_at(
+ &self,
+ f: &Path,
+ x: Vec<Position>,
+ ) -> Result<
+ Vec<Option<(Position, Position)>>,
+ RequestError<MatchingBrace>,
+ > {
+ self.request_immediate::<MatchingBrace>(&MatchingBraceParams {
+ text_document: f.tid(),
+ positions: x,
+ })
+ }
+
+ pub fn matching_brace<'a>(
+ &'static self,
+ f: &Path,
+ t: &'a mut TextArea,
+ ) {
+ if let Ok(x) =
+ self.matching_brace_at(f, t.cursor.positions(&t.rope))
+ {
+ for (c, p) in t.cursor.inner.iter_mut().zip(x) {
+ if let Some(p) = p {
+ c.position = t.rope.l_position(p.1).unwrap();
+ }
+ }
+ }
+ }
+
+ pub fn legend(&self) -> Option<&SemanticTokensLegend> {
+ match &self.initialized{Some(lsp_types::InitializeResult {capabilities: ServerCapabilities {semantic_tokens_provider:Some(SemanticTokensServerCapabilities::SemanticTokensOptions(SemanticTokensOptions{legend,..})),..}, ..})=> {Some(legend)},_ => None,}
+ }
+ pub fn inlay(
+ &'static self,
+ f: &Path,
+ t: &TextArea,
+ ) -> impl Future<
+ Output = Result<
+ Vec<InlayHint>,
+ RequestError<lsp_request!("textDocument/inlayHint")>,
+ >,
+ > + use<> {
+ self.request_::<lsp_request!("textDocument/inlayHint"), { Redraw }>(&InlayHintParams {
+ work_done_progress_params: default(),
+ text_document: f.tid(),
+ range: t.to_l_range(lower::saturating::math!{
+ t.rope.try_line_to_char(t.vo-t.r).unwrap_or(0)..t.rope.try_line_to_char(t.vo + t.r + t.r).unwrap_or(t.rope.len_chars())
+ }).unwrap()
+ }).unwrap().0.map(|x| x.map(Option::unwrap_or_default))
+ // async {
+ // if let Ok(z) = z.await {
+ // let mut into = vec![];
+ // for lem in z.into_iter(){
+ // // if let Some(_) = lem.data {
+ // into.push(self.request::<lsp_request!("inlayHint/resolve")>(&lem).unwrap().0.await.unwrap());
+ // // }
+ // }
+ // // std::fs::write("inlay", serde_json::to_string_pretty(&into).unwrap()).unwrap();
+ // Ok(into)
+ // } else {
+ // panic!()
+ // }
+ // }
+ }
+ pub fn format(
+ &'static self,
+ f: &Path,
+ ) -> impl Future<
+ Output = Result<Option<Vec<TextEdit>>, RequestError<Formatting>>,
+ > {
+ self.request::<lsp_request!("textDocument/formatting")>(
+ &DocumentFormattingParams {
+ text_document: f.tid(),
+ options: FormattingOptions {
+ tab_size: 4,
+ insert_spaces: false,
+ properties: default(),
+ trim_trailing_whitespace: Some(true),
+ insert_final_newline: Some(true),
+ trim_final_newlines: Some(false),
+ },
+ work_done_progress_params: default(),
+ },
+ )
+ .unwrap()
+ .0
+ }
+ pub fn rq_semantic_tokens(
+ &'static self,
+ to: &mut Rq<
+ Box<[SemanticToken]>,
+ Box<[SemanticToken]>,
+ (),
+ RequestError<SemanticTokensFullRequest>,
+ >,
+ f: &Path,
+ ) -> anyhow::Result<()> {
+ debug!("requested semantic tokens");
+
+ let Some(b"rs") = f.extension().map(|x| x.as_encoded_bytes())
+ else {
+ return Ok(());
+ };
+ let (rx, _) = self
+ .request_::<SemanticTokensFullRequest, { Redraw }>(
+ &SemanticTokensParams {
+ work_done_progress_params: default(),
+ partial_result_params: default(),
+ text_document: f.tid(),
+ },
+ )?;
+ let x = self.runtime.spawn(async move {
+ let t = rx.await;
+ let y = t?.unwrap();
+ debug!("received semantic tokens");
+ let r = match y {
+ SemanticTokensResult::Partial(_) =>
+ panic!("i told the lsp i dont support this"),
+ SemanticTokensResult::Tokens(x) =>
+ x.data.into_boxed_slice(),
+ };
+ Ok(r)
+ });
+ to.request(x);
+
+ Ok(())
+ }
+
+ pub fn enter<'a>(&self, f: &Path, t: &'a mut TextArea) {
+ ceach!(t.cursor, |c| {
+ let r = self
+ .request_immediate::<OnEnter>(
+ &TextDocumentPositionParams {
+ text_document: f.tid(),
+ position: t.to_l_position(*c).unwrap(),
+ },
+ )
+ .unwrap();
+ match r {
+ None => t.enter(),
+ Some(mut r) => {
+ r.sort_tedits();
+ for f in r {
+ t.apply_snippet_tedit(&f).unwrap();
+ }
+ }
+ }
+ });
+ }
+ pub fn runnables(
+ &'static self,
+ t: &Path,
+ c: Option<Position>,
+ ) -> Result<
+ impl Future<Output = Result<Vec<Runnable>, RequestError<Runnables>>>
+ + use<>,
+ SendError<Message>,
+ > {
+ self.request::<Runnables>(&RunnablesParams {
+ text_document: t.tid(),
+ position: c,
+ })
+ .map(|(x, _)| x)
+ }
+}
+
+pub trait PathURI {
+ fn tid(&self) -> TextDocumentIdentifier;
+}
+impl PathURI for Path {
+ fn tid(&self) -> TextDocumentIdentifier {
+ TextDocumentIdentifier {
+ uri: Url::from_file_path(self).expect("ok"),
+ }
+ }
+}
diff --git a/src/lsp/communication.rs b/src/lsp/communication.rs
new file mode 100644
index 0000000..8899f0f
--- /dev/null
+++ b/src/lsp/communication.rs
@@ -0,0 +1,227 @@
+use std::backtrace::Backtrace;
+use std::collections::HashMap;
+use std::mem::forget;
+use std::sync::Arc;
+use std::time::Instant;
+
+use crossbeam::channel::{Receiver, RecvError, SendError, Sender};
+use log::{debug, error, trace};
+use lsp_server::{
+ ErrorCode, Message, Notification as N, Request as LRq, Response as Re,
+ ResponseError,
+};
+use lsp_types::notification::*;
+use lsp_types::request::*;
+use lsp_types::*;
+use tokio::sync::oneshot;
+use winit::window::Window;
+
+use crate::lsp::BehaviourAfter::{self, *};
+use crate::lsp::RequestError;
+pub fn handler(
+ window_rx: oneshot::Receiver<Arc<dyn Window + 'static>>,
+ progress: &papaya::HashMap<
+ NumberOrString,
+ Option<(WorkDoneProgress, WorkDoneProgressBegin)>,
+ >,
+ _req_tx: Sender<LRq>,
+ d: &papaya::HashMap<Url, Vec<Diagnostic>>,
+ not_tx: Sender<N>,
+ rx: Receiver<Message>,
+ req_rx: Receiver<(i32, oneshot::Sender<Re>, BehaviourAfter)>,
+) {
+ let mut map = HashMap::new();
+ let w = window_rx.blocking_recv().unwrap();
+ loop {
+ crossbeam::select! {
+ recv(req_rx) -> x => match x {
+ Ok((x, y, and)) => {
+ debug!("received request {x}");
+ assert!(map.insert(x, (y, Instant::now(), and)).is_none());
+ }
+ Err(RecvError) => return,
+ },
+ recv(rx) -> x => match x {
+ Ok(Message::Request(rq @ LRq { method: "window/workDoneProgress/create", .. })) => {
+ match rq.load::<WorkDoneProgressCreate>() {
+ Ok((_, x)) => {
+ let g = progress.guard();
+ progress.insert(x.token, None, &g);
+ },
+ Err(lsp_server::ExtractError::MethodMismatch(..)) => {},
+ e => {
+ error!("{e:?}");
+ }
+ };
+ }
+ Ok(Message::Request(x)) => {
+ if let Err(e) = _req_tx.send(x) {
+ let m = e.to_string();
+ error!("couldnt receive request {m}: {:?}", e.into_inner());
+ }
+ }
+ Ok(Message::Response(x)) => {
+ if let Some(e) = &x.error {
+ if e.code == ErrorCode::RequestCanceled as i32 {}
+ else if e.code == ErrorCode::ServerCancelled as i32 {
+ if let Some((s, _, t)) = map.remove(&x.id.i32()) {
+ log::info!("request {} cancelled", x.id);
+ _ = s.send(x);
+ if t == Redraw { w.request_redraw() }
+ }
+ } else {
+ if let Some((s, _, t)) = map.remove(&x.id.i32()) {
+ _ = s.send(x.clone());
+ if t == Redraw { w.request_redraw() }
+ trace!("received error from lsp for response {x:?}");
+ } else {
+ error!("received error from lsp for response {x:?}");
+ }
+ }
+ }
+ else if let Some((s, took, t)) = map.remove(&x.id.i32()) {
+ log::info!("request {} took {:?}", x.id, took.elapsed());
+ match s.send(x) {
+ Ok(()) => {}
+ Err(e) => {
+ error!(
+ "unable to respond to {e:?}",
+ );
+ }
+ }
+ // w.request_redraw();
+ if t == Redraw { w.request_redraw() }
+ } else {
+ error!("request {x:?} was dropped.")
+ }
+ }
+ Ok(Message::Notification(rq @ N { method: "textDocument/publishDiagnostics", .. })) => {
+ debug!("got diagnostics");
+ match rq.load::<PublishDiagnostics>() {
+ Ok(x) => {
+ d.insert(x.uri, x.diagnostics, &d.guard());
+ w.request_redraw();
+ },
+ e => error!("{e:?}"),
+ }
+ },
+ Ok(Message::Notification(x @ N { method: "$/progress", .. })) => {
+ let ProgressParams {token,value:ProgressParamsValue::WorkDone(x) } = x.load::<Progress>().unwrap();
+ match x.clone() {
+ WorkDoneProgress::Begin(y) => {
+ progress.update(token, move |_| Some((x.clone(), y.clone())), &progress.guard());
+ },
+ WorkDoneProgress::Report(_) | WorkDoneProgress::End(_) => {
+ progress.update(token, move |v| Some((x.clone(), v.clone().expect("evil lsp").1)), &progress.guard());
+ }
+ }
+ w.request_redraw();
+ }
+ Ok(Message::Notification(notification)) => {
+ debug!("rx {notification:?}");
+ not_tx
+ .send(notification)
+ .expect("why library drop this??? no drop!!");
+ }
+ Err(RecvError) => return,
+ }
+ }
+ }
+}
+impl super::Client {
+ pub fn notify<X: Notification>(
+ &self,
+ y: &X::Params,
+ ) -> Result<(), SendError<Message>> {
+ self.tx.send(Message::Notification(N {
+ method: X::METHOD.into(),
+ params: serde_json::to_value(y).unwrap(),
+ }))
+ }
+ pub fn cancel(&self, rid: i32) {
+ _ = self.notify::<Cancel>(&CancelParams { id: rid.into() });
+ }
+ pub fn request_immediate<'me, X: Request>(
+ &'me self,
+ y: &X::Params,
+ ) -> Result<X::Result, RequestError<X>> {
+ self.runtime.block_on(self.request_::<X, { Nil }>(y)?.0)
+ }
+
+ pub fn request<'me, X: Request>(
+ &'me self,
+ y: &X::Params,
+ ) -> Result<
+ (
+ impl Future<Output = Result<X::Result, RequestError<X>>>
+ + use<'me, X>,
+ i32,
+ ),
+ SendError<Message>,
+ > {
+ self.request_::<X, { Redraw }>(y)
+ }
+ #[must_use]
+ pub(super) fn request_<'me, X: Request, const THEN: BehaviourAfter>(
+ &'me self,
+ y: &X::Params,
+ ) -> Result<
+ (
+ impl Future<Output = Result<X::Result, RequestError<X>>>
+ + use<'me, X, THEN>,
+ i32,
+ ),
+ SendError<Message>,
+ > {
+ let id = self.id.fetch_add(1, std::sync::atomic::Ordering::AcqRel);
+ self.tx.send(Message::Request(LRq {
+ id: id.into(),
+ method: X::METHOD.into(),
+ params: serde_json::to_value(y).unwrap(),
+ }))?;
+ let (tx, rx) = oneshot::channel();
+ if self.initialized.is_some() {
+ debug!("sent request {} ({id})'s handler", X::METHOD);
+ self.send_to
+ .send((id, tx, THEN))
+ .expect("oughtnt really fail");
+ }
+ Ok((
+ async move {
+ let g = scopeguard::guard((), |()| {
+ self.cancel(id);
+ });
+
+ let mut x = rx.await?;
+ forget(g);
+ if let Some(ResponseError { code, ref mut data, .. }) =
+ x.error
+ {
+ if code == ErrorCode::ServerCancelled as i32 {
+ let e = serde_json::from_value(
+ data.take().unwrap_or_default(),
+ );
+ Err(RequestError::Cancelled(x, e.expect("lsp??")))
+ } else {
+ Err(RequestError::Failure(
+ x,
+ Some(Backtrace::capture()),
+ ))
+ }
+ } else {
+ Ok(serde_json::from_value::<X::Result>(
+ x.result.clone().unwrap_or_default(),
+ )
+ .unwrap_or_else(|_| {
+ panic!(
+ "lsp failure for {x:?}\ndidnt follow spec \
+ for {}\npossibly spec issue",
+ X::METHOD
+ )
+ }))
+ }
+ },
+ id,
+ ))
+ }
+}
diff --git a/src/lsp/init_opts.rs b/src/lsp/init_opts.rs
new file mode 100644
index 0000000..19bd140
--- /dev/null
+++ b/src/lsp/init_opts.rs
@@ -0,0 +1,362 @@
+use Default::default;
+use lsp_types::*;
+use serde_json::json;
+pub fn get(workspace: WorkspaceFolder) -> InitializeParams {
+ InitializeParams {
+ process_id: Some(std::process::id()),
+
+ capabilities: ClientCapabilities {
+ window: Some(WindowClientCapabilities {
+ work_done_progress: Some(true),
+ ..default()
+ }),
+ workspace: Some(WorkspaceClientCapabilities {
+ symbol: Some(WorkspaceSymbolClientCapabilities {
+ symbol_kind: Some(SymbolKindCapability {
+ value_set: Some(SymbolKind::ALL.to_vec()),
+ }),
+ tag_support: Some(TagSupport {
+ value_set: SymbolTag::ALL.to_vec(),
+ }),
+ resolve_support: Some(
+ WorkspaceSymbolResolveSupportCapability {
+ properties: vec!["location".into()],
+ },
+ ),
+ ..default()
+ }),
+ diagnostic: Some(DiagnosticWorkspaceClientCapabilities {
+ refresh_support: Some(true),
+ }),
+ inlay_hint: Some(InlayHintWorkspaceClientCapabilities {
+ refresh_support: Some(true),
+ }),
+ workspace_edit: Some(WorkspaceEditClientCapabilities {
+ document_changes: Some(true),
+ resource_operations: Some(vec![
+ ResourceOperationKind::Create,
+ ResourceOperationKind::Rename,
+ ResourceOperationKind::Delete,
+ ]),
+ failure_handling: Some(FailureHandlingKind::Abort),
+ normalizes_line_endings: Some(false),
+ change_annotation_support: Some(
+ ChangeAnnotationWorkspaceEditClientCapabilities {
+ groups_on_label: Some(false),
+ },
+ ),
+ }),
+ ..default()
+ }),
+ text_document: Some(TextDocumentClientCapabilities {
+ on_type_formatting: Some(
+ DocumentOnTypeFormattingClientCapabilities {
+ dynamic_registration: Some(false),
+ },
+ ),
+ document_highlight: Some(default()),
+ formatting: Some(DynamicRegistrationClientCapabilities {
+ dynamic_registration: Some(false),
+ }),
+ inlay_hint: Some(InlayHintClientCapabilities {
+ dynamic_registration: None,
+ resolve_support: Some(
+ InlayHintResolveClientCapabilities {
+ properties: vec![
+ "textEdits".into(),
+ "tooltip".into(),
+ "label.tooltip".into(),
+ "label.command".into(),
+ ],
+ },
+ ),
+ }),
+ document_symbol: Some(DocumentSymbolClientCapabilities {
+ tag_support: Some(TagSupport {
+ value_set: SymbolTag::ALL.to_vec(),
+ }),
+ symbol_kind: Some(SymbolKindCapability {
+ value_set: Some(SymbolKind::ALL.to_vec()),
+ }),
+ hierarchical_document_symbol_support: Some(true),
+ ..default()
+ }),
+ definition: Some(GotoCapability {
+ link_support: Some(true),
+ ..default()
+ }),
+ code_action: Some(CodeActionClientCapabilities {
+ data_support: Some(true),
+ resolve_support: Some(
+ CodeActionCapabilityResolveSupport {
+ properties: vec!["edit".to_string()],
+ },
+ ),
+ code_action_literal_support: Some(
+ CodeActionLiteralSupport {
+ code_action_kind:
+ CodeActionKindLiteralSupport {
+ value_set: [
+ "",
+ "Empty",
+ "QuickFix",
+ "Refactor",
+ "RefactorExtract",
+ "RefactorInline",
+ "RefactorRewrite",
+ "Source",
+ "SourceOrganizeImports",
+ "quickfix",
+ "refactor",
+ "refactor.extract",
+ "refactor.inline",
+ "refactor.rewrite",
+ "source",
+ "source.organizeImports",
+ ]
+ .map(String::from)
+ .into(),
+ },
+ },
+ ),
+ ..default()
+ }),
+ rename: Some(RenameClientCapabilities {
+ prepare_support: Some(true),
+ prepare_support_default_behavior: Some(
+ PrepareSupportDefaultBehavior::IDENTIFIER,
+ ),
+ honors_change_annotations: Some(false),
+ ..default()
+ }),
+ hover: Some(HoverClientCapabilities {
+ dynamic_registration: None,
+ content_format: Some(vec![
+ MarkupKind::PlainText,
+ MarkupKind::Markdown,
+ ]),
+ }),
+ diagnostic: Some(DiagnosticClientCapabilities {
+ dynamic_registration: None,
+ related_document_support: Some(true),
+ }),
+ publish_diagnostics: Some(
+ PublishDiagnosticsClientCapabilities {
+ related_information: Some(true),
+ code_description_support: Some(true),
+ data_support: Some(true),
+ ..default()
+ },
+ ),
+ signature_help: Some(SignatureHelpClientCapabilities {
+ dynamic_registration: None,
+ signature_information: Some(
+ SignatureInformationSettings {
+ documentation_format: Some(vec![
+ MarkupKind::Markdown,
+ MarkupKind::PlainText,
+ ]),
+ parameter_information: Some(
+ ParameterInformationSettings {
+ label_offset_support: Some(true),
+ },
+ ),
+ active_parameter_support: Some(true),
+ },
+ ),
+ context_support: Some(false),
+ }),
+ completion: Some(CompletionClientCapabilities {
+ dynamic_registration: Some(false),
+ completion_item: Some(CompletionItemCapability {
+ snippet_support: Some(true),
+ commit_characters_support: Some(true),
+ documentation_format: Some(vec![
+ MarkupKind::Markdown,
+ MarkupKind::PlainText,
+ ]),
+ deprecated_support: None,
+ preselect_support: None,
+ tag_support: Some(TagSupport {
+ value_set: vec![CompletionItemTag::DEPRECATED],
+ }),
+
+ resolve_support: Some(
+ CompletionItemCapabilityResolveSupport {
+ properties: vec![
+ "additionalTextEdits".into(),
+ "documentation".into(),
+ ],
+ },
+ ),
+ insert_replace_support: Some(false),
+ insert_text_mode_support: Some(
+ InsertTextModeSupport {
+ value_set: vec![InsertTextMode::AS_IS],
+ },
+ ),
+ label_details_support: Some(true),
+ ..default()
+ }),
+ completion_item_kind: Some(
+ CompletionItemKindCapability {
+ value_set: Some(vec![
+ CompletionItemKind::TEXT,
+ CompletionItemKind::METHOD,
+ CompletionItemKind::FUNCTION,
+ CompletionItemKind::CONSTRUCTOR,
+ CompletionItemKind::FIELD,
+ CompletionItemKind::VARIABLE,
+ CompletionItemKind::CLASS,
+ CompletionItemKind::INTERFACE,
+ CompletionItemKind::MODULE,
+ CompletionItemKind::PROPERTY,
+ CompletionItemKind::UNIT,
+ CompletionItemKind::VALUE,
+ CompletionItemKind::ENUM,
+ CompletionItemKind::KEYWORD,
+ CompletionItemKind::SNIPPET,
+ CompletionItemKind::COLOR,
+ CompletionItemKind::FILE,
+ CompletionItemKind::REFERENCE,
+ CompletionItemKind::FOLDER,
+ CompletionItemKind::ENUM_MEMBER,
+ CompletionItemKind::CONSTANT,
+ CompletionItemKind::STRUCT,
+ CompletionItemKind::EVENT,
+ CompletionItemKind::OPERATOR,
+ CompletionItemKind::TYPE_PARAMETER,
+ ]),
+ // value_set: Some(vec![CompletionItemKind::]),
+ },
+ ),
+ context_support: None,
+ insert_text_mode: Some(InsertTextMode::AS_IS),
+ completion_list: Some(CompletionListCapability {
+ item_defaults: None,
+ }),
+ }),
+ semantic_tokens: Some(SemanticTokensClientCapabilities {
+ dynamic_registration: Some(false),
+ requests: SemanticTokensClientCapabilitiesRequests {
+ range: Some(true),
+ full: Some(
+ lsp_types::SemanticTokensFullOptions::Bool(
+ true,
+ ),
+ ),
+ },
+ token_modifiers: [
+ "associated",
+ "attribute",
+ "callable",
+ "constant",
+ "consuming",
+ "controlFlow",
+ "crateRoot",
+ "injected",
+ "intraDocLink",
+ "library",
+ "macro",
+ "mutable",
+ "procMacro",
+ "public",
+ "reference",
+ "trait",
+ "unsafe",
+ ]
+ .map(|x| x.into())
+ .to_vec(),
+ overlapping_token_support: Some(true),
+ multiline_token_support: Some(true),
+ server_cancel_support: Some(false),
+ augments_syntax_tokens: Some(false),
+ ..default()
+ }),
+ ..default()
+ }),
+
+ general: Some(GeneralClientCapabilities {
+ markdown: Some(MarkdownClientCapabilities {
+ version: Some("1.0.0".into()),
+ parser: "markdown".into(),
+ allowed_tags: Some(vec![]),
+ }),
+ position_encodings: Some(vec![PositionEncodingKind::UTF8]),
+ ..default()
+ }),
+ experimental: Some(json! {{
+ "matchingBrace": true,
+ "snippetTextEdit": true,
+ "colorDiagnosticOutput": true,
+ "codeActionGroup": true,
+ "serverStatusNotification": true,
+ "hoverActions": true,
+ "workspaceSymbolScopeKindFiltering": true,
+ "onEnter": true,
+ "localDocs": true,
+ }}),
+ ..default()
+ },
+ client_info: Some(ClientInfo {
+ name: "gracilaria".into(),
+ version: Some(env!("CARGO_PKG_VERSION").into()),
+ }),
+ initialization_options: Some(json! {{
+ "cargo": {
+ "buildScripts": { "enable": true }
+ },
+ "procMacro": {
+ "enable": true,
+ "attributes": { "enable": true }
+ },
+ "hover": {
+ "documentation": {
+ "keywords": { "enable": false },
+ },
+ },
+ "inlayHints": {
+ "closureReturnTypeHints": { "enable": "with_block" },
+ "closingBraceHints": { "minLines": 5 },
+ "closureStyle": "rust_analyzer",
+ "genericParameterHints": { "type": { "enable": true } },
+ "rangeExclusiveHints": { "enable": true },
+ "closureCaptureHints": { "enable": true },
+ },
+ "typing": { "triggerChars": ".=<>{(+" },
+ "assist": { "preferSelf": true },
+ "checkOnSave": true,
+ "diagnostics": { "enable": true },
+ "semanticHighlighting": {
+ "punctuation": {
+ "separate": {
+ "macroBang": true
+ },
+ "specialization": { "enable": true },
+ "enable": true
+ }
+ },
+ "workspace": {
+ "symbol": {
+ "search": { "limit": 1024 }
+ }
+ },
+ "showUnlinkedFileNotification": false,
+ "completion": {
+ "fullFunctionSignatures": { "enable": true, },
+ "autoIter": { "enable": false, },
+ "autoImport": { "enable": true, },
+ "termSearch": { "enable": true, },
+ "autoself": { "enable": true, },
+ "privateEditable": { "enable": true },
+ },
+ "imports": {
+ "granularity": "group",
+ },
+ }}),
+ trace: None,
+ workspace_folders: Some(vec![workspace]),
+
+ ..default()
+ }
+}
diff --git a/src/lsp/rq.rs b/src/lsp/rq.rs
new file mode 100644
index 0000000..18c6616
--- /dev/null
+++ b/src/lsp/rq.rs
@@ -0,0 +1,171 @@
+use std::backtrace::Backtrace;
+use std::fmt::Debug;
+use std::marker::PhantomData;
+use std::sync::Arc;
+
+use crossbeam::channel::SendError;
+use lsp_server::{Message, Response as Re};
+use lsp_types::DiagnosticServerCancellationData;
+use lsp_types::request::Request;
+use serde::{Deserialize, Serialize};
+use tokio::sync::oneshot;
+use tokio::task;
+use tokio_util::task::AbortOnDropHandle;
+use winit::window::Window;
+
+#[derive(Serialize, Deserialize)]
+pub enum RequestError<X> {
+ Rx(PhantomData<X>),
+ Failure(Re, #[serde(skip)] Option<Backtrace>),
+ Cancelled(Re, DiagnosticServerCancellationData),
+ Send(Message),
+}
+pub type AQErr = RequestError<LSPError>;
+impl Request for LSPError {
+ type Params = ();
+ type Result = ();
+ const METHOD: &'static str = "<unknown method>";
+}
+#[derive(Debug)]
+pub struct LSPError {}
+pub trait Anonymize<T> {
+ fn anonymize(self) -> Result<T, RequestError<LSPError>>;
+}
+impl<T, E> Anonymize<T> for Result<T, RequestError<E>> {
+ fn anonymize(self) -> Result<T, RequestError<LSPError>> {
+ self.map_err(|e| match e {
+ RequestError::Send(x) => RequestError::Send(x),
+ RequestError::Rx(_) => RequestError::Rx(PhantomData),
+ RequestError::Failure(r, b) => RequestError::Failure(r, b),
+ RequestError::Cancelled(r, d) => RequestError::Cancelled(r, d),
+ })
+ }
+}
+
+// impl<X> Debug for RequestError<X> {}
+impl<X> From<oneshot::error::RecvError> for RequestError<X> {
+ fn from(_: oneshot::error::RecvError) -> Self {
+ Self::Rx(PhantomData)
+ }
+}
+impl<X: Request + Debug> std::error::Error for RequestError<X> {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ None
+ }
+}
+impl<X> From<SendError<Message>> for RequestError<X> {
+ fn from(x: SendError<Message>) -> Self {
+ Self::Send(x.into_inner())
+ }
+}
+impl<X: Request> std::fmt::Display for RequestError<X> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Self::Send(x) =>
+ write!(f, "{} failed; couldnt send {x:?}", X::METHOD),
+ Self::Rx(_) =>
+ write!(f, "{} failed; couldnt get from thingy", X::METHOD),
+ Self::Failure(x, bt) => write!(
+ f,
+ "{} failed; returned badge :( {x:?} ({bt:?})",
+ X::METHOD
+ ),
+ Self::Cancelled(x, y) =>
+ write!(f, "server cancelled {}. {x:?} {y:?}", X::METHOD),
+ }
+ }
+}
+impl<R: Request> Debug for RequestError<R> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(&self, f)
+ }
+}
+
+fn none<T>() -> Option<T> {
+ None
+}
+impl<T: Clone, R, D, E> Clone for Rq<T, R, D, E> {
+ fn clone(&self) -> Self {
+ Self { result: self.result.clone(), request: None }
+ }
+}
+#[derive(serde_derive::Serialize, serde_derive::Deserialize)]
+pub struct Rq<T, R, D = (), E = RequestError<R>> {
+ #[serde(skip_serializing_if = "Option::is_none", default = "none")]
+ pub result: Option<T>,
+ #[serde(skip, default = "none")]
+ pub request: Option<(AbortOnDropHandle<Result<R, E>>, D)>,
+}
+impl<T: Debug, R, D: Debug, E> Debug for Rq<T, R, D, E> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct(&format!("Rq<{}>", std::any::type_name::<R>()))
+ .field("result", &self.result)
+ .field("request", &self.request)
+ .finish()
+ }
+}
+
+pub type RqS<T, R: Request, D = ()> = Rq<T, R::Result, D, RequestError<R>>;
+impl<T, R, D, E> Default for Rq<T, R, D, E> {
+ fn default() -> Self {
+ Self { result: None, request: None }
+ }
+}
+impl<T, R, E> Rq<T, R, (), E> {
+ pub fn new(f: task::JoinHandle<Result<R, E>>) -> Self {
+ Self {
+ request: Some((AbortOnDropHandle::new(f), ())),
+ result: None,
+ }
+ }
+ pub fn request(&mut self, f: task::JoinHandle<Result<R, E>>) {
+ self.request = Some((AbortOnDropHandle::new(f), ()));
+ }
+}
+impl<T, R, D, E> Rq<T, R, D, E> {
+ pub fn running(&self) -> bool {
+ matches!(
+ self,
+ Self { result: Some(_), .. } | Self { request: Some(_), .. },
+ )
+ }
+ pub fn poll(
+ &mut self,
+ f: impl FnOnce(Result<R, E>, (D, Option<T>)) -> Option<T>,
+ ) -> bool {
+ if self.request.as_mut().is_some_and(|(x, _)| x.is_finished())
+ && let Some((task, _)) = self.request.as_mut()
+ && let Some(x) = futures::FutureExt::now_or_never(task)
+ {
+ let (_, d) = self.request.take().unwrap();
+ self.result = f(
+ match x {
+ Ok(x) => x,
+ Err(e) => {
+ log::error!(
+ "unexpected join error from request poll: {e}"
+ );
+ return false;
+ }
+ },
+ (d, self.result.take()),
+ );
+ true
+ } else {
+ false
+ }
+ }
+
+ pub fn poll_r(
+ &mut self,
+ f: impl FnOnce(Result<R, E>, (D, Option<T>)) -> Option<T>,
+ _runtime: &tokio::runtime::Runtime,
+ _w: Option<&Arc<dyn Window>>,
+ ) -> bool {
+ self.poll(|x, y| {
+ f(x, y).inspect(|_| {
+ // w.map(|x| x.request_redraw());
+ })
+ })
+ }
+}
diff --git a/src/main.rs b/src/main.rs
index e70d96a..110005c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -146,7 +146,7 @@ pub(crate) fn entry(event_loop: EventLoop) {
Some((.., c)) => c.take(),
None => None,
};
- let (fw, fh) = dsb::dims(&fonts.bold, ls);
+ let (_fw, _fh) = dsb::dims(&fonts.bold, ls);
let title = ed.title();
let app = winit_app::WinitAppBuilder::with_init(
move |elwt| {