Refactor: split Project::format logic into local and remote cases

This commit is contained in:
Max Brunsfeld 2023-02-17 15:29:54 -08:00
parent eebce28b32
commit 76975c29a9

View file

@ -2822,126 +2822,126 @@ impl Project {
trigger: FormatTrigger, trigger: FormatTrigger,
cx: &mut ModelContext<Project>, cx: &mut ModelContext<Project>,
) -> Task<Result<ProjectTransaction>> { ) -> Task<Result<ProjectTransaction>> {
let mut local_buffers = Vec::new(); if self.is_local() {
let mut remote_buffers = None; let mut buffers_with_paths_and_servers = buffers
for buffer_handle in buffers { .into_iter()
let buffer = buffer_handle.read(cx); .filter_map(|buffer_handle| {
if let Some(file) = File::from_dyn(buffer.file()) { let buffer = buffer_handle.read(cx);
if let Some(buffer_abs_path) = file.as_local().map(|f| f.abs_path(cx)) { let file = File::from_dyn(buffer.file())?;
if let Some((_, server)) = self.language_server_for_buffer(buffer, cx) { let buffer_abs_path = file.as_local()?.abs_path(cx);
local_buffers.push((buffer_handle, buffer_abs_path, server.clone())); let (_, server) = self.language_server_for_buffer(buffer, cx)?;
} Some((buffer_handle, buffer_abs_path, server.clone()))
} else { })
remote_buffers.get_or_insert(Vec::new()).push(buffer_handle); .collect::<Vec<_>>();
}
} else {
return Task::ready(Ok(Default::default()));
}
}
let remote_buffers = self.remote_id().zip(remote_buffers); cx.spawn(|this, mut cx| async move {
let client = self.client.clone(); // Do not allow multiple concurrent formatting requests for the
// same buffer.
cx.spawn(|this, mut cx| async move { this.update(&mut cx, |this, _| {
let mut project_transaction = ProjectTransaction::default(); buffers_with_paths_and_servers
.retain(|(buffer, _, _)| this.buffers_being_formatted.insert(buffer.id()));
if let Some((project_id, remote_buffers)) = remote_buffers {
let response = client
.request(proto::FormatBuffers {
project_id,
trigger: trigger as i32,
buffer_ids: remote_buffers
.iter()
.map(|buffer| buffer.read_with(&cx, |buffer, _| buffer.remote_id()))
.collect(),
})
.await?
.transaction
.ok_or_else(|| anyhow!("missing transaction"))?;
project_transaction = this
.update(&mut cx, |this, cx| {
this.deserialize_project_transaction(response, push_to_history, cx)
})
.await?;
}
// Do not allow multiple concurrent formatting requests for the
// same buffer.
this.update(&mut cx, |this, _| {
local_buffers
.retain(|(buffer, _, _)| this.buffers_being_formatted.insert(buffer.id()));
});
let _cleanup = defer({
let this = this.clone();
let mut cx = cx.clone();
let local_buffers = &local_buffers;
move || {
this.update(&mut cx, |this, _| {
for (buffer, _, _) in local_buffers {
this.buffers_being_formatted.remove(&buffer.id());
}
});
}
});
for (buffer, buffer_abs_path, language_server) in &local_buffers {
let (format_on_save, formatter, tab_size) = buffer.read_with(&cx, |buffer, cx| {
let settings = cx.global::<Settings>();
let language_name = buffer.language().map(|language| language.name());
(
settings.format_on_save(language_name.as_deref()),
settings.formatter(language_name.as_deref()),
settings.tab_size(language_name.as_deref()),
)
}); });
let transaction = match (formatter, format_on_save) { let _cleanup = defer({
(_, FormatOnSave::Off) if trigger == FormatTrigger::Save => continue, let this = this.clone();
let mut cx = cx.clone();
let local_buffers = &buffers_with_paths_and_servers;
move || {
this.update(&mut cx, |this, _| {
for (buffer, _, _) in local_buffers {
this.buffers_being_formatted.remove(&buffer.id());
}
});
}
});
(Formatter::LanguageServer, FormatOnSave::On | FormatOnSave::Off) let mut project_transaction = ProjectTransaction::default();
| (_, FormatOnSave::LanguageServer) => Self::format_via_lsp( for (buffer, buffer_abs_path, language_server) in &buffers_with_paths_and_servers {
&this, let (format_on_save, formatter, tab_size) =
&buffer, buffer.read_with(&cx, |buffer, cx| {
&buffer_abs_path, let settings = cx.global::<Settings>();
&language_server, let language_name = buffer.language().map(|language| language.name());
tab_size, (
&mut cx, settings.format_on_save(language_name.as_deref()),
) settings.formatter(language_name.as_deref()),
.await settings.tab_size(language_name.as_deref()),
.context("failed to format via language server")?, )
});
( let transaction = match (formatter, format_on_save) {
Formatter::External { command, arguments }, (_, FormatOnSave::Off) if trigger == FormatTrigger::Save => continue,
FormatOnSave::On | FormatOnSave::Off,
) (Formatter::LanguageServer, FormatOnSave::On | FormatOnSave::Off)
| (_, FormatOnSave::External { command, arguments }) => { | (_, FormatOnSave::LanguageServer) => Self::format_via_lsp(
Self::format_via_external_command( &this,
&buffer, &buffer,
&buffer_abs_path, &buffer_abs_path,
&command, &language_server,
&arguments, tab_size,
&mut cx, &mut cx,
) )
.await .await
.context(format!( .context("failed to format via language server")?,
"failed to format via external command {:?}",
command
))?
}
};
if let Some(transaction) = transaction { (
if !push_to_history { Formatter::External { command, arguments },
buffer.update(&mut cx, |buffer, _| { FormatOnSave::On | FormatOnSave::Off,
buffer.forget_transaction(transaction.id) )
}); | (_, FormatOnSave::External { command, arguments }) => {
Self::format_via_external_command(
&buffer,
&buffer_abs_path,
&command,
&arguments,
&mut cx,
)
.await
.context(format!(
"failed to format via external command {:?}",
command
))?
}
};
if let Some(transaction) = transaction {
if !push_to_history {
buffer.update(&mut cx, |buffer, _| {
buffer.forget_transaction(transaction.id)
});
}
project_transaction.0.insert(buffer.clone(), transaction);
} }
project_transaction.0.insert(buffer.clone(), transaction);
} }
}
Ok(project_transaction) Ok(project_transaction)
}) })
} else {
let remote_id = self.remote_id();
let client = self.client.clone();
cx.spawn(|this, mut cx| async move {
let mut project_transaction = ProjectTransaction::default();
if let Some(project_id) = remote_id {
let response = client
.request(proto::FormatBuffers {
project_id,
trigger: trigger as i32,
buffer_ids: buffers
.iter()
.map(|buffer| buffer.read_with(&cx, |buffer, _| buffer.remote_id()))
.collect(),
})
.await?
.transaction
.ok_or_else(|| anyhow!("missing transaction"))?;
project_transaction = this
.update(&mut cx, |this, cx| {
this.deserialize_project_transaction(response, push_to_history, cx)
})
.await?;
}
Ok(project_transaction)
})
}
} }
async fn format_via_lsp( async fn format_via_lsp(