lsp: Add removeBinding command

Add a command to remove a proerty binding from an element.
This commit is contained in:
Tobias Hunger 2022-11-30 22:59:55 +01:00 committed by Tobias Hunger
parent 3a601ba6ef
commit b6f36e7ce1
2 changed files with 141 additions and 3 deletions

View file

@ -683,6 +683,52 @@ pub(crate) fn set_binding<'a>(
))
}
fn create_workspace_edit_for_remove_binding<'a>(
uri: &lsp_types::Url,
version: i32,
property: &PropertyInformation,
) -> Option<lsp_types::WorkspaceEdit> {
property.defined_at.as_ref().map(|defined_at| {
let edit = lsp_types::TextEdit { range: defined_at.delete_range, new_text: String::new() };
let edits = vec![lsp_types::OneOf::Left(edit)];
let text_document_edits = vec![lsp_types::TextDocumentEdit {
text_document: lsp_types::OptionalVersionedTextDocumentIdentifier::new(
uri.clone(),
version,
),
edits,
}];
lsp_types::WorkspaceEdit {
document_changes: Some(lsp_types::DocumentChanges::Edits(text_document_edits)),
..Default::default()
}
})
}
pub(crate) fn remove_binding<'a>(
document_cache: &mut DocumentCache,
uri: &lsp_types::Url,
element: &ElementRc,
property_name: &str,
) -> Result<WorkspaceEdit, Error> {
let property = get_property_information(
element,
&mut &mut document_cache.offset_to_position_mapper(uri),
property_name,
)?;
let workspace_edit = create_workspace_edit_for_remove_binding(
uri,
document_cache
.document_version(uri)
.ok_or_else(|| Into::<Error>::into("Document not found in cache"))?,
&property,
)
.ok_or_else(|| Into::<Error>::into("Failed to create workspace edit to remove property"))?;
Ok(workspace_edit)
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -133,17 +133,18 @@ impl Drop for ProgressReporter {
}
}
const SHOW_PREVIEW_COMMAND: &str = "showPreview";
const QUERY_PROPERTIES_COMMAND: &str = "queryProperties";
const REMOVE_BINDING_COMMAND: &str = "removeBinding";
const SHOW_PREVIEW_COMMAND: &str = "showPreview";
const SET_BINDING_COMMAND: &str = "setBinding";
fn command_list() -> Vec<String> {
let mut result = vec![];
result.push(QUERY_PROPERTIES_COMMAND.into());
result.push(REMOVE_BINDING_COMMAND.into());
#[cfg(any(feature = "preview", feature = "preview-lense"))]
result.push(SHOW_PREVIEW_COMMAND.into());
result.push(QUERY_PROPERTIES_COMMAND.into());
result.push(SET_BINDING_COMMAND.into());
result
@ -401,6 +402,9 @@ pub fn register_request_handlers(rh: &mut RequestHandler) {
if params.command.as_str() == SET_BINDING_COMMAND {
return Ok(Some(set_binding_command(&params.arguments, &ctx).await?));
}
if params.command.as_str() == REMOVE_BINDING_COMMAND {
return Ok(Some(remove_binding_command(&params.arguments, &ctx).await?));
}
Ok(None::<serde_json::Value>)
});
rh.register::<DocumentColor, _>(|params, ctx| async move {
@ -630,6 +634,94 @@ pub async fn set_binding_command(
Ok(serde_json::to_value(result).expect("Failed to serialize set_binding result!"))
}
pub async fn remove_binding_command(
params: &[serde_json::Value],
ctx: &Rc<Context>,
) -> Result<serde_json::Value, Error> {
use crate::properties;
let text_document = serde_json::from_value::<lsp_types::OptionalVersionedTextDocumentIdentifier>(
params.get(0).ok_or_else(|| "No text document provided")?.clone(),
)?;
let element_range = serde_json::from_value::<lsp_types::Range>(
params.get(1).ok_or_else(|| "No element range provided")?.clone(),
)?;
let property_name = serde_json::from_value::<String>(
params.get(2).ok_or_else(|| "No property name provided")?.clone(),
)?;
let edit = {
let document_cache = &mut ctx.document_cache.borrow_mut();
let uri = text_document.uri;
if let Some(source_version) = text_document.version {
if let Some(current_version) = document_cache.document_version(&uri) {
if current_version != source_version {
return Err(
"Document version mismatch. Please refresh your property information"
.into(),
);
}
} else {
return Err(
format!("Document with uri {} not found in cache", uri.to_string()).into()
);
}
}
let element =
element_at_position(document_cache, &uri, &element_range.start).ok_or_else(|| {
format!("No element found at the given start position {:?}", &element_range.start)
})?;
let node_range = element
.borrow()
.node
.as_ref()
.ok_or_else(|| "The element was found, but had no range defined!")?
.text_range();
let (start, end) = {
let mut offset_to_position = document_cache.offset_to_position_mapper(&uri);
(
offset_to_position.map(node_range.start().into()),
offset_to_position.map(node_range.end().into()),
)
};
if start != element_range.start {
return Err(format!(
"Element found, but does not start at the expected place (){start:?} != {:?}).",
element_range.start
)
.into());
}
if end != element_range.end {
return Err(format!(
"Element found, but does not end at the expected place (){end:?} != {:?}).",
element_range.end
)
.into());
}
properties::remove_binding(document_cache, &uri, &element, &property_name)?
};
let response = ctx
.server_notifier
.send_request::<lsp_types::request::ApplyWorkspaceEdit>(
lsp_types::ApplyWorkspaceEditParams { label: Some("set binding".into()), edit },
)?
.await?;
if !response.applied {
return Err(response
.failure_reason
.unwrap_or("Operation failed, no specific reason given".into())
.into());
}
Ok(serde_json::to_value(()).expect("Failed to serialize ()!"))
}
#[cfg(feature = "preview")]
/// Workaround for editor that do not support code action: using the goto definition on a comment
/// that says "preview" will show the preview.