Unnamed repository; edit this file 'description' to name the repository.
add req macro
| -rw-r--r-- | Cargo.toml | 61 | ||||
| -rw-r--r-- | src/act.rs | 20 | ||||
| -rw-r--r-- | src/bar.rs | 2 | ||||
| -rw-r--r-- | src/commands.rs | 26 | ||||
| -rw-r--r-- | src/complete.rs | 52 | ||||
| -rw-r--r-- | src/edi/input_handlers/click.rs | 19 | ||||
| -rw-r--r-- | src/edi/input_handlers/cursor.rs | 63 | ||||
| -rw-r--r-- | src/edi/input_handlers/keyboard.rs | 50 | ||||
| -rw-r--r-- | src/edi/lsp_impl.rs | 159 | ||||
| -rw-r--r-- | src/edi/lsp_mn.rs | 14 | ||||
| -rw-r--r-- | src/edi/lsp_mn/stdio.rs | 75 | ||||
| -rw-r--r-- | src/edi/ra.rs | 9 | ||||
| -rw-r--r-- | src/edi/st.rs | 7 | ||||
| -rw-r--r-- | src/edi/wsedit.rs | 312 | ||||
| -rw-r--r-- | src/gotolist.rs | 36 | ||||
| -rw-r--r-- | src/hov.rs | 11 | ||||
| -rw-r--r-- | src/lsp.rs | 24 | ||||
| -rw-r--r-- | src/lsp/client.rs | 272 | ||||
| -rw-r--r-- | src/lsp/communication.rs | 74 | ||||
| -rw-r--r-- | src/lsp/init_opts.rs | 180 | ||||
| -rw-r--r-- | src/lsp/rq.rs | 17 | ||||
| -rw-r--r-- | src/main.rs | 31 | ||||
| -rw-r--r-- | src/rnd.rs | 39 | ||||
| -rw-r--r-- | src/sig.rs | 19 | ||||
| -rw-r--r-- | src/sym.rs | 164 | ||||
| -rw-r--r-- | src/text.rs | 33 | ||||
| -rw-r--r-- | src/text/hist.rs | 2 | ||||
| -rw-r--r-- | src/text/inlay.rs | 6 |
28 files changed, 1178 insertions, 599 deletions
@@ -10,73 +10,78 @@ pattypan = { git = "https://git.bendn.org/pattypan" } implicit-fn = "0.1.0" ropey = "1.6.1" softbuffer = { version = "0.4.8", default-features = false, features = ["wayland", "x11"] } -swash = "0.2.9" +swash = "0.2.10" winit = { version = "0.31.0-beta.2", default-features = false, features = ["wayland", "x11"] } -tree-sitter = "0.25.10" -memchr = "2.8.2" +tree-sitter = "0.26.12" +memchr = "2.8.3" lower = "0.2.1" amap = "0.1.4" run_times = "0.1.0" array_chunks = "1.0.0" rust-fsm = { git = "https://git.bendn.org/rust-fsm", features = ["diagram"] } clipp = "0.1.1" -regex = { version = "1.12.4", features = ["unstable", "use_std"] } -tree-house = { version = "0.3.0", features = ["fixtures"] } +regex = { version = "1.13.1", features = ["unstable", "use_std"] } +tree-house = { version = "0.4.0", features = ["fixtures"] } # for tree house grammar loading helix-loader = { git = "https://git.bendn.org/helix" } helix-core = { git = "https://git.bendn.org/helix" } -lsp-types = { git = "https://git.bendn.org/helix", package = "helix-lsp-types", features = [ - "proposed", -] } +# lsp-types = { git = "https://git.bendn.org/helix", package = "helix-lsp-types", features = [ + # "proposed", +# ] } -lsp-server = { git = "https://git.bendn.org/rust-analyzer" } -rust-analyzer = { git = "https://git.bendn.org/rust-analyzer" } + +lsp-server = { version = "0.7.9" } +# rust-analyzer = { git = "https://git.bendn.org/rust-analyzer" } # lsp-server = { path = "../rust-analyzer/lib/lsp-server" } -# rust-analyzer = { path = "../rust-analyzer/crates/rust-analyzer" } +rust-analyzer = { path = "../rust-analyzer/crates/rust-analyzer" } -serde_json = "1.0.150" -serde = { version = "1.0.228", features = ["unstable"] } -serde_derive = "1.0.228" -log = "0.4.32" +serde_json = "1.0.151" +serde = { version = "1.0.229", features = ["unstable"] } +serde_derive = "1.0.229" +log = "0.4.33" crossbeam = { version = "0.8.4", features = ["nightly", "crossbeam-channel"] } test-log = "0.2.21" -env_logger = "0.11.10" +env_logger = "0.11.11" url = "2.5.8" -tokio = { version = "1.52.3", features = ["rt-multi-thread", "sync", "time"] } +tokio = { version = "1.53.1", features = ["rt-multi-thread", "sync", "time"] } regex-cursor = "0.1.5" papaya = "0.2.4" markdown = "1.0.0" -itertools = "0.14.0" +itertools = "0.15.0" pin-project = "1.1.13" replace_with = "0.1.8" nucleo = "0.5.0" -tokio-util = { version = "0.7.18", features = ["rt"] } +tokio-util = { version = "0.7.19", features = ["rt"] } scopeguard = "1.2.0" -arc-swap = "1.9.1" -atools = "0.1.12" +arc-swap = "1.9.2" +atools = "0.1.13" swizzle = "0.1.0" walkdir = "2.5.0" -libc = "0.2.186" -rustc-hash = "=2.1.1" -git2 = "0.20.4" +libc = "0.2.189" +rustc-hash = "2.1.3" +git2 = "0.21.0" imara-diff = "0.2.0" vecto = "0.1.1" rangemap = { version = "1.7.1", features = ["const_fn", "nightly", "serde1"] } itern = "0.1.1" smol_str = "0.3.6" -futures = "0.3.32" -rootcause = { version = "0.12.1", features = ["compat-anyhow1"] } +futures = "0.3.33" +rootcause = { version = "0.13.0", features = ["compat-anyhow1"] } ttools = { git = "https://git.bendn.org/ttools" } # ttools = { path = "../ttools/" } ftools = { git = "https://git.bendn.org/ftools" } json_value_merge = "2.0.1" annotate-snippets = "0.12.16" -toml = "1.1.2" +toml = "1.1.4" bendncode = { git = "https://git.bendn.org/bendncode" } rfd = "0.17.2" +lsp-types = { version = "0.11.0", package = "gen-lsp-types", features = ["url"] } +rayon = "1.12.0" +tracing-subscriber = "0.3.23" +crossbeam-channel = "0.5.16" [target.'cfg(unix)'.dependencies] kitty-rc = { version = "0.4.2", git = "https://github.com/bend-n/kitty-rc-rs" } @@ -109,4 +114,6 @@ incremental = false [patch.crates-io] smol_str = { git = "https://git.bendn.org/rust-analyzer" } ttools = { git = "https://git.bendn.org/ttools" } +# lsp-server = { path = "../rust-analyzer/lib/lsp-server" } # winit = { git = "https://github.com/rust-windowing/winit" } + @@ -1,7 +1,8 @@ use dsb::Cell; use dsb::cell::Style; use itertools::Itertools; -use lsp_types::{CodeAction, CodeActionKind}; +use lsp_types::CodeActionKind; +use rust_analyzer::lsp::ext::CodeAction; #[derive(Debug, Clone)] pub enum N<T> { @@ -165,14 +166,15 @@ fn write(x: &CodeAction, c: usize, selected: bool, to: &mut Vec<Cell>) { vec![Cell { style: Style::new(FG, bg), ..Default::default() }; c]; let t = match &x.kind { - Some(x) if x == &CodeActionKind::QUICKFIX => '', - Some(x) - if x == &CodeActionKind::REFACTOR - || x == &CodeActionKind::REFACTOR_EXTRACT - || x == &CodeActionKind::REFACTOR_INLINE - || x == &CodeActionKind::REFACTOR_REWRITE => - '', - Some(x) if x == &CodeActionKind::SOURCE => '', + Some(CodeActionKind::QuickFix) => '', + Some( + CodeActionKind::Refactor + | CodeActionKind::RefactorExtract + | CodeActionKind::RefactorInline + | CodeActionKind::RefactorRewrite + | CodeActionKind::RefactorMove, + ) => '', + Some(CodeActionKind::Source) => '', _ => '', /* ☭ */ }; into[0].style.fg = col!("#E5C07B"); @@ -3,9 +3,9 @@ use std::sync::Arc; use dsb::Cell; use dsb::cell::Style; -use lsp_types::WorkDoneProgress; use crate::killring::KillRM; +use crate::lsp::communication::WorkDoneProgress; use crate::lsp::{Client, Rq}; use crate::rnd::simplify_path; use crate::sym::Symbols; diff --git a/src/commands.rs b/src/commands.rs index 014a3f8..b020c22 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -312,7 +312,7 @@ impl Editor { .text .to_l_position(*self.text.cursor.first()) .unwrap(); - let mut x = l.request_immediate::<rust_analyzer::lsp::ext::MoveItem>(&MoveItemParams { + let mut x = l.request_immediate::<rust_analyzer::lsp::ext::MoveItemRequest>(&MoveItemParams { direction: if let Cmd::RAMoveIU = z { MoveItemDirection::Up } else { MoveItemDirection::Down }, text_document: o.tid(), range: Range { start : r, end : r}, @@ -324,11 +324,11 @@ impl Editor { } } Cmd::RARestart => { - _ = l.request::<ReloadWorkspace>(&())?.0; + _ = l.request::<ReloadWorkspaceRequest>(&())?.0; } Cmd::RAParent => { - let Some(GotoDefinitionResponse::Link([ref x])) = - l.request_immediate::<ParentModule>( + let Some(DefinitionResponse::DefinitionLinkList([ref x])) = + l.request_immediate::<ParentModuleRequest>( &TextDocumentPositionParams { text_document: o.tid(), position: self @@ -344,8 +344,8 @@ impl Editor { self.go(x, w, lsp_mn)?; } Cmd::RAJoinLines => { - let teds = - l.request_immediate::<JoinLines>(&JoinLinesParams { + let teds = l.request_immediate::<JoinLinesRequest>( + &JoinLinesParams { ranges: self .text .cursor @@ -357,13 +357,14 @@ impl Editor { }) .collect(), text_document: o.tid(), - })?; + }, + )?; self.text .apply_tedits(&mut { teds }) .map_err(|_| report!("couldnt apply edits"))?; } Cmd::RADocs => { - let u = l.request_immediate::<ExternalDocs>( + let u = l.request_immediate::<ExternalDocsRequest>( &TextDocumentPositionParams { position: self .text @@ -405,12 +406,13 @@ impl Editor { .unwrap(); } Cmd::RARebuildProcMacros => { - _ = l.request::<RebuildProcMacros>(&())?; + _ = l.request::<RebuildProcMacrosRequest>(&())?; } - Cmd::RACancelFlycheck => l.notify::<CancelFlycheck>(&())?, + Cmd::RACancelFlycheck => + l.notify::<CancelFlycheckNotification>(&())?, Cmd::RAOpenCargoToml => { - let Some(GotoDefinitionResponse::Scalar(x)) = - &l.request_immediate::<OpenCargoToml>( + let Some(DefinitionResponse::Definition(x)) = + &l.request_immediate::<OpenCargoTomlRequest>( &OpenCargoTomlParams { text_document: o.tid() }, )? else { diff --git a/src/complete.rs b/src/complete.rs index fc652c3..3ab13d3 100644 --- a/src/complete.rs +++ b/src/complete.rs @@ -63,8 +63,8 @@ fn filter_c<'a>( ) -> impl Iterator<Item = &'a CompletionItem> { let x = &completion.r; let y = match x { - CompletionResponse::Array(x) => x, - CompletionResponse::List(x) => &x.items, + CompletionResponse::CompletionItemList(x) => x, + CompletionResponse::CompletionList(x) => &x.items, }; filter(y.iter(), f) } @@ -82,7 +82,9 @@ pub fn s(completion: &Complete, c: usize, f: &str) -> Vec<Cell> { }); out -} +}pub const fn i<T:Copy>(x: T) -> usize { + (unsafe { *<*const _>::from(&x).cast::<u32>() }) as usize + } #[implicit_fn::implicit_fn] fn r( x: &CompletionItem, @@ -96,34 +98,36 @@ fn r( let ds = Style::new(FG, bg); let d: Cell = Cell { letter: None, style: ds }; let mut b = vec![d; c]; + const MAP: [([u8; 3], [u8; 3], &str); 26] = { ( amap::amap! { - const { CompletionItemKind::TEXT.0 as usize } => ("#9a9b9a", " "), - const { CompletionItemKind::METHOD.0 as usize } | const { CompletionItemKind::FUNCTION.0 as usize } => ("#FFD173", "λ "), - const { CompletionItemKind::CONSTRUCTOR.0 as usize } => ("#FFAD66", "->"), - const { CompletionItemKind::FIELD.0 as usize } => ("#E06C75", "x."), - const { CompletionItemKind::VARIABLE.0 as usize } => ("#E06C75", "x "), - const { CompletionItemKind::MODULE.0 as usize } => ("#D5FF80", "::"), - const { CompletionItemKind::PROPERTY.0 as usize } => ("#e6e1cf", "x."), - const { CompletionItemKind::VALUE.0 as usize } => ("#DFBFFF", "4 "), - const { CompletionItemKind::ENUM.0 as usize } => ("#73b9ff", "u"), - const { CompletionItemKind::ENUM_MEMBER.0 as usize } => ("#73b9ff", ":"), - const { CompletionItemKind::SNIPPET.0 as usize } => ("#9a9b9a", "! "), - const { CompletionItemKind::INTERFACE.0 as usize } => ("#E5C07B", "t "), - const { CompletionItemKind::REFERENCE.0 as usize } => ("#9a9b9a", "& "), - const { CompletionItemKind::CONSTANT.0 as usize } => ("#DFBFFF", "N "), - const { CompletionItemKind::STRUCT.0 as usize } => ("#73D0FF", "X{"), - const { CompletionItemKind::OPERATOR.0 as usize } => ("#F29E74", "+ "), - const { CompletionItemKind::TYPE_PARAMETER.0 as usize } => ("#9a9b9a", "T "), - const { CompletionItemKind::KEYWORD.0 as usize } => ("#FFAD66", "as"), + const { i(CompletionItemKind::Text) } => ("#9a9b9a", " "), + const { i(CompletionItemKind::Method) } | const { i(CompletionItemKind::Function) } => ("#FFD173", "λ "), + const { i(CompletionItemKind::Constructor) } => ("#FFAD66", "->"), + const { i(CompletionItemKind::Field) } => ("#E06C75", "x."), + const { i(CompletionItemKind::Variable) } => ("#E06C75", "x "), + const { i(CompletionItemKind::Module) } => ("#D5FF80", "::"), + const { i(CompletionItemKind::Property) } => ("#e6e1cf", "x."), + const { i(CompletionItemKind::Value) } => ("#DFBFFF", "4 "), + const { i(CompletionItemKind::Enum) } => ("#73b9ff", "u"), + const { i(CompletionItemKind::EnumMember) } => ("#73b9ff", ":"), + const { i(CompletionItemKind::Snippet) } => ("#9a9b9a", "! "), + const { i(CompletionItemKind::Interface) } => ("#E5C07B", "t "), + const { i(CompletionItemKind::Reference) } => ("#9a9b9a", "& "), + const { i(CompletionItemKind::Constant) } => ("#DFBFFF", "N "), + const { i(CompletionItemKind::Struct) } => ("#73D0FF", "X{"), + const { i(CompletionItemKind::Operator) } => ("#F29E74", "+ "), + const { i(CompletionItemKind::TypeParameter) } => ("#9a9b9a", "T "), + const { i(CompletionItemKind::Keyword) } => ("#FFAD66", "as"), + _ => ("#9a9b9a", " ") }).map(const |(x, y)| (set_a(color_(x), 0.5), color_(x), y), ) }; let (bgt, col, ty) = - MAP[x.kind.unwrap_or(CompletionItemKind(25)).0 as usize]; + MAP[i(x.kind.unwrap_or(CompletionItemKind::Text))]; b.iter_mut().zip(ty.chars()).for_each(|(x, c)| { *x = (Style::new(col, bgt) | Style::BOLD).basic(c) }); @@ -234,7 +238,7 @@ impl Editor { let sel = x.sel(&crate::filter(&self.text)); let sel = lsp.resolve(sel.clone()).unwrap(); let CompletionItem { - text_edit: Some(CompletionTextEdit::Edit(ed)), + text_edit: Some(CompletionItemTextEdit::TextEdit(ed)), additional_text_edits, insert_text_format, .. @@ -243,7 +247,7 @@ impl Editor { return; }; match insert_text_format { - Some(InsertTextFormat::SNIPPET) => { + Some(InsertTextFormat::Snippet) => { self.text.apply_snippet(&ed).unwrap(); } _ => { diff --git a/src/edi/input_handlers/click.rs b/src/edi/input_handlers/click.rs index 2fc4db3..82ddd2b 100644 --- a/src/edi/input_handlers/click.rs +++ b/src/edi/input_handlers/click.rs @@ -60,21 +60,26 @@ impl Editor { } Do::GoToDefinition(tdpp) => { dbg!(&tdpp); - if let Some(x) = self.requests.def.result.clone().or_else(|| { - tdpp.zip(lsp!(self)).and_then(|(tdpp, lsp)| { - lsp.request_immediate::<lsp_request!("textDocument/definition")>( - &GotoDefinitionParams { + if let Some(x) = self.requests.def.result.clone() + && let Err(e) = self.go(&x, w.clone(), lsp_mn) + { + log::error!("gtd: {e}"); + } else if let Some(x) = { + tdpp.zip(lsp!(self)).and_then(|(tdpp, lsp)| { + lsp.request_immediate::< lsp_request!("textDocument/definition")>( + &DefinitionParams { text_document_position_params: tdpp, work_done_progress_params: default(), partial_result_params: default(), }, ).ok() }).flatten().and_then(|x| match &x { - GotoDefinitionResponse::Link([x, ..]) => Some(x.clone()), + DefinitionResponse::Definition(Definition::Location(x)) => Some(x.clone()), + DefinitionResponse::Definition(Definition::LocationList([x])) => Some(x.clone()), + // DefinitionResponse::DefinitionLinkList([x, ..]) => Some(x.clone()), _ => None, }) - }) - && let Err(e) = self.go(&x, w.clone(),lsp_mn) + } && let Err(e) = self.go(&x, w.clone(), lsp_mn) { log::error!("gtd: {e}"); } diff --git a/src/edi/input_handlers/cursor.rs b/src/edi/input_handlers/cursor.rs index 953d320..8887045 100644 --- a/src/edi/input_handlers/cursor.rs +++ b/src/edi/input_handlers/cursor.rs @@ -3,11 +3,11 @@ 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 url::Url; use winit::window::Window; enum Set<T> { To(T), @@ -17,6 +17,7 @@ enum Set<T> { use crate::edi::*; use crate::hov::{DiagnosticHovr, Hoverable, Hovring}; use crate::lsp::BehaviourAfter; +use crate::lsp::communication::send; use crate::rnd::CellBuffer; impl Editor { #[implicit_fn] @@ -221,12 +222,15 @@ impl Editor { .is_none_or(|&(_, x)| x != cursor_position) { let handle = lsp.runtime.spawn( - lsp.request::<lsp_request!("textDocument/definition")>( - &GotoDefinitionParams { - text_document_position_params: tdpp.clone(), - work_done_progress_params: default(), - partial_result_params: default(), - }, + send!( + lsp.request, + "textDocument/definition", + _ { + text_document_position_params: tdpp + .clone(), + work_done_progress_params: default(), + partial_result_params: default(), + }, ) .unwrap() .0, @@ -279,29 +283,30 @@ impl Editor { return Ok(None::<Hovr>); }; let (width, cells) = spawn_blocking(move || { + #[allow(deprecated)] 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::<String>(), - ), - lsp_types::HoverContents::Markup( - markup_content, - ) => Cow::Borrowed(&*markup_content.value), + Contents::MarkedString(marked_string) => + match marked_string { + MarkedString::MarkedStringWithLanguage( + x, + ) => Cow::Borrowed(&*x.value), + MarkedString::String(x) => + Cow::Borrowed(&**x), + }, + Contents::MarkedStringList(marked_strings) => + Cow::Owned( + marked_strings + .iter() + .map(|x| match x { + MarkedString::MarkedStringWithLanguage( + x, + ) => &*x.value, + MarkedString::String(x) => &*x, + }) + .collect::<String>(), + ), + Contents::MarkupContent(markup_content) => + Cow::Borrowed(&*markup_content.value), }; if l.is_some_and(|l| { matches!( diff --git a/src/edi/input_handlers/keyboard.rs b/src/edi/input_handlers/keyboard.rs index 7dadc9b..08376aa 100644 --- a/src/edi/input_handlers/keyboard.rs +++ b/src/edi/input_handlers/keyboard.rs @@ -5,11 +5,9 @@ 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 ttools::{IteratorOfTuples, IteratorOfTuplesWithF, hrf}; @@ -275,10 +273,10 @@ impl Editor { self.hist.lc = self.text.cursor.clone(); self.hist.test_push(&mut self.text); let act = lsp - .request_immediate::<CodeActionResolveRequest>(&act) + .request_immediate::<rust_analyzer::lsp::ext::CodeActionResolveRequest>(&act) .unwrap(); if let Some(x) = act.edit - && let Err(e) = self.apply_wsedit(x) + && let Err(e) = self.apply_swsedit(x) { log::error!("{e}"); } @@ -665,23 +663,22 @@ impl Editor { || t.iter().any(|y| y == x)) && self.text.cursor.inner.len() == 1 && change!(just self).is_some() - && let Ok(Ok(Some(mut x))) = l.request_by::<OnTypeFormatting>( - &DocumentOnTypeFormattingParams { - text_document_position: TextDocumentPositionParams { + && let Ok(Ok(Some(mut x))) = l + .request_by::<rust_analyzer::lsp::ext::DocumentOnTypeFormattingRequest>( + &DocumentOnTypeFormattingParams { text_document: p.tid(), position: self .text .to_l_position(*self.text.cursor.first()) .unwrap(), + ch: x.into(), + options: FormattingOptions { + tab_size: 4, + ..default() + }, }, - ch: x.into(), - options: FormattingOptions { - tab_size: 4, - ..default() - }, - }, - acceptable_duration(), - ) + acceptable_duration(), + ) { x.sort_tedits(); for x in x { @@ -814,15 +811,16 @@ impl Editor { 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(), - }, + text_document_position_params: + TextDocumentPositionParams { + text_document: f.tid(), + position: self + .text + .to_l_position( + self.text.cursor.first().position, + ) + .unwrap(), + }, new_name, work_done_progress_params: default(), }, @@ -856,7 +854,7 @@ impl Editor { return; } let r = lsp - .request::<lsp_request!("textDocument/codeAction")>( + .request::<rust_analyzer::lsp::ext::CodeActionRequest>( &CodeActionParams { text_document: f.tid(), range: self @@ -867,7 +865,7 @@ impl Editor { ) .unwrap(), context: CodeActionContext { - trigger_kind: Some(CodeActionTriggerKind::INVOKED), + 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![] }, diff --git a/src/edi/lsp_impl.rs b/src/edi/lsp_impl.rs index 269ddb0..6026a3d 100644 --- a/src/edi/lsp_impl.rs +++ b/src/edi/lsp_impl.rs @@ -2,8 +2,8 @@ use std::iter::repeat; use Default::default; use lsp_server::Request as LRq; -use lsp_types::request::*; use lsp_types::*; +use rust_analyzer::lsp::ext::CodeAction; use serde::{Deserialize, Serialize}; use ttools::{Tupl, With}; @@ -40,7 +40,7 @@ pub struct Requests { Box<[SemanticToken]>, Box<[SemanticToken]>, (), - RequestError<SemanticTokensFullRequest>, + RequestError<lsp_request!("textDocument/semanticTokens/full")>, >, pub diag: Rq< String, @@ -55,11 +55,11 @@ pub struct Requests { (), RequestError<lsp_request!("textDocument/inlayHint")>, >, - pub def: Rq< + pub def: crate::RqS< LocationLink, - Option<GotoDefinitionResponse>, + lsp_request!("textDocument/definition"), (usize, usize), - RequestError<lsp_request!("textDocument/definition")>, + // RequestError<lsp_request!("textDocument/definition")>, >, #[serde(skip)] pub document_symbols: Rq< @@ -71,6 +71,89 @@ pub struct Requests { #[serde(skip)] pub git_diff: Rq<imara_diff::Diff, imara_diff::Diff, (), ()>, } +use serde::ser::SerializeSeq; + +fn untokenr<'de, D>( + deserializer: D, +) -> Result<Vec<SemanticToken>, D::Error> +where + D: serde::Deserializer<'de>, +{ + let data = Vec::<u32>::deserialize(deserializer)?; + let chunks = data.chunks_exact(5); + + if !chunks.remainder().is_empty() { + return Result::Err(serde::de::Error::custom( + "Length is not divisible by 5", + )); + } + + Result::Ok( + chunks + .map(|chunk| SemanticToken { + delta_line: chunk[0], + delta_start: chunk[1], + length: chunk[2], + token_type: chunk[3], + token_modifiers_bitset: chunk[4], + }) + .collect(), + ) +} + +fn tokenr<S>( + tokens: &[SemanticToken], + serializer: S, +) -> Result<S::Ok, S::Error> +where + S: serde::Serializer, +{ + let mut seq = serializer.serialize_seq(Some(tokens.len() * 5))?; + for token in tokens.iter() { + seq.serialize_element(&token.delta_line)?; + seq.serialize_element(&token.delta_start)?; + seq.serialize_element(&token.length)?; + seq.serialize_element(&token.token_type)?; + seq.serialize_element(&token.token_modifiers_bitset)?; + } + seq.end() +} + +fn deserialize_tokens_opt<'de, D>( + deserializer: D, +) -> Result<Option<Vec<SemanticToken>>, D::Error> +where + D: serde::Deserializer<'de>, +{ + #[derive(Deserialize)] + #[serde(transparent)] + struct Wrapper { + #[serde(deserialize_with = "untokenr")] + tokens: Vec<SemanticToken>, + } + + Ok(Option::<Wrapper>::deserialize(deserializer)? + .map(|wrapper| wrapper.tokens)) +} + +fn serialize_tokens_opt<S>( + data: Option<&[SemanticToken]>, + serializer: S, +) -> Result<S::Ok, S::Error> +where + S: serde::Serializer, +{ + #[derive(Serialize)] + #[serde(transparent)] + struct Wrapper<'a> { + #[serde(serialize_with = "tokenr")] + tokens: &'a [SemanticToken], + } + + let opt = data.as_ref().map(|t| Wrapper { tokens: t }); + + opt.serialize(serializer) +} pub fn deserialize_tokens<'de, D: serde::Deserializer<'de>>( ser: D, ) -> Result< @@ -78,26 +161,44 @@ pub fn deserialize_tokens<'de, D: serde::Deserializer<'de>>( Box<[SemanticToken]>, Box<[SemanticToken]>, (), - RequestError<SemanticTokensFullRequest>, + RequestError<lsp_request!("textDocument/semanticTokens/full")>, >, D::Error, > { - SemanticToken::deserialize_tokens_opt(ser) - .map(|x| Rq { result: x.map(Into::into), request: None }) + { + #[derive(Deserialize)] + #[serde(transparent)] + struct Wrapper { + #[serde(deserialize_with = "untokenr")] + tokens: Vec<SemanticToken>, + } + + Ok(Option::<Wrapper>::deserialize(ser)? + .map(|wrapper| wrapper.tokens)) + } + .map(|x| Rq { result: x.map(Into::into), request: None }) } pub fn serialize_tokens<S: serde::Serializer>( s: &Rq< Box<[SemanticToken]>, Box<[SemanticToken]>, (), - RequestError<SemanticTokensFullRequest>, + RequestError<lsp_request!("textDocument/semanticTokens/full")>, >, ser: S, ) -> Result<S::Ok, S::Error> { - SemanticToken::serialize_tokens_opt( - &s.result.clone().map(|x| x.to_vec()), - ser, - ) + { + let data: &Option<Vec<SemanticToken>> = + &s.result.clone().map(|x| x.to_vec()); + #[derive(Serialize)] + #[serde(transparent)] + struct Wrapper { + #[serde(serialize_with = "tokenr")] + tokens: Vec<SemanticToken>, + } + let opt = data.as_ref().map(|t| Wrapper { tokens: t.to_vec() }); + opt.serialize(ser) + } } impl crate::edi::Editor { pub fn poll(&mut self) { @@ -149,14 +250,7 @@ impl crate::edi::Editor { } 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(); + let lems = x.ok()??; if lems.is_empty() { self.bar.last_action = "no code actions available".into(); @@ -218,18 +312,13 @@ impl crate::edi::Editor { x.ok().map(|x| { x.and_then(|x| try { z.data.0 = match x { - GotoDefinitionResponse::Scalar( - location, + ImplementationResponse::Definition( + Definition::Location(location), ) => vec![(GoTo::from( location, ),None)], - GotoDefinitionResponse::Array( - locations, - ) => locations - .into_iter() - .map(GoTo::from).zip(repeat(None)) - .collect(), - GotoDefinitionResponse::Link( + + ImplementationResponse::DefinitionLinkList( location_links, ) => location_links .into_iter() @@ -242,6 +331,7 @@ impl crate::edi::Editor { ) }).zip(repeat(None)) .collect(), + ImplementationResponse::Definition(Definition::LocationList(x)) => {unimplemented!()}, }; }); }) @@ -281,7 +371,10 @@ impl crate::edi::Editor { } self.requests.def.poll(|x, _| { x.ok().flatten().and_then(|x| match &x { - GotoDefinitionResponse::Link([x, ..]) => Some(x.clone()), + // DefinitionResponse::Definition(x) => Some(x.clone()), + DefinitionResponse::DefinitionLinkList([x, ..]) => + Some(x.clone()), + // DefinitionRequest::Definition([x, ..]) => Some(x.clone()), _ => None, }) }); @@ -303,8 +396,8 @@ impl crate::edi::Editor { 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), + DocumentSymbolResponse::SymbolInformationList(_) => None, + DocumentSymbolResponse::DocumentSymbolList(x) => Some(x), }) }); } diff --git a/src/edi/lsp_mn.rs b/src/edi/lsp_mn.rs index 25aa478..45cfd24 100644 --- a/src/edi/lsp_mn.rs +++ b/src/edi/lsp_mn.rs @@ -4,11 +4,11 @@ use std::path::PathBuf; use std::process::Stdio; use std::sync::Arc; -use log::info; -use lsp_server::{Connection, IoThreads}; +use lsp_server::Connection; use lsp_types::*; use tokio::sync::oneshot::Sender; use tree_house::Language; +use url::Url; use winit::window::Window; pub struct LSPM { @@ -20,7 +20,12 @@ pub struct LoadedLSP { pub iot: Option<IoThreads>, pub comms: std::thread::JoinHandle<()>, } - +impl LoadedLSP { + pub fn status(&self) { + assert!(!self.comms.is_finished()); + } +} +mod stdio; impl LSPM { pub fn load( &mut self, @@ -45,6 +50,7 @@ impl LSPM { Some((v.c.clone(), Some(w))) } } +use crate::edi::lsp_mn::stdio::IoThreads; use crate::lsp::Client; pub fn load( workspace: &PathBuf, @@ -89,7 +95,7 @@ pub fn load( }) .ok()?; - let (x, iot) = Connection::stdio( + let (x, iot) = stdio::transport( BufReader::new(c.stdout.take().unwrap()), c.stdin.take().unwrap(), ); diff --git a/src/edi/lsp_mn/stdio.rs b/src/edi/lsp_mn/stdio.rs new file mode 100644 index 0000000..fa5a480 --- /dev/null +++ b/src/edi/lsp_mn/stdio.rs @@ -0,0 +1,75 @@ +use std::io::{self, Read, Stdin, Stdout, Write, stdin, stdout}; +use std::thread; + +use crossbeam_channel::{Receiver, Sender, bounded, unbounded}; +use log::trace; +use lsp_server::{Connection, Message}; +/// Creates an LSP connection via stdio. +pub fn transport( + mut read_from: impl Read + std::io::BufRead + Sync + Send + 'static, + mut write_to: impl Write + Sync + Send + 'static, +) -> (Connection, IoThreads) { + let (writer_sender, writer_receiver) = unbounded::<Message>(); + let writer = thread::Builder::new() + .name("send to lsp".to_owned()) + .spawn(move || { + loop { + let it = writer_receiver.recv().unwrap(); + trace!("sent message {it:#?}"); + let result = it.write(&mut write_to).unwrap(); + result + } + }) + .unwrap(); + let (reader_sender, reader_receiver) = bounded::<Message>(0); + let reader: thread::JoinHandle<Result<(), io::Error>> = thread::Builder::new() + .name("read from lsp".to_owned()) + .spawn(move || { + while let Some(msg) = Message::read(&mut read_from)? { + let is_exit = matches!(&msg, Message::Notification(n) if n.method == "exit"); + trace!("received message {msg:#?}"); + if let Err(e) = reader_sender.send(msg) { + return Err(io::Error::other(e)); + } + + if is_exit { + break; + } + } + Ok(()) + }) + .unwrap(); + let threads = IoThreads { reader, writer }; + ( + Connection { sender: writer_sender, receiver: reader_receiver }, + threads, + ) +} + +// Creates an IoThreads +pub(crate) fn make_io_threads( + reader: thread::JoinHandle<io::Result<()>>, + writer: thread::JoinHandle<io::Result<()>>, +) -> IoThreads { + IoThreads { reader, writer } +} + +pub struct IoThreads { + pub reader: thread::JoinHandle<io::Result<()>>, + pub writer: thread::JoinHandle<io::Result<()>>, +} + +impl IoThreads { + pub fn join(self) -> io::Result<()> { + match self.reader.join() { + Ok(r) => r?, + Err(err) => std::panic::panic_any(err), + } + match self.writer.join() { + Ok(r) => r, + Err(err) => { + std::panic::panic_any(err); + } + } + } +} diff --git a/src/edi/ra.rs b/src/edi/ra.rs index ef0c7c8..bed6931 100644 --- a/src/edi/ra.rs +++ b/src/edi/ra.rs @@ -31,7 +31,14 @@ pub fn ra( println!("RA panic @ {}", info.location().unwrap()); } })); - rust_analyzer::bin::run_server(b).into_rootcause() + + rayon::ThreadPoolBuilder::new() + .thread_name(|ix| format!("RayonWorker{}", ix)) + .build_global() + .unwrap(); + + rust_analyzer::session::run_session(b, None, None) + .into_rootcause() }) .unwrap(); (jh, a) diff --git a/src/edi/st.rs b/src/edi/st.rs index efc2081..40be312 100644 --- a/src/edi/st.rs +++ b/src/edi/st.rs @@ -1,10 +1,9 @@ #![allow(dead_code, unused, unexpected_cfgs)] use Default::default; use NamedKey::*; -use lsp_types::request::HoverRequest; use lsp_types::*; use regex::Regex; -use rust_analyzer::lsp::ext::Runnable; +use rust_analyzer::lsp::ext::{CodeAction, Runnable}; use winit::event::MouseButton; use winit::keyboard::{Key, NamedKey, SmolStr}; @@ -125,7 +124,7 @@ Runnables(_x) => { K(Key::Named(Escape)) => Default, }, #[cfg(target_family = "unix")] -Runnables(RqS::<Run, rust_analyzer::lsp::ext::Runnables> => Rq { result: Some(mut x), request }) => { +Runnables(RqS::<Run, rust_analyzer::lsp::ext::RunnablesRequest> => Rq { result: Some(mut x), request }) => { #[cfg(target_family = "unix")] K(Key::Named(Tab) if shift()) => Runnables({ x.next(); Rq { result: Some(x), request }}), #[cfg(target_family = "unix")] @@ -193,7 +192,7 @@ CodeAction(Rq { result : Some(_x), request }) => { K(Key::Named(Enter | ArrowRight)) => _ [CASelectRight], K(Key::Named(ArrowLeft)) => _ [CASelectLeft], }, -CodeAction(RqS<act::CodeActions, lsp_request!("textDocument/codeAction")> => rq) => { +CodeAction(RqS<act::CodeActions, rust_analyzer::lsp::ext::CodeActionRequest> => rq) => { K(Key::Named(Escape)) => Default, C(_) => _, M(_) => _, diff --git a/src/edi/wsedit.rs b/src/edi/wsedit.rs index eb11a8b..55e0827 100644 --- a/src/edi/wsedit.rs +++ b/src/edi/wsedit.rs @@ -5,6 +5,10 @@ use lsp_types::*; use rootcause::prelude::{IteratorExt, ResultExt}; use rootcause::report; use ropey::Rope; +use rust_analyzer::lsp::ext::{ + SnippetDocumentChangeOperation, SnippetTextDocumentEdit, + SnippetWorkspaceEdit, +}; use super::*; use crate::error::WDebug; @@ -52,23 +56,221 @@ impl Editor { ) -> rootcause::Result<()> { self.apply_tds( &text_document + .text_document_identifier .uri .to_file_path() .map_err(|_| report!("sad"))?, edits, - TextArea::apply_snippet_tedit, - TextArea::apply_snippet_tedit_raw, + |t, e| match e { + Edit::AnnotatedTextEdit(AnnotatedTextEdit { + text_edit, + .. + }) + | Edit::TextEdit(text_edit) => + Ok(drop(t.apply(text_edit)?)), + Edit::SnippetTextEdit(SnippetTextEdit { + range, + snippet, + annotation_id, + }) => { + let x2 = rust_analyzer::lsp::ext::SnippetTextEdit { + range: *range, + new_text: snippet.value.clone(), + insert_text_format: None, + annotation_id: annotation_id.clone(), + }; + t.apply_snippet_tedit(&x2) + } + }, + |e, t| match e { + Edit::AnnotatedTextEdit(AnnotatedTextEdit { + text_edit, + .. + }) + | Edit::TextEdit(text_edit) => + TextArea::apply_raw(text_edit, t), + Edit::SnippetTextEdit(SnippetTextEdit { + range, + snippet, + annotation_id, + }) => { + let x2 = rust_analyzer::lsp::ext::SnippetTextEdit { + range: *range, + new_text: snippet.value.clone(), + insert_text_format: None, + annotation_id: annotation_id.clone(), + }; + TextArea::apply_snippet_tedit_raw(&x2, t) + } + }, + // TextArea::apply_snippet_tedit, + // TextArea::apply_snippet_tedit_raw, ) } - fn apply_dco( + fn apply_sdc( &mut self, - op: DocumentChangeOperation, + op: SnippetDocumentChangeOperation, ) -> rootcause::Result<()> { match op.clone() { - DocumentChangeOperation::Edit(t) => self.apply_tde(t)?, - DocumentChangeOperation::Op(ResourceOp::Create( - CreateFile { uri, options, .. }, - )) => { + SnippetDocumentChangeOperation::Edit( + SnippetTextDocumentEdit { text_document, edits }, + ) => self.apply_tds( + &text_document + .text_document_identifier + .uri + .to_file_path() + .map_err(|_| report!("sad"))?, + edits, + TextArea::apply_snippet_tedit, + TextArea::apply_snippet_tedit_raw, + ), + SnippetDocumentChangeOperation::Change(dc) => + self.apply_dc(dc), + } + } + // pub fn apply_sdc( + // &mut self, + // x: SnippetDocumentChangeOperation, + // ) -> rootcause::Result<()> { + // match x { + // // self.apply_tde(x) + // DocumentChange::TextDocumentEdit(text_document_edit) => + // self.apply_tde(text_document_edit)?, + // DocumentChange::CreateFile(CreateFile { + // uri, + // options, + // .. + // }) => { + // let f = uri + // .to_file_path() + // .map_err(|()| report!("not path"))?; + // let mut opts = OpenOptions::new(); + // opts.write(true) + // // .create(true) + // .create(matches!( + // options, + // Some(CreateFileOptions { + // ignore_if_exists: Some(true) | None, + // .. + // }) | None + // )) + // .create_new(matches!( + // options, + // Some(CreateFileOptions { + // ignore_if_exists: Some(false), + // .. + // }) + // )) + // .truncate(true); + // match opts.open(&f) { + // Ok(_f) => {} + // Err(e) + // if let Some(CreateFileOptions { + // overwrite: Some(true), + // .. + // }) = options + // && e.kind() + // == std::io::ErrorKind::AlreadyExists => {} + // Err(e) => + // Err(e) + // .context_custom::<WDebug, _>(f) + // .context_custom::<WDebug, _>((opts, options))?, + // }; + // } + // DocumentChange::RenameFile(RenameFile { + // old_uri, + // new_uri, + // options, + // annotation_id, + // }) => { + // let old_f = old_uri + // .to_file_path() + // .map_err(|()| report!("not path"))?; + // let new_f = new_uri + // .to_file_path() + // .map_err(|()| report!("not path"))?; + + // // FIXME: overwrite + // if let Some(p) = new_f.parent() + // && !p.exists() + // { + // std::fs::create_dir_all(p)?; + // } + // match std::fs::rename(&old_f, &new_f) { + // Err(e) + // if e.kind() + // == std::io::ErrorKind::IsADirectory => + // { + // do yeet report!("unhandled case: dir rename"); + // } + // Err(e) + // if let Some(RenameFileOptions { + // ignore_if_exists: Some(true), + // .. + // }) = options + // && e.kind() + // == std::io::ErrorKind::AlreadyExists => {} + // Ok(_) => {} + // Err(e) => Err(e).context_with(|| { + // format!("renaming {old_f:?} to {new_f:?}") + // })?, + // } + // if let Some(f) = self.files.get_mut(&old_f) + // // .or(Some(self).filter(|x| x.origin == Some(old_f))) + // { + // f.bar.last_action = + // annotation_id.unwrap_or("renamed".into()); + // f.origin = Some(new_f); + // f.mtime = Editor::modify(f.origin.as_deref()); + // } else if self.origin == Some(old_f) { + // self.bar.last_action = + // annotation_id.unwrap_or("renamed".into()); + // self.origin = Some(new_f); + // self.mtime = Editor::modify(self.origin.as_deref()); + // } + // } + // DocumentChange::DeleteFile(DeleteFile { + // uri, + // options, + // .. + // }) => { + // let f = uri + // .to_file_path() + // .map_err(|()| report!("not path"))?; + // match std::fs::remove_file(&f) { + // Ok(()) => (), + // Err(e) + // if e.kind() + // == std::io::ErrorKind::IsADirectory => + // { + // std::fs::remove_dir_all(f)?; + // } + // Err(e) + // if let Some(DeleteFileOptions { + // ignore_if_not_exists: Some(true), + // .. + // }) = options + // && e.kind() + // == std::io::ErrorKind::NotFound => {} + // Err(e) => do yeet e, + // } + // } + // }; + // Ok(()) + // } + pub fn apply_dc( + &mut self, + x: DocumentChange, + ) -> rootcause::Result<()> { + match x { + // self.apply_tde(x) + DocumentChange::TextDocumentEdit(text_document_edit) => + self.apply_tde(text_document_edit)?, + DocumentChange::CreateFile(CreateFile { + uri, + options, + .. + }) => { let f = uri .to_file_path() .map_err(|()| report!("not path"))?; @@ -105,9 +307,12 @@ impl Editor { .context_custom::<WDebug, _>((opts, options))?, }; } - DocumentChangeOperation::Op(ResourceOp::Rename( - RenameFile { old_uri, new_uri, options, annotation_id }, - )) => { + DocumentChange::RenameFile(RenameFile { + old_uri, + new_uri, + options, + annotation_id, + }) => { let old_f = old_uri .to_file_path() .map_err(|()| report!("not path"))?; @@ -154,31 +359,11 @@ impl Editor { self.mtime = Editor::modify(self.origin.as_deref()); } } - DocumentChangeOperation::Op(ResourceOp::Delete( - DeleteFile { - uri, - options: - Some(DeleteFileOptions { - recursive: Some(true), // ??? seems redundant - ignore_if_not_exists, - .. - }), - }, - )) => { - let f = uri - .to_file_path() - .map_err(|()| report!("not path"))?; - match std::fs::remove_dir_all(f) { - Ok(()) => (), - Err(e) - if e.kind() == std::io::ErrorKind::NotFound - && ignore_if_not_exists == Some(true) => {} - Err(e) => do yeet e, - } - } - DocumentChangeOperation::Op(ResourceOp::Delete( - DeleteFile { uri, options }, - )) => { + DocumentChange::DeleteFile(DeleteFile { + uri, + options, + .. + }) => { let f = uri .to_file_path() .map_err(|()| report!("not path"))?; @@ -200,8 +385,41 @@ impl Editor { Err(e) => do yeet e, } } + }; + Ok(()) + } + pub fn apply_swsedit( + &mut self, + x: SnippetWorkspaceEdit, + ) -> rootcause::Result<()> { + match x { + SnippetWorkspaceEdit { document_changes: Some(x), .. } => x + .into_iter() + .map(|x| self.apply_sdc(x)) + .collect_reports() + .context("couldnt apply one or more wsedits")?, + SnippetWorkspaceEdit { changes: Some(x), .. } => x + .into_iter() + .map(|(p, e)| { + self.apply_tds( + &p.to_file_path().map_err(|_| { + report!("evil path") + .context_custom::<WDebug, _>(e.clone()) + })?, + e.clone(), + |x, y| x.apply(y).map(drop), + TextArea::apply_raw, + ) + .context_custom::<WDebug, _>(e) + }) + .collect_reports() + .context("couldnt apply one or more fs operations")?, + x => + do yeet report!("strange workspace edit") + .context_custom::<WDebug, _>(x), } - + change!(self); + self.hist.record(&self.text); Ok(()) } #[must_use] @@ -210,27 +428,11 @@ impl Editor { x: WorkspaceEdit, ) -> rootcause::Result<()> { match x { - WorkspaceEdit { - document_changes: Some(DocumentChanges::Edits(x)), - .. - } => x + WorkspaceEdit { document_changes: Some(x), .. } => x .into_iter() - .map(|x| self.apply_tde(x)) + .map(|x| self.apply_dc(x)) .collect_reports() .context("couldnt apply one or more wsedits")?, - WorkspaceEdit { - document_changes: Some(DocumentChanges::Operations(x)), - .. - } => x - .clone() - .into_iter() - .map(|op: DocumentChangeOperation| { - self.apply_dco(op.clone()) - .context_custom::<WDebug, _>(op) - }) - .collect_reports() - .context("couldnt apply one or more fs operations") - .context_custom::<WDebug, _>(x)?, WorkspaceEdit { changes: Some(x), .. } => x .into_iter() .map(|(p, e)| { diff --git a/src/gotolist.rs b/src/gotolist.rs index bed2abf..1999446 100644 --- a/src/gotolist.rs +++ b/src/gotolist.rs @@ -4,10 +4,10 @@ use std::path::Path; use dsb::Cell; use dsb::cell::Style; -use lsp_types::request::GotoImplementation; use lsp_types::{ - CallHierarchyIncomingCall, CallHierarchyOutgoingCall, Location, - LocationLink, Range, + CallHierarchyIncomingCall, CallHierarchyOutgoingCall, Definition, + DefinitionResponse, ImplementationRequest, Location, LocationLink, + LocationUriOnly, Range, WorkspaceSymbolLocation, }; use crate::FG; @@ -20,8 +20,8 @@ pub type GoToList = GenericMenu<GTL>; pub enum GTL {} #[derive(Debug)] pub enum O { - Impl(RqS<(), GotoImplementation>), - References(RqS<(), lsp_types::request::References>), + Impl(RqS<(), ImplementationRequest>), + References(RqS<(), lsp_types::ReferencesRequest>), Incoming( Rq<(), Vec<CallHierarchyIncomingCall>, (), rootcause::Report>, ), @@ -118,6 +118,22 @@ impl<'a> Hash for GoTo<'a> { self.path.hash(state); } } +impl From<&LocationUriOnly> for GoTo<'static> { + fn from(LocationUriOnly { uri }: &LocationUriOnly) -> Self { + Self { + path: Cow::Owned(uri.to_file_path().unwrap()), + at: At::P(0), + } + } +} +impl<'a> From<&'a WorkspaceSymbolLocation> for GoTo<'static> { + fn from(value: &'a WorkspaceSymbolLocation) -> Self { + match value { + WorkspaceSymbolLocation::Location(location) => location.into(), + WorkspaceSymbolLocation::LocationUriOnly(x) => x.into(), + } + } +} impl From<Location> for GoTo<'static> { fn from(Location { uri, range }: Location) -> Self { Self { @@ -135,6 +151,16 @@ impl From<&Location> for GoTo<'static> { } } +impl From<&Definition> for GoTo<'static> { + fn from(x: &Definition) -> Self { + match x { + Definition::LocationList([x]) | Definition::Location(x) => + x.into(), + _ => panic!(), + } + } +} + impl From<&LocationLink> for GoTo<'static> { fn from( LocationLink { target_uri, target_range, .. }: &LocationLink, @@ -496,12 +496,15 @@ pub fn to_snippet<'a: 'c, 'b: 'c, 'c>( ) -> annotate_snippets::Group<'c> { use annotate_snippets::*; match severity { - Some(DiagnosticSeverity::WARNING) => Level::WARNING, - Some(DiagnosticSeverity::INFORMATION) => Level::INFO, - Some(DiagnosticSeverity::HINT) => Level::HELP, + Some(DiagnosticSeverity::Warning) => Level::WARNING, + Some(DiagnosticSeverity::Information) => Level::INFO, + Some(DiagnosticSeverity::Hint) => Level::HELP, _ => Level::ERROR, } - .primary_title(message) + .primary_title(match message { + lsp_types::Message::String(x) => x, + lsp_types::Message::MarkupContent(_) => unimplemented!(), + }) .element(Snippet::source(t).annotation( AnnotationKind::Primary.span(t.l_range(*range).unwrap()), )) @@ -12,14 +12,12 @@ use helix_core::syntax::config::{ use json_value_merge::Merge; use log::info; use lsp_server::Message; -use lsp_types::notification::*; -use lsp_types::request::*; use lsp_types::*; use rootcause::report; use tokio::sync::oneshot; use winit::window::Window; mod client; -mod communication; +pub(crate) mod communication; pub use client::*; mod init_opts; mod rq; @@ -45,12 +43,14 @@ pub fn run( let (not_tx, not_rx) = unbounded(); let (_req_tx, _req_rx) = unbounded(); let (window_tx, window_rx) = oneshot::channel::<Arc<dyn Window>>(); + let mut rt = tokio::runtime::Builder::new_multi_thread(); + #[cfg(target_family = "unix")] + rt.enable_io(); let mut c = Client { tx: Tx(tx), progress: Box::leak(Box::new(papaya::HashMap::new())), - runtime: tokio::runtime::Builder::new_multi_thread() + runtime: rt .enable_time() - // .enable_io() .worker_threads(3) .thread_name("lsp runtime") .build()?, @@ -76,12 +76,16 @@ pub fn run( } }; info!("spawned {}", serde_json::to_string_pretty(&opts).unwrap()); - _ = c.request::<Initialize>(&opts)?; + _ = c.request::<InitializeRequest>(&opts)?; let x = rx .iter() .find_map(|x| { serde_json::from_value::<InitializeResult>( - x.response()?.result?, + match x { + Message::Notification(_) | Message::Request(_) => None, + Message::Response(response) => Some(response), + }? + .result?, ) .ok() }) @@ -92,10 +96,8 @@ pub fn run( // Some(PositionEncodingKind::UTF8) // ); c.initialized = Some(x); - c.notify::<lsp_types::notification::Initialized>( - &InitializedParams {}, - )?; - c.notify::<SetTrace>(&SetTraceParams { + c.notify::<lsp_types::InitializedNotification>(&InitializedParams {})?; + c.notify::<SetTraceNotification>(&SetTraceParams { value: lsp_types::TraceValue::Verbose, })?; let progress = c.progress; diff --git a/src/lsp/client.rs b/src/lsp/client.rs index a0db994..ddbe7c4 100644 --- a/src/lsp/client.rs +++ b/src/lsp/client.rs @@ -14,15 +14,15 @@ 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 rootcause::option_ext::OptionExt; use rust_analyzer::lsp::ext::*; use tokio::sync::oneshot; use ttools::*; +use url::Url; use crate::lsp::BehaviourAfter::{self, *}; +use crate::lsp::communication::{WorkDoneProgress, send}; use crate::lsp::{RequestError, Require, Requiring, Rq, RqSendError}; use crate::text::cursor::ceach; use crate::text::{LOADER, RopeExt, SortTedits, TextArea}; @@ -55,7 +55,7 @@ pub struct Client { ProgressToken, Option<(WorkDoneProgress, WorkDoneProgressBegin)>, >, - pub diagnostics: &'static papaya::HashMap<Url, Vec<Diagnostic>>, + pub diagnostics: &'static papaya::HashMap<url::Url, Vec<Diagnostic>>, #[allow(dead_code)] // TODO: handle notifications from the server pub not_rx: Receiver<N>, @@ -70,7 +70,7 @@ pub struct Client { impl Drop for Client { fn drop(&mut self) { - _ = self.notify::<Exit>(&()); + _ = self.notify::<ExitNotification>(&()); println!( "dropped lsp({}) @ {}", self.lsp_data.1.name, self.workspace.uri @@ -90,22 +90,25 @@ impl Client { l: helix_core::Language, ) -> Result<(), SendError<Message>> { let l = LOADER.language(l).config(); - self.notify::<DidOpenTextDocument>(&DidOpenTextDocumentParams { - text_document: TextDocumentItem { - uri: url::Url::from_file_path(f).unwrap(), - language_id: l - .language_server_language_id - .clone() - .unwrap_or(l.language_id.clone()), - version: 0, - text, + self.notify::<DidOpenTextDocumentNotification>( + &DidOpenTextDocumentParams { + text_document: TextDocumentItem { + uri: url::Url::from_file_path(f).unwrap(), + language_id: l + .language_server_language_id + .clone() + .unwrap_or(l.language_id.clone()) + .into(), + version: 0, + text, + }, }, - }) + ) } pub fn close(&self, f: &Path) -> Result<(), SendError<Message>> { - self.notify::<DidCloseTextDocument>(&DidCloseTextDocumentParams { - text_document: f.tid(), - }) + self.notify::<DidCloseTextDocumentNotification>( + &DidCloseTextDocumentParams { text_document: f.tid() }, + ) } pub fn edit( &self, @@ -113,17 +116,16 @@ impl Client { text: String, ) -> Result<(), SendError<Message>> { static V: AtomicI32 = AtomicI32::new(0); - self.notify::<lsp_types::notification::DidChangeTextDocument>( + self.notify::<lsp_types::DidChangeTextDocumentNotification>( &DidChangeTextDocumentParams { text_document: VersionedTextDocumentIdentifier { - uri: url::Url::from_file_path(f).unwrap(), + text_document_identifier: f.tid(), version: V.fetch_add(1, Relaxed), }, - content_changes: vec![TextDocumentContentChangeEvent { - range: None, - range_length: None, - text, - }], + content_changes: vec![ + (TextDocumentContentChangeWholeDocument { text }) + .into(), + ], }, ) } @@ -131,8 +133,13 @@ impl Client { pub fn resolve( &self, x: CompletionItem, - ) -> Result<CompletionItem, RequestError<ResolveCompletionItem>> { - self.request_immediate::<ResolveCompletionItem>(&x) + ) -> Result< + CompletionItem, + RequestError<lsp_request!("completionItem/resolve")>, + > { + self.request_immediate::<lsp_request!("completionItem/resolve")>( + &x, + ) } pub fn request_complete<'me>( @@ -145,18 +152,15 @@ impl Client { impl Future< Output = Result< Option<CompletionResponse>, - RequestError<Completion>, + RequestError<lsp_request!("textDocument/completion")>, >, > + use<>, > { self.caps().completion_provider.require()?; let (rx, _) = self - .request_::<Completion, { Redraw }>(&CompletionParams { - text_document_position: TextDocumentPositionParams { - text_document: f.tid(), - position: Position { line: y as _, character: x as _ }, - }, + .request_::<lsp_request!("textDocument/completion"), { Redraw }>(&CompletionParams { + text_document_position_params: TextDocumentPositionParams::new(f.tid(), Position { line: y as _, character: x as _ }), work_done_progress_params: default(), partial_result_params: default(), context: Some(c), @@ -268,7 +272,7 @@ impl Client { .diagnostic_provider .as_ref()? { - DiagnosticServerCapabilities::RegistrationOptions( + DiagnosticProvider::DiagnosticRegistrationOptions( x, ) => x.diagnostic_options.identifier.clone()?, _ => None?, @@ -293,8 +297,8 @@ impl Client { }; // dbg!(&x); match x.clone() { - DocumentDiagnosticReportResult::Report( - DocumentDiagnosticReport::Full( + // DocumentDiagnosticReport::DocumentDiagnosticReport( + DocumentDiagnosticReport::RelatedFullDocumentDiagnosticReport( RelatedFullDocumentDiagnosticReport { related_documents, full_document_diagnostic_report: @@ -303,7 +307,7 @@ impl Client { result_id, }, }, - ), + ) => { let l = self.diagnostics.guard(); self.diagnostics.insert(f.tid().uri, items, &l); @@ -311,14 +315,14 @@ impl Client { related_documents.into_iter().flatten() { match rel { - DocumentDiagnosticReportKind::Full( + RelatedDocument::FullDocumentDiagnosticReport( FullDocumentDiagnosticReport { items, .. }, ) => { self.diagnostics.insert(uri, items, &l); } - DocumentDiagnosticReportKind::Unchanged(_) => { + RelatedDocument::UnchangedDocumentDiagnosticReport(_) => { } } } @@ -343,15 +347,14 @@ impl Client { > + use<>, > { self.caps().document_highlight_provider.require()?; - let p = DocumentHighlightParams { + Ok(send!(self.request_::<{Redraw}>, "textDocument/documentHighlight", _ { text_document_position_params: TextDocumentPositionParams { text_document: f.tid(), position: cursor, }, work_done_progress_params: default(), partial_result_params: default(), - }; - Ok(self.request_::<lsp_request!("textDocument/documentHighlight"), {Redraw}>(&p) + }) .unwrap() .0 .map(|x| x.map(|x| x.unwrap_or_default()))) @@ -369,8 +372,7 @@ impl Client { > + use<>, > { self.caps().document_symbol_provider.require()?; - Ok(self.request_::<lsp_request!("textDocument/documentSymbol"), { Redraw }>( - &DocumentSymbolParams { + Ok(send!(self.request_::<{ Redraw }>, "textDocument/documentSymbol", _ { text_document: p.tid(), work_done_progress_params: default(), partial_result_params: default(), @@ -387,20 +389,22 @@ impl Client { impl Future< Output = Result< Option<WorkspaceSymbolResponse>, - RequestError<lsp_request!("workspace/symbol")>, + RequestError< + rust_analyzer::lsp::ext::WorkspaceSymbolRequest, + >, >, > + use<>, > { self.caps().workspace_symbol_provider.require()?; Ok(self - .request_::<lsp_request!("workspace/symbol"), { Redraw }>( - &lsp_types::WorkspaceSymbolParams { + .request_::<rust_analyzer::lsp::ext::WorkspaceSymbolRequest, { Redraw }>( + &rust_analyzer::lsp::ext::WorkspaceSymbolParams { query: f, search_scope: Some( - lsp_types::WorkspaceSymbolSearchScope::Workspace, + WorkspaceSymbolSearchScope::Workspace, ), search_kind: Some( - lsp_types::WorkspaceSymbolSearchKind::AllSymbols, + WorkspaceSymbolSearchKind::AllSymbols, ), ..Default::default() }, @@ -413,11 +417,10 @@ impl Client { &self, f: &Path, x: Vec<Position>, - ) -> Result<Vec<Option<Position>>, RequestError<MatchingBrace>> { - self.request_immediate::<MatchingBrace>(&MatchingBraceParams { - text_document: f.tid(), - positions: x, - }) + ) -> Result<Vec<[Position;2]>, RequestError<MatchingBraceRequest>> { + self.request_immediate::<MatchingBraceRequest>( + &MatchingBraceParams { text_document: f.tid(), positions: x }, + ) } pub fn matching_brace<'a>(&self, f: &Path, t: &'a mut TextArea) { @@ -425,16 +428,23 @@ impl Client { 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).unwrap(); - } + // if let Some(p) = p { + c.position = t.rope.l_position(p[1]).unwrap(); + // } } } } pub fn legend(&self) -> Option<&SemanticTokensLegend> { - match &self.caps(){ - ServerCapabilities {semantic_tokens_provider:Some(SemanticTokensServerCapabilities::SemanticTokensOptions(SemanticTokensOptions{legend,..})), ..}=> {Some(legend)},_ => None, + match &self.caps() { + ServerCapabilities { + semantic_tokens_provider: + Some(SemanticTokensProvider::SemanticTokensOptions( + SemanticTokensOptions { legend, .. }, + )), + .. + } => Some(legend), + _ => None, } } pub fn inlay( @@ -451,7 +461,7 @@ impl Client { > + use<>, > { self.caps().inlay_hint_provider.require()?; - Ok(self.request_::<lsp_request!("textDocument/inlayHint"), { Redraw }>(&InlayHintParams { + Ok(send!(self.request_::<{ Redraw }>, "textDocument/inlayHint", _ { work_done_progress_params: default(), text_document: f.tid(), range: t.to_l_range(lower::saturating::math!{ @@ -481,28 +491,26 @@ impl Client { impl Future< Output = Result< Option<Vec<TextEdit>>, - RequestError<Formatting>, + RequestError<lsp_request!("textDocument/formatting")>, >, >, > { self.caps().document_formatting_provider.require()?; - Ok(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) + Ok(send!(self.request, "textDocument/formatting", + _ { + 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( &self, @@ -510,12 +518,15 @@ impl Client { Box<[SemanticToken]>, Box<[SemanticToken]>, (), - RequestError<SemanticTokensFullRequest>, + RequestError<lsp_request!("textDocument/semanticTokens/full")>, >, f: &Path, ) -> Requiring< "semantic_tokens", - Result<(), RequestError<SemanticTokensFullRequest>>, + Result< + (), + RequestError<lsp_request!("textDocument/semanticTokens/full")>, + >, > { self.caps().semantic_tokens_provider.require()?; debug!("requested semantic tokens"); @@ -524,25 +535,23 @@ impl Client { // return Ok(()); // }; Ok(try bikeshed Result<(), RequestError<_>> { - let (rx, _) = self.request::<SemanticTokensFullRequest>( - &SemanticTokensParams { - work_done_progress_params: default(), - partial_result_params: default(), - text_document: f.tid(), - }, - )?; + let (rx, _) = send!(self.request, "textDocument/semanticTokens/full", _ { + 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?.ok_or(RequestError::Rx(std::marker::PhantomData))?; 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) + // let r = match y { + // SemanticTokensResult::Partial(_) => + // panic!("i told the lsp i dont support this"), + // SemanticTokensResult::Tokens(x) => + // x.data.into_boxed_slice(), + // }; + Ok(y.data.into_boxed_slice()) }); to.request(x); }) @@ -555,7 +564,7 @@ impl Client { ) -> rootcause::Result<()> { ceach!(t.cursor, |c| try bikeshed rootcause::Result<()> { let r = self - .request_by::<OnEnter>( + .request_by::<OnEnterRequest>( &TextDocumentPositionParams { text_document: f.tid(), position: t.to_l_position(*c).unwrap(), @@ -580,11 +589,12 @@ impl Client { t: &Path, c: Option<Position>, ) -> Result< - impl Future<Output = Result<Vec<Runnable>, RequestError<Runnables>>> - + use<>, - RqSendError<Runnables>, + impl Future< + Output = Result<Vec<Runnable>, RequestError<RunnablesRequest>>, + > + use<>, + RqSendError<RunnablesRequest>, > { - self.request::<Runnables>(&RunnablesParams { + self.request::<RunnablesRequest>(&RunnablesParams { text_document: t.tid(), position: c, }) @@ -598,13 +608,13 @@ impl Client { ) -> Result< impl Future< Output = Result< - <ChildModules as Request>::Result, - RequestError<ChildModules>, + <ChildModulesRequest as Request>::Result, + RequestError<ChildModulesRequest>, >, >, - RqSendError<ChildModules>, + RqSendError<ChildModulesRequest>, > { - self.request::<ChildModules>(&TextDocumentPositionParams { + self.request::<ChildModulesRequest>(&TextDocumentPositionParams { position: p, text_document: t.tid(), }) @@ -616,17 +626,19 @@ impl Client { ) -> Result< impl Future< Output = Result< - <GotoImplementation as Request>::Result, - RequestError<GotoImplementation>, + <lsp_request!("textDocument/implementation") as Request>::Result, + RequestError<lsp_request!("textDocument/implementation")>, >, > + use<>, - RqSendError<GotoImplementation>, - > { - self.request::<GotoImplementation>(&GotoImplementationParams { - text_document_position_params: tdpp, - work_done_progress_params: default(), - partial_result_params: default(), - }) + RqSendError<lsp_request!("textDocument/implementation")>, + >{ + send!(self.request, "textDocument/implementation", + _ { + text_document_position_params: tdpp, + work_done_progress_params: default(), + partial_result_params: default(), + }, + ) .map(fst) } @@ -637,17 +649,19 @@ impl Client { impl Future< Output = Result< Option<Vec<Location>>, - RequestError<References>, + RequestError<lsp_request!("textDocument/references")>, >, > + use<>, - RqSendError<References>, + RqSendError<lsp_request!("textDocument/references")>, > { - self.request::<References>(&ReferenceParams { - text_document_position: tdpp, - work_done_progress_params: default(), - partial_result_params: default(), - context: ReferenceContext { include_declaration: false }, - }) + send!(self.request, "textDocument/references", + _ { + text_document_position_params: tdpp, + work_done_progress_params: default(), + partial_result_params: default(), + context: ReferenceContext { include_declaration: false }, + }, + ) .map(fst) } @@ -666,15 +680,19 @@ impl Client { impl Future< Output = Result< Option<CallHierarchyItem>, - RequestError<CallHierarchyPrepare>, + RequestError< + lsp_request!("textDocument/prepareCallHierarchy"), + >, >, > + use<>, - RqSendError<CallHierarchyPrepare>, + RqSendError<lsp_request!("textDocument/prepareCallHierarchy")>, > { - self.request::<CallHierarchyPrepare>(&CallHierarchyPrepareParams { - text_document_position_params: at, - work_done_progress_params: default(), - }) + send!(self.request, "textDocument/prepareCallHierarchy", + _ { + text_document_position_params: at, + work_done_progress_params: default(), + } + ) .map(fst) .map(|x| x.map(|x| x.map(|x| x.and_then(|mut x| x.try_remove(0))))) } @@ -683,7 +701,7 @@ impl Client { at: TextDocumentPositionParams, ) -> rootcause::Result<Vec<CallHierarchyIncomingCall>> { let calls = self - .request::<CallHierarchyIncomingCalls>( + .request::<CallHierarchyIncomingCallsRequest>( &CallHierarchyIncomingCallsParams { item: self .find_function(at)? @@ -703,7 +721,7 @@ impl Client { at: TextDocumentPositionParams, ) -> rootcause::Result<Vec<CallHierarchyOutgoingCall>> { let calls = self - .request::<CallHierarchyOutgoingCalls>( + .request::<CallHierarchyOutgoingCallsRequest>( &CallHierarchyOutgoingCallsParams { item: self .find_function(at)? diff --git a/src/lsp/communication.rs b/src/lsp/communication.rs index 4c4f49d..efc44ee 100644 --- a/src/lsp/communication.rs +++ b/src/lsp/communication.rs @@ -7,22 +7,29 @@ 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, + ErrorCode, ExtractError, Message, Notification as N, Request as LRq, + RequestId, Response as Re, ResponseError, }; -use lsp_types::notification::*; -use lsp_types::request::*; use lsp_types::*; +use serde::{Deserialize, Serialize}; use tokio::sync::oneshot; use tokio::time::error::Elapsed; +use url::Url; use winit::window::Window; +#[derive(Debug, PartialEq, Deserialize, Serialize, Clone)] +#[serde(untagged)] +pub enum WorkDoneProgress { + Begin(WorkDoneProgressBegin), + Report(WorkDoneProgressReport), + End(WorkDoneProgressEnd), +} use crate::lsp::BehaviourAfter::{self, *}; use crate::lsp::{RequestError, RqSendError}; pub fn handler( window_rx: oneshot::Receiver<Arc<dyn Window + 'static>>, progress: &papaya::HashMap< - NumberOrString, + ProgressToken, Option<(WorkDoneProgress, WorkDoneProgressBegin)>, >, _req_tx: Sender<LRq>, @@ -40,13 +47,13 @@ pub fn handler( Ok((.., BehaviourAfter::RedrawNow)) => w.request_redraw(), Ok((x, y, and)) => { debug!("received request {x}"); - assert!(map.insert(x, (y, Instant::now(), and)).is_none()); + assert!(map.insert(RequestId::from(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>() { + match load_rq::<lsp_request!("window/workDoneProgress/create")>(rq) { Ok((_, x)) => { let g = progress.guard(); progress.insert(x.token, None, &g); @@ -67,13 +74,13 @@ pub fn handler( 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()) { + if let Some((s, _, t)) = map.remove(&x.id) { 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()) { + if let Some((s, _, t)) = map.remove(&x.id) { _ = s.send(x.clone()); if t == Redraw { w.request_redraw() } trace!("received error from lsp for response {x:?}"); @@ -82,7 +89,7 @@ pub fn handler( } } } - else if let Some((s, took, t)) = map.remove(&x.id.i32()) { + else if let Some((s, took, t)) = map.remove(&x.id) { log::debug!("request {} took {:?}", x.id, took.elapsed()); match s.send(x) { Ok(()) => {} @@ -100,7 +107,7 @@ pub fn handler( } Ok(Message::Notification(rq @ N { method: "textDocument/publishDiagnostics", .. })) => { debug!("got diagnostics"); - match rq.load::<PublishDiagnostics>() { + match load_n::<lsp_notification!("textDocument/publishDiagnostics")>(rq) { Ok(x) => { d.insert(x.uri, x.diagnostics, &d.guard()); w.request_redraw(); @@ -109,7 +116,8 @@ pub fn handler( } }, Ok(Message::Notification(x @ N { method: "$/progress", .. })) => { - let ProgressParams {token,value:ProgressParamsValue::WorkDone(x) } = x.load::<Progress>().unwrap(); + let ProgressParams {token,value:x } = load_n::<lsp_notification!("$/progress")>(x).unwrap(); + let Ok(x) = serde_json::from_value::<WorkDoneProgress>(x.clone()) else { error!("{x:?}"); continue }; match x.clone() { WorkDoneProgress::Begin(y) => { progress.update(token, move |_| Some((x.clone(), y.clone())), &progress.guard()); @@ -131,6 +139,34 @@ pub fn handler( } } } +#[derive(Debug)] +#[allow(dead_code)] +pub enum ExtractErr<T> { + /// The extracted message was of a different method than expected. + MethodMismatch(T), + /// Failed to deserialize the message. + JsonError { method: String, error: serde_json::Error }, +} +fn load_n<T: lsp_types::Notification>( + me: lsp_server::Notification, +) -> Result<T::Params, ExtractErr<lsp_server::Notification>> { + (T::METHOD.as_str() == me.method) + .ok_or(ExtractErr::MethodMismatch(me.clone()))?; + serde_json::from_value(me.params) + .map_err(|e| ExtractErr::JsonError { method: me.method, error: e }) +} +pub fn load_rq<P: lsp_types::Request>( + me: LRq, +) -> Result<(RequestId, P::Params), ExtractError<LRq>> { + if me.method != P::METHOD.to_string() { + return Err(ExtractError::MethodMismatch(me)); + } + match serde_json::from_value(me.params) { + Ok(params) => Ok((me.id, params)), + Err(error) => + Err(ExtractError::JsonError { method: me.method, error }), + } +} impl super::Tx { pub fn notify<X: Notification>( &self, @@ -142,7 +178,9 @@ impl super::Tx { })) } pub fn cancel(&self, rid: i32) { - _ = self.notify::<Cancel>(&CancelParams { id: rid.into() }); + _ = self.notify::<CancelNotification>(&CancelParams { + id: rid.into(), + }); } } impl super::Client { @@ -266,3 +304,13 @@ impl super::Client { self.send_to.send((0, tx, BehaviourAfter::RedrawNow)) } } +macro_rules! send + { + ($c:ident.$call:ident$(::<$($arg:block),*>)?, $request:tt, _ { $($field:ident: $expr:expr),* $(,)? $(,..$base:expr)? } $(,)?) => {{ + type R = lsp_request!($request); + type P = <R as Request>::Params; + $c.$call::<R $($(,$arg)*)?>(&P { $($field: $expr),* }) + }}; +} + +pub(crate) use send; diff --git a/src/lsp/init_opts.rs b/src/lsp/init_opts.rs index 345e2a4..797ba17 100644 --- a/src/lsp/init_opts.rs +++ b/src/lsp/init_opts.rs @@ -6,13 +6,13 @@ pub fn get( conf: &Option<serde_json::Value>, is_ra: bool, ) -> InitializeParams { - let sym = Some(if is_ra { - SymbolKind::ALL.to_vec() - } else { - (1..=26).map(SymbolKind).collect() - }); + // let sym = Some(if is_ra { + // SymbolKind::ALL.to_vec() + // } else { + // (1..=26).map(SymbolKind).collect() + // }); InitializeParams { - process_id: Some(std::process::id()), + process_id: Some(std::process::id() as _), capabilities: ClientCapabilities { window: Some(WindowClientCapabilities { @@ -21,20 +21,18 @@ pub fn get( }), workspace: Some(WorkspaceClientCapabilities { symbol: Some(WorkspaceSymbolClientCapabilities { - symbol_kind: Some(SymbolKindCapability { - value_set: sym.clone(), + // symbol_kind: Some(ClientSymbolKindOptions { + // value_set: sym.clone(), + // }), + // tag_support: Some(ClientSymbolTagOptions { + // value_set: SymbolTag::ALL.to_vec(), + // }), + resolve_support: Some(ClientSymbolResolveOptions { + properties: vec!["location".into()], }), - tag_support: Some(TagSupport { - value_set: SymbolTag::ALL.to_vec(), - }), - resolve_support: Some( - WorkspaceSymbolResolveSupportCapability { - properties: vec!["location".into()], - }, - ), ..default() }), - diagnostic: Some(DiagnosticWorkspaceClientCapabilities { + diagnostics: Some(DiagnosticWorkspaceClientCapabilities { refresh_support: Some(true), }), inlay_hint: Some(InlayHintWorkspaceClientCapabilities { @@ -50,10 +48,12 @@ pub fn get( failure_handling: Some(FailureHandlingKind::Abort), normalizes_line_endings: Some(false), change_annotation_support: Some( - ChangeAnnotationWorkspaceEditClientCapabilities { + ChangeAnnotationsSupportOptions { groups_on_label: Some(false), }, ), + metadata_support: None, + snippet_edit_support: Some(true), }), ..default() }), @@ -64,47 +64,45 @@ pub fn get( }, ), document_highlight: Some(default()), - formatting: Some(DynamicRegistrationClientCapabilities { + formatting: Some(DocumentFormattingClientCapabilities { 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(), - ], - }, - ), + resolve_support: Some(ClientInlayHintResolveOptions { + 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: sym, - }), + // tag_support: Some(ClientSymbolTagSupport { + // value_set: SymbolTag::ALL.to_vec(), + // }), + // symbol_kind: Some(ClientSymbolKindOptions { + // value_set: sym, + // }), hierarchical_document_symbol_support: Some(true), ..default() }), - definition: Some(GotoCapability { + definition: Some(DefinitionClientCapabilities { link_support: Some(true), ..default() }), code_action: Some(CodeActionClientCapabilities { data_support: Some(true), resolve_support: Some( - CodeActionCapabilityResolveSupport { + ClientCodeActionResolveOptions { properties: vec!["edit".to_string()], }, ), code_action_literal_support: Some( - CodeActionLiteralSupport { + ClientCodeActionLiteralOptions { code_action_kind: - CodeActionKindLiteralSupport { + ClientCodeActionKindOptions { value_set: [ "", "Empty", @@ -123,7 +121,7 @@ pub fn get( "source", "source.organizeImports", ] - .map(String::from) + .map(Into::into) .into(), }, }, @@ -133,7 +131,7 @@ pub fn get( rename: Some(RenameClientCapabilities { prepare_support: Some(true), prepare_support_default_behavior: Some( - PrepareSupportDefaultBehavior::IDENTIFIER, + PrepareSupportDefaultBehavior::Identifier, ), honors_change_annotations: Some(false), ..default() @@ -148,36 +146,40 @@ pub fn get( diagnostic: Some(DiagnosticClientCapabilities { dynamic_registration: None, related_document_support: Some(true), + markup_message_support: None, + diagnostics_capabilities: default(), }), publish_diagnostics: Some( PublishDiagnosticsClientCapabilities { - related_information: Some(true), - code_description_support: Some(true), - data_support: Some(true), + // 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 { + ClientSignatureInformationOptions { documentation_format: Some(vec![ MarkupKind::Markdown, MarkupKind::PlainText, ]), parameter_information: Some( - ParameterInformationSettings { + ClientSignatureParameterInformationOptions { label_offset_support: Some(true), }, ), active_parameter_support: Some(true), + no_active_parameter_support: Some(true), + }, ), context_support: Some(false), }), completion: Some(CompletionClientCapabilities { dynamic_registration: Some(false), - completion_item: Some(CompletionItemCapability { + completion_item: Some(ClientCompletionItemOptions { snippet_support: Some(true), commit_characters_support: Some(true), documentation_format: Some(vec![ @@ -186,12 +188,12 @@ pub fn get( ]), deprecated_support: None, preselect_support: None, - tag_support: Some(TagSupport { - value_set: vec![CompletionItemTag::DEPRECATED], + tag_support: Some(CompletionItemTagOptions { + value_set: vec![CompletionItemTag::Deprecated], }), resolve_support: Some( - CompletionItemCapabilityResolveSupport { + ClientCompletionItemResolveOptions { properties: vec![ "additionalTextEdits".into(), "documentation".into(), @@ -200,60 +202,56 @@ pub fn get( ), insert_replace_support: Some(false), insert_text_mode_support: Some( - InsertTextModeSupport { - value_set: vec![InsertTextMode::AS_IS], + ClientCompletionItemInsertTextModeOptions { + value_set: vec![InsertTextMode::AsIs], }, ), label_details_support: Some(true), ..default() }), completion_item_kind: Some( - CompletionItemKindCapability { + ClientCompletionItemOptionsKind { 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, + 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::EnumMember, + CompletionItemKind::Constant, + CompletionItemKind::Struct, + CompletionItemKind::Event, + CompletionItemKind::Operator, + CompletionItemKind::TypeParameter, ]), - // value_set: Some(vec![CompletionItemKind::]), }, ), context_support: None, - insert_text_mode: Some(InsertTextMode::AS_IS), - completion_list: Some(CompletionListCapability { + insert_text_mode: Some(InsertTextMode::AsIs), + completion_list: Some(CompletionListCapabilities { item_defaults: None, + apply_kind_support: None, }), }), semantic_tokens: Some(SemanticTokensClientCapabilities { dynamic_registration: Some(false), - requests: SemanticTokensClientCapabilitiesRequests { - range: Some(true), - full: Some( - lsp_types::SemanticTokensFullOptions::Bool( - true, - ), - ), + requests: ClientSemanticTokensRequestOptions { + range: Some(true.into()), + full: Some(true.into()), }, token_modifiers: [ "associated", @@ -313,8 +311,10 @@ pub fn get( }), initialization_options: conf.clone(), trace: None, - workspace_folders: Some(vec![workspace]), - + workspace_folders_initialize_params: + WorkspaceFoldersInitializeParams { + workspace_folders: Some(vec![workspace].into()), + }, ..default() } } diff --git a/src/lsp/rq.rs b/src/lsp/rq.rs index b7e42e0..50a6fd3 100644 --- a/src/lsp/rq.rs +++ b/src/lsp/rq.rs @@ -4,8 +4,10 @@ use std::marker::PhantomData; use crossbeam::channel::SendError; use lsp_server::{Message, Response as Re}; -use lsp_types::DiagnosticServerCancellationData; -use lsp_types::request::Request; +use lsp_types::{ + DiagnosticServerCancellationData, LspRequestMethod, MessageDirection, + Request, +}; use serde::{Deserialize, Serialize}; use tokio::sync::oneshot; use tokio::task; @@ -26,7 +28,10 @@ pub type AQErr = RequestError<LSPError>; impl Request for LSPError { type Params = (); type Result = (); - const METHOD: &'static str = "<unknown method>"; + const MESSAGE_DIRECTION: lsp_types::MessageDirection = + MessageDirection::ServerToClient; + const METHOD: LspRequestMethod<'static> = + LspRequestMethod::Custom("<unknown method>"); } #[derive(Debug)] pub struct LSPError {} @@ -158,11 +163,11 @@ impl<T: Clone, R, D, E> Clone for Rq<T, R, D, E> { } } #[derive(serde_derive::Serialize, serde_derive::Deserialize)] -pub struct Rq<T, R, D = (), E = RequestError<R>> { +pub struct Rq<Output, Res, Data = (), Error = LSPError> { #[serde(skip_serializing_if = "Option::is_none", default = "none")] - pub result: Option<T>, + pub result: Option<Output>, #[serde(skip, default = "none")] - pub request: Option<(AbortOnDropHandle<Result<R, E>>, D)>, + pub request: Option<(AbortOnDropHandle<Result<Res, Error>>, Data)>, } 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 { diff --git a/src/main.rs b/src/main.rs index 8c617ba..9ac477a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ #![feature( + const_convert, unsized_const_params, exact_div, yeet_expr, @@ -64,8 +65,10 @@ use Default::default; use NamedKey::*; use dsb::cell::Style; use dsb::{Cell, F}; +use env_logger::{Env, TimestampPrecision}; use fimg::Image; use libc::{atexit, signal}; +use log::LevelFilter; use lsp::Rq; use lsp_types::*; use rust_fsm::StateMachine; @@ -103,10 +106,15 @@ fn main() { let _x = 4; // let x = HashMap::new(); unsafe { std::env::set_var("CARGO_UNSTABLE_RUSTC_UNICODE", "true") }; - env_logger::builder() - .filter_level(log::LevelFilter::Info) - .parse_env("GRA_LOG") - .init(); + env_logger::builder().filter_level(LevelFilter::Error).init(); + // .format_timestamp(Some(TimestampPrecision::Seconds)) + // .filter(Some("regex_cursor"), LevelFilter::Error) + // .filter(Some("calloop"), LevelFilter::Error) + // .filter(Some("tracing::span"), LevelFilter::Error) + // .parse_env(Env::new().default_filter_or("info")) + // .filter_level(log::LevelFilter::Info) + // .parse_env("G"RA_LOG") + // .init(); // lsp::x(); entry(EventLoop::new().unwrap()) } @@ -236,7 +244,12 @@ pub(crate) fn entry(event_loop: EventLoop) { if let Some((.., c)) = &mut ed.lsp && let Some(c) = c.take() { c.send(window.clone()).unwrap(); } + let lsp_mn = &mut lsp_mn; + for lem in &lsp_mn.four { + lem.1.status(); + // dbg!(lem.1); + } let (fw, fh) = dsb::dims(&FONT, ppem); let (c, r) = dsb::fit( &FONT, @@ -504,11 +517,11 @@ rust_fsm::state_machine! { pub(crate) CompletionState => #[derive(Debug)] pub(crate) CompletionAction<'i> => pub(crate) CDo None => Click => None, None => K(Key<&'i str> => Key::Character(k @ ("." | ":"))) => Complete( - RqS<Complete, lsp_types::request::Completion, usize> => default() - ) [Request(CompletionContext => CompletionContext {trigger_kind: CompletionTriggerKind::TRIGGER_CHARACTER, trigger_character:Some(k.to_string()) })], - None => K(Key::Character(" ") if ctrl()) => Complete(default()) [Request(CompletionContext { trigger_kind: CompletionTriggerKind::INVOKED, trigger_character:None })], + RqS<Complete, lsp_types::CompletionRequest, usize> => default() + ) [Request(CompletionContext => CompletionContext {trigger_kind: CompletionTriggerKind::TriggerCharacter, trigger_character:Some(k.to_string()) })], + None => K(Key::Character(" ") if ctrl()) => Complete(default()) [Request(CompletionContext { trigger_kind: CompletionTriggerKind::Invoked, trigger_character:None })], None => K(Key::Character(x) if x.chars().all(char::is_alphabetic) - && !matches!(x, "w"|"a"|"s"|"d" if alt())) => Complete(default()) [Request(CompletionContext { trigger_kind: CompletionTriggerKind::INVOKED, trigger_character:None })], + && !matches!(x, "w"|"a"|"s"|"d" if alt())) => Complete(default()) [Request(CompletionContext { trigger_kind: CompletionTriggerKind::Invoked, trigger_character:None })], None => K(_) => _, // when @@ -526,7 +539,7 @@ rust_fsm::state_machine! { Complete(Rq { result: Some(x), .. }) => K(Key::Named(NamedKey::Enter)) => None [Finish(Complete => x)], - Complete(_x) => K(_) => _ [Request(CompletionContext { trigger_kind: CompletionTriggerKind::TRIGGER_FOR_INCOMPLETE_COMPLETIONS, trigger_character:None })], + Complete(_x) => K(_) => _ [Request(CompletionContext { trigger_kind: CompletionTriggerKind::TriggerForIncompleteCompletions, trigger_character:None })], } use complete::Complete; @@ -262,14 +262,23 @@ pub fn render( .flat_map(|diag| { let sev = diag .severity - .unwrap_or(DiagnosticSeverity::ERROR); + .unwrap_or(DiagnosticSeverity::Error); let sev_ = match sev { - DiagnosticSeverity::ERROR => EType::Error, - DiagnosticSeverity::WARNING => EType::Warning, - DiagnosticSeverity::HINT => EType::Hint, + DiagnosticSeverity::Error => EType::Error, + DiagnosticSeverity::Warning => EType::Warning, + DiagnosticSeverity::Hint => EType::Hint, _ => EType::Info, }; - once((diag.range, &*diag.message, sev_)).chain( + once(( + diag.range, + match &diag.message { + Message::String(x) => &**x, + Message::MarkupContent(x) => + unimplemented!(), + }, + sev_, + )) + .chain( diag.related_information .iter() .flatten() @@ -285,7 +294,7 @@ pub fn render( }) .for_each(|(mut r, m, sev)| { if let EType::Related(x) = sev - && x != DiagnosticSeverity::ERROR + && x != DiagnosticSeverity::Error { return; } @@ -301,7 +310,7 @@ pub fn render( | EType::Hint | EType::Info => col!("#9469242c"), EType::Related( - DiagnosticSeverity::ERROR, + DiagnosticSeverity::Error, ) => col!("#dfbfff26"), EType::Related(_) => col!("#ffad6625"), }); @@ -342,7 +351,7 @@ pub fn render( col!("#ff942f1b", "#f26462") } EType::Related( - DiagnosticSeverity::WARNING, + DiagnosticSeverity::Warning, ) => { col!("#dfbfff26", "#DFBFFF") } @@ -581,13 +590,13 @@ pub fn render( continue; } - let r = if let Some(r) = e.sticky_range { - let r = text.l_range(r).unwrap(); - text.char_to_line(r.start)..=text.char_to_line(r.end) - } else { - let x = text.char_to_line(p); - x..=x - }; + // let r = if let Some(r) = e.sticky_range { + // let r = text.l_range(r).unwrap(); + // text.char_to_line(r.start)..=text.char_to_line(r.end) + // } else { + let x = text.char_to_line(p); + let r = x..=x; + // }; let d = Cell { style: Style { @@ -3,8 +3,8 @@ use std::iter::repeat_n; use dsb::Cell; use dsb::cell::Style; use lsp_types::{ - MarkupContent, ParameterInformation, SignatureHelp, - SignatureInformation, + MarkupContent, ParameterInformation, ParameterInformationLabel, + SignatureHelp, SignatureInformation, }; use crate::FG; @@ -16,9 +16,14 @@ pub fn active( let y = &sig.signatures.get(sig.active_signature? as usize)?; Some(( y, - sig.active_parameter - .zip(y.parameters.as_ref()) - .and_then(|(i, x)| x.get(i as usize)), + sig.active_parameter.zip(y.parameters.as_ref()).and_then( + |(i, x)| { + x.get(match i { + lsp_types::ActiveParameter::Int(n) => n as usize, + lsp_types::ActiveParameter::Null => return None, + }) + }, + ), )) } @@ -32,8 +37,8 @@ pub fn sig( let sig = y.label.chars().zip(0..).map(|(x, i)| { let mut a = ds.basic(x); if p.is_some_and(|x| match x.label { - lsp_types::ParameterLabel::Simple(_) => false, - lsp_types::ParameterLabel::LabelOffsets([a, b]) => + ParameterInformationLabel::String(_) => false, + ParameterInformationLabel::Tuple((a, b)) => (a..b).contains(&i), }) { a.style |= (Style::BOLD, color_("#ffcc66")); @@ -5,10 +5,12 @@ use std::path::{Path, PathBuf}; use Default::default; use dsb::Cell; use dsb::cell::Style; -use itern::Iter3; +use itern::{Iter3, Iter4}; use lsp_types::*; +use url::Url; use crate::FG; +use crate::complete::i; use crate::gotolist::At; pub use crate::gotolist::GoTo; use crate::menu::generic::{GenericMenu, MenuData}; @@ -32,38 +34,48 @@ impl MenuData for Symb { (r, tree, bmks, _, origin): &Self::Data, ) -> impl Iterator<Item = Self::Element<'_>> { match r { - SymbolsList::Document(DocumentSymbolResponse::Flat(x)) => - Iter3::A(x.iter().map(UsedSI::from)), - SymbolsList::Document(DocumentSymbolResponse::Nested(x)) => - Iter3::B( - gen { - for bmk in &**bmks { - yield UsedSI { - name: &bmk.text, - kind: SymbolKind::BOOKMARK, - tags: None, - at: GoTo { - path: origin.into(), - at: At::P(bmk.position), - }, - right: None, - } + SymbolsList::Document( + DocumentSymbolResponse::SymbolInformationList(x), + ) => Iter4::A(x.iter().map(UsedSI::from)), + SymbolsList::Document( + DocumentSymbolResponse::DocumentSymbolList(x), + ) => Iter4::B( + gen { + for bmk in &**bmks { + yield UsedSI { + name: &bmk.text, + // kind: SymbolKind::Bookmark, + kind: SymbolKind::Class, // FIXME + tags: None, + at: GoTo { + path: origin.into(), + at: At::P(bmk.position), + }, + right: None, } } - .chain(x.iter().flat_map( - move |x| gen move { - let mut q = VecDeque::with_capacity(12); - q.push_back(x); - while let Some(x) = q.pop_front() { - q.extend(x.children.iter().flatten()); - yield (x, origin).into(); - } - }, - )), - ), - SymbolsList::Workspace(WorkspaceSymbolResponse::Flat(x)) => - Iter3::C(chain(tree, x.iter()).map(UsedSI::from)), - _ => unreachable!("please no"), + } + .chain(x.iter().flat_map( + move |x| gen move { + let mut q = VecDeque::with_capacity(12); + q.push_back(x); + while let Some(x) = q.pop_front() { + q.extend(x.children.iter().flatten()); + yield (x, origin).into(); + } + }, + )), + ), + SymbolsList::Workspace( + WorkspaceSymbolResponse::WorkspaceSymbolList(x), + ) => Iter4::C(chain( + tree.iter().map(UsedSI::from), + x.iter().map(UsedSI::from), + )), + SymbolsList::Workspace( + WorkspaceSymbolResponse::SymbolInformationList(x), + ) => Iter4::D(chain(tree, x.iter()).map(UsedSI::from)), + // _ => unreachable!("please no"), } } @@ -97,7 +109,7 @@ pub struct UsedSI<'a> { impl<'a> std::hash::Hash for UsedSI<'a> { fn hash<H: std::hash::Hasher>(&self, state: &mut H) { self.name.hash(state); - self.kind.0.hash(state); + self.kind.hash(state); self.at.hash(state); self.right.hash(state); } @@ -105,11 +117,9 @@ impl<'a> std::hash::Hash for UsedSI<'a> { impl<'a> From<&'a SymbolInformation> for UsedSI<'a> { fn from( SymbolInformation { - name, - kind, - tags, + base_symbol_information: + BaseSymbolInformation { name, kind, tags, container_name }, location, - container_name, .. }: &'a SymbolInformation, ) -> Self { @@ -122,6 +132,24 @@ impl<'a> From<&'a SymbolInformation> for UsedSI<'a> { } } } +impl<'a> From<&'a WorkspaceSymbol> for UsedSI<'a> { + fn from( + WorkspaceSymbol { + location, + data: _, + base_symbol_information: + BaseSymbolInformation { name, kind, tags, container_name }, + }: &'a WorkspaceSymbol, + ) -> Self { + UsedSI { + name, + kind: *kind, + tags: tags.as_deref(), + at: location.into(), + right: container_name.as_deref(), + } + } +} impl<'a> From<(&'a DocumentSymbol, &'a PathBuf)> for UsedSI<'a> { fn from( ( @@ -160,7 +188,9 @@ pub enum SymbolsList { } impl Default for SymbolsList { fn default() -> Self { - Self::Workspace(WorkspaceSymbolResponse::Flat(vec![])) + Self::Workspace(WorkspaceSymbolResponse::WorkspaceSymbolList( + vec![], + )) } } @@ -169,8 +199,17 @@ impl GenericMenu<Symb> { let tree = tree .iter() .map(|x| SymbolInformation { - name: x.file_name().unwrap().to_str().unwrap().to_string(), - kind: SymbolKind::FILE, + base_symbol_information: BaseSymbolInformation { + name: x + .file_name() + .unwrap() + .to_str() + .unwrap() + .to_string(), + kind: SymbolKind::File, + container_name: None, + tags: None, + }, location: Location { range: lsp_types::Range { end: default(), @@ -178,10 +217,9 @@ impl GenericMenu<Symb> { }, uri: Url::from_file_path(&x).unwrap(), }, - container_name: None, + #[allow(deprecated)] deprecated: None, - tags: None, }) .collect(); Self { data: (default(), tree, bmk, default(), orig), ..default() } @@ -211,35 +249,35 @@ fn r<'a>( const MAP: [([u8; 3], [u8; 3], &str); 85] = { ( amap::amap! { - const { SymbolKind::FILE.0 as usize } => ("#9a9b9a", " "), - const { SymbolKind::METHOD.0 as usize } | const { SymbolKind::FUNCTION.0 as usize } => ("#FFD173", "λ "), - const { SymbolKind::CONSTRUCTOR.0 as usize } => ("#FFAD66", "->"), - const { SymbolKind::FIELD.0 as usize } => ("#E06C75", "x."), - const { SymbolKind::VARIABLE.0 as usize } => ("#E06C75", "x "), - const { SymbolKind::MODULE.0 as usize } => ("#D5FF80", "::"), - const { SymbolKind::PROPERTY.0 as usize } => ("#e6e1cf", "x."), - // const { SymbolKind::VALUE.0 as usize } => ("#DFBFFF", "4 "), - const { SymbolKind::ENUM.0 as usize } => ("#73b9ff", "u"), - const { SymbolKind::ENUM_MEMBER.0 as usize } => ("#73b9ff", ":"), - // const { SymbolKind::SNIPPET.0 as usize } => ("#9a9b9a", "! "), - const { SymbolKind::INTERFACE.0 as usize } => ("#E5C07B", "t "), - // const { SymbolKind::REFERENCE.0 as usize } => ("#9a9b9a", "& "), - const { SymbolKind::CONSTANT.0 as usize } => ("#DFBFFF", "N "), - const { SymbolKind::STRUCT.0 as usize } => ("#73D0FF", "X{"), - const { SymbolKind::OPERATOR.0 as usize } => ("#F29E74", "+ "), - const { SymbolKind::TYPE_PARAMETER.0 as usize } => ("#9a9b9a", "T "), - // const { SymbolKind::KEYWORD.0 as usize } => ("#FFAD66", "as"), + const { i(SymbolKind::File) } => ("#9a9b9a", " "), + const { i(SymbolKind::Method) } | const { i(SymbolKind::Function) } => ("#FFD173", "λ "), + const { i(SymbolKind::Constructor) } => ("#FFAD66", "->"), + const { i(SymbolKind::Field) } => ("#E06C75", "x."), + const { i(SymbolKind::Variable) } => ("#E06C75", "x "), + const { i(SymbolKind::Module) } => ("#D5FF80", "::"), + const { i(SymbolKind::Property) } => ("#e6e1cf", "x."), + // const { SymbolKind::Val.0 as usize } => ("#DFBFFF", "4 "), + const { i(SymbolKind::Enum) } => ("#73b9ff", "u"), + const { i(SymbolKind::EnumMember) } => ("#73b9ff", ":"), + // const { SymbolKind::Snipp.0 as usize } => ("#9a9b9a", "! "), + const { i(SymbolKind::Interface) } => ("#E5C07B", "t "), + // const { SymbolKind::Referen.0 as usize } => ("#9a9b9a", "& "), + const { i(SymbolKind::Constant) } => ("#DFBFFF", "N "), + const { i(SymbolKind::Struct) } => ("#73D0FF", "X{"), + const { i(SymbolKind::Operator) } => ("#F29E74", "+ "), + const { i(SymbolKind::TypeParameter) } => ("#9a9b9a", "T "), + // const { SymbolKind::Keyword.0 as usize } => ("#FFAD66", "as"), - const { SymbolKind::MACRO.0 as usize } => ("#f28f74", "! "), - const { SymbolKind::PROC_MACRO.0 as usize } => ("#f28f74", "r!"), - const { SymbolKind::BOOKMARK.0 as usize } => ("#73D0FF", " "), + // const { SymbolKind::Macro.0 as usize } => ("#f28f74", "! "), + // const { SymbolKind::ProcMacro.0 as usize } => ("#f28f74", "r!"), + // const { SymbolKind::Bookmark.0 as usize } => ("#73D0FF", " "), _ => ("#9a9b9a", " ") }) .map( const |(x, y)| (set_a(color_(x), 0.5), color_(x), y), ) }; - let (bgt, col, ty) = MAP[x.kind.0 as usize]; + let (bgt, col, ty) = MAP[i(x.kind)]; b.iter_mut().zip(ty.chars()).for_each(|(x, c)| { *x = (Style::new(col, bgt) | Style::BOLD).basic(c) }); diff --git a/src/text.rs b/src/text.rs index f3677b0..59dd5ae 100644 --- a/src/text.rs +++ b/src/text.rs @@ -16,14 +16,14 @@ use helix_core::syntax::{HighlightEvent, Loader}; use implicit_fn::implicit_fn; use itertools::Itertools; use lsp_types::{ - DocumentSymbol, Location, SemanticTokensLegend, SnippetTextEdit, - TextEdit, + DocumentSymbol, Edit, Location, SemanticTokensLegend, TextEdit, }; pub use manipulations::Manip; use rootcause::option_ext::OptionExt; use rootcause::prelude::{IteratorExt, ResultExt}; use rootcause::report; use ropey::{Rope, RopeSlice}; +use rust_analyzer::lsp::ext::SnippetTextEdit; use serde::{Deserialize, Serialize}; use tree_house::Language; pub mod cursor; @@ -358,7 +358,7 @@ impl TextArea { text: &'b mut Rope, ) -> rootcause::Result<()> { match insert_text_format { - Some(lsp_types::InsertTextFormat::SNIPPET) => { + Some(lsp_types::InsertTextFormat::Snippet) => { let begin = text.l_position(range.start).ok_or_report()?; let end = text.l_position(range.end).ok_or_report()?; text.try_remove(begin..end)?; @@ -380,7 +380,7 @@ impl TextArea { SnippetTextEdit { range,new_text, insert_text_format, .. }: &'b SnippetTextEdit, ) -> rootcause::Result<()> { match insert_text_format { - Some(lsp_types::InsertTextFormat::SNIPPET) => self + Some(lsp_types::InsertTextFormat::Snippet) => self .apply_snippet(&TextEdit { range: range.clone(), new_text: new_text.clone(), @@ -523,13 +523,11 @@ impl TextArea { Some(x) => { self.cursor.one(Cursor::new(x.r().end, &self.rope)); } - None => { - self.cursor.one(Cursor::new( - x.last.clone().unwrap().r().end, - &self.rope, - )); + None if let Some(x) = &x.last => { + self.cursor.one(Cursor::new(x.r().end, &self.rope)); self.tabstops = None; } + None => self.tabstops = None, }, } } @@ -1449,12 +1447,21 @@ fn apply2() { pub trait SortTedits { fn sort_tedits(&mut self); } +impl SortTedits for [Edit] { + fn sort_tedits(&mut self) { + self.as_mut().sort_by_key(|t| match t { + Edit::TextEdit(t) => Reverse(t.range.start), + Edit::AnnotatedTextEdit(t) => Reverse(t.text_edit.range.start), + Edit::SnippetTextEdit(t) => Reverse(t.range.start), + }); + } +} impl SortTedits for [TextEdit] { fn sort_tedits(&mut self) { self.as_mut().sort_by_key(|t| Reverse(t.range.start)); } } -impl SortTedits for [SnippetTextEdit] { +impl SortTedits for [rust_analyzer::lsp::ext::SnippetTextEdit] { fn sort_tedits(&mut self) { self.as_mut().sort_by_key(|t| Reverse(t.range.start)); } @@ -1462,13 +1469,13 @@ impl SortTedits for [SnippetTextEdit] { #[test] fn inlays() { - use lsp_types::{InlayHint, InlayHintLabel}; + use lsp_types::InlayHint; let mut t = TextArea::default(); _ = t.insert("let x = 4;"); t.set_inlay(&[InlayHint { position: lsp_types::Position { line: 0, character: 4 }, - label: InlayHintLabel::String("u".into()), - kind: Some(lsp_types::InlayHintKind::TYPE), + label: lsp_types::Label::String("u".into()), + kind: Some(lsp_types::InlayHintKind::Type), text_edits: None, tooltip: None, padding_left: None, diff --git a/src/text/hist.rs b/src/text/hist.rs index df678d2..05a5bcb 100644 --- a/src/text/hist.rs +++ b/src/text/hist.rs @@ -224,7 +224,7 @@ impl Hist { t.cursor .inner .iter_mut() - .for_each(|x| x.position = x.position.max(l)); + .for_each(|x| x.position = x.position.min(l)); r } pub fn redo(&mut self, t: &mut TextArea) -> ropey::Result<Option<()>> { diff --git a/src/text/inlay.rs b/src/text/inlay.rs index 6712069..049aad7 100644 --- a/src/text/inlay.rs +++ b/src/text/inlay.rs @@ -1,5 +1,5 @@ use Default::default; -use lsp_types::{InlayHint, InlayHintLabel, Location}; +use lsp_types::{InlayHint, Label, Location}; use serde_derive::{Deserialize, Serialize}; use crate::text::{RopeExt, TextArea}; @@ -39,9 +39,9 @@ impl TextArea { .iter() .filter_map(|i| { let mut label = match &i.label { - InlayHintLabel::String(x) => + Label::String(x) => x.chars().map(|x| (x, None)).collect::<Vec<_>>(), - InlayHintLabel::LabelParts(v) => v + Label::InlayHintLabelPartList(v) => v .iter() .flat_map(|x| { x.value |