use std::thread::park_timeout;
use lady_deirdre::{lexis::ToSpan, sync::Trigger};
use log::{error, warn};
use lsp_types::{
error_codes::{REQUEST_CANCELLED, REQUEST_FAILED},
request::{GotoImplementation, GotoImplementationResponse},
Location,
Position,
Uri,
};
use crate::{
analysis::{
symbols::{LookupOptions, ModuleSymbol, SymbolKind},
ModuleError,
ModuleRead,
},
server::{
file::{LspModule, ANALYSIS_PRIORITY},
logger::LSP_SERVER_LOG,
rpc::{LspHandle, OutgoingEx, RpcId, RpcLatches},
tasks::{Task, TaskExecution, COOL_DOWN},
utils::{lsp_position_to_ld, span_to_range},
RpcSender,
},
};
pub(super) struct SendGotoImplementation {
pub(super) latches: RpcLatches,
pub(super) outgoing: RpcSender,
pub(super) module: LspModule,
}
impl Task for SendGotoImplementation {
const EXECUTION: TaskExecution = TaskExecution::ExecuteEach;
type Config = Self;
type Message = SendGotoImplementationMessage;
#[inline(always)]
fn init(config: Self::Config) -> Self {
config
}
fn handle(&mut self, message: Self::Message) -> bool {
loop {
if message.cancel.is_active() {
warn!(target: LSP_SERVER_LOG, "[{}] Send goto implementation cancelled by the client.", message.uri.as_str());
self.outgoing.send_err_response(
&self.latches,
message.id,
REQUEST_CANCELLED,
"Send goto implementation cancelled by the client.",
);
break;
}
let handle = LspHandle::new(&message.cancel);
let module_read_guard = match self.module.as_ref().read(&handle, ANALYSIS_PRIORITY) {
Ok(guard) => guard,
Err(ModuleError::Interrupted(_)) => {
if message.cancel.is_active() {
warn!(target: LSP_SERVER_LOG, "[{}] Send goto implementation cancelled by the client.", message.uri.as_str());
self.outgoing.send_err_response(
&self.latches,
message.id,
REQUEST_CANCELLED,
"Send goto implementation cancelled by the client.",
);
break;
}
warn!(target: LSP_SERVER_LOG, "[{}] Send goto implementation interrupted.", message.uri.as_str());
park_timeout(COOL_DOWN);
continue;
}
Err(error) => {
error!(target: LSP_SERVER_LOG, "[{}] Send goto implementation error. {error}", message.uri.as_str());
self.outgoing.send_err_response(
&self.latches,
message.id,
REQUEST_FAILED,
"Send goto implementation error.",
);
break;
}
};
let position = lsp_position_to_ld(&message.position);
const GOTO_SYMBOLS: u32 = (SymbolKind::Ident as u32) | (SymbolKind::Field as u32);
let symbols = match module_read_guard.symbols(
position..position,
LookupOptions::new().filter(GOTO_SYMBOLS),
) {
Ok(symbols) => symbols,
Err(ModuleError::Interrupted(_)) => {
if message.cancel.is_active() {
warn!(target: LSP_SERVER_LOG, "[{}] Send goto implementation cancelled by the client.", message.uri.as_str());
self.outgoing.send_err_response(
&self.latches,
message.id,
REQUEST_CANCELLED,
"Send goto implementation cancelled by the client.",
);
break;
}
warn!(target: LSP_SERVER_LOG, "[{}] Send goto implementation interrupted.", message.uri.as_str());
park_timeout(COOL_DOWN);
continue;
}
Err(error) => {
error!(target: LSP_SERVER_LOG, "[{}] Send goto implementation error. {error}", message.uri.as_str());
self.outgoing.send_err_response(
&self.latches,
message.id,
REQUEST_FAILED,
"Send goto implementation error.",
);
break;
}
};
let ty = match symbols.first() {
Some(ModuleSymbol::Ident(symbol)) => symbol.ty(&module_read_guard),
Some(ModuleSymbol::Field(symbol)) => symbol.ty(&module_read_guard),
_ => {
self.outgoing.send_ok_response::<GotoImplementation>(
&self.latches,
message.id,
None,
);
break;
}
};
let ty = match ty {
Ok(symbol) => symbol,
Err(ModuleError::Interrupted(_)) => {
if message.cancel.is_active() {
warn!(target: LSP_SERVER_LOG, "[{}] Send goto implementation cancelled by the client.", message.uri.as_str());
self.outgoing.send_err_response(
&self.latches,
message.id,
REQUEST_CANCELLED,
"Send goto implementation cancelled by the client.",
);
break;
}
warn!(target: LSP_SERVER_LOG, "[{}] Send goto implementation interrupted.", message.uri.as_str());
park_timeout(COOL_DOWN);
continue;
}
Err(error) => {
error!(target: LSP_SERVER_LOG, "[{}] Send goto implementation error. {error}", message.uri.as_str());
self.outgoing.send_err_response(
&self.latches,
message.id,
REQUEST_FAILED,
"Send goto implementation error.",
);
break;
}
};
let origin = ty.impl_symbol.origin(&module_read_guard);
let text = module_read_guard.text();
let Some(span) = origin.to_position_span(&text) else {
self.outgoing.send_ok_response::<GotoImplementation>(
&self.latches,
message.id,
None,
);
break;
};
let range = span_to_range(&span);
self.outgoing.send_ok_response::<GotoImplementation>(
&self.latches,
message.id,
Some(GotoImplementationResponse::Scalar(Location {
uri: message.uri,
range,
})),
);
break;
}
true
}
#[inline(always)]
fn module(&self) -> &LspModule {
&self.module
}
}
pub(super) struct SendGotoImplementationMessage {
pub(super) id: RpcId,
pub(super) uri: Uri,
pub(super) cancel: Trigger,
pub(super) position: Position,
}