diff --git a/crates/squawk_ide/src/completion.rs b/crates/squawk_ide/src/completion.rs index c50aecb1..ca591e1c 100644 --- a/crates/squawk_ide/src/completion.rs +++ b/crates/squawk_ide/src/completion.rs @@ -963,7 +963,7 @@ mod tests { fn completions(sql: &str) -> String { let (offset, sql) = fixture(sql); let db = Database::default(); - let file = File::new(&db, sql, 0); + let file = File::new(&db, sql.into()); let items = completion(&db, file, offset); assert!( !items.is_empty(), @@ -975,7 +975,7 @@ mod tests { fn completions_not_found(sql: &str) { let (offset, sql) = fixture(sql); let db = Database::default(); - let file = File::new(&db, sql, 0); + let file = File::new(&db, sql.into()); let items = completion(&db, file, offset); assert_eq!( items, diff --git a/crates/squawk_ide/src/db.rs b/crates/squawk_ide/src/db.rs index a5fb8d0f..d3af2b01 100644 --- a/crates/squawk_ide/src/db.rs +++ b/crates/squawk_ide/src/db.rs @@ -2,6 +2,7 @@ use ::line_index::LineIndex; use salsa::Database as Db; use salsa::Storage; use squawk_syntax::{Parse, SourceFile}; +use std::sync::Arc; use crate::binder; use crate::binder::Binder; @@ -9,8 +10,7 @@ use crate::binder::Binder; #[salsa::input] pub struct File { #[returns(ref)] - pub content: String, - pub version: i32, + pub content: Arc, } #[salsa::tracked] diff --git a/crates/squawk_ide/src/document_symbols.rs b/crates/squawk_ide/src/document_symbols.rs index fd09880a..341642fd 100644 --- a/crates/squawk_ide/src/document_symbols.rs +++ b/crates/squawk_ide/src/document_symbols.rs @@ -854,7 +854,7 @@ mod tests { fn symbols_not_found(sql: &str) { let db = Database::default(); - let file = File::new(&db, sql.to_string(), 0); + let file = File::new(&db, sql.to_string().into()); let symbols = document_symbols(&db, file); if !symbols.is_empty() { panic!("Symbols found. If this is expected, use `symbols` instead.") @@ -863,7 +863,7 @@ mod tests { fn symbols(sql: &str) -> String { let db = Database::default(); - let file = File::new(&db, sql.to_string(), 0); + let file = File::new(&db, sql.to_string().into()); let symbols = document_symbols(&db, file); if symbols.is_empty() { panic!("No symbols found. If this is expected, use `symbols_not_found` instead.") diff --git a/crates/squawk_ide/src/find_references.rs b/crates/squawk_ide/src/find_references.rs index 8847a938..d4772cb5 100644 --- a/crates/squawk_ide/src/find_references.rs +++ b/crates/squawk_ide/src/find_references.rs @@ -130,7 +130,7 @@ mod test { let (mut offset, sql) = fixture(sql); offset = offset.checked_sub(1.into()).unwrap_or_default(); let db = Database::default(); - let file = File::new(&db, sql.clone(), 0); + let file = File::new(&db, sql.clone().into()); assert_eq!(crate::db::parse(&db, file).errors(), vec![]); let references = find_references(&db, file, offset); diff --git a/crates/squawk_ide/src/folding_ranges.rs b/crates/squawk_ide/src/folding_ranges.rs index 7bfb6019..79d43bed 100644 --- a/crates/squawk_ide/src/folding_ranges.rs +++ b/crates/squawk_ide/src/folding_ranges.rs @@ -235,7 +235,7 @@ mod tests { fn check(sql: &str) -> String { let db = Database::default(); - let file = File::new(&db, sql.to_string(), 0); + let file = File::new(&db, sql.to_string().into()); let folds = folding_ranges(&db, file); if folds.is_empty() { diff --git a/crates/squawk_ide/src/goto_definition.rs b/crates/squawk_ide/src/goto_definition.rs index 97b3bd2d..f20ed8d7 100644 --- a/crates/squawk_ide/src/goto_definition.rs +++ b/crates/squawk_ide/src/goto_definition.rs @@ -188,7 +188,7 @@ mod test { // marker after the item we're trying to go to def on. offset = offset.checked_sub(1.into()).unwrap_or_default(); let db = Database::default(); - let file = File::new(&db, sql.clone(), 0); + let file = File::new(&db, sql.clone().into()); assert_eq!(crate::db::parse(&db, file).errors(), vec![]); let results = goto_definition(&db, file, offset); if !results.is_empty() { diff --git a/crates/squawk_ide/src/hover.rs b/crates/squawk_ide/src/hover.rs index 301978f9..a10c5e47 100644 --- a/crates/squawk_ide/src/hover.rs +++ b/crates/squawk_ide/src/hover.rs @@ -1687,7 +1687,7 @@ mod test { let db = Database::default(); let (mut offset, sql) = fixture(sql); offset = offset.checked_sub(1.into()).unwrap_or_default(); - let file = File::new(&db, sql.clone(), 0); + let file = File::new(&db, sql.clone().into()); assert_eq!(crate::db::parse(&db, file).errors(), vec![]); if let Some(type_info) = hover(&db, file, offset) { diff --git a/crates/squawk_ide/src/inlay_hints.rs b/crates/squawk_ide/src/inlay_hints.rs index 8c7004fd..da604534 100644 --- a/crates/squawk_ide/src/inlay_hints.rs +++ b/crates/squawk_ide/src/inlay_hints.rs @@ -239,7 +239,7 @@ mod test { #[track_caller] fn check_inlay_hints(sql: &str) -> String { let db = Database::default(); - let file = File::new(&db, sql.to_string(), 0); + let file = File::new(&db, sql.to_string().into()); assert_eq!(crate::db::parse(&db, file).errors(), vec![]); diff --git a/crates/squawk_server/src/handlers.rs b/crates/squawk_server/src/handlers.rs index 2e53b9f9..22397e45 100644 --- a/crates/squawk_server/src/handlers.rs +++ b/crates/squawk_server/src/handlers.rs @@ -1,5 +1,6 @@ mod code_action; mod completion; +mod diagnostic; mod document_symbol; mod folding_range; mod goto_definition; @@ -13,6 +14,7 @@ mod tokens; pub(crate) use code_action::handle_code_action; pub(crate) use completion::handle_completion; +pub(crate) use diagnostic::handle_document_diagnostic; pub(crate) use document_symbol::handle_document_symbol; pub(crate) use folding_range::handle_folding_range; pub(crate) use goto_definition::handle_goto_definition; diff --git a/crates/squawk_server/src/handlers/diagnostic.rs b/crates/squawk_server/src/handlers/diagnostic.rs new file mode 100644 index 00000000..51b5ac28 --- /dev/null +++ b/crates/squawk_server/src/handlers/diagnostic.rs @@ -0,0 +1,29 @@ +use anyhow::Result; +use lsp_types::{ + DocumentDiagnosticParams, DocumentDiagnosticReport, DocumentDiagnosticReportResult, + FullDocumentDiagnosticReport, RelatedFullDocumentDiagnosticReport, +}; + +use crate::system::System; + +pub(crate) fn handle_document_diagnostic( + system: &dyn System, + params: DocumentDiagnosticParams, +) -> Result { + let uri = params.text_document.uri; + + let diagnostics = system + .file(&uri) + .map(|file| crate::lint::lint(system.db(), file)) + .unwrap_or_default(); + + Ok(DocumentDiagnosticReportResult::Report( + DocumentDiagnosticReport::Full(RelatedFullDocumentDiagnosticReport { + related_documents: None, + full_document_diagnostic_report: FullDocumentDiagnosticReport { + result_id: None, + items: diagnostics, + }, + }), + )) +} diff --git a/crates/squawk_server/src/handlers/notifications.rs b/crates/squawk_server/src/handlers/notifications.rs index 9253afa9..59bff095 100644 --- a/crates/squawk_server/src/handlers/notifications.rs +++ b/crates/squawk_server/src/handlers/notifications.rs @@ -1,64 +1,33 @@ use anyhow::Result; use lsp_server::{Connection, Message, Notification}; use lsp_types::{ - Diagnostic, DidChangeTextDocumentParams, DidCloseTextDocumentParams, DidOpenTextDocumentParams, - PublishDiagnosticsParams, Url, + DidChangeTextDocumentParams, DidCloseTextDocumentParams, DidOpenTextDocumentParams, + PublishDiagnosticsParams, notification::{Notification as _, PublishDiagnostics}, }; use crate::lsp_utils; use crate::system::{Document, System}; -fn publish_diagnostics( - connection: &Connection, - uri: Url, - version: i32, - diagnostics: Vec, -) -> Result<()> { - let publish_params = PublishDiagnosticsParams { - uri, - diagnostics, - version: Some(version), - }; - - let notification = Notification { - method: PublishDiagnostics::METHOD.to_owned(), - params: serde_json::to_value(publish_params)?, - }; - - connection - .sender - .send(Message::Notification(notification))?; - Ok(()) -} - pub(crate) fn handle_did_open( - connection: &Connection, + _connection: &Connection, params: DidOpenTextDocumentParams, system: &mut dyn System, ) -> Result<()> { let uri = params.text_document.uri; let content = params.text_document.text; - let version = params.text_document.version; - - system.set(uri.clone(), Document { content, version }); - let db = system.db(); - let file = system.file(&uri).unwrap(); - let diagnostics = crate::lint::lint(db, file); - // TODO: we need a better setup for "run func when input changed" - publish_diagnostics(connection, uri, version, diagnostics)?; + system.set(uri, Document { content }); Ok(()) } pub(crate) fn handle_did_change( - connection: &Connection, + _connection: &Connection, params: DidChangeTextDocumentParams, system: &mut dyn System, ) -> Result<()> { let uri = params.text_document.uri; - let version = params.text_document.version; let db = system.db(); let file = system.file(&uri).unwrap(); @@ -67,16 +36,11 @@ pub(crate) fn handle_did_change( let updated_content = lsp_utils::apply_incremental_changes(content, params.content_changes); system.set( - uri.clone(), + uri, Document { content: updated_content, - version, }, ); - let db = system.db(); - let file = system.file(&uri).unwrap(); - let diagnostics = crate::lint::lint(db, file); - publish_diagnostics(connection, uri, version, diagnostics)?; Ok(()) } diff --git a/crates/squawk_server/src/ignore.rs b/crates/squawk_server/src/ignore.rs index e4651a70..1d0dc9ba 100644 --- a/crates/squawk_server/src/ignore.rs +++ b/crates/squawk_server/src/ignore.rs @@ -202,7 +202,7 @@ create table c ( fn lint_sql(sql: &str) -> Vec { let db = Database::default(); - let file = File::new(&db, sql.to_owned(), 0); + let file = File::new(&db, sql.to_owned().into()); lint(&db, file) } } diff --git a/crates/squawk_server/src/server.rs b/crates/squawk_server/src/server.rs index 350163b3..d69a86d1 100644 --- a/crates/squawk_server/src/server.rs +++ b/crates/squawk_server/src/server.rs @@ -3,22 +3,23 @@ use log::info; use lsp_server::{Connection, Message}; use lsp_types::{ CodeActionKind, CodeActionOptions, CodeActionProviderCapability, CompletionOptions, - FoldingRangeProviderCapability, HoverProviderCapability, InitializeParams, OneOf, - SelectionRangeProviderCapability, ServerCapabilities, TextDocumentSyncCapability, - TextDocumentSyncKind, WorkDoneProgressOptions, + DiagnosticOptions, DiagnosticServerCapabilities, FoldingRangeProviderCapability, + HoverProviderCapability, InitializeParams, OneOf, SelectionRangeProviderCapability, + ServerCapabilities, TextDocumentSyncCapability, TextDocumentSyncKind, WorkDoneProgressOptions, notification::{DidChangeTextDocument, DidCloseTextDocument, DidOpenTextDocument}, request::{ - CodeActionRequest, Completion, DocumentSymbolRequest, FoldingRangeRequest, GotoDefinition, - HoverRequest, InlayHintRequest, References, SelectionRangeRequest, + CodeActionRequest, Completion, DocumentDiagnosticRequest, DocumentSymbolRequest, + FoldingRangeRequest, GotoDefinition, HoverRequest, InlayHintRequest, References, + SelectionRangeRequest, }, }; use crate::dispatch::{NotificationDispatcher, RequestDispatcher}; use crate::handlers::{ SyntaxTreeRequest, TokensRequest, handle_code_action, handle_completion, handle_did_change, - handle_did_close, handle_did_open, handle_document_symbol, handle_folding_range, - handle_goto_definition, handle_hover, handle_inlay_hints, handle_references, - handle_selection_range, handle_syntax_tree, handle_tokens, + handle_did_close, handle_did_open, handle_document_diagnostic, handle_document_symbol, + handle_folding_range, handle_goto_definition, handle_hover, handle_inlay_hints, + handle_references, handle_selection_range, handle_syntax_tree, handle_tokens, }; use crate::system::GlobalState; @@ -46,6 +47,14 @@ pub fn run() -> Result<()> { 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 { + identifier: None, + inter_file_dependencies: false, + workspace_diagnostics: false, + work_done_progress_options: WorkDoneProgressOptions { + work_done_progress: None, + }, + })), document_symbol_provider: Some(OneOf::Left(true)), folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)), completion_provider: Some(CompletionOptions { @@ -102,6 +111,7 @@ fn main_loop(connection: Connection, params: serde_json::Value) -> Result<()> { .on::(handle_document_symbol)? .on::(handle_folding_range)? .on::(handle_completion)? + .on::(handle_document_diagnostic)? .on::(handle_syntax_tree)? .on::(handle_tokens)? .on::(handle_references)? diff --git a/crates/squawk_server/src/system.rs b/crates/squawk_server/src/system.rs index 77f4677f..3a5e43f6 100644 --- a/crates/squawk_server/src/system.rs +++ b/crates/squawk_server/src/system.rs @@ -5,8 +5,6 @@ use std::collections::HashMap; pub(crate) struct Document { pub(crate) content: String, - #[allow(dead_code)] - pub(crate) version: i32, } pub(crate) trait System { @@ -41,10 +39,9 @@ impl System for GlobalState { fn set(&mut self, uri: Url, doc: Document) { if let Some(file) = self.files.get(&uri).copied() { - file.set_content(&mut self.db).to(doc.content); - file.set_version(&mut self.db).to(doc.version); + file.set_content(&mut self.db).to(doc.content.into()); } else { - let file = File::new(&self.db, doc.content, doc.version); + let file = File::new(&self.db, doc.content.into()); self.files.insert(uri, file); } } diff --git a/crates/squawk_wasm/src/lib.rs b/crates/squawk_wasm/src/lib.rs index f57de406..468f66ac 100644 --- a/crates/squawk_wasm/src/lib.rs +++ b/crates/squawk_wasm/src/lib.rs @@ -45,14 +45,13 @@ impl SquawkDatabase { } pub fn open_file(&mut self, content: String) { - let file = File::new(&self.db, content, 0); + let file = File::new(&self.db, content.into()); self.file = Some(file); } - pub fn update_file(&mut self, content: String, version: i32) { + pub fn update_file(&mut self, content: String) { if let Some(file) = self.file { - file.set_content(&mut self.db).to(content); - file.set_version(&mut self.db).to(version); + file.set_content(&mut self.db).to(content.into()); } }