diff --git a/Cargo.lock b/Cargo.lock index 4f86003e..df7e724e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -832,6 +832,17 @@ dependencies = [ "slab", ] +[[package]] +name = "gen-lsp-types" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "946cd448051e8061e4e17d88ecaceea8d84c33da52b93a9391b4a657def1b8bd" +dependencies = [ + "serde", + "serde_json", + "url", +] + [[package]] name = "generic-array" version = "0.14.9" @@ -1385,19 +1396,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "lsp-types" -version = "0.95.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e34d33a8e9b006cd3fc4fe69a921affa097bae4bb65f76271f4644f9a334365" -dependencies = [ - "bitflags 1.3.2", - "serde", - "serde_json", - "serde_repr", - "url", -] - [[package]] name = "memchr" version = "2.8.0" @@ -2249,14 +2247,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.143" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", "memchr", - "ryu", "serde", + "serde_core", + "zmij", ] [[package]] @@ -2268,17 +2267,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_repr" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2448,7 +2436,6 @@ dependencies = [ "line-index", "log", "lsp-server", - "lsp-types", "rayon", "serde", "serde_json", @@ -2556,11 +2543,11 @@ dependencies = [ "anyhow", "crossbeam-channel", "etcetera", + "gen-lsp-types", "insta", "line-index", "log", "lsp-server", - "lsp-types", "rowan", "rustc-hash 2.1.1", "salsa", @@ -2573,6 +2560,7 @@ dependencies = [ "squawk-syntax", "squawk-thread", "tracing", + "url", ] [[package]] @@ -2975,14 +2963,15 @@ checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "url" -version = "2.5.7" +version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", "idna", "percent-encoding", "serde", + "serde_derive", ] [[package]] @@ -3602,3 +3591,9 @@ dependencies = [ "quote", "syn 2.0.106", ] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index 7257aab2..aefc67cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ jiff = "0.2.24" itertools = "0.14.0" line-index = "0.1.2" lsp-server = "0.7.8" -lsp-types = "0.95" +gen-lsp-types = { version = "0.9", features = ["url"] } serde-wasm-bindgen = "0.6.5" wasm-bindgen = "0.2.114" wasm-bindgen-test = "0.3.50" diff --git a/crates/squawk/Cargo.toml b/crates/squawk/Cargo.toml index 36e7f063..de5f75b1 100644 --- a/crates/squawk/Cargo.toml +++ b/crates/squawk/Cargo.toml @@ -39,7 +39,6 @@ anyhow.workspace = true annotate-snippets.workspace = true line-index.workspace = true lsp-server.workspace = true -lsp-types.workspace = true [dev-dependencies] insta.workspace = true diff --git a/crates/squawk_server/Cargo.toml b/crates/squawk_server/Cargo.toml index ff6e2cbe..14e77ea0 100644 --- a/crates/squawk_server/Cargo.toml +++ b/crates/squawk_server/Cargo.toml @@ -18,7 +18,8 @@ anyhow.workspace = true log.workspace = true simplelog.workspace = true lsp-server.workspace = true -lsp-types.workspace = true +gen-lsp-types.workspace = true +url.workspace = true rowan.workspace = true salsa.workspace = true serde.workspace = true diff --git a/crates/squawk_server/src/diagnostic.rs b/crates/squawk_server/src/diagnostic.rs index de5ae2ce..a643430a 100644 --- a/crates/squawk_server/src/diagnostic.rs +++ b/crates/squawk_server/src/diagnostic.rs @@ -11,9 +11,9 @@ pub(crate) struct AssociatedDiagnosticData { pub(crate) title: String, /// Edits to fix the diagnostic. If this is empty, a fix /// does not exist. - pub(crate) edits: Vec, + pub(crate) edits: Vec, /// Edit to ignore the rule the line - pub(crate) ignore_line_edit: Option, + pub(crate) ignore_line_edit: Option, /// Edit to ignore the rule for the file - pub(crate) ignore_file_edit: Option, + pub(crate) ignore_file_edit: Option, } diff --git a/crates/squawk_server/src/dispatch.rs b/crates/squawk_server/src/dispatch.rs index a2865c4c..76695252 100644 --- a/crates/squawk_server/src/dispatch.rs +++ b/crates/squawk_server/src/dispatch.rs @@ -3,9 +3,9 @@ use std::panic::UnwindSafe; use anyhow::Result; +use gen_lsp_types::{Notification as LspNotification, Request as LspRequest}; use log::{error, info}; use lsp_server::{Request, Response}; -use lsp_types::{notification::Notification as LspNotification, request::Request as LspRequest}; use salsa::Cancelled; use serde::{Serialize, de::DeserializeOwned}; use squawk_thread::ThreadIntent; @@ -32,9 +32,11 @@ impl<'a> RequestDispatcher<'a> { where R: LspRequest, { - let req = self.req.take_if(|req| req.method.as_str() == R::METHOD)?; + let req = self + .req + .take_if(|req| req.method.as_str() == R::METHOD.as_str())?; let id = req.id.clone(); - match from_json(R::METHOD, &req.params) { + match from_json(R::METHOD.as_str(), &req.params) { Ok(params) => Some((req, params)), Err(err) => { let response = Response::new_err( @@ -150,7 +152,7 @@ fn thread_result_to_response( result: Result, PanicError>, ) -> Result where - R: lsp_types::request::Request, + R: gen_lsp_types::Request, R::Params: DeserializeOwned, R::Result: Serialize, { @@ -186,7 +188,7 @@ fn result_to_response( result: anyhow::Result, ) -> std::result::Result where - R: lsp_types::request::Request, + R: gen_lsp_types::Request, { match result { Ok(resp) => Ok(lsp_server::Response::new_ok(id, &resp)), @@ -230,9 +232,9 @@ impl<'a> NotificationDispatcher<'a> { { let notif = self .notif - .take_if(|notif| notif.method.as_str() == N::METHOD)?; + .take_if(|notif| notif.method.as_str() == N::METHOD.as_str())?; - match notif.extract(N::METHOD) { + match notif.extract(N::METHOD.as_str()) { Ok(params) => Some(params), Err(err) => { error!("Failed to parse notification params: {err}"); diff --git a/crates/squawk_server/src/global_state.rs b/crates/squawk_server/src/global_state.rs index 84a29e29..874a1ca5 100644 --- a/crates/squawk_server/src/global_state.rs +++ b/crates/squawk_server/src/global_state.rs @@ -1,23 +1,24 @@ use std::{num::NonZeroUsize, sync::Arc, time::Instant}; use crossbeam_channel::{Receiver, Sender, select, unbounded}; +use gen_lsp_types::Notification as _; +use gen_lsp_types::{ + CancelNotification, DidChangeTextDocumentNotification, DidCloseTextDocumentNotification, + DidOpenTextDocumentNotification, ExitNotification, +}; use log::info; use lsp_server::{Message, Request, Response}; -use lsp_types::Url; -use lsp_types::notification::Notification as _; -use lsp_types::notification::{ - Cancel, DidChangeTextDocument, DidCloseTextDocument, DidOpenTextDocument, -}; use rustc_hash::FxHashMap; use salsa::Setter; use squawk_ide::builtins::{builtins_file, builtins_url}; use squawk_ide::db::{Database, File}; use squawk_thread::TaskPool; +use url::Url; -use lsp_types::request::{ - CodeActionRequest, Completion, DocumentDiagnosticRequest, DocumentSymbolRequest, - FoldingRangeRequest, GotoDefinition, HoverRequest, InlayHintRequest, References, - SelectionRangeRequest, SemanticTokensFullRequest, SemanticTokensRangeRequest, Shutdown, +use gen_lsp_types::{ + CodeActionRequest, CompletionRequest, DefinitionRequest, DocumentDiagnosticRequest, + DocumentSymbolRequest, FoldingRangeRequest, HoverRequest, InlayHintRequest, ReferencesRequest, + SelectionRangeRequest, SemanticTokensRangeRequest, SemanticTokensRequest, ShutdownRequest, }; use crate::dispatch::{NotificationDispatcher, RequestDispatcher}; @@ -161,15 +162,15 @@ impl GlobalState { Message::Notification(notif) => { info!("Received notification: method={}", notif.method); - if notif.method == lsp_types::notification::Exit::METHOD { + if notif.method == ExitNotification::METHOD.as_str() { return Ok(()); } NotificationDispatcher::new(notif, self) - .on::(handle_cancel)? - .on::(handle_did_open)? - .on::(handle_did_change)? - .on::(handle_did_close)? + .on::(handle_cancel)? + .on::(handle_did_open)? + .on::(handle_did_change)? + .on::(handle_did_close)? .finish(); } }, @@ -229,14 +230,14 @@ impl GlobalState { RequestDispatcher::new(req, self) // Request handlers that must run on the main thread because they // mutate GlobalState: - .on_sync_mut::(handle_shutdown) + .on_sync_mut::(handle_shutdown) // Request handlers which are related to the user typing are run on // the main thread to reduce latency: .on_sync::(handle_selection_range) // latency-sensitive but can't run on the main thread due to // semantic analysis, so we use a higher priority thread - .on_latency_sensitive::(handle_completion) - .on::(handle_goto_definition) + .on_latency_sensitive::(handle_completion) + .on::(handle_goto_definition) .on::(handle_hover) .on::(handle_code_action) .on::(handle_inlay_hints) @@ -245,8 +246,8 @@ impl GlobalState { .on::(handle_document_diagnostic) .on::(handle_syntax_tree) .on::(handle_tokens) - .on::(handle_references) - .on::(handle_semantic_tokens_full) + .on::(handle_references) + .on::(handle_semantic_tokens_full) .on::(handle_semantic_tokens_range) .finish(); } diff --git a/crates/squawk_server/src/handlers/code_action.rs b/crates/squawk_server/src/handlers/code_action.rs index 89a2086e..9ff9fa2f 100644 --- a/crates/squawk_server/src/handlers/code_action.rs +++ b/crates/squawk_server/src/handlers/code_action.rs @@ -1,7 +1,6 @@ use anyhow::{Context, Result}; -use lsp_types::{ - CodeAction, CodeActionKind, CodeActionOrCommand, CodeActionParams, CodeActionResponse, Command, - WorkspaceEdit, +use gen_lsp_types::{ + Code, CodeAction, CodeActionKind, CodeActionParams, CodeActionResponse, Command, WorkspaceEdit, }; use rustc_hash::FxHashMap; use squawk_ide::code_actions::code_actions; @@ -14,10 +13,10 @@ use crate::lsp_utils; pub(crate) fn handle_code_action( snapshot: &Snapshot, params: CodeActionParams, -) -> Result> { +) -> Result>> { let uri = params.text_document.uri; - let mut actions: CodeActionResponse = vec![]; + let mut actions: Vec = vec![]; let db = snapshot.db(); let file = snapshot.file(&uri).unwrap(); @@ -28,7 +27,7 @@ pub(crate) fn handle_code_action( for action in ide_actions { let lsp_action = lsp_utils::code_action(&line_index, uri.clone(), action); - actions.push(CodeActionOrCommand::CodeAction(lsp_action)); + actions.push(CodeActionResponse::CodeAction(lsp_action)); } for mut diagnostic in params @@ -38,8 +37,8 @@ pub(crate) fn handle_code_action( .filter(|diagnostic| diagnostic.source.as_deref() == Some(DIAGNOSTIC_NAME)) { let Some(rule_name) = diagnostic.code.as_ref().map(|x| match x { - lsp_types::NumberOrString::String(s) => s.clone(), - lsp_types::NumberOrString::Number(n) => n.to_string(), + Code::String(s) => s.clone(), + Code::Int(n) => n.to_string(), }) else { continue; }; @@ -53,39 +52,46 @@ pub(crate) fn handle_code_action( if let Some(ignore_line_edit) = associated_data.ignore_line_edit { let disable_line_action = CodeAction { title: format!("Disable {rule_name} for this line"), - kind: Some(CodeActionKind::QUICKFIX), + kind: Some(CodeActionKind::QuickFix), diagnostics: Some(vec![diagnostic.clone()]), - edit: Some(WorkspaceEdit::new({ - let mut changes = FxHashMap::default(); - changes.insert(uri.clone(), vec![ignore_line_edit]); - changes.into_iter().collect() - })), + edit: Some(WorkspaceEdit { + changes: Some({ + let mut changes = FxHashMap::default(); + changes.insert(uri.clone(), vec![ignore_line_edit]); + changes.into_iter().collect() + }), + ..Default::default() + }), ..Default::default() }; - actions.push(CodeActionOrCommand::CodeAction(disable_line_action)); + actions.push(CodeActionResponse::CodeAction(disable_line_action)); } if let Some(ignore_file_edit) = associated_data.ignore_file_edit { let disable_file_action = CodeAction { title: format!("Disable {rule_name} for the entire file"), - kind: Some(CodeActionKind::QUICKFIX), + kind: Some(CodeActionKind::QuickFix), diagnostics: Some(vec![diagnostic.clone()]), - edit: Some(WorkspaceEdit::new({ - let mut changes = FxHashMap::default(); - changes.insert(uri.clone(), vec![ignore_file_edit]); - changes.into_iter().collect() - })), + edit: Some(WorkspaceEdit { + changes: Some({ + let mut changes = FxHashMap::default(); + changes.insert(uri.clone(), vec![ignore_file_edit]); + changes.into_iter().collect() + }), + ..Default::default() + }), ..Default::default() }; - actions.push(CodeActionOrCommand::CodeAction(disable_file_action)); + actions.push(CodeActionResponse::CodeAction(disable_file_action)); } let title = format!("Show documentation for {rule_name}"); let documentation_action = CodeAction { title: title.clone(), - kind: Some(CodeActionKind::QUICKFIX), + kind: Some(CodeActionKind::QuickFix), diagnostics: Some(vec![diagnostic.clone()]), command: Some(Command { title, + tooltip: None, command: "vscode.open".to_string(), arguments: Some(vec![serde_json::to_value(format!( "https://squawkhq.com/docs/{rule_name}" @@ -93,22 +99,25 @@ pub(crate) fn handle_code_action( }), ..Default::default() }; - actions.push(CodeActionOrCommand::CodeAction(documentation_action)); + actions.push(CodeActionResponse::CodeAction(documentation_action)); if !associated_data.title.is_empty() && !associated_data.edits.is_empty() { let fix_action = CodeAction { title: associated_data.title, - kind: Some(CodeActionKind::QUICKFIX), + kind: Some(CodeActionKind::QuickFix), diagnostics: Some(vec![diagnostic.clone()]), - edit: Some(WorkspaceEdit::new({ - let mut changes = FxHashMap::default(); - changes.insert(uri.clone(), associated_data.edits); - changes.into_iter().collect() - })), + edit: Some(WorkspaceEdit { + changes: Some({ + let mut changes = FxHashMap::default(); + changes.insert(uri.clone(), associated_data.edits); + changes.into_iter().collect() + }), + ..Default::default() + }), is_preferred: Some(true), ..Default::default() }; - actions.push(CodeActionOrCommand::CodeAction(fix_action)); + actions.push(CodeActionResponse::CodeAction(fix_action)); } } diff --git a/crates/squawk_server/src/handlers/completion.rs b/crates/squawk_server/src/handlers/completion.rs index b757d810..a11723d6 100644 --- a/crates/squawk_server/src/handlers/completion.rs +++ b/crates/squawk_server/src/handlers/completion.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use lsp_types::{CompletionParams, CompletionResponse}; +use gen_lsp_types::{CompletionParams, CompletionResponse}; use squawk_ide::completion::completion; use crate::global_state::Snapshot; @@ -9,14 +9,14 @@ pub(crate) fn handle_completion( snapshot: &Snapshot, params: CompletionParams, ) -> Result> { - let uri = params.text_document_position.text_document.uri; - let position = params.text_document_position.position; + let uri = params.text_document_position_params.text_document.uri; + let position = params.text_document_position_params.position; let db = snapshot.db(); let file = snapshot.file(&uri).unwrap(); let Some(position) = lsp_utils::offset(db, file, position) else { - return Ok(Some(CompletionResponse::Array(vec![]))); + return Ok(Some(CompletionResponse::CompletionItemList(vec![]))); }; let completion_items = completion(db, position) @@ -24,5 +24,7 @@ pub(crate) fn handle_completion( .map(lsp_utils::completion_item) .collect(); - Ok(Some(CompletionResponse::Array(completion_items))) + Ok(Some(CompletionResponse::CompletionItemList( + completion_items, + ))) } diff --git a/crates/squawk_server/src/handlers/diagnostic.rs b/crates/squawk_server/src/handlers/diagnostic.rs index 70e981c6..5c503177 100644 --- a/crates/squawk_server/src/handlers/diagnostic.rs +++ b/crates/squawk_server/src/handlers/diagnostic.rs @@ -1,7 +1,7 @@ use anyhow::Result; -use lsp_types::{ - DocumentDiagnosticParams, DocumentDiagnosticReport, DocumentDiagnosticReportResult, - FullDocumentDiagnosticReport, RelatedFullDocumentDiagnosticReport, +use gen_lsp_types::{ + DocumentDiagnosticParams, DocumentDiagnosticReport, FullDocumentDiagnosticReport, + RelatedFullDocumentDiagnosticReport, }; use crate::global_state::Snapshot; @@ -9,7 +9,7 @@ use crate::global_state::Snapshot; pub(crate) fn handle_document_diagnostic( snapshot: &Snapshot, params: DocumentDiagnosticParams, -) -> Result { +) -> Result { let uri = params.text_document.uri; let diagnostics = snapshot @@ -17,13 +17,15 @@ pub(crate) fn handle_document_diagnostic( .map(|file| crate::lint::lint(snapshot.db(), file)) .unwrap_or_default(); - Ok(DocumentDiagnosticReportResult::Report( - DocumentDiagnosticReport::Full(RelatedFullDocumentDiagnosticReport { - related_documents: None, - full_document_diagnostic_report: FullDocumentDiagnosticReport { - result_id: None, - items: diagnostics, + Ok( + DocumentDiagnosticReport::RelatedFullDocumentDiagnosticReport( + RelatedFullDocumentDiagnosticReport { + related_documents: None, + full_document_diagnostic_report: FullDocumentDiagnosticReport { + result_id: None, + items: diagnostics, + }, }, - }), - )) + ), + ) } diff --git a/crates/squawk_server/src/handlers/document_symbol.rs b/crates/squawk_server/src/handlers/document_symbol.rs index b92f55bc..ee3c6f34 100644 --- a/crates/squawk_server/src/handlers/document_symbol.rs +++ b/crates/squawk_server/src/handlers/document_symbol.rs @@ -1,6 +1,6 @@ use ::line_index::LineIndex; use anyhow::Result; -use lsp_types::{DocumentSymbol, DocumentSymbolParams, DocumentSymbolResponse, SymbolKind}; +use gen_lsp_types::{DocumentSymbol, DocumentSymbolParams, DocumentSymbolResponse, SymbolKind}; use squawk_ide::db::line_index; use squawk_ide::document_symbols::{DocumentSymbolKind, document_symbols}; @@ -38,33 +38,33 @@ pub(crate) fn handle_document_symbol( name: sym.name, detail: sym.detail, kind: match sym.kind { - DocumentSymbolKind::Schema => SymbolKind::NAMESPACE, - DocumentSymbolKind::Table => SymbolKind::STRUCT, - DocumentSymbolKind::View => SymbolKind::STRUCT, - DocumentSymbolKind::MaterializedView => SymbolKind::STRUCT, - DocumentSymbolKind::Function => SymbolKind::FUNCTION, - DocumentSymbolKind::Aggregate => SymbolKind::FUNCTION, - DocumentSymbolKind::Procedure => SymbolKind::FUNCTION, - DocumentSymbolKind::Type => SymbolKind::CLASS, - DocumentSymbolKind::Enum => SymbolKind::ENUM, - DocumentSymbolKind::Index => SymbolKind::KEY, - DocumentSymbolKind::Domain => SymbolKind::CLASS, - DocumentSymbolKind::Sequence => SymbolKind::CONSTANT, - DocumentSymbolKind::Trigger => SymbolKind::EVENT, - DocumentSymbolKind::Tablespace => SymbolKind::NAMESPACE, - DocumentSymbolKind::Database => SymbolKind::MODULE, - DocumentSymbolKind::Server => SymbolKind::OBJECT, - DocumentSymbolKind::Extension => SymbolKind::PACKAGE, - DocumentSymbolKind::Column => SymbolKind::FIELD, - DocumentSymbolKind::Variant => SymbolKind::ENUM_MEMBER, - DocumentSymbolKind::Cursor => SymbolKind::VARIABLE, - DocumentSymbolKind::PreparedStatement => SymbolKind::VARIABLE, - DocumentSymbolKind::Channel => SymbolKind::EVENT, - DocumentSymbolKind::EventTrigger => SymbolKind::EVENT, - DocumentSymbolKind::Role => SymbolKind::CLASS, - DocumentSymbolKind::Rule => SymbolKind::EVENT, - DocumentSymbolKind::Policy => SymbolKind::VARIABLE, - DocumentSymbolKind::PropertyGraph => SymbolKind::STRUCT, + DocumentSymbolKind::Schema => SymbolKind::Namespace, + DocumentSymbolKind::Table => SymbolKind::Struct, + DocumentSymbolKind::View => SymbolKind::Struct, + DocumentSymbolKind::MaterializedView => SymbolKind::Struct, + DocumentSymbolKind::Function => SymbolKind::Function, + DocumentSymbolKind::Aggregate => SymbolKind::Function, + DocumentSymbolKind::Procedure => SymbolKind::Function, + DocumentSymbolKind::Type => SymbolKind::Class, + DocumentSymbolKind::Enum => SymbolKind::Enum, + DocumentSymbolKind::Index => SymbolKind::Key, + DocumentSymbolKind::Domain => SymbolKind::Class, + DocumentSymbolKind::Sequence => SymbolKind::Constant, + DocumentSymbolKind::Trigger => SymbolKind::Event, + DocumentSymbolKind::Tablespace => SymbolKind::Namespace, + DocumentSymbolKind::Database => SymbolKind::Module, + DocumentSymbolKind::Server => SymbolKind::Object, + DocumentSymbolKind::Extension => SymbolKind::Package, + DocumentSymbolKind::Column => SymbolKind::Field, + DocumentSymbolKind::Variant => SymbolKind::EnumMember, + DocumentSymbolKind::Cursor => SymbolKind::Variable, + DocumentSymbolKind::PreparedStatement => SymbolKind::Variable, + DocumentSymbolKind::Channel => SymbolKind::Event, + DocumentSymbolKind::EventTrigger => SymbolKind::Event, + DocumentSymbolKind::Role => SymbolKind::Class, + DocumentSymbolKind::Rule => SymbolKind::Event, + DocumentSymbolKind::Policy => SymbolKind::Variable, + DocumentSymbolKind::PropertyGraph => SymbolKind::Struct, }, tags: None, range, @@ -80,5 +80,7 @@ pub(crate) fn handle_document_symbol( .map(|sym| convert_symbol(sym, &line_index)) .collect(); - Ok(Some(DocumentSymbolResponse::Nested(lsp_symbols))) + Ok(Some(DocumentSymbolResponse::DocumentSymbolList( + lsp_symbols, + ))) } diff --git a/crates/squawk_server/src/handlers/folding_range.rs b/crates/squawk_server/src/handlers/folding_range.rs index de0d0e25..be0d5b80 100644 --- a/crates/squawk_server/src/handlers/folding_range.rs +++ b/crates/squawk_server/src/handlers/folding_range.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use lsp_types::{FoldingRange, FoldingRangeParams}; +use gen_lsp_types::{FoldingRange, FoldingRangeParams}; use squawk_ide::db::line_index; use squawk_ide::folding_ranges::folding_ranges; diff --git a/crates/squawk_server/src/handlers/goto_definition.rs b/crates/squawk_server/src/handlers/goto_definition.rs index 1809dceb..384fc7c8 100644 --- a/crates/squawk_server/src/handlers/goto_definition.rs +++ b/crates/squawk_server/src/handlers/goto_definition.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use lsp_types::{GotoDefinitionParams, GotoDefinitionResponse}; +use gen_lsp_types::{Definition, DefinitionParams, DefinitionResponse}; use squawk_ide::goto_definition::goto_definition; use crate::global_state::Snapshot; @@ -7,8 +7,8 @@ use crate::lsp_utils::{self, to_location}; pub(crate) fn handle_goto_definition( snapshot: &Snapshot, - params: GotoDefinitionParams, -) -> Result> { + params: DefinitionParams, +) -> Result> { let uri = params.text_document_position_params.text_document.uri; let position = params.text_document_position_params.position; @@ -27,5 +27,7 @@ pub(crate) fn handle_goto_definition( }) .collect(); - Ok(Some(GotoDefinitionResponse::Array(ranges))) + Ok(Some(DefinitionResponse::Definition( + Definition::LocationList(ranges), + ))) } diff --git a/crates/squawk_server/src/handlers/hover.rs b/crates/squawk_server/src/handlers/hover.rs index 8e8bb0a5..c0e90446 100644 --- a/crates/squawk_server/src/handlers/hover.rs +++ b/crates/squawk_server/src/handlers/hover.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use lsp_types::{Hover, HoverContents, HoverParams, MarkedString}; +use gen_lsp_types::{Contents, Hover, HoverParams, MarkupContent, MarkupKind}; use squawk_ide::hover::hover; use crate::global_state::Snapshot; @@ -16,7 +16,10 @@ pub(crate) fn handle_hover(snapshot: &Snapshot, params: HoverParams) -> Result InlayHintKind::PARAMETER, - squawk_ide::inlay_hints::InlayHintKind::Type => InlayHintKind::TYPE, + squawk_ide::inlay_hints::InlayHintKind::Parameter => InlayHintKind::Parameter, + squawk_ide::inlay_hints::InlayHintKind::Type => InlayHintKind::Type, }; let label = if let Some(target) = hint.target { let target_uri = snapshot.uri(target.file_id)?; let target_line_index = line_index(db, target.file_id); - InlayHintLabel::LabelParts(vec![InlayHintLabelPart { + Label::InlayHintLabelPartList(vec![InlayHintLabelPart { value: hint.label, location: Some(Location { uri: target_uri, @@ -44,7 +44,7 @@ pub(crate) fn handle_inlay_hints( command: None, }]) } else { - InlayHintLabel::String(hint.label) + Label::String(hint.label) }; Some(InlayHint { diff --git a/crates/squawk_server/src/handlers/notifications.rs b/crates/squawk_server/src/handlers/notifications.rs index 10207a3c..e2d0f200 100644 --- a/crates/squawk_server/src/handlers/notifications.rs +++ b/crates/squawk_server/src/handlers/notifications.rs @@ -1,18 +1,18 @@ use anyhow::Result; -use lsp_server::{Message, Notification}; -use lsp_types::{ +use gen_lsp_types::{ CancelParams, DidChangeTextDocumentParams, DidCloseTextDocumentParams, - DidOpenTextDocumentParams, PublishDiagnosticsParams, - notification::{Notification as _, PublishDiagnostics}, + DidOpenTextDocumentParams, Id, Notification as _, PublishDiagnosticsNotification, + PublishDiagnosticsParams, }; +use lsp_server::{Message, Notification}; use crate::global_state::GlobalState; use crate::lsp_utils; pub(crate) fn handle_cancel(state: &mut GlobalState, params: CancelParams) -> Result<()> { let id: lsp_server::RequestId = match params.id { - lsp_types::NumberOrString::Number(id) => id.into(), - lsp_types::NumberOrString::String(id) => id.into(), + Id::Int(id) => id.into(), + Id::String(id) => id.into(), }; state.cancel(id); Ok(()) @@ -34,7 +34,7 @@ pub(crate) fn handle_did_change( state: &mut GlobalState, params: DidChangeTextDocumentParams, ) -> Result<()> { - let uri = params.text_document.uri; + let uri = params.text_document.text_document_identifier.uri; let db = state.db(); let file = state.file(&uri).unwrap(); @@ -62,7 +62,7 @@ pub(crate) fn handle_did_close( }; let notification = Notification { - method: PublishDiagnostics::METHOD.to_owned(), + method: PublishDiagnosticsNotification::METHOD.to_string(), params: serde_json::to_value(publish_params)?, }; diff --git a/crates/squawk_server/src/handlers/references.rs b/crates/squawk_server/src/handlers/references.rs index 9124c000..905b798e 100644 --- a/crates/squawk_server/src/handlers/references.rs +++ b/crates/squawk_server/src/handlers/references.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use lsp_types::{Location, ReferenceParams}; +use gen_lsp_types::{Location, ReferenceParams}; use squawk_ide::find_references::find_references; use crate::global_state::Snapshot; @@ -9,8 +9,8 @@ pub(crate) fn handle_references( snapshot: &Snapshot, params: ReferenceParams, ) -> Result>> { - let uri = params.text_document_position.text_document.uri; - let position = params.text_document_position.position; + let uri = params.text_document_position_params.text_document.uri; + let position = params.text_document_position_params.position; let db = snapshot.db(); let file = snapshot.file(&uri).unwrap(); diff --git a/crates/squawk_server/src/handlers/selection_range.rs b/crates/squawk_server/src/handlers/selection_range.rs index 9376f629..80a21ec1 100644 --- a/crates/squawk_server/src/handlers/selection_range.rs +++ b/crates/squawk_server/src/handlers/selection_range.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use lsp_types::SelectionRangeParams; +use gen_lsp_types::SelectionRangeParams; use rowan::TextRange; use squawk_ide::db::{line_index, parse}; @@ -9,7 +9,7 @@ use crate::lsp_utils; pub(crate) fn handle_selection_range( snapshot: &Snapshot, params: SelectionRangeParams, -) -> Result>> { +) -> Result>> { let uri = params.text_document.uri; let db = snapshot.db(); @@ -40,12 +40,12 @@ pub(crate) fn handle_selection_range( } } - let mut range = lsp_types::SelectionRange { + let mut range = gen_lsp_types::SelectionRange { range: lsp_utils::range(&line_index, *ranges.last().unwrap()), parent: None, }; for &r in ranges.iter().rev().skip(1) { - range = lsp_types::SelectionRange { + range = gen_lsp_types::SelectionRange { range: lsp_utils::range(&line_index, r), parent: Some(Box::new(range)), } diff --git a/crates/squawk_server/src/handlers/semantic_tokens.rs b/crates/squawk_server/src/handlers/semantic_tokens.rs index c61c0d63..29f62b61 100644 --- a/crates/squawk_server/src/handlers/semantic_tokens.rs +++ b/crates/squawk_server/src/handlers/semantic_tokens.rs @@ -1,8 +1,5 @@ use anyhow::Result; -use lsp_types::{ - SemanticTokens, SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, - SemanticTokensResult, -}; +use gen_lsp_types::{SemanticTokens, SemanticTokensParams, SemanticTokensRangeParams}; use squawk_ide::db::line_index; use squawk_ide::semantic_tokens::semantic_tokens; @@ -13,23 +10,23 @@ use crate::lsp_utils; pub(crate) fn handle_semantic_tokens_full( snapshot: &Snapshot, params: SemanticTokensParams, -) -> Result> { +) -> Result> { let uri = params.text_document.uri; let db = snapshot.db(); let file = snapshot.file(&uri).unwrap(); let line_index = line_index(db, file); let text = file.content(db); let tokens = semantic_tokens(db, file, None); - Ok(Some(SemanticTokensResult::Tokens(SemanticTokens { + Ok(Some(SemanticTokens { result_id: None, data: lsp_utils::to_semantic_tokens(text, line_index, tokens), - }))) + })) } pub(crate) fn handle_semantic_tokens_range( snapshot: &Snapshot, params: SemanticTokensRangeParams, -) -> Result> { +) -> Result> { let uri = params.text_document.uri; let db = snapshot.db(); let file = snapshot.file(&uri).unwrap(); @@ -37,8 +34,8 @@ pub(crate) fn handle_semantic_tokens_range( let range_to_highlight = lsp_utils::text_range(&line_index, params.range); let tokens = semantic_tokens(db, file, range_to_highlight); let text = file.content(db); - Ok(Some(SemanticTokensRangeResult::Tokens(SemanticTokens { + Ok(Some(SemanticTokens { result_id: None, data: lsp_utils::to_semantic_tokens(text, line_index, tokens), - }))) + })) } diff --git a/crates/squawk_server/src/handlers/syntax_tree.rs b/crates/squawk_server/src/handlers/syntax_tree.rs index 8b7e1d6f..0488da7d 100644 --- a/crates/squawk_server/src/handlers/syntax_tree.rs +++ b/crates/squawk_server/src/handlers/syntax_tree.rs @@ -1,6 +1,6 @@ use anyhow::Result; +use gen_lsp_types::{LspRequestMethod, MessageDirection, Request}; use log::info; -use lsp_types::request::Request; use squawk_ide::db::parse; use crate::global_state::Snapshot; @@ -8,7 +8,7 @@ use crate::global_state::Snapshot; #[derive(serde::Deserialize, serde::Serialize)] pub(crate) struct SyntaxTreeParams { #[serde(rename = "textDocument")] - text_document: lsp_types::TextDocumentIdentifier, + text_document: gen_lsp_types::TextDocumentIdentifier, } pub(crate) enum SyntaxTreeRequest {} @@ -16,7 +16,8 @@ pub(crate) enum SyntaxTreeRequest {} impl Request for SyntaxTreeRequest { type Params = SyntaxTreeParams; type Result = String; - const METHOD: &'static str = "squawk/syntaxTree"; + const METHOD: LspRequestMethod<'static> = LspRequestMethod::new("squawk/syntaxTree"); + const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } pub(crate) fn handle_syntax_tree(snapshot: &Snapshot, params: SyntaxTreeParams) -> Result { diff --git a/crates/squawk_server/src/handlers/tokens.rs b/crates/squawk_server/src/handlers/tokens.rs index 4fa2061b..112eb927 100644 --- a/crates/squawk_server/src/handlers/tokens.rs +++ b/crates/squawk_server/src/handlers/tokens.rs @@ -1,13 +1,13 @@ use anyhow::Result; +use gen_lsp_types::{LspRequestMethod, MessageDirection, Request}; use log::info; -use lsp_types::request::Request; use crate::global_state::Snapshot; #[derive(serde::Deserialize, serde::Serialize)] pub(crate) struct TokensParams { #[serde(rename = "textDocument")] - text_document: lsp_types::TextDocumentIdentifier, + text_document: gen_lsp_types::TextDocumentIdentifier, } pub(crate) enum TokensRequest {} @@ -15,7 +15,8 @@ pub(crate) enum TokensRequest {} impl Request for TokensRequest { type Params = TokensParams; type Result = String; - const METHOD: &'static str = "squawk/tokens"; + const METHOD: LspRequestMethod<'static> = LspRequestMethod::new("squawk/tokens"); + const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } pub(crate) fn handle_tokens(snapshot: &Snapshot, params: TokensParams) -> Result { diff --git a/crates/squawk_server/src/ignore.rs b/crates/squawk_server/src/ignore.rs index 1d0dc9ba..1dd9fe88 100644 --- a/crates/squawk_server/src/ignore.rs +++ b/crates/squawk_server/src/ignore.rs @@ -165,7 +165,7 @@ create table c ( "); } - fn apply_text_edits(sql: &str, mut edits: Vec) -> String { + fn apply_text_edits(sql: &str, mut edits: Vec) -> String { use line_index::{LineCol, LineIndex}; // Sort edits by position (reverse order to apply from end to start) @@ -200,7 +200,7 @@ create table c ( result } - fn lint_sql(sql: &str) -> Vec { + fn lint_sql(sql: &str) -> Vec { let db = Database::default(); let file = File::new(&db, sql.to_owned().into()); lint(&db, file) diff --git a/crates/squawk_server/src/lint.rs b/crates/squawk_server/src/lint.rs index 39d8340b..ee7d6ac3 100644 --- a/crates/squawk_server/src/lint.rs +++ b/crates/squawk_server/src/lint.rs @@ -1,8 +1,11 @@ use ::line_index::LineIndex; -use lsp_types::{CodeDescription, Diagnostic, DiagnosticSeverity, Position, Range, TextEdit, Url}; +use gen_lsp_types::{ + Code, CodeDescription, Diagnostic, DiagnosticSeverity, Message, Position, Range, TextEdit, +}; use salsa::Database as Db; use squawk_ide::db::{File, line_index as file_line_index, parse}; use squawk_linter::{Edit, Linter}; +use url::Url; use crate::{ diagnostic::{AssociatedDiagnosticData, DIAGNOSTIC_NAME}, @@ -45,15 +48,13 @@ pub(crate) fn lint(db: &dyn Db, file: File) -> Vec { Position::new(start_line_col.line, start_line_col.col), Position::new(end_line_col.line, end_line_col.col), ), - severity: Some(DiagnosticSeverity::ERROR), - code: Some(lsp_types::NumberOrString::String( - "syntax-error".to_string(), - )), + severity: Some(DiagnosticSeverity::Error), + code: Some(Code::String("syntax-error".to_string())), code_description: Some(CodeDescription { href: Url::parse("https://squawkhq.com/docs/syntax-error").unwrap(), }), source: Some(DIAGNOSTIC_NAME.to_string()), - message: error.message().to_string(), + message: Message::String(error.message().to_string()), ..Default::default() }; diagnostics.push(diagnostic); @@ -97,15 +98,13 @@ pub(crate) fn lint(db: &dyn Db, file: File) -> Vec { Position::new(start_line_col.line, start_line_col.col), Position::new(end_line_col.line, end_line_col.col), ), - severity: Some(DiagnosticSeverity::WARNING), - code: Some(lsp_types::NumberOrString::String( - violation.code.to_string(), - )), + severity: Some(DiagnosticSeverity::Warning), + code: Some(Code::String(violation.code.to_string())), code_description: Some(CodeDescription { href: Url::parse(&format!("https://squawkhq.com/docs/{}", violation.code)).unwrap(), }), source: Some(DIAGNOSTIC_NAME.to_string()), - message: violation.message, + message: Message::String(violation.message), data: Some(serde_json::to_value(data).unwrap()), ..Default::default() }; diff --git a/crates/squawk_server/src/lsp_utils.rs b/crates/squawk_server/src/lsp_utils.rs index 15e622ea..abd61d2e 100644 --- a/crates/squawk_server/src/lsp_utils.rs +++ b/crates/squawk_server/src/lsp_utils.rs @@ -3,22 +3,23 @@ use std::ops::Range; use rustc_hash::FxHashMap; use ::line_index::{LineIndex, TextRange, TextSize}; -use log::warn; -use lsp_types::{ +use gen_lsp_types::{ CodeAction, CodeActionKind, FoldingRange, FoldingRangeKind as LspFoldingRangeKind, Location, - SemanticToken, Url, WorkspaceEdit, + SemanticToken, TextDocumentContentChangeEvent, WorkspaceEdit, }; +use log::warn; use salsa::Database as Db; use squawk_ide::code_actions::ActionKind; use squawk_ide::db::{File, line_index}; use squawk_ide::file::InFile; use squawk_ide::folding_ranges::{Fold, FoldKind}; use squawk_ide::semantic_tokens::{SemanticTokenModifier, SemanticTokenType}; +use url::Url; use crate::global_state::Snapshot; use crate::semantic_tokens; -pub(crate) fn text_range(index: &LineIndex, range: lsp_types::Range) -> Option { +pub(crate) fn text_range(index: &LineIndex, range: gen_lsp_types::Range) -> Option { let start = text_size(index, range.start)?; let end = text_size(index, range.end)?; if end >= start { @@ -33,7 +34,7 @@ pub(crate) fn text_range(index: &LineIndex, range: lsp_types::Range) -> Option Option { +fn text_size(index: &LineIndex, position: gen_lsp_types::Position) -> Option { let line_range = index.line(position.line)?; let col = TextSize::from(position.character); @@ -54,7 +55,7 @@ fn text_size(index: &LineIndex, position: lsp_types::Position) -> Option Option> { let line_index = line_index(db, file); let offset = text_size(&line_index, position)?; @@ -65,28 +66,31 @@ pub(crate) fn code_action( line_index: &LineIndex, uri: Url, action: squawk_ide::code_actions::CodeAction, -) -> lsp_types::CodeAction { +) -> gen_lsp_types::CodeAction { let kind = match action.kind { - ActionKind::QuickFix => CodeActionKind::QUICKFIX, - ActionKind::RefactorRewrite => CodeActionKind::REFACTOR_REWRITE, + ActionKind::QuickFix => CodeActionKind::QuickFix, + ActionKind::RefactorRewrite => CodeActionKind::RefactorRewrite, }; CodeAction { title: action.title, kind: Some(kind), - edit: Some(WorkspaceEdit::new({ - let mut changes = FxHashMap::default(); - let edits = action - .edits - .into_iter() - .map(|edit| lsp_types::TextEdit { - range: range(line_index, edit.text_range), - new_text: edit.text.unwrap_or_default(), - }) - .collect(); - changes.insert(uri, edits); - changes.into_iter().collect() - })), + edit: Some(WorkspaceEdit { + changes: Some({ + let mut changes = FxHashMap::default(); + let edits = action + .edits + .into_iter() + .map(|edit| gen_lsp_types::TextEdit { + range: range(line_index, edit.text_range), + new_text: edit.text.unwrap_or_default(), + }) + .collect(); + changes.insert(uri, edits); + changes.into_iter().collect() + }), + ..Default::default() + }), is_preferred: Some(true), ..Default::default() } @@ -94,30 +98,31 @@ pub(crate) fn code_action( pub(crate) fn completion_item( item: squawk_ide::completion::CompletionItem, -) -> lsp_types::CompletionItem { +) -> gen_lsp_types::CompletionItem { use squawk_ide::completion::{CompletionInsertTextFormat, CompletionItemKind}; let kind = match item.kind { - CompletionItemKind::Schema => lsp_types::CompletionItemKind::MODULE, - CompletionItemKind::Keyword => lsp_types::CompletionItemKind::KEYWORD, - CompletionItemKind::Table => lsp_types::CompletionItemKind::STRUCT, - CompletionItemKind::Column => lsp_types::CompletionItemKind::FIELD, - CompletionItemKind::Function => lsp_types::CompletionItemKind::FUNCTION, - CompletionItemKind::Type => lsp_types::CompletionItemKind::CLASS, - CompletionItemKind::Snippet => lsp_types::CompletionItemKind::SNIPPET, - CompletionItemKind::Operator => lsp_types::CompletionItemKind::OPERATOR, + CompletionItemKind::Schema => gen_lsp_types::CompletionItemKind::Module, + CompletionItemKind::Keyword => gen_lsp_types::CompletionItemKind::Keyword, + CompletionItemKind::Table => gen_lsp_types::CompletionItemKind::Struct, + CompletionItemKind::Column => gen_lsp_types::CompletionItemKind::Field, + CompletionItemKind::Function => gen_lsp_types::CompletionItemKind::Function, + CompletionItemKind::Type => gen_lsp_types::CompletionItemKind::Class, + CompletionItemKind::Snippet => gen_lsp_types::CompletionItemKind::Snippet, + CompletionItemKind::Operator => gen_lsp_types::CompletionItemKind::Operator, }; let sort_text = Some(item.sort_text()); let insert_text_format = item.insert_text_format.map(|x| match x { - CompletionInsertTextFormat::PlainText => lsp_types::InsertTextFormat::PLAIN_TEXT, - CompletionInsertTextFormat::Snippet => lsp_types::InsertTextFormat::SNIPPET, + CompletionInsertTextFormat::PlainText => gen_lsp_types::InsertTextFormat::PlainText, + CompletionInsertTextFormat::Snippet => gen_lsp_types::InsertTextFormat::Snippet, }); let command = if item.trigger_completion_after_insert { - Some(lsp_types::Command { + Some(gen_lsp_types::Command { title: "Trigger Completion".to_owned(), + tooltip: None, command: "editor.action.triggerSuggest".to_owned(), arguments: None, }) @@ -127,14 +132,14 @@ pub(crate) fn completion_item( let label_details = item .detail - .map(|detail| lsp_types::CompletionItemLabelDetails { + .map(|detail| gen_lsp_types::CompletionItemLabelDetails { detail: None, // Use description instead of detail so VSCode puts it to the right // of the item's name instead of smushing them together. description: Some(detail), }); - lsp_types::CompletionItem { + gen_lsp_types::CompletionItem { label: item.label, kind: Some(kind), // We use label_details instead of detail so that VSCode shows the type @@ -150,13 +155,13 @@ pub(crate) fn completion_item( } } -pub(crate) fn range(line_index: &LineIndex, range: TextRange) -> lsp_types::Range { +pub(crate) fn range(line_index: &LineIndex, range: TextRange) -> gen_lsp_types::Range { let start = line_index.line_col(range.start()); let end = line_index.line_col(range.end()); - lsp_types::Range::new( - lsp_types::Position::new(start.line, start.col), - lsp_types::Position::new(end.line, end.col), + gen_lsp_types::Range::new( + gen_lsp_types::Position::new(start.line, start.col), + gen_lsp_types::Position::new(end.line, end.col), ) } @@ -181,19 +186,26 @@ pub(crate) fn folding_range(line_index: &LineIndex, fold: Fold) -> FoldingRange // see: https://github.com/rust-lang/rust-analyzer/blob/3816d0ae53c19fe75532a8b41d8c546d94246b53/crates/rust-analyzer/src/lsp/utils.rs#L168C1-L168C1 pub(crate) fn apply_incremental_changes( content: &str, - mut content_changes: Vec, + mut content_changes: Vec, ) -> String { // If at least one of the changes is a full document change, use the last // of them as the starting point and ignore all previous changes. - let (mut text, content_changes) = match content_changes - .iter() - .rposition(|change| change.range.is_none()) - { + let (mut text, content_changes) = match content_changes.iter().rposition(|change| { + matches!( + change, + TextDocumentContentChangeEvent::TextDocumentContentChangeWholeDocument(_) + ) + }) { Some(idx) => { - let text = std::mem::take(&mut content_changes[idx].text); - (text, &content_changes[idx + 1..]) + let tail = content_changes.split_off(idx + 1); + let TextDocumentContentChangeEvent::TextDocumentContentChangeWholeDocument(whole) = + content_changes.pop().expect("idx is a valid index") + else { + unreachable!("checked above via rposition") + }; + (whole.text, tail) } - None => (content.to_owned(), &content_changes[..]), + None => (content.to_owned(), content_changes), }; if content_changes.is_empty() { @@ -208,15 +220,18 @@ pub(crate) fn apply_incremental_changes( // remember the last valid line in the index and only rebuild it if needed. let mut index_valid = !0u32; for change in content_changes { - // The None case can't happen as we have handled it above already - if let Some(range) = change.range { - if index_valid <= range.end.line { - line_index = LineIndex::new(&text); - } - index_valid = range.start.line; - if let Some(range) = text_range(&line_index, range) { - text.replace_range(Range::::from(range), &change.text); - } + // The whole-document case can't happen as we have handled it above already + let TextDocumentContentChangeEvent::TextDocumentContentChangePartial(partial) = change + else { + continue; + }; + let range = partial.range; + if index_valid <= range.end.line { + line_index = LineIndex::new(&text); + } + index_valid = range.start.line; + if let Some(range) = text_range(&line_index, range) { + text.replace_range(Range::::from(range), &partial.text); } } @@ -239,7 +254,7 @@ pub(crate) fn to_semantic_tokens( text: &str, line_index: LineIndex, semantic_tokens: Vec, -) -> Vec { +) -> Vec { let mut encoder = Encoder { tokens: Vec::with_capacity(semantic_tokens.len()), prev_line: 0, @@ -276,7 +291,7 @@ struct Encoder { impl Encoder { fn push_token_at( &mut self, - start: lsp_types::Position, + start: gen_lsp_types::Position, length: u32, ty: SemanticTokenType, _modifiers: Option, @@ -307,34 +322,54 @@ impl Encoder { } } -fn to_token_type(ty: SemanticTokenType) -> lsp_types::SemanticTokenType { +fn to_token_type(ty: SemanticTokenType) -> gen_lsp_types::SemanticTokenTypes { match ty { - SemanticTokenType::Keyword => lsp_types::SemanticTokenType::KEYWORD, - SemanticTokenType::String => lsp_types::SemanticTokenType::STRING, - SemanticTokenType::Bool => lsp_types::SemanticTokenType::KEYWORD, - SemanticTokenType::Number => lsp_types::SemanticTokenType::NUMBER, - SemanticTokenType::Function => lsp_types::SemanticTokenType::FUNCTION, - SemanticTokenType::Operator => lsp_types::SemanticTokenType::OPERATOR, - SemanticTokenType::Punctuation => lsp_types::SemanticTokenType::OPERATOR, - SemanticTokenType::Name => lsp_types::SemanticTokenType::VARIABLE, - SemanticTokenType::NameRef => lsp_types::SemanticTokenType::VARIABLE, - SemanticTokenType::Comment => lsp_types::SemanticTokenType::COMMENT, - SemanticTokenType::Type => lsp_types::SemanticTokenType::TYPE, + SemanticTokenType::Keyword => gen_lsp_types::SemanticTokenTypes::Keyword, + SemanticTokenType::String => gen_lsp_types::SemanticTokenTypes::String, + SemanticTokenType::Bool => gen_lsp_types::SemanticTokenTypes::Keyword, + SemanticTokenType::Number => gen_lsp_types::SemanticTokenTypes::Number, + SemanticTokenType::Function => gen_lsp_types::SemanticTokenTypes::Function, + SemanticTokenType::Operator => gen_lsp_types::SemanticTokenTypes::Operator, + SemanticTokenType::Punctuation => gen_lsp_types::SemanticTokenTypes::Operator, + SemanticTokenType::Name => gen_lsp_types::SemanticTokenTypes::Variable, + SemanticTokenType::NameRef => gen_lsp_types::SemanticTokenTypes::Variable, + SemanticTokenType::Comment => gen_lsp_types::SemanticTokenTypes::Comment, + SemanticTokenType::Type => gen_lsp_types::SemanticTokenTypes::Type, SemanticTokenType::PositionalParam | SemanticTokenType::Parameter => { - lsp_types::SemanticTokenType::PARAMETER + gen_lsp_types::SemanticTokenTypes::Parameter } - SemanticTokenType::Column => lsp_types::SemanticTokenType::VARIABLE, + SemanticTokenType::Column => gen_lsp_types::SemanticTokenTypes::Variable, SemanticTokenType::PropertyGraph | SemanticTokenType::Table => { - lsp_types::SemanticTokenType::STRUCT + gen_lsp_types::SemanticTokenTypes::Struct } - SemanticTokenType::Schema => lsp_types::SemanticTokenType::NAMESPACE, + SemanticTokenType::Schema => gen_lsp_types::SemanticTokenTypes::Namespace, } } #[cfg(test)] mod tests { use super::*; - use lsp_types::{Position, Range, TextDocumentContentChangeEvent}; + use gen_lsp_types::{ + Position, Range, TextDocumentContentChangePartial, TextDocumentContentChangeWholeDocument, + }; + + fn partial_change(range: Range, text: &str) -> TextDocumentContentChangeEvent { + TextDocumentContentChangeEvent::TextDocumentContentChangePartial( + TextDocumentContentChangePartial { + range, + text: text.to_string(), + ..Default::default() + }, + ) + } + + fn whole_change(text: &str) -> TextDocumentContentChangeEvent { + TextDocumentContentChangeEvent::TextDocumentContentChangeWholeDocument( + TextDocumentContentChangeWholeDocument { + text: text.to_string(), + }, + ) + } #[test] fn apply_incremental_changes_no_changes() { @@ -347,11 +382,7 @@ mod tests { #[test] fn apply_incremental_changes_full_document_change() { let content = "old content"; - let changes = vec![TextDocumentContentChangeEvent { - range: None, - range_length: None, - text: "new content".to_string(), - }]; + let changes = vec![whole_change("new content")]; let result = apply_incremental_changes(content, changes); assert_eq!(result, "new content"); } @@ -359,11 +390,10 @@ mod tests { #[test] fn apply_incremental_changes_single_line_edit() { let content = "hello world"; - let changes = vec![TextDocumentContentChangeEvent { - range: Some(Range::new(Position::new(0, 6), Position::new(0, 11))), - range_length: None, - text: "rust".to_string(), - }]; + let changes = vec![partial_change( + Range::new(Position::new(0, 6), Position::new(0, 11)), + "rust", + )]; let result = apply_incremental_changes(content, changes); assert_eq!(result, "hello rust"); } @@ -372,16 +402,14 @@ mod tests { fn apply_incremental_changes_multiple_edits() { let content = "line 1\nline 2\nline 3"; let changes = vec![ - TextDocumentContentChangeEvent { - range: Some(Range::new(Position::new(0, 4), Position::new(0, 6))), - range_length: None, - text: " updated".to_string(), - }, - TextDocumentContentChangeEvent { - range: Some(Range::new(Position::new(2, 4), Position::new(2, 6))), - range_length: None, - text: " also updated".to_string(), - }, + partial_change( + Range::new(Position::new(0, 4), Position::new(0, 6)), + " updated", + ), + partial_change( + Range::new(Position::new(2, 4), Position::new(2, 6)), + " also updated", + ), ]; let result = apply_incremental_changes(content, changes); assert_eq!(result, "line updated\nline 2\nline also updated"); @@ -390,11 +418,10 @@ mod tests { #[test] fn apply_incremental_changes_insertion() { let content = "hello world"; - let changes = vec![TextDocumentContentChangeEvent { - range: Some(Range::new(Position::new(0, 5), Position::new(0, 5))), - range_length: None, - text: " foo".to_string(), - }]; + let changes = vec![partial_change( + Range::new(Position::new(0, 5), Position::new(0, 5)), + " foo", + )]; let result = apply_incremental_changes(content, changes); assert_eq!(result, "hello foo world"); } @@ -402,11 +429,10 @@ mod tests { #[test] fn apply_incremental_changes_deletion() { let content = "hello foo world"; - let changes = vec![TextDocumentContentChangeEvent { - range: Some(Range::new(Position::new(0, 5), Position::new(0, 9))), - range_length: None, - text: "".to_string(), - }]; + let changes = vec![partial_change( + Range::new(Position::new(0, 5), Position::new(0, 9)), + "", + )]; let result = apply_incremental_changes(content, changes); assert_eq!(result, "hello world"); } @@ -414,11 +440,10 @@ mod tests { #[test] fn apply_incremental_changes_multiline_edit() { let content = "line 1\nline 2\nline 3"; - let changes = vec![TextDocumentContentChangeEvent { - range: Some(Range::new(Position::new(0, 6), Position::new(1, 6))), - range_length: None, - text: " and\nreplaced".to_string(), - }]; + let changes = vec![partial_change( + Range::new(Position::new(0, 6), Position::new(1, 6)), + " and\nreplaced", + )]; let result = apply_incremental_changes(content, changes); assert_eq!(result, "line 1 and\nreplaced\nline 3"); } @@ -427,16 +452,11 @@ mod tests { fn apply_incremental_changes_full_then_incremental() { let content = "original"; let changes = vec![ - TextDocumentContentChangeEvent { - range: None, - range_length: None, - text: "hello world".to_string(), - }, - TextDocumentContentChangeEvent { - range: Some(Range::new(Position::new(0, 6), Position::new(0, 11))), - range_length: None, - text: "rust".to_string(), - }, + whole_change("hello world"), + partial_change( + Range::new(Position::new(0, 6), Position::new(0, 11)), + "rust", + ), ]; let result = apply_incremental_changes(content, changes); assert_eq!(result, "hello rust"); @@ -445,11 +465,10 @@ mod tests { #[test] fn apply_incremental_changes_invalid_range_ignored() { let content = "hello"; - let changes = vec![TextDocumentContentChangeEvent { - range: Some(Range::new(Position::new(10, 0), Position::new(10, 5))), - range_length: None, - text: "invalid".to_string(), - }]; + let changes = vec![partial_change( + Range::new(Position::new(10, 0), Position::new(10, 5)), + "invalid", + )]; let result = apply_incremental_changes(content, changes); assert_eq!(result, "hello"); } @@ -457,11 +476,10 @@ mod tests { #[test] fn apply_incremental_changes_with_invalid_line_no() { let content = "hello world"; - let changes = vec![TextDocumentContentChangeEvent { - range: Some(Range::new(Position::new(10, 0), Position::new(10, 5))), - range_length: None, - text: "invalid".to_string(), - }]; + let changes = vec![partial_change( + Range::new(Position::new(10, 0), Position::new(10, 5)), + "invalid", + )]; let result = apply_incremental_changes(content, changes); assert_eq!(result, "hello world"); } @@ -469,11 +487,10 @@ mod tests { #[test] fn apply_incremental_changes_column_clamping() { let content = "short\nlong line"; - let changes = vec![TextDocumentContentChangeEvent { - range: Some(Range::new(Position::new(0, 3), Position::new(0, 100))), - range_length: None, - text: " extended".to_string(), - }]; + let changes = vec![partial_change( + Range::new(Position::new(0, 3), Position::new(0, 100)), + " extended", + )]; let result = apply_incremental_changes(content, changes); assert_eq!(result, "sho extendedlong line"); } diff --git a/crates/squawk_server/src/semantic_tokens.rs b/crates/squawk_server/src/semantic_tokens.rs index 4e888d24..73b4e269 100644 --- a/crates/squawk_server/src/semantic_tokens.rs +++ b/crates/squawk_server/src/semantic_tokens.rs @@ -1,26 +1,26 @@ -use lsp_types::{SemanticTokenModifier, SemanticTokenType}; +use gen_lsp_types::{SemanticTokenModifiers, SemanticTokenTypes}; -pub(crate) const SUPPORTED_TYPES: &[SemanticTokenType] = &[ - SemanticTokenType::COMMENT, - SemanticTokenType::FUNCTION, - SemanticTokenType::KEYWORD, - SemanticTokenType::NAMESPACE, - SemanticTokenType::NUMBER, - SemanticTokenType::OPERATOR, - SemanticTokenType::PARAMETER, - SemanticTokenType::PROPERTY, - SemanticTokenType::STRING, - SemanticTokenType::STRUCT, - SemanticTokenType::TYPE, - SemanticTokenType::VARIABLE, +pub(crate) const SUPPORTED_TYPES: &[SemanticTokenTypes] = &[ + SemanticTokenTypes::Comment, + SemanticTokenTypes::Function, + SemanticTokenTypes::Keyword, + SemanticTokenTypes::Namespace, + SemanticTokenTypes::Number, + SemanticTokenTypes::Operator, + SemanticTokenTypes::Parameter, + SemanticTokenTypes::Property, + SemanticTokenTypes::String, + SemanticTokenTypes::Struct, + SemanticTokenTypes::Type, + SemanticTokenTypes::Variable, ]; -pub(crate) const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[ - SemanticTokenModifier::DECLARATION, - SemanticTokenModifier::DEFINITION, - SemanticTokenModifier::READONLY, +pub(crate) const SUPPORTED_MODIFIERS: &[SemanticTokenModifiers] = &[ + SemanticTokenModifiers::Declaration, + SemanticTokenModifiers::Definition, + SemanticTokenModifiers::Readonly, ]; -pub(crate) fn type_index(ty: SemanticTokenType) -> u32 { +pub(crate) fn type_index(ty: SemanticTokenTypes) -> u32 { SUPPORTED_TYPES.iter().position(|it| *it == ty).unwrap() as u32 } diff --git a/crates/squawk_server/src/server.rs b/crates/squawk_server/src/server.rs index 1f846aee..75520a80 100644 --- a/crates/squawk_server/src/server.rs +++ b/crates/squawk_server/src/server.rs @@ -1,14 +1,14 @@ use anyhow::Result; +use gen_lsp_types::{ + CodeActionKind, CodeActionOptions, CodeActionProvider, CompletionOptions, DefinitionProvider, + DiagnosticOptions, DiagnosticProvider, DocumentSymbolProvider, FoldingRangeProvider, Full, + HoverProvider, InitializeParams, InlayHintProvider, ReferencesProvider, SelectionRangeProvider, + SemanticTokensLegend, SemanticTokensOptions, SemanticTokensOptionsRange, + SemanticTokensProvider, ServerCapabilities, TextDocumentSync, TextDocumentSyncKind, + WorkDoneProgressOptions, +}; use log::info; use lsp_server::Connection; -use lsp_types::{ - CodeActionKind, CodeActionOptions, CodeActionProviderCapability, CompletionOptions, - DiagnosticOptions, DiagnosticServerCapabilities, FoldingRangeProviderCapability, - HoverProviderCapability, InitializeParams, OneOf, SelectionRangeProviderCapability, - SemanticTokensFullOptions, SemanticTokensLegend, SemanticTokensOptions, - SemanticTokensServerCapabilities, ServerCapabilities, TextDocumentSyncCapability, - TextDocumentSyncKind, WorkDoneProgressOptions, -}; use crate::{ global_state::GlobalState, @@ -21,25 +21,24 @@ pub fn run() -> Result<()> { let (connection, io_threads) = Connection::stdio(); let server_capabilities = serde_json::to_value(&ServerCapabilities { - text_document_sync: Some(TextDocumentSyncCapability::Kind( - TextDocumentSyncKind::INCREMENTAL, - )), - code_action_provider: Some(CodeActionProviderCapability::Options(CodeActionOptions { + text_document_sync: Some(TextDocumentSync::Kind(TextDocumentSyncKind::Incremental)), + code_action_provider: Some(CodeActionProvider::CodeActionOptions(CodeActionOptions { code_action_kinds: Some(vec![ - CodeActionKind::QUICKFIX, - CodeActionKind::REFACTOR_REWRITE, + CodeActionKind::QuickFix, + CodeActionKind::RefactorRewrite, ]), work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None, }, resolve_provider: None, + documentation: None, })), - selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)), - references_provider: Some(OneOf::Left(true)), - definition_provider: Some(OneOf::Left(true)), - hover_provider: Some(HoverProviderCapability::Simple(true)), - inlay_hint_provider: Some(OneOf::Left(true)), - diagnostic_provider: Some(DiagnosticServerCapabilities::Options(DiagnosticOptions { + selection_range_provider: Some(SelectionRangeProvider::Bool(true)), + references_provider: Some(ReferencesProvider::Bool(true)), + definition_provider: Some(DefinitionProvider::Bool(true)), + hover_provider: Some(HoverProvider::Bool(true)), + inlay_hint_provider: Some(InlayHintProvider::Bool(true)), + diagnostic_provider: Some(DiagnosticProvider::DiagnosticOptions(DiagnosticOptions { identifier: None, inter_file_dependencies: false, workspace_diagnostics: false, @@ -47,8 +46,8 @@ pub fn run() -> Result<()> { work_done_progress: None, }, })), - document_symbol_provider: Some(OneOf::Left(true)), - folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)), + document_symbol_provider: Some(DocumentSymbolProvider::Bool(true)), + folding_range_provider: Some(FoldingRangeProvider::Bool(true)), completion_provider: Some(CompletionOptions { resolve_provider: Some(false), trigger_characters: Some(vec![".".to_owned()]), @@ -58,17 +57,21 @@ pub fn run() -> Result<()> { }, completion_item: None, }), - semantic_tokens_provider: Some(SemanticTokensServerCapabilities::SemanticTokensOptions( + semantic_tokens_provider: Some(SemanticTokensProvider::SemanticTokensOptions( SemanticTokensOptions { work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None, }, legend: SemanticTokensLegend { - token_types: SUPPORTED_TYPES.to_vec(), - token_modifiers: SUPPORTED_MODIFIERS.to_vec(), + token_types: SUPPORTED_TYPES.iter().cloned().map(String::from).collect(), + token_modifiers: SUPPORTED_MODIFIERS + .iter() + .cloned() + .map(String::from) + .collect(), }, - range: Some(true), - full: Some(SemanticTokensFullOptions::Bool(true)), + range: Some(SemanticTokensOptionsRange::Bool(true)), + full: Some(Full::Bool(true)), }, )), ..Default::default()