use {
crate::{
TRACER,
database::{
PartitionKey,
PartitionWriteContextRef,
},
partitions::DocumentSymbols,
protocol::{
jsonrpc,
lsp::{
Location,
LocationLink,
PartialResultParams,
TextDocumentPositionParams,
WorkDoneProgressParams,
},
},
scheduler::task::TaskContext,
},
opentelemetry::trace::FutureExt,
serde::{
Deserialize,
Serialize,
},
};
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GotoDefinitionParams {
#[serde(flatten)]
pub text_document_position_params: TextDocumentPositionParams,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
#[serde(flatten)]
pub partial_result_params: PartialResultParams,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(untagged)]
pub enum GotoDefinitionResponse {
Scalar(Location),
Array(Vec<Location>),
Link(Vec<LocationLink>),
}
impl From<Location> for GotoDefinitionResponse {
fn from(location: Location) -> Self {
Self::Scalar(location)
}
}
impl From<Vec<Location>> for GotoDefinitionResponse {
fn from(locations: Vec<Location>) -> Self {
Self::Array(locations)
}
}
impl From<Vec<LocationLink>> for GotoDefinitionResponse {
fn from(locations: Vec<LocationLink>) -> Self {
Self::Link(locations)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GotoDeclarationParams {
#[serde(flatten)]
pub text_document_position_params: TextDocumentPositionParams,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
#[serde(flatten)]
pub partial_result_params: PartialResultParams,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(untagged)]
pub enum GotoDeclarationResponse {
Scalar(Location),
Array(Vec<Location>),
Link(Vec<LocationLink>),
}
impl From<Location> for GotoDeclarationResponse {
fn from(location: Location) -> Self {
Self::Scalar(location)
}
}
impl From<Vec<Location>> for GotoDeclarationResponse {
fn from(locations: Vec<Location>) -> Self {
Self::Array(locations)
}
}
impl From<Vec<LocationLink>> for GotoDeclarationResponse {
fn from(locations: Vec<LocationLink>) -> Self {
Self::Link(locations)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GotoTypeDefinitionParams {
#[serde(flatten)]
pub text_document_position_params: TextDocumentPositionParams,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
#[serde(flatten)]
pub partial_result_params: PartialResultParams,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(untagged)]
pub enum GotoTypeDefinitionResponse {
Scalar(Location),
Array(Vec<Location>),
Link(Vec<LocationLink>),
}
impl From<Location> for GotoTypeDefinitionResponse {
fn from(location: Location) -> Self {
Self::Scalar(location)
}
}
impl From<Vec<Location>> for GotoTypeDefinitionResponse {
fn from(locations: Vec<Location>) -> Self {
Self::Array(locations)
}
}
impl From<Vec<LocationLink>> for GotoTypeDefinitionResponse {
fn from(locations: Vec<LocationLink>) -> Self {
Self::Link(locations)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GotoImplementationParams {
#[serde(flatten)]
pub text_document_position_params: TextDocumentPositionParams,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
#[serde(flatten)]
pub partial_result_params: PartialResultParams,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(untagged)]
pub enum GotoImplementationResponse {
Scalar(Location),
Array(Vec<Location>),
Link(Vec<LocationLink>),
}
impl From<Location> for GotoImplementationResponse {
fn from(location: Location) -> Self {
Self::Scalar(location)
}
}
impl From<Vec<Location>> for GotoImplementationResponse {
fn from(locations: Vec<Location>) -> Self {
Self::Array(locations)
}
}
impl From<Vec<LocationLink>> for GotoImplementationResponse {
fn from(locations: Vec<LocationLink>) -> Self {
Self::Link(locations)
}
}
pub trait GotoService<
P: crate::database::storage::Partitions,
T: crate::protocol::lsp::LanguageServer<P>,
>: Send + Sync + 'static
{
fn goto_declaration(
&self,
params: GotoDeclarationParams,
ctx: &mut TaskContext<P, T>,
writer: &mut PartitionWriteContextRef<'_, P>,
) -> impl std::future::Future<
Output = jsonrpc::Result<Option<GotoDeclarationResponse>>,
> + Send {
let uri = params
.text_document_position_params
.text_document
.uri
.clone();
let line = params.text_document_position_params.position.line;
let character = params.text_document_position_params.position.character;
let definition_params = GotoDefinitionParams {
text_document_position_params: params.text_document_position_params,
work_done_progress_params: params.work_done_progress_params,
partial_result_params: params.partial_result_params,
};
let cx = otel::span!(^
"laburnum.lsp.goto_declaration",
"document.uri" = uri.to_string(),
"position.line" = line as i64,
"position.character" = character as i64
);
async move {
let result = self.goto_definition(definition_params, ctx, writer).await?;
Ok(result.map(|response| {
match response {
| GotoDefinitionResponse::Scalar(location) => {
GotoDeclarationResponse::Scalar(location)
},
| GotoDefinitionResponse::Array(locations) => {
GotoDeclarationResponse::Array(locations)
},
| GotoDefinitionResponse::Link(links) => {
GotoDeclarationResponse::Link(links)
},
}
}))
}
.with_context(cx)
}
const GOTO_DECLARATION_LANE: crate::scheduler::lanes::Lane =
crate::scheduler::lanes::DEFAULT_LANE;
fn goto_definition(
&self,
params: GotoDefinitionParams,
ctx: &mut TaskContext<P, T>,
writer: &mut PartitionWriteContextRef<'_, P>,
) -> impl std::future::Future<
Output = jsonrpc::Result<Option<GotoDefinitionResponse>>,
> + Send {
let _ = writer;
let cx = otel::span!(^
"laburnum.lsp.goto_definition",
"document.uri" = params.text_document_position_params.text_document.uri.to_string(),
"position.line" = params.text_document_position_params.position.line as i64,
"position.character" = params.text_document_position_params.position.character as i64
);
async move {
let uri = params.text_document_position_params.text_document.uri;
let position = params.text_document_position_params.position;
let source_key = {
let cache = ctx.source_cache();
let guard = cache.read();
match guard.latest_key(&uri) {
| Some(key) => key,
| None => {
otel::event!("goto.source_key_not_found", "uri" = uri.to_string());
return Ok(None);
},
}
};
use crate::{
partitions::TextDocumentPosition,
record::LaburnumRecordRef,
source::line_ops::LineOps,
};
let encoding = ctx.position_encoding();
let source_cache = ctx.source_cache_reader();
let byte_offset = {
let source = match source_cache.get_source(source_key) {
Some(s) => s,
None => return Ok(None),
};
match source.line_col_to_byte(position.line, position.character, &encoding) {
Some(o) => o as u64,
None => return Ok(None),
}
};
let symbol_hash = ctx
.query_client()
.span_index_get::<TextDocumentPosition>(&uri, byte_offset)
.and_then(|record| {
record.as_text_document_position().map(|r| r.symbol_hash())
});
if let Some(location) = symbol_hash.and_then(|hash| {
ctx
.query_client()
.get_by_hash::<DocumentSymbols>(hash)
.and_then(|r| {
r.as_document_symbol()
.and_then(|sym| sym.definition_location(&source_cache, &encoding))
})
}) {
otel::event!("goto.found", "uri" = location.uri.to_string());
return Ok(Some(GotoDefinitionResponse::Scalar(location)));
}
otel::event!("goto.returning_none");
Ok(None)
}
.with_context(cx)
}
const GOTO_DEFINITION_LANE: crate::scheduler::lanes::Lane =
crate::scheduler::lanes::DEFAULT_LANE;
fn goto_type_definition(
&self,
params: GotoTypeDefinitionParams,
ctx: &mut TaskContext<P, T>,
writer: &mut PartitionWriteContextRef<'_, P>,
) -> impl std::future::Future<
Output = jsonrpc::Result<Option<GotoTypeDefinitionResponse>>,
> + Send {
async move {
otel::span!("laburnum.lsp.goto_type_definition");
Err(jsonrpc::Error::method_not_found())
}
}
const GOTO_TYPE_DEFINITION_LANE: crate::scheduler::lanes::Lane =
crate::scheduler::lanes::DEFAULT_LANE;
fn goto_implementation(
&self,
params: GotoImplementationParams,
ctx: &mut TaskContext<P, T>,
writer: &mut PartitionWriteContextRef<'_, P>,
) -> impl std::future::Future<
Output = jsonrpc::Result<Option<GotoImplementationResponse>>,
> + Send {
async move {
otel::span!("laburnum.lsp.goto_implementation");
Err(jsonrpc::Error::method_not_found())
}
}
const GOTO_IMPLEMENTATION_LANE: crate::scheduler::lanes::Lane =
crate::scheduler::lanes::DEFAULT_LANE;
}