use {
crate::{
TRACER,
Uri,
database::PartitionWriteContextRef,
protocol::{
jsonrpc::{
self,
Error,
Response,
Result,
},
lsp::{
ClientCapabilities,
ClientInfo,
DeclarationCapability,
DeclarationOptions,
DefinitionOptions,
DiagnosticOptions,
DiagnosticServerCapabilities,
DocumentFormattingOptions,
DocumentLinkOptions,
DocumentOnTypeFormattingOptions,
DocumentRangeFormattingOptions,
DocumentSymbolOptions,
HoverOptions,
HoverProviderCapability,
OneOf,
PositionEncodingKind,
ReferenceOptions,
RenameOptions,
SaveOptions,
ServerCapabilities,
ServerInfo,
StaticTextDocumentRegistrationOptions,
TextDocumentSyncCapability,
TextDocumentSyncKind,
TextDocumentSyncOptions,
TextDocumentSyncSaveOptions,
TraceValue,
TypeDefinitionProviderCapability,
WorkDoneProgressOptions,
WorkDoneProgressParams,
WorkspaceFolder,
WorkspaceFoldersServerCapabilities,
WorkspaceServerCapabilities,
WorkspaceSymbolOptions,
},
task::State,
},
scheduler::task::TaskContext,
},
opentelemetry::trace::FutureExt,
serde::{
Deserialize,
Serialize,
},
};
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InitializeParams {
pub process_id: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[deprecated(note = "Use `root_uri` instead when possible")]
pub root_path: Option<String>,
#[serde(default)]
#[deprecated(note = "Use `workspace_folders` instead when possible")]
pub root_uri: Option<Uri>,
#[serde(skip_serializing_if = "Option::is_none")]
pub initialization_options: Option<serde_json::Value>,
pub capabilities: ClientCapabilities,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub trace: Option<TraceValue>,
#[serde(skip_serializing_if = "Option::is_none")]
pub workspace_folders: Option<Vec<WorkspaceFolder>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub client_info: Option<ClientInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub locale: Option<String>,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
}
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InitializeResult {
pub capabilities: ServerCapabilities,
#[serde(skip_serializing_if = "Option::is_none")]
pub server_info: Option<ServerInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg(feature = "proposed")]
pub offset_encoding: Option<String>,
}
pub trait InitializeService<
P: crate::database::storage::Partitions,
T: crate::protocol::lsp::LanguageServer<P>,
>: Send + Sync + 'static
{
fn initialize(
&self,
params: InitializeParams,
ctx: &mut TaskContext<P, T>,
writer: &mut PartitionWriteContextRef<'_, P>,
) -> impl std::future::Future<Output = jsonrpc::Result<InitializeResult>> + Send
{
let cx = otel::span!(^
"laburnum.lsp.initialize",
"has_workspace_folders" = params.workspace_folders.is_some(),
"has_root_uri" = {
#[allow(deprecated)]
params.root_uri.is_some()
},
"client_name" = params.client_info.as_ref().map(|c| c.name.clone()).unwrap_or_else(|| "unknown".to_string())
);
async move {
use crate::protocol::lsp::SourceFileMeta;
let position_encoding = {
let client_encodings = params
.capabilities
.general
.as_ref()
.and_then(|g| g.position_encodings.as_ref());
if let Some(encodings) = client_encodings {
if encodings.contains(&PositionEncodingKind::UTF8) {
PositionEncodingKind::UTF8
} else if encodings.contains(&PositionEncodingKind::UTF32) {
PositionEncodingKind::UTF32
} else {
PositionEncodingKind::DEFAULT
}
} else {
PositionEncodingKind::DEFAULT
}
};
if let Some(mut client) = ctx.scheduler().registry().get_mut(ctx.client_id()) {
client.set_position_encoding(position_encoding.clone());
}
let position_encoding = Some(position_encoding);
let glob_patterns = ctx.server().source_files_glob();
let file_operation_filters: Vec<
crate::protocol::lsp::FileOperationFilter,
> = glob_patterns
.iter()
.map(|pattern| {
crate::protocol::lsp::FileOperationFilter {
scheme: Some("file".to_string()),
pattern: crate::protocol::lsp::FileOperationPattern {
glob: pattern.clone(),
matches: None,
options: None,
},
}
})
.collect();
#[allow(deprecated)]
if !glob_patterns.is_empty() {
let mut workspace_uris = Vec::new();
if let Some(folders) = ¶ms.workspace_folders {
let source_cache = ctx.source_cache();
let mut cache = source_cache.write();
for folder in folders {
cache.add_workspace_folder(folder.clone());
workspace_uris.push(folder.uri.clone());
}
} else if let Some(root_uri) = params.root_uri {
workspace_uris.push(root_uri.clone());
}
if !workspace_uris.is_empty() {
let task = crate::source::task::SourceCacheIndexTask::create(
ctx.scheduler(),
workspace_uris,
glob_patterns,
);
ctx.scheduler().queue_task(task);
}
}
let file_operations = if file_operation_filters.is_empty() {
None
} else {
Some(
crate::protocol::lsp::WorkspaceFileOperationsServerCapabilities {
did_create: Some(
crate::protocol::lsp::FileOperationRegistrationOptions {
filters: file_operation_filters.clone(),
},
),
will_create: None,
did_rename: Some(
crate::protocol::lsp::FileOperationRegistrationOptions {
filters: file_operation_filters.clone(),
},
),
will_rename: None,
did_delete: Some(
crate::protocol::lsp::FileOperationRegistrationOptions {
filters: file_operation_filters,
},
),
will_delete: None,
},
)
};
let mut capabilities = ServerCapabilities {
position_encoding,
text_document_sync: Some(TextDocumentSyncCapability::Options(
TextDocumentSyncOptions {
open_close: Some(true),
change: Some(TextDocumentSyncKind::INCREMENTAL),
will_save: Some(false),
will_save_wait_until: Some(false),
save: Some(
TextDocumentSyncSaveOptions::SaveOptions(SaveOptions {
include_text: Some(true),
}),
),
},
)),
workspace: Some(WorkspaceServerCapabilities {
workspace_folders: Some(WorkspaceFoldersServerCapabilities {
supported: Some(true),
change_notifications: Some(OneOf::Left(true)),
}),
file_operations,
}),
document_symbol_provider: Some(OneOf::Right(DocumentSymbolOptions {
work_done_progress_options: WorkDoneProgressOptions {
work_done_progress: Some(true),
},
label: None,
})),
workspace_symbol_provider: Some(OneOf::Right(WorkspaceSymbolOptions {
work_done_progress_options: WorkDoneProgressOptions {
work_done_progress: Some(true),
},
resolve_provider: Some(true),
})),
document_formatting_provider: Some(OneOf::Right(
DocumentFormattingOptions {
work_done_progress_options: WorkDoneProgressOptions {
work_done_progress: Some(true),
},
},
)),
document_range_formatting_provider: Some(OneOf::Right(
DocumentRangeFormattingOptions {
work_done_progress_options: WorkDoneProgressOptions {
work_done_progress: Some(true),
},
},
)),
document_link_provider: Some(DocumentLinkOptions {
work_done_progress_options: WorkDoneProgressOptions {
work_done_progress: Some(true),
},
resolve_provider: None,
}),
hover_provider: Some(HoverProviderCapability::Options(HoverOptions {
work_done_progress_options: WorkDoneProgressOptions {
work_done_progress: Some(true),
},
})),
definition_provider: Some(OneOf::Right(DefinitionOptions {
work_done_progress_options: WorkDoneProgressOptions {
work_done_progress: Some(true),
},
})),
declaration_provider: Some(DeclarationCapability::Options(
DeclarationOptions {
work_done_progress_options: WorkDoneProgressOptions {
work_done_progress: Some(true),
},
},
)),
type_definition_provider: Some(
TypeDefinitionProviderCapability::Options(
StaticTextDocumentRegistrationOptions {
document_selector: None,
id: None,
},
),
),
rename_provider: Some(OneOf::Right(RenameOptions {
prepare_provider: Some(false),
work_done_progress_options: WorkDoneProgressOptions {
work_done_progress: Some(true),
},
})),
references_provider: Some(OneOf::Right(ReferenceOptions {
work_done_progress_options: WorkDoneProgressOptions {
work_done_progress: Some(true),
},
})),
diagnostic_provider: Some(DiagnosticServerCapabilities::Options(
DiagnosticOptions {
identifier: None,
inter_file_dependencies: true,
workspace_diagnostics: true,
work_done_progress_options: WorkDoneProgressOptions {
work_done_progress: Some(true),
},
},
)),
..Default::default()
};
match T::on_initialize(ctx, writer, &mut capabilities).await {
| Ok(_) => (),
| Err(err) => return Err(err),
}
Ok(InitializeResult {
capabilities,
server_info: T::SERVER_NAME.map(|name| ServerInfo {
name: name.to_string(),
version: T::SERVER_VERSION.map(str::to_string),
}),
offset_encoding: None,
})
}
.with_context(cx)
}
const INITIALIZE_LANE: crate::scheduler::lanes::Lane =
crate::scheduler::lanes::DEFAULT_LANE;
}