use {
crate::{
TRACER,
database::{
DynPartition,
PartitionKey,
PartitionWriteContextRef,
},
partitions::{
DocumentFoldingRange,
document_folding_range::FoldingRangeSortKey,
},
protocol::{
jsonrpc,
lsp::{
PartialResultParams,
StaticTextDocumentColorProviderOptions,
TextDocumentIdentifier,
WorkDoneProgressParams,
},
},
record::LaburnumRecordRef,
scheduler::task::TaskContext,
},
opentelemetry::trace::FutureExt,
serde::{
Deserialize,
Serialize,
},
};
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FoldingRangeParams {
pub text_document: TextDocumentIdentifier,
#[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 FoldingRangeProviderCapability {
Simple(bool),
FoldingProvider(FoldingProviderOptions),
Options(StaticTextDocumentColorProviderOptions),
}
impl From<StaticTextDocumentColorProviderOptions>
for FoldingRangeProviderCapability
{
fn from(from: StaticTextDocumentColorProviderOptions) -> Self {
Self::Options(from)
}
}
impl From<FoldingProviderOptions> for FoldingRangeProviderCapability {
fn from(from: FoldingProviderOptions) -> Self {
Self::FoldingProvider(from)
}
}
impl From<bool> for FoldingRangeProviderCapability {
fn from(from: bool) -> Self {
Self::Simple(from)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct FoldingProviderOptions {}
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FoldingRangeKindCapability {
#[serde(skip_serializing_if = "Option::is_none")]
pub value_set: Option<Vec<FoldingRangeKind>>,
}
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FoldingRangeCapability {
#[serde(skip_serializing_if = "Option::is_none")]
pub collapsed_text: Option<bool>,
}
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FoldingRangeClientCapabilities {
#[serde(skip_serializing_if = "Option::is_none")]
pub dynamic_registration: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub range_limit: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub line_folding_only: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub folding_range_kind: Option<FoldingRangeKindCapability>,
#[serde(skip_serializing_if = "Option::is_none")]
pub folding_range: Option<FoldingRangeCapability>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum FoldingRangeKind {
Comment,
Imports,
Region,
}
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FoldingRange {
pub start_line: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub start_character: Option<u32>,
pub end_line: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub end_character: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub kind: Option<FoldingRangeKind>,
#[serde(skip_serializing_if = "Option::is_none")]
pub collapsed_text: Option<String>,
}
pub trait FoldingRangeService<
P: crate::database::storage::Partitions,
T: crate::protocol::lsp::LanguageServer<P>,
>: Send + Sync + 'static
{
fn folding_range(
&self,
params: FoldingRangeParams,
ctx: &mut TaskContext<P, T>,
_writer: &mut PartitionWriteContextRef<'_, P>,
) -> impl std::future::Future<
Output = jsonrpc::Result<Option<Vec<FoldingRange>>>,
> + Send {
let cx = otel::span!(
^"laburnum.lsp.folding_range",
"document.uri" = params.text_document.uri.to_string()
);
async move {
let uri = params.text_document.uri;
let source_key = {
let cache = ctx.source_cache();
let guard = cache.read();
match guard.latest_key(&uri) {
| Some(key) => key,
| None => return Ok(None),
}
};
let file_prefix =
FoldingRangeSortKey::FilePrefix { source_key }.to_string();
let query_results = ctx
.query_client()
.prefix_internal(DocumentFoldingRange::KEY, file_prefix)
.await;
let mut ranges: Vec<FoldingRange> = Vec::new();
for record_meta in query_results.records().iter() {
if let Some(record_ref) = query_results.get(record_meta)
&& let Some(folding_range_record) = record_ref.as_folding_range()
{
let range = folding_range_record.to_folding_range();
if range.start_line == range.end_line {
continue;
}
ranges.push(range);
}
}
if ranges.is_empty() {
Ok(None)
} else {
Ok(Some(ranges))
}
}
.with_context(cx)
}
const FOLDING_RANGE_LANE: crate::scheduler::lanes::Lane =
crate::scheduler::lanes::DEFAULT_LANE;
}