use std::collections::HashMap;
use ecow::EcoString;
use lsp_types::{
ChangeAnnotation, ChangeAnnotationIdentifier, CodeActionDisabled, CodeActionKind, Command,
Diagnostic, InsertTextFormat, OneOf, OptionalVersionedTextDocumentIdentifier, ResourceOp, Url,
};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use super::LspRange;
use crate::completion::EcoTextEdit;
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct EcoSnippetTextEdit {
#[serde(flatten)]
edit: EcoTextEdit,
insert_text_format: Option<InsertTextFormat>,
}
impl EcoSnippetTextEdit {
pub fn new_plain(range: LspRange, new_text: EcoString) -> EcoSnippetTextEdit {
EcoSnippetTextEdit {
edit: EcoTextEdit::new(range, new_text),
insert_text_format: Some(InsertTextFormat::PLAIN_TEXT),
}
}
pub fn new(range: LspRange, new_text: EcoString) -> EcoSnippetTextEdit {
EcoSnippetTextEdit {
edit: EcoTextEdit::new(range, new_text),
insert_text_format: Some(InsertTextFormat::SNIPPET),
}
}
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct EcoAnnotatedTextEdit {
#[serde(flatten)]
pub text_edit: EcoSnippetTextEdit,
pub annotation_id: ChangeAnnotationIdentifier,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct EcoTextDocumentEdit {
pub text_document: OptionalVersionedTextDocumentIdentifier,
pub edits: Vec<OneOf<EcoSnippetTextEdit, EcoAnnotatedTextEdit>>,
}
#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CodeAction {
pub title: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub kind: Option<CodeActionKind>,
#[serde(skip_serializing_if = "Option::is_none")]
pub diagnostics: Option<Vec<Diagnostic>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub edit: Option<EcoWorkspaceEdit>,
#[serde(skip_serializing_if = "Option::is_none")]
pub command: Option<Command>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_preferred: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub disabled: Option<CodeActionDisabled>,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<Value>,
}
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct EcoWorkspaceEdit {
#[serde(with = "url_map")]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub changes: Option<HashMap<Url, Vec<EcoSnippetTextEdit>>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub document_changes: Option<EcoDocumentChanges>,
#[serde(skip_serializing_if = "Option::is_none")]
pub change_annotations: Option<HashMap<ChangeAnnotationIdentifier, ChangeAnnotation>>,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum EcoDocumentChanges {
Edits(Vec<EcoTextDocumentEdit>),
Operations(Vec<EcoDocumentChangeOperation>),
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(untagged, rename_all = "lowercase")]
pub enum EcoDocumentChangeOperation {
Op(ResourceOp),
Edit(EcoTextDocumentEdit),
}
mod url_map {
use std::marker::PhantomData;
use std::{collections::HashMap, fmt};
use lsp_types::Url;
use serde::de;
pub fn deserialize<'de, D, V>(deserializer: D) -> Result<Option<HashMap<Url, V>>, D::Error>
where
D: serde::Deserializer<'de>,
V: de::DeserializeOwned,
{
struct UrlMapVisitor<V> {
_marker: PhantomData<V>,
}
impl<V: de::DeserializeOwned> Default for UrlMapVisitor<V> {
fn default() -> Self {
UrlMapVisitor {
_marker: PhantomData,
}
}
}
impl<'de, V: de::DeserializeOwned> de::Visitor<'de> for UrlMapVisitor<V> {
type Value = HashMap<Url, V>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("map")
}
fn visit_map<M>(self, mut visitor: M) -> Result<Self::Value, M::Error>
where
M: de::MapAccess<'de>,
{
let mut values = HashMap::with_capacity(visitor.size_hint().unwrap_or(0));
while let Some((key, value)) = visitor.next_entry::<Url, _>()? {
values.insert(key, value);
}
Ok(values)
}
}
struct OptionUrlMapVisitor<V> {
_marker: PhantomData<V>,
}
impl<V: de::DeserializeOwned> Default for OptionUrlMapVisitor<V> {
fn default() -> Self {
OptionUrlMapVisitor {
_marker: PhantomData,
}
}
}
impl<'de, V: de::DeserializeOwned> de::Visitor<'de> for OptionUrlMapVisitor<V> {
type Value = Option<HashMap<Url, V>>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("option")
}
#[inline]
fn visit_unit<E>(self) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(None)
}
#[inline]
fn visit_none<E>(self) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(None)
}
#[inline]
fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer
.deserialize_map(UrlMapVisitor::<V>::default())
.map(Some)
}
}
deserializer.deserialize_option(OptionUrlMapVisitor::default())
}
pub fn serialize<S, V>(
changes: &Option<HashMap<Url, V>>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
V: serde::Serialize,
{
use serde::ser::SerializeMap;
match *changes {
Some(ref changes) => {
let mut map = serializer.serialize_map(Some(changes.len()))?;
for (k, v) in changes {
map.serialize_entry(k.as_str(), v)?;
}
map.end()
}
None => serializer.serialize_none(),
}
}
}