Manually refresh code action when `data` is not present

Antonio Scandurra created

Change summary

crates/project/src/project.rs | 46 ++++++++++++++++++++++++++----------
1 file changed, 33 insertions(+), 13 deletions(-)

Detailed changes

crates/project/src/project.rs 🔗

@@ -1167,13 +1167,13 @@ impl Project {
 
     pub fn apply_code_action(
         &self,
-        buffer: ModelHandle<Buffer>,
+        buffer_handle: ModelHandle<Buffer>,
         mut action: CodeAction<language::Anchor>,
         cx: &mut ModelContext<Self>,
     ) -> Task<Result<HashMap<ModelHandle<Buffer>, Vec<(Range<language::Anchor>, clock::Local)>>>>
     {
         if self.is_local() {
-            let buffer = buffer.read(cx);
+            let buffer = buffer_handle.read(cx);
             let lang_name = if let Some(lang) = buffer.language() {
                 lang.name().to_string()
             } else {
@@ -1196,21 +1196,41 @@ impl Project {
                     .and_then(|d| d.get_mut("range"))
                 {
                     *range = serde_json::to_value(&lsp::Range::new(position, position)).unwrap();
-                    action = CodeAction {
-                        position: action.position,
-                        lsp_action: lang_server
-                            .request::<lsp::request::CodeActionResolveRequest>(action.lsp_action)
-                            .await?,
-                    }
+                    action.lsp_action = lang_server
+                        .request::<lsp::request::CodeActionResolveRequest>(action.lsp_action)
+                        .await?;
+                } else {
+                    let actions = buffer_handle
+                        .update(&mut cx, |buffer, cx| {
+                            buffer.code_actions(action.position.clone(), cx)
+                        })
+                        .await?;
+                    action.lsp_action = actions
+                        .into_iter()
+                        .find(|a| a.lsp_action.title == action.lsp_action.title)
+                        .ok_or_else(|| anyhow!("code action is outdated"))?
+                        .lsp_action;
                 }
 
                 let mut operations = Vec::new();
-                match action.lsp_action.edit.and_then(|e| e.document_changes) {
-                    Some(lsp::DocumentChanges::Edits(edits)) => {
-                        operations.extend(edits.into_iter().map(lsp::DocumentChangeOperation::Edit))
+                if let Some(edit) = action.lsp_action.edit {
+                    if let Some(document_changes) = edit.document_changes {
+                        match document_changes {
+                            lsp::DocumentChanges::Edits(edits) => operations
+                                .extend(edits.into_iter().map(lsp::DocumentChangeOperation::Edit)),
+                            lsp::DocumentChanges::Operations(ops) => operations = ops,
+                        }
+                    } else if let Some(changes) = edit.changes {
+                        operations.extend(changes.into_iter().map(|(uri, edits)| {
+                            lsp::DocumentChangeOperation::Edit(lsp::TextDocumentEdit {
+                                text_document: lsp::OptionalVersionedTextDocumentIdentifier {
+                                    uri,
+                                    version: None,
+                                },
+                                edits: edits.into_iter().map(lsp::OneOf::Left).collect(),
+                            })
+                        }));
                     }
-                    Some(lsp::DocumentChanges::Operations(ops)) => operations = ops,
-                    None => {}
                 }
 
                 let mut edited_buffers = HashMap::default();