diff --git a/compiler/server/src/diagnostics.rs b/compiler/server/src/diagnostics.rs index 517eaebb..96c43158 100644 --- a/compiler/server/src/diagnostics.rs +++ b/compiler/server/src/diagnostics.rs @@ -2,21 +2,18 @@ use crate::span::ToLocationExt; use diagnostics::{Code, Highlight, LintCode, Role, Severity, UnboxedUntaggedDiagnostic}; use span::SourceMap; use std::{collections::BTreeSet, default::default}; -use tower_lsp::lsp_types::{ - self, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, MessageType, - NumberOrString, OneOf, Range, -}; +use tower_lsp::lsp_types as lsp; const DIAGNOSTIC_SOURCE: &str = "source"; -pub(crate) type LspMessage = (MessageType, String); +pub(crate) type LspMessage = (lsp::MessageType, String); pub(crate) trait DiagnosticExt { - fn into_lsp_type(self, map: &SourceMap) -> OneOf; + fn into_lsp_type(self, map: &SourceMap) -> lsp::OneOf; } impl DiagnosticExt for UnboxedUntaggedDiagnostic { - fn into_lsp_type(self, map: &SourceMap) -> OneOf { + fn into_lsp_type(self, map: &SourceMap) -> lsp::OneOf { // @Task explain the " "-hack let message = self.message.unwrap_or_else(|| " ".into()).into(); @@ -24,10 +21,10 @@ impl DiagnosticExt for UnboxedUntaggedDiagnostic { Some((range, related_information)) => { let tags = self.code.and_then(|code| { (code == Code::Lint(LintCode::Deprecated)) - .then(|| vec![DiagnosticTag::DEPRECATED]) + .then(|| vec![lsp::DiagnosticTag::DEPRECATED]) }); - OneOf::Left(lsp_types::Diagnostic { + lsp::OneOf::Left(lsp::Diagnostic { range, severity: Some(IntoLspType::into(self.severity)), code: self.code.map(IntoLspType::into), @@ -38,7 +35,7 @@ impl DiagnosticExt for UnboxedUntaggedDiagnostic { ..default() }) } - None => OneOf::Right((IntoLspType::into(self.severity), message)), + None => lsp::OneOf::Right((IntoLspType::into(self.severity), message)), } } } @@ -46,7 +43,7 @@ impl DiagnosticExt for UnboxedUntaggedDiagnostic { fn convert_highlights( highlights: BTreeSet, map: &SourceMap, -) -> Option<(Range, Vec)> { +) -> Option<(lsp::Range, Vec)> { let mut range = None; let mut related_information = Vec::new(); @@ -56,7 +53,7 @@ fn convert_highlights( // @Beacon @Bug we are ignoring the file assoc w/ the span!!! range = Some(highlight.span.to_location(map).range); } else { - related_information.push(DiagnosticRelatedInformation { + related_information.push(lsp::DiagnosticRelatedInformation { location: highlight.span.to_location(map), // @Task explain " "-hack message: highlight.label.unwrap_or_else(|| " ".into()).into(), @@ -71,28 +68,28 @@ trait IntoLspType { fn into(self) -> Output; } -impl IntoLspType for Severity { - fn into(self) -> DiagnosticSeverity { +impl IntoLspType for Severity { + fn into(self) -> lsp::DiagnosticSeverity { match self { - Self::Bug | Self::Error => DiagnosticSeverity::ERROR, - Self::Warning => DiagnosticSeverity::WARNING, - Self::Debug => DiagnosticSeverity::INFORMATION, + Self::Bug | Self::Error => lsp::DiagnosticSeverity::ERROR, + Self::Warning => lsp::DiagnosticSeverity::WARNING, + Self::Debug => lsp::DiagnosticSeverity::INFORMATION, } } } -impl IntoLspType for Severity { - fn into(self) -> MessageType { +impl IntoLspType for Severity { + fn into(self) -> lsp::MessageType { match self { - Self::Bug | Self::Error => MessageType::ERROR, - Self::Warning => MessageType::WARNING, - Self::Debug => MessageType::INFO, + Self::Bug | Self::Error => lsp::MessageType::ERROR, + Self::Warning => lsp::MessageType::WARNING, + Self::Debug => lsp::MessageType::INFO, } } } -impl IntoLspType for Code { - fn into(self) -> NumberOrString { - NumberOrString::String(self.to_string()) +impl IntoLspType for Code { + fn into(self) -> lsp::NumberOrString { + lsp::NumberOrString::String(self.to_string()) } } diff --git a/compiler/server/src/lib.rs b/compiler/server/src/lib.rs index b9872cd9..a1733b46 100644 --- a/compiler/server/src/lib.rs +++ b/compiler/server/src/lib.rs @@ -16,16 +16,7 @@ use std::{ path::Path, sync::{Arc, RwLock}, }; -use tower_lsp::{ - jsonrpc, - lsp_types::{ - DidChangeTextDocumentParams, DidCloseTextDocumentParams, DidOpenTextDocumentParams, - GotoDefinitionParams, GotoDefinitionResponse, InitializeParams, InitializeResult, - InitializedParams, MessageType, OneOf, ServerCapabilities, ServerInfo, - TextDocumentSyncKind, TextDocumentSyncOptions, Url, - }, - Client, -}; +use tower_lsp::{jsonrpc, lsp_types as lsp, Client}; use utilities::{ComponentIndex, FormatError, HashMap}; mod diagnostics; @@ -46,7 +37,7 @@ pub async fn serve(map: Arc>) { struct Server { map: Arc>, // @Beacon @Task remove this, we don't need this!! we have a frckin SourceMap here!!! - documents: RwLock>>, + documents: RwLock>>, client: Client, } @@ -69,15 +60,8 @@ impl Server { *self.map.write().unwrap() = default(); } - async fn validate_file(&self, uri: Url, version: i32, content: Arc) { - let scheme = uri.scheme(); - if scheme != "file" { - self.client - .log_message( - MessageType::ERROR, - format!("Unsupported URI scheme ‘{scheme}’"), - ) - .await; + async fn validate_file(&self, uri: lsp::Url, version: i32, content: Arc) { + if self.validate_uri(&uri).await.is_err() { return; } @@ -99,14 +83,16 @@ impl Server { self.report(diagnostics, uri, version).await; } - async fn report(&self, diagnostics: BTreeSet, uri: Url, version: i32) { + async fn report(&self, diagnostics: BTreeSet, uri: lsp::Url, version: i32) { let mut lsp_diagnostics = Vec::new(); for diagnostic in diagnostics { let diagnostic = diagnostic.into_lsp_type(&self.map.read().unwrap()); match diagnostic { - OneOf::Left(diagnostic) => lsp_diagnostics.push(diagnostic), - OneOf::Right((type_, message)) => self.client.show_message(type_, message).await, + lsp::OneOf::Left(diagnostic) => lsp_diagnostics.push(diagnostic), + lsp::OneOf::Right((type_, message)) => { + self.client.show_message(type_, message).await + } } } @@ -115,26 +101,42 @@ impl Server { .publish_diagnostics(uri, lsp_diagnostics, Some(version)) .await; } + + async fn validate_uri(&self, uri: &lsp::Url) -> Result<(), ()> { + let scheme = uri.scheme(); + + if scheme != "file" { + self.client + .show_message( + lsp::MessageType::ERROR, + format!("unsupported URI scheme ‘{scheme}’"), + ) + .await; + return Err(()); + } + + Ok(()) + } } // @Task replace the async tower_lsp library with a sync server. async is not worth it #[tower_lsp::async_trait] impl tower_lsp::LanguageServer for Server { - async fn initialize(&self, _: InitializeParams) -> jsonrpc::Result { - Ok(InitializeResult { - capabilities: ServerCapabilities { + async fn initialize(&self, _: lsp::InitializeParams) -> jsonrpc::Result { + Ok(lsp::InitializeResult { + capabilities: lsp::ServerCapabilities { text_document_sync: Some( - TextDocumentSyncOptions { + lsp::TextDocumentSyncOptions { open_close: Some(true), - change: Some(TextDocumentSyncKind::FULL), + change: Some(lsp::TextDocumentSyncKind::FULL), ..default() } .into(), ), - definition_provider: Some(OneOf::Left(true)), + definition_provider: Some(lsp::OneOf::Left(true)), ..default() }, - server_info: Some(ServerInfo { + server_info: Some(lsp::ServerInfo { name: NAME.into(), // @Task supply version here (do what main.rs does) version: None, @@ -142,9 +144,9 @@ impl tower_lsp::LanguageServer for Server { }) } - async fn initialized(&self, _: InitializedParams) { + async fn initialized(&self, _: lsp::InitializedParams) { self.client - .log_message(MessageType::INFO, "Language server initialized") + .log_message(lsp::MessageType::INFO, "Language server initialized") .await; } @@ -152,7 +154,7 @@ impl tower_lsp::LanguageServer for Server { Ok(()) } - async fn did_open(&self, parameters: DidOpenTextDocumentParams) { + async fn did_open(&self, parameters: lsp::DidOpenTextDocumentParams) { let content = Arc::new(parameters.text_document.text); self.documents @@ -165,7 +167,7 @@ impl tower_lsp::LanguageServer for Server { .await; } - async fn did_change(&self, mut parameters: DidChangeTextDocumentParams) { + async fn did_change(&self, mut parameters: lsp::DidChangeTextDocumentParams) { // @Bug incredibly fragile! let content = Arc::new(parameters.content_changes.swap_remove(0).text); @@ -182,7 +184,7 @@ impl tower_lsp::LanguageServer for Server { .await; } - async fn did_close(&self, parameters: DidCloseTextDocumentParams) { + async fn did_close(&self, parameters: lsp::DidCloseTextDocumentParams) { // @Task unless it's a workspace(?) (a package), get rid of any diagnostics self.client .publish_diagnostics(parameters.text_document.uri, Vec::new(), None) @@ -191,17 +193,19 @@ impl tower_lsp::LanguageServer for Server { async fn goto_definition( &self, - parameters: GotoDefinitionParams, - ) -> jsonrpc::Result> { - Ok((|| { - let uri = parameters.text_document_position_params.text_document.uri; + parameters: lsp::GotoDefinitionParams, + ) -> jsonrpc::Result> { + let uri = parameters.text_document_position_params.text_document.uri; + if self.validate_uri(&uri).await.is_err() { + return Ok(None); + } - let path = Path::new(uri.path()); - // @Task error on unsupported URI scheme! - let content = self.documents.read().unwrap().get(&uri).unwrap().clone(); + let path = Path::new(uri.path()); + let content = self.documents.read().unwrap().get(&uri).unwrap().clone(); - self.reset_source_map(); + self.reset_source_map(); + Ok((|| { let (session, component_roots) = check_file(path, content, &self.map, Reporter::silent()).ok()?; @@ -227,7 +231,7 @@ impl tower_lsp::LanguageServer for Server { return None; }; - Some(GotoDefinitionResponse::Scalar( + Some(lsp::GotoDefinitionResponse::Scalar( session[index] .source .span() diff --git a/compiler/server/src/span.rs b/compiler/server/src/span.rs index 8427fb68..c1eb746d 100644 --- a/compiler/server/src/span.rs +++ b/compiler/server/src/span.rs @@ -1,29 +1,29 @@ use span::{ByteIndex, LocalByteIndex, SourceMap, Span}; use std::path::Path; -use tower_lsp::lsp_types::{Location, Position, Range, Url}; +use tower_lsp::lsp_types as lsp; pub(crate) trait ToLocationExt { - fn to_location(self, map: &SourceMap) -> Location; + fn to_location(self, map: &SourceMap) -> lsp::Location; } impl ToLocationExt for Span { - fn to_location(self, map: &SourceMap) -> Location { + fn to_location(self, map: &SourceMap) -> lsp::Location { let lines = map.lines_with_highlight(self); - Location { + lsp::Location { // @Beacon @Task handle anonymous SourceFiles smh!!! - uri: Url::from_file_path(lines.path.unwrap()).unwrap(), - range: Range { - start: Position { + uri: lsp::Url::from_file_path(lines.path.unwrap()).unwrap(), + range: lsp::Range { + start: lsp::Position { line: lines.first.number - 1, character: lines.first.highlight.start - 1, }, end: match lines.last { - Some(line) => Position { + Some(line) => lsp::Position { line: line.number - 1, character: line.highlight.end - 1, }, - None => Position { + None => lsp::Position { line: lines.first.number - 1, character: lines.first.highlight.end - 1, }, @@ -34,12 +34,12 @@ impl ToLocationExt for Span { } pub(crate) trait FromPositionExt { - fn from_position(position: Position, path: &Path, map: &SourceMap) -> Self; + fn from_position(position: lsp::Position, path: &Path, map: &SourceMap) -> Self; } impl FromPositionExt for ByteIndex { // @Beacon @Note this is an abomination!!! - fn from_position(position: Position, path: &Path, map: &SourceMap) -> Self { + fn from_position(position: lsp::Position, path: &Path, map: &SourceMap) -> Self { let file = map.file_by_path(path).unwrap(); let mut index = LocalByteIndex::new(0);