use {
crate::{
TRACER,
database::{
PartitionKey,
PartitionWriteContextRef,
},
protocol::{
jsonrpc,
lsp::{
MarkedString,
MarkupContent,
MarkupKind,
Range,
TextDocumentPositionParams,
TextDocumentRegistrationOptions,
WorkDoneProgressOptions,
WorkDoneProgressParams,
},
},
partitions::DocumentSymbols,
record::LaburnumRecordRef,
scheduler::task::TaskContext,
source::line_ops::LineOps,
},
opentelemetry::trace::FutureExt,
serde::{
Deserialize,
Serialize,
},
};
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct HoverClientCapabilities {
#[serde(skip_serializing_if = "Option::is_none")]
pub dynamic_registration: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub content_format: Option<Vec<MarkupKind>>,
}
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct HoverOptions {
#[serde(flatten)]
pub work_done_progress_options: WorkDoneProgressOptions,
}
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct HoverRegistrationOptions {
#[serde(flatten)]
pub text_document_registration_options: TextDocumentRegistrationOptions,
#[serde(flatten)]
pub hover_options: HoverOptions,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(untagged)]
pub enum HoverProviderCapability {
Simple(bool),
Options(HoverOptions),
}
impl From<HoverOptions> for HoverProviderCapability {
fn from(from: HoverOptions) -> Self {
Self::Options(from)
}
}
impl From<bool> for HoverProviderCapability {
fn from(from: bool) -> Self {
Self::Simple(from)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct HoverParams {
#[serde(flatten)]
pub text_document_position_params: TextDocumentPositionParams,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct Hover {
pub contents: HoverContents,
#[serde(skip_serializing_if = "Option::is_none")]
pub range: Option<Range>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(untagged)]
pub enum HoverContents {
Scalar(MarkedString),
Array(Vec<MarkedString>),
Markup(MarkupContent),
}
pub trait HoverService<
P: crate::database::storage::Partitions,
T: crate::protocol::lsp::LanguageServer<P>,
>: Send + Sync + 'static
{
fn hover(
&self,
params: HoverParams,
ctx: &mut TaskContext<P, T>,
writer: &mut PartitionWriteContextRef<'_, P>,
) -> impl std::future::Future<Output = jsonrpc::Result<Option<Hover>>> + Send
{
let _ = writer;
let cx = otel::span!(^
"laburnum.lsp.hover",
"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!("hover.source_key_not_found", "uri" = uri.to_string());
return Ok(None);
},
}
};
{
use crate::partitions::{
DocumentSymbols,
document_symbols::DocumentSymbolSortKey,
};
let file_prefix = DocumentSymbolSortKey::FilePrefix { source_key };
let results = ctx
.query_client()
.query_partition(DocumentSymbols)
.sort_key_begins_with(file_prefix)
.execute()
.await;
otel::event!(
"hover.document_symbols",
"source_key" = source_key.to_string(),
"symbol_count" = results.records.len() as i64
);
}
use crate::partitions::TextDocumentPosition;
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(hover) = symbol_hash.and_then(|hash| {
ctx
.query_client()
.get_by_hash::<DocumentSymbols>(hash)
.and_then(|r| {
r.as_document_symbol()
.and_then(|sym| sym.hover_info(&source_cache, &encoding))
})
}) {
otel::event!("hover.found");
return Ok(Some(hover));
}
otel::event!("hover.returning_none");
Ok(None)
}
.with_context(cx)
}
const HOVER_LANE: crate::scheduler::lanes::Lane =
crate::scheduler::lanes::DEFAULT_LANE;
}