Allow applying code actions that use commands

Co-Authored-By: Antonio Scandurra <me@as-cii.com>
This commit is contained in:
Max Brunsfeld 2022-04-01 10:16:26 -07:00
parent fed5d141bc
commit 56523b5775
2 changed files with 62 additions and 24 deletions

View file

@ -563,8 +563,9 @@ impl Drop for Subscription {
}
#[cfg(any(test, feature = "test-support"))]
#[derive(Clone)]
pub struct FakeLanguageServer {
server: Arc<LanguageServer>,
pub server: Arc<LanguageServer>,
notifications_rx: channel::Receiver<(String, String)>,
}

View file

@ -64,6 +64,7 @@ pub struct Project {
HashMap<(WorktreeId, LanguageServerName), Task<Option<Arc<LanguageServer>>>>,
language_server_statuses: BTreeMap<usize, LanguageServerStatus>,
language_server_settings: Arc<Mutex<serde_json::Value>>,
last_workspace_edits_by_language_server: HashMap<usize, ProjectTransaction>,
next_language_server_id: usize,
client: Arc<client::Client>,
next_entry_id: Arc<AtomicUsize>,
@ -346,6 +347,7 @@ impl Project {
language_servers: Default::default(),
started_language_servers: Default::default(),
language_server_statuses: Default::default(),
last_workspace_edits_by_language_server: Default::default(),
language_server_settings: Default::default(),
next_language_server_id: 0,
nonce: StdRng::from_entropy().gen(),
@ -433,6 +435,7 @@ impl Project {
)
})
.collect(),
last_workspace_edits_by_language_server: Default::default(),
next_language_server_id: 0,
opened_buffers: Default::default(),
buffer_snapshots: Default::default(),
@ -1736,18 +1739,21 @@ impl Project {
}
LanguageServerEvent::WorkspaceEdit(params) => {
let transaction = Self::deserialize_workspace_edit(
this,
this.clone(),
params.edit,
false,
true,
adapter.clone(),
language_server.clone(),
cx,
)
.await
.log_err();
// Check if there is a code action currently running, using the state that is
// set in `start_code_action`. If so, then store the transaction for later use.
this.update(cx, |this, _| {
if let Some(transaction) = transaction {
this.last_workspace_edits_by_language_server
.insert(language_server_id, transaction);
}
});
}
}
}
@ -2740,7 +2746,6 @@ impl Project {
)
.await
} else if let Some(command) = action.lsp_action.command {
this.update(&mut cx, |this, _| this.start_code_action());
lang_server
.request::<lsp::request::ExecuteCommand>(lsp::ExecuteCommandParams {
command: command.command,
@ -2748,7 +2753,11 @@ impl Project {
..Default::default()
})
.await?;
Ok(this.update(&mut cx, |this, cx| this.finish_code_action(cx)))
Ok(this.update(&mut cx, |this, _| {
this.last_workspace_edits_by_language_server
.remove(&lang_server.server_id())
.unwrap_or_default()
}))
} else {
Ok(ProjectTransaction::default())
}
@ -2907,17 +2916,6 @@ impl Project {
Ok(project_transaction)
}
fn start_code_action(&mut self) {
// Set some state that will be read inside of `on_lsp_event` when handling a `WorkspaceEdit`
// event, and will cause the `ProjectTransaction` to be stored.
}
fn finish_code_action(&mut self, cx: &mut ModelContext<Self>) -> ProjectTransaction {
// Retrieve all stored `ProjectTransactions` that have been received since `start_code_action`
// was called, and combine them together.
Default::default()
}
pub fn prepare_rename<T: ToPointUtf16>(
&self,
buffer: ModelHandle<Buffer>,
@ -6089,7 +6087,7 @@ mod tests {
fs.insert_tree(
"/dir",
json!({
"a.ts": "",
"a.ts": "a",
}),
)
.await;
@ -6116,7 +6114,7 @@ mod tests {
let actions = project.update(cx, |project, cx| project.code_actions(&buffer, 0..0, cx));
fake_server
.handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
.handle_request::<lsp::request::CodeActionRequest, _, _>(|_, _| async move {
Ok(Some(vec![
lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
title: "The code action".into(),
@ -6144,12 +6142,51 @@ mod tests {
|action, _| async move { Ok(action) },
);
fake_server
.handle_request::<lsp::request::ExecuteCommand, _, _>(move |params, cx| async move {
// fake_server.send();
Ok(Some(json!(null)))
.handle_request::<lsp::request::ExecuteCommand, _, _>({
let fake = fake_server.clone();
move |params, _| {
assert_eq!(params.command, "_the/command");
let fake = fake.clone();
async move {
fake.server
.request::<lsp::request::ApplyWorkspaceEdit>(
lsp::ApplyWorkspaceEditParams {
label: None,
edit: lsp::WorkspaceEdit {
changes: Some(
[(
lsp::Url::from_file_path("/dir/a.ts").unwrap(),
vec![lsp::TextEdit {
range: lsp::Range::new(
lsp::Position::new(0, 0),
lsp::Position::new(0, 0),
),
new_text: "X".into(),
}],
)]
.into_iter()
.collect(),
),
..Default::default()
},
},
)
.await
.unwrap();
Ok(Some(json!(null)))
}
}
})
.next()
.await;
let transaction = apply.await.unwrap();
assert!(transaction.0.contains_key(&buffer));
buffer.update(cx, |buffer, cx| {
assert_eq!(buffer.text(), "Xa");
buffer.undo(cx);
assert_eq!(buffer.text(), "a");
});
}
#[gpui::test]