mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 15:21:12 +00:00
208 lines
6.6 KiB
Rust
208 lines
6.6 KiB
Rust
use analysis::HIGHLIGHT_TOKENS_LEGEND;
|
|
use parking_lot::{Mutex, MutexGuard};
|
|
use registry::{DocumentChange, Registry};
|
|
use tower_lsp::jsonrpc::Result;
|
|
use tower_lsp::lsp_types::*;
|
|
use tower_lsp::{Client, LanguageServer, LspService, Server};
|
|
|
|
mod analysis;
|
|
mod convert;
|
|
mod registry;
|
|
|
|
#[derive(Debug)]
|
|
struct RocLs {
|
|
client: Client,
|
|
registry: Mutex<Registry>,
|
|
}
|
|
|
|
impl std::panic::RefUnwindSafe for RocLs {}
|
|
|
|
impl RocLs {
|
|
pub fn new(client: Client) -> Self {
|
|
Self {
|
|
client,
|
|
registry: Mutex::new(Registry::default()),
|
|
}
|
|
}
|
|
|
|
fn registry(&self) -> MutexGuard<Registry> {
|
|
self.registry.lock()
|
|
}
|
|
|
|
pub fn capabilities() -> ServerCapabilities {
|
|
let text_document_sync = TextDocumentSyncCapability::Options(
|
|
// TODO: later on make this incremental
|
|
TextDocumentSyncOptions {
|
|
open_close: Some(true),
|
|
change: Some(TextDocumentSyncKind::FULL),
|
|
..TextDocumentSyncOptions::default()
|
|
},
|
|
);
|
|
let hover_provider = HoverProviderCapability::Simple(true);
|
|
let definition_provider = DefinitionOptions {
|
|
work_done_progress_options: WorkDoneProgressOptions {
|
|
work_done_progress: None,
|
|
},
|
|
};
|
|
let document_formatting_provider = DocumentFormattingOptions {
|
|
work_done_progress_options: WorkDoneProgressOptions {
|
|
work_done_progress: None,
|
|
},
|
|
};
|
|
let semantic_tokens_provider =
|
|
SemanticTokensServerCapabilities::SemanticTokensOptions(SemanticTokensOptions {
|
|
work_done_progress_options: WorkDoneProgressOptions {
|
|
work_done_progress: None,
|
|
},
|
|
legend: SemanticTokensLegend {
|
|
token_types: HIGHLIGHT_TOKENS_LEGEND.into(),
|
|
token_modifiers: vec![],
|
|
},
|
|
range: None,
|
|
full: Some(SemanticTokensFullOptions::Bool(true)),
|
|
});
|
|
|
|
ServerCapabilities {
|
|
text_document_sync: Some(text_document_sync),
|
|
hover_provider: Some(hover_provider),
|
|
definition_provider: Some(OneOf::Right(definition_provider)),
|
|
document_formatting_provider: Some(OneOf::Right(document_formatting_provider)),
|
|
semantic_tokens_provider: Some(semantic_tokens_provider),
|
|
..ServerCapabilities::default()
|
|
}
|
|
}
|
|
|
|
/// Records a document content change.
|
|
async fn change(&self, fi: Url, text: String, version: i32) {
|
|
self.registry()
|
|
.apply_change(DocumentChange::Modified(fi.clone(), text));
|
|
|
|
let diagnostics = match std::panic::catch_unwind(|| self.registry().diagnostics(&fi)) {
|
|
Ok(ds) => ds,
|
|
Err(_) => return,
|
|
};
|
|
|
|
self.client
|
|
.publish_diagnostics(fi, diagnostics, Some(version))
|
|
.await;
|
|
}
|
|
|
|
async fn close(&self, fi: Url) {
|
|
self.registry().apply_change(DocumentChange::Closed(fi));
|
|
}
|
|
}
|
|
|
|
#[tower_lsp::async_trait]
|
|
impl LanguageServer for RocLs {
|
|
async fn initialize(&self, _: InitializeParams) -> Result<InitializeResult> {
|
|
Ok(InitializeResult {
|
|
capabilities: Self::capabilities(),
|
|
..InitializeResult::default()
|
|
})
|
|
}
|
|
|
|
async fn initialized(&self, _: InitializedParams) {
|
|
self.client
|
|
.log_message(MessageType::INFO, "Roc language server initialized.")
|
|
.await;
|
|
}
|
|
|
|
async fn did_open(&self, params: DidOpenTextDocumentParams) {
|
|
let TextDocumentItem {
|
|
uri, text, version, ..
|
|
} = params.text_document;
|
|
self.change(uri, text, version).await;
|
|
}
|
|
|
|
async fn did_change(&self, params: DidChangeTextDocumentParams) {
|
|
let VersionedTextDocumentIdentifier { uri, version, .. } = params.text_document;
|
|
|
|
// NOTE: We specify that we expect full-content syncs in the server capabilities,
|
|
// so here we assume the only change passed is a change of the entire document's content.
|
|
let TextDocumentContentChangeEvent { text, .. } =
|
|
params.content_changes.into_iter().next().unwrap();
|
|
|
|
self.change(uri, text, version).await;
|
|
}
|
|
|
|
async fn did_close(&self, params: DidCloseTextDocumentParams) {
|
|
let TextDocumentIdentifier { uri } = params.text_document;
|
|
self.close(uri).await;
|
|
}
|
|
|
|
async fn shutdown(&self) -> Result<()> {
|
|
Ok(())
|
|
}
|
|
|
|
async fn hover(&self, params: HoverParams) -> Result<Option<Hover>> {
|
|
let HoverParams {
|
|
text_document_position_params:
|
|
TextDocumentPositionParams {
|
|
text_document,
|
|
position,
|
|
},
|
|
work_done_progress_params: _,
|
|
} = params;
|
|
|
|
panic_wrapper(|| self.registry().hover(&text_document.uri, position))
|
|
}
|
|
|
|
async fn goto_definition(
|
|
&self,
|
|
params: GotoDefinitionParams,
|
|
) -> Result<Option<GotoDefinitionResponse>> {
|
|
let GotoDefinitionParams {
|
|
text_document_position_params:
|
|
TextDocumentPositionParams {
|
|
text_document,
|
|
position,
|
|
},
|
|
work_done_progress_params: _,
|
|
partial_result_params: _,
|
|
} = params;
|
|
|
|
panic_wrapper(|| {
|
|
self.registry()
|
|
.goto_definition(&text_document.uri, position)
|
|
})
|
|
}
|
|
|
|
async fn formatting(&self, params: DocumentFormattingParams) -> Result<Option<Vec<TextEdit>>> {
|
|
let DocumentFormattingParams {
|
|
text_document,
|
|
options: _,
|
|
work_done_progress_params: _,
|
|
} = params;
|
|
|
|
panic_wrapper(|| self.registry().formatting(&text_document.uri))
|
|
}
|
|
|
|
async fn semantic_tokens_full(
|
|
&self,
|
|
params: SemanticTokensParams,
|
|
) -> Result<Option<SemanticTokensResult>> {
|
|
let SemanticTokensParams {
|
|
text_document,
|
|
work_done_progress_params: _,
|
|
partial_result_params: _,
|
|
} = params;
|
|
|
|
panic_wrapper(|| self.registry().semantic_tokens(&text_document.uri))
|
|
}
|
|
}
|
|
|
|
fn panic_wrapper<T>(f: impl FnOnce() -> Option<T> + std::panic::UnwindSafe) -> Result<Option<T>> {
|
|
match std::panic::catch_unwind(f) {
|
|
Ok(r) => Ok(r),
|
|
Err(_) => Err(tower_lsp::jsonrpc::Error::internal_error()),
|
|
}
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
let stdin = tokio::io::stdin();
|
|
let stdout = tokio::io::stdout();
|
|
|
|
let (service, socket) = LspService::new(RocLs::new);
|
|
Server::new(stdin, stdout, socket).serve(service).await;
|
|
}
|