use tower_lsp::jsonrpc::Result;
use tower_lsp::lsp_types::*;
use tower_lsp::{Client, LanguageServer};
use crate::semantic_tokens;
use crate::state::ServerState;
pub struct LogicAffeineServer {
client: Client,
state: ServerState,
}
impl LogicAffeineServer {
pub fn new(client: Client) -> Self {
LogicAffeineServer {
client,
state: ServerState::new(),
}
}
async fn publish_diagnostics(&self, uri: Url) {
if let Some(doc) = self.state.documents.get(&uri) {
self.client
.publish_diagnostics(uri.clone(), doc.diagnostics.clone(), Some(doc.version))
.await;
}
}
}
#[tower_lsp::async_trait]
impl LanguageServer for LogicAffeineServer {
async fn initialize(&self, _: InitializeParams) -> Result<InitializeResult> {
Ok(InitializeResult {
capabilities: ServerCapabilities {
text_document_sync: Some(TextDocumentSyncCapability::Kind(
TextDocumentSyncKind::FULL,
)),
semantic_tokens_provider: Some(
SemanticTokensServerCapabilities::SemanticTokensOptions(
SemanticTokensOptions {
legend: semantic_tokens::legend(),
full: Some(SemanticTokensFullOptions::Bool(true)),
range: None,
..Default::default()
},
),
),
document_symbol_provider: Some(OneOf::Left(true)),
definition_provider: Some(OneOf::Left(true)),
hover_provider: Some(HoverProviderCapability::Simple(true)),
completion_provider: Some(CompletionOptions {
trigger_characters: Some(vec![
".".to_string(),
":".to_string(),
"'".to_string(),
]),
..Default::default()
}),
references_provider: Some(OneOf::Left(true)),
signature_help_provider: Some(SignatureHelpOptions {
trigger_characters: Some(vec![
"with".to_string(),
",".to_string(),
]),
..Default::default()
}),
code_action_provider: Some(CodeActionProviderCapability::Simple(true)),
rename_provider: Some(OneOf::Right(RenameOptions {
prepare_provider: Some(true),
work_done_progress_options: Default::default(),
})),
folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),
inlay_hint_provider: Some(OneOf::Left(true)),
code_lens_provider: Some(CodeLensOptions {
resolve_provider: Some(false),
}),
document_formatting_provider: Some(OneOf::Left(true)),
..Default::default()
},
server_info: Some(ServerInfo {
name: "logicaffeine-lsp".to_string(),
version: Some(env!("CARGO_PKG_VERSION").to_string()),
}),
})
}
async fn initialized(&self, _: InitializedParams) {
log::info!("LogicAffeine LSP initialized");
}
async fn shutdown(&self) -> Result<()> {
Ok(())
}
async fn did_open(&self, params: DidOpenTextDocumentParams) {
let uri = params.text_document.uri.clone();
self.state.open_document(
params.text_document.uri,
params.text_document.text,
params.text_document.version,
);
self.publish_diagnostics(uri).await;
}
async fn did_change(&self, params: DidChangeTextDocumentParams) {
let uri = params.text_document.uri.clone();
if let Some(change) = params.content_changes.into_iter().next() {
self.state.update_document(
&uri,
change.text,
params.text_document.version,
);
}
self.publish_diagnostics(uri).await;
}
async fn did_close(&self, params: DidCloseTextDocumentParams) {
self.state.close_document(¶ms.text_document.uri);
self.client
.publish_diagnostics(params.text_document.uri, vec![], None)
.await;
}
async fn semantic_tokens_full(
&self,
params: SemanticTokensParams,
) -> Result<Option<SemanticTokensResult>> {
let uri = ¶ms.text_document.uri;
let doc = match self.state.documents.get(uri) {
Some(doc) => doc,
None => return Ok(None),
};
let tokens = semantic_tokens::encode_tokens(&doc.tokens, &doc.line_index);
Ok(Some(SemanticTokensResult::Tokens(SemanticTokens {
result_id: None,
data: tokens,
})))
}
async fn document_symbol(
&self,
params: DocumentSymbolParams,
) -> Result<Option<DocumentSymbolResponse>> {
let uri = ¶ms.text_document.uri;
let doc = match self.state.documents.get(uri) {
Some(doc) => doc,
None => return Ok(None),
};
let symbols = crate::document_symbols::document_symbols(&doc);
Ok(Some(DocumentSymbolResponse::Nested(symbols)))
}
async fn goto_definition(
&self,
params: GotoDefinitionParams,
) -> Result<Option<GotoDefinitionResponse>> {
let uri = ¶ms.text_document_position_params.text_document.uri;
let position = params.text_document_position_params.position;
let doc = match self.state.documents.get(uri) {
Some(doc) => doc,
None => return Ok(None),
};
Ok(crate::definition::goto_definition(&doc, position, uri))
}
async fn hover(&self, params: HoverParams) -> Result<Option<Hover>> {
let uri = ¶ms.text_document_position_params.text_document.uri;
let position = params.text_document_position_params.position;
let doc = match self.state.documents.get(uri) {
Some(doc) => doc,
None => return Ok(None),
};
Ok(crate::hover::hover(&doc, position))
}
async fn completion(&self, params: CompletionParams) -> Result<Option<CompletionResponse>> {
let uri = ¶ms.text_document_position.text_document.uri;
let position = params.text_document_position.position;
let doc = match self.state.documents.get(uri) {
Some(doc) => doc,
None => return Ok(None),
};
Ok(crate::completion::completions(&doc, position))
}
async fn references(&self, params: ReferenceParams) -> Result<Option<Vec<Location>>> {
let uri = ¶ms.text_document_position.text_document.uri;
let position = params.text_document_position.position;
let doc = match self.state.documents.get(uri) {
Some(doc) => doc,
None => return Ok(None),
};
let include_declaration = params.context.include_declaration;
let locations =
crate::references::find_references(&doc, position, uri, include_declaration);
if locations.is_empty() {
Ok(None)
} else {
Ok(Some(locations))
}
}
async fn signature_help(&self, params: SignatureHelpParams) -> Result<Option<SignatureHelp>> {
let uri = ¶ms.text_document_position_params.text_document.uri;
let position = params.text_document_position_params.position;
let doc = match self.state.documents.get(uri) {
Some(doc) => doc,
None => return Ok(None),
};
Ok(crate::signature_help::signature_help(&doc, position))
}
async fn code_action(&self, params: CodeActionParams) -> Result<Option<CodeActionResponse>> {
let uri = ¶ms.text_document.uri;
let range = params.range;
let doc = match self.state.documents.get(uri) {
Some(doc) => doc,
None => return Ok(None),
};
let actions = crate::code_actions::code_actions(&doc, range, uri);
if actions.is_empty() {
Ok(None)
} else {
Ok(Some(actions))
}
}
async fn rename(&self, params: RenameParams) -> Result<Option<WorkspaceEdit>> {
let uri = ¶ms.text_document_position.text_document.uri;
let position = params.text_document_position.position;
let doc = match self.state.documents.get(uri) {
Some(doc) => doc,
None => return Ok(None),
};
Ok(crate::rename::rename(&doc, position, params.new_name, uri))
}
async fn prepare_rename(
&self,
params: TextDocumentPositionParams,
) -> Result<Option<PrepareRenameResponse>> {
let uri = ¶ms.text_document.uri;
let position = params.position;
let doc = match self.state.documents.get(uri) {
Some(doc) => doc,
None => return Ok(None),
};
Ok(crate::rename::prepare_rename(&doc, position).map(|(range, text)| {
PrepareRenameResponse::RangeWithPlaceholder {
range,
placeholder: text,
}
}))
}
async fn folding_range(&self, params: FoldingRangeParams) -> Result<Option<Vec<FoldingRange>>> {
let uri = ¶ms.text_document.uri;
let doc = match self.state.documents.get(uri) {
Some(doc) => doc,
None => return Ok(None),
};
let ranges = crate::folding::folding_ranges(&doc);
if ranges.is_empty() {
Ok(None)
} else {
Ok(Some(ranges))
}
}
async fn inlay_hint(&self, params: InlayHintParams) -> Result<Option<Vec<InlayHint>>> {
let uri = ¶ms.text_document.uri;
let doc = match self.state.documents.get(uri) {
Some(doc) => doc,
None => return Ok(None),
};
let hints = crate::inlay_hints::inlay_hints(&doc, params.range);
if hints.is_empty() {
Ok(None)
} else {
Ok(Some(hints))
}
}
async fn code_lens(&self, params: CodeLensParams) -> Result<Option<Vec<CodeLens>>> {
let uri = ¶ms.text_document.uri;
let doc = match self.state.documents.get(uri) {
Some(doc) => doc,
None => return Ok(None),
};
let lenses = crate::code_lens::code_lenses(&doc, uri);
if lenses.is_empty() {
Ok(None)
} else {
Ok(Some(lenses))
}
}
async fn formatting(&self, params: DocumentFormattingParams) -> Result<Option<Vec<TextEdit>>> {
let uri = ¶ms.text_document.uri;
let doc = match self.state.documents.get(uri) {
Some(doc) => doc,
None => return Ok(None),
};
let edits = crate::formatting::format_document(&doc);
if edits.is_empty() {
Ok(None)
} else {
Ok(Some(edits))
}
}
}