use {
crate::{
TRACER,
database::PartitionWriteContextRef,
protocol::{
jsonrpc,
lsp::{
DocumentSelector,
DynamicRegistrationClientCapabilities,
Range,
TextDocumentIdentifier,
TextDocumentPositionParams,
TextEdit,
WorkDoneProgressParams,
},
},
scheduler::task::TaskContext,
},
serde::{
Deserialize,
Serialize,
},
std::collections::HashMap,
};
pub type DocumentFormattingClientCapabilities =
DynamicRegistrationClientCapabilities;
pub type DocumentRangeFormattingClientCapabilities =
DynamicRegistrationClientCapabilities;
pub type DocumentOnTypeFormattingClientCapabilities =
DynamicRegistrationClientCapabilities;
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DocumentOnTypeFormattingOptions {
pub first_trigger_character: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub more_trigger_character: Option<Vec<String>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DocumentFormattingParams {
pub text_document: TextDocumentIdentifier,
pub options: FormattingOptions,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
}
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FormattingOptions {
pub tab_size: u32,
pub insert_spaces: bool,
#[serde(flatten)]
pub properties: HashMap<String, FormattingProperty>,
#[serde(skip_serializing_if = "Option::is_none")]
pub trim_trailing_whitespace: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub insert_final_newline: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub trim_final_newlines: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(untagged)]
pub enum FormattingProperty {
Bool(bool),
Number(i32),
String(String),
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DocumentRangeFormattingParams {
pub text_document: TextDocumentIdentifier,
pub range: Range,
pub options: FormattingOptions,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DocumentOnTypeFormattingParams {
#[serde(flatten)]
pub text_document_position: TextDocumentPositionParams,
pub ch: String,
pub options: FormattingOptions,
}
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DocumentOnTypeFormattingRegistrationOptions {
pub document_selector: Option<DocumentSelector>,
pub first_trigger_character: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub more_trigger_character: Option<Vec<String>>,
}
pub trait FormattingService<
P: crate::database::storage::Partitions,
T: crate::protocol::lsp::LanguageServer<P>,
>: Send + Sync + 'static
{
fn formatting(
&self,
params: DocumentFormattingParams,
ctx: &mut TaskContext<P, T>,
writer: &mut PartitionWriteContextRef<'_, P>,
) -> impl std::future::Future<Output = jsonrpc::Result<Option<Vec<TextEdit>>>> + Send
{
async move {
otel::span!(
"laburnum.lsp.formatting",
"document.uri" = params.text_document.uri.to_string()
);
Err(jsonrpc::Error::method_not_found())
}
}
const FORMATTING_LANE: crate::scheduler::lanes::Lane =
crate::scheduler::lanes::DEFAULT_LANE;
fn range_formatting(
&self,
params: DocumentRangeFormattingParams,
ctx: &mut TaskContext<P, T>,
writer: &mut PartitionWriteContextRef<'_, P>,
) -> impl std::future::Future<Output = jsonrpc::Result<Option<Vec<TextEdit>>>> + Send
{
async move {
otel::span!(
"laburnum.lsp.range_formatting",
"document.uri" = params.text_document.uri.to_string(),
"range.start.line" = params.range.start.line as i64,
"range.end.line" = params.range.end.line as i64
);
Err(jsonrpc::Error::method_not_found())
}
}
const RANGE_FORMATTING_LANE: crate::scheduler::lanes::Lane =
crate::scheduler::lanes::DEFAULT_LANE;
fn on_type_formatting(
&self,
params: DocumentOnTypeFormattingParams,
ctx: &mut TaskContext<P, T>,
writer: &mut PartitionWriteContextRef<'_, P>,
) -> impl std::future::Future<Output = jsonrpc::Result<Option<Vec<TextEdit>>>> + Send
{
async move {
otel::span!(
"laburnum.lsp.on_type_formatting",
"document.uri" =
params.text_document_position.text_document.uri.to_string(),
"position.line" = params.text_document_position.position.line as i64,
"character" = params.ch.to_string()
);
Err(jsonrpc::Error::method_not_found())
}
}
const ON_TYPE_FORMATTING_LANE: crate::scheduler::lanes::Lane =
crate::scheduler::lanes::DEFAULT_LANE;
}
#[cfg(test)]
mod tests {
use {
super::*,
crate::protocol::tests::test_serialization,
};
#[test]
fn formatting_options() {
test_serialization(
&FormattingOptions {
tab_size: 123,
insert_spaces: true,
properties: HashMap::new(),
trim_trailing_whitespace: None,
insert_final_newline: None,
trim_final_newlines: None,
},
r#"{"tabSize":123,"insertSpaces":true}"#,
);
test_serialization(
&FormattingOptions {
tab_size: 123,
insert_spaces: true,
properties: vec![(
"prop".to_string(),
FormattingProperty::Number(1),
)]
.into_iter()
.collect(),
trim_trailing_whitespace: None,
insert_final_newline: None,
trim_final_newlines: None,
},
r#"{"tabSize":123,"insertSpaces":true,"prop":1}"#,
);
}
}