use {
crate::{
Uri,
database::PartitionWriteContextRef,
protocol::{
jsonrpc,
lsp::{
Position,
PositionEncodingKind,
Range,
},
macros::lsp_enum,
},
scheduler::task::TaskContext,
},
serde::{
Deserialize,
Serialize,
de::Error,
},
std::collections::HashMap,
};
#[cfg(feature = "proposed")]
use crate::protocol::lsp::OneOf;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct LspVersion(i32);
impl LspVersion {
pub const fn raw(self) -> i32 {
self.0
}
#[cfg(test)]
pub const fn new(value: i32) -> Self {
Self(value)
}
}
impl From<i32> for LspVersion {
fn from(value: i32) -> Self {
Self(value)
}
}
impl From<LspVersion> for i32 {
fn from(value: LspVersion) -> Self {
value.0
}
}
fn deserialize_lsp_version<'de, D>(deserializer: D) -> Result<LspVersion, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = i32::deserialize(deserializer)?;
Ok(value.into())
}
fn serialize_lsp_version<S>(version: &LspVersion, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_i32(version.raw())
}
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TextEdit {
pub range: Range,
pub new_text: String,
}
impl TextEdit {
#[must_use]
pub const fn new(range: Range, new_text: String) -> Self {
Self { range, new_text }
}
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ChangeAnnotation {
pub label: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub needs_confirmation: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
}
pub type ChangeAnnotationIdentifier = String;
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AnnotatedTextEdit {
#[serde(flatten)]
pub text_edit: TextEdit,
pub annotation_id: ChangeAnnotationIdentifier,
}
#[cfg(feature = "proposed")]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(tag = "kind")]
pub enum StringValue {
Snippet(String),
}
#[cfg(feature = "proposed")]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SnippetTextEdit {
pub range: Range,
pub snippet: StringValue,
#[serde(skip_serializing_if = "Option::is_none")]
pub annotation_id: Option<ChangeAnnotationIdentifier>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TextDocumentEdit {
pub text_document: OptionalVersionedTextDocumentIdentifier,
#[cfg(not(feature = "proposed"))]
pub edits: Vec<OneOf<TextEdit, AnnotatedTextEdit>>,
#[cfg(feature = "proposed")]
pub edits: Vec<OneOf<TextEdit, OneOf<AnnotatedTextEdit, SnippetTextEdit>>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TextDocumentItem {
pub uri: Uri,
pub language_id: String,
#[serde(deserialize_with = "deserialize_lsp_version", serialize_with = "serialize_lsp_version")]
pub version: LspVersion,
pub text: String,
}
impl TextDocumentItem {
#[must_use]
pub const fn new(
uri: Uri,
language_id: String,
version: LspVersion,
text: String,
) -> Self {
Self {
uri,
language_id,
version,
text,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct TextDocumentIdentifier {
pub uri: Uri,
}
impl TextDocumentIdentifier {
#[must_use]
pub const fn new(uri: Uri) -> Self {
Self { uri }
}
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct VersionedTextDocumentIdentifier {
pub uri: Uri,
#[serde(deserialize_with = "deserialize_lsp_version", serialize_with = "serialize_lsp_version")]
pub version: LspVersion,
}
impl VersionedTextDocumentIdentifier {
#[must_use]
pub const fn new(uri: Uri, version: LspVersion) -> Self {
Self { uri, version }
}
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct OptionalVersionedTextDocumentIdentifier {
pub uri: Uri,
pub version: Option<i32>,
}
impl OptionalVersionedTextDocumentIdentifier {
#[must_use]
pub const fn new(uri: Uri, version: i32) -> Self {
Self {
uri,
version: Some(version),
}
}
}
pub use super::text_document_position::TextDocumentPositionParams;
#[derive(Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
#[serde(transparent)]
pub struct TextDocumentSyncKind(i32);
lsp_enum! {
impl TextDocumentSyncKind {
const NONE = 0;
const FULL = 1;
const INCREMENTAL = 2;
}
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct DocumentFilter {
#[serde(skip_serializing_if = "Option::is_none")]
pub language: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub scheme: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pattern: Option<String>,
}
pub type DocumentSelector = Vec<DocumentFilter>;
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TextDocumentRegistrationOptions {
pub document_selector: Option<DocumentSelector>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DidOpenTextDocumentParams {
pub text_document: TextDocumentItem,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DidChangeTextDocumentParams {
pub text_document: VersionedTextDocumentIdentifier,
pub content_changes: Vec<TextDocumentContentChangeEvent>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TextDocumentContentChangeEvent {
#[serde(skip_serializing_if = "Option::is_none")]
pub range: Option<Range>,
#[serde(skip_serializing_if = "Option::is_none")]
pub range_length: Option<u32>,
pub text: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TextDocumentChangeRegistrationOptions {
pub document_selector: Option<DocumentSelector>,
pub sync_kind: TextDocumentSyncKind,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WillSaveTextDocumentParams {
pub text_document: TextDocumentIdentifier,
pub reason: TextDocumentSaveReason,
}
#[derive(Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
#[serde(transparent)]
pub struct TextDocumentSaveReason(i32);
lsp_enum! {
impl TextDocumentSaveReason {
const MANUAL = 1;
const AFTER_DELAY = 2;
const FOCUS_OUT = 3;
}
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DidCloseTextDocumentParams {
pub text_document: TextDocumentIdentifier,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DidSaveTextDocumentParams {
pub text_document: TextDocumentIdentifier,
#[serde(skip_serializing_if = "Option::is_none")]
pub text: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TextDocumentSaveRegistrationOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub include_text: Option<bool>,
#[serde(flatten)]
pub text_document_registration_options: TextDocumentRegistrationOptions,
}
pub trait TextDocumentService<
P: crate::database::storage::Partitions,
T: crate::protocol::lsp::LanguageServer<P>,
>: Send + Sync + 'static
{
fn did_open(
&self,
params: DidOpenTextDocumentParams,
ctx: &mut TaskContext<P, T>,
writer: &mut PartitionWriteContextRef<'_, P>,
) -> impl std::future::Future<Output = ()> + Send {
let client_id = ctx.client_id();
async move {
let uri = params.text_document.uri.clone();
let version = params.text_document.version;
let source_cache = ctx.source_cache();
let mut cache = source_cache.write();
match cache.upsert_with_version(
uri.clone(),
params.text_document.text,
version,
client_id,
) {
| Ok((key, changed)) => {
cache.mark_file_open(uri.clone(), client_id);
},
| Err(e) => {},
}
}
}
const DID_OPEN_LANE: crate::scheduler::lanes::Lane =
crate::scheduler::lanes::INPUT_CONTINUOUS_LANE;
fn did_change(
&self,
params: DidChangeTextDocumentParams,
ctx: &mut TaskContext<P, T>,
writer: &mut PartitionWriteContextRef<'_, P>,
) -> impl std::future::Future<Output = ()> + Send {
let client_id = ctx.client_id();
let encoding = ctx.position_encoding();
async move {
let cache_lock = ctx.source_cache();
let mut cache = cache_lock.write();
match cache.apply_content_changes(
¶ms.text_document.uri,
params.text_document.version,
params.content_changes,
&encoding,
client_id,
) {
| Ok((key, changed)) => {},
| Err(e) => {},
}
}
}
const DID_CHANGE_LANE: crate::scheduler::lanes::Lane =
crate::scheduler::lanes::INPUT_CONTINUOUS_LANE;
fn did_save(
&self,
params: DidSaveTextDocumentParams,
ctx: &mut TaskContext<P, T>,
writer: &mut PartitionWriteContextRef<'_, P>,
) -> impl std::future::Future<Output = ()> + Send {
async move {
let _ = params;
}
}
const DID_SAVE_LANE: crate::scheduler::lanes::Lane =
crate::scheduler::lanes::DEFAULT_LANE;
fn did_close(
&self,
params: DidCloseTextDocumentParams,
ctx: &mut TaskContext<P, T>,
writer: &mut PartitionWriteContextRef<'_, P>,
) -> impl std::future::Future<Output = ()> + Send {
let client_id = ctx.client_id();
async move {
let cache_lock = ctx.source_cache();
let mut cache = cache_lock.write();
cache.mark_file_closed(¶ms.text_document.uri, client_id);
}
}
const DID_CLOSE_LANE: crate::scheduler::lanes::Lane =
crate::scheduler::lanes::DEFAULT_LANE;
const TEXT_DOCUMENT_LANE: crate::scheduler::lanes::Lane =
crate::scheduler::lanes::DEFAULT_LANE;
}
pub trait TextDocumentHookService<
P: crate::database::storage::Partitions,
T: crate::protocol::lsp::LanguageServer<P>,
>: Send + Sync + 'static
{
fn will_save(
&self,
params: WillSaveTextDocumentParams,
ctx: &mut TaskContext<P, T>,
writer: &mut PartitionWriteContextRef<'_, P>,
) -> impl std::future::Future<Output = ()> + Send {
async move {}
}
const WILL_SAVE_LANE: crate::scheduler::lanes::Lane =
crate::scheduler::lanes::DEFAULT_LANE;
fn will_save_wait_until(
&self,
params: WillSaveTextDocumentParams,
ctx: &mut TaskContext<P, T>,
writer: &mut PartitionWriteContextRef<'_, P>,
) -> impl std::future::Future<Output = jsonrpc::Result<Option<Vec<TextEdit>>>> + Send
{
async move { Ok(None) }
}
const WILL_SAVE_WAIT_UNTIL_LANE: crate::scheduler::lanes::Lane =
crate::scheduler::lanes::DEFAULT_LANE;
}