Allow formatting selections via LSP (#18752)
Release Notes: - Added a new `editor: format selections` action that allows formatting only the currently selected text via the primary language server. --------- Co-authored-by: Thorsten Ball <mrnugget@gmail.com>
This commit is contained in:
co-authored by
Thorsten Ball
parent
eb76065ad3
commit
84df3a0cad
@@ -237,6 +237,7 @@ gpui::actions!(
|
||||
ToggleFold,
|
||||
ToggleFoldRecursive,
|
||||
Format,
|
||||
FormatSelections,
|
||||
GoToDeclaration,
|
||||
GoToDeclarationSplit,
|
||||
GoToDefinition,
|
||||
|
||||
@@ -122,7 +122,7 @@ use multi_buffer::{
|
||||
use ordered_float::OrderedFloat;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use project::{
|
||||
lsp_store::FormatTrigger,
|
||||
lsp_store::{FormatTarget, FormatTrigger},
|
||||
project_settings::{GitGutterSetting, ProjectSettings},
|
||||
CodeAction, Completion, CompletionIntent, DocumentHighlight, InlayHint, Item, Location,
|
||||
LocationLink, Project, ProjectPath, ProjectTransaction, TaskSourceKind,
|
||||
@@ -10386,13 +10386,39 @@ impl Editor {
|
||||
None => return None,
|
||||
};
|
||||
|
||||
Some(self.perform_format(project, FormatTrigger::Manual, cx))
|
||||
Some(self.perform_format(project, FormatTrigger::Manual, FormatTarget::Buffer, cx))
|
||||
}
|
||||
|
||||
fn format_selections(
|
||||
&mut self,
|
||||
_: &FormatSelections,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<Task<Result<()>>> {
|
||||
let project = match &self.project {
|
||||
Some(project) => project.clone(),
|
||||
None => return None,
|
||||
};
|
||||
|
||||
let selections = self
|
||||
.selections
|
||||
.all_adjusted(cx)
|
||||
.into_iter()
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect_vec();
|
||||
|
||||
Some(self.perform_format(
|
||||
project,
|
||||
FormatTrigger::Manual,
|
||||
FormatTarget::Ranges(selections),
|
||||
cx,
|
||||
))
|
||||
}
|
||||
|
||||
fn perform_format(
|
||||
&mut self,
|
||||
project: Model<Project>,
|
||||
trigger: FormatTrigger,
|
||||
target: FormatTarget,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
let buffer = self.buffer().clone();
|
||||
@@ -10402,7 +10428,9 @@ impl Editor {
|
||||
}
|
||||
|
||||
let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
|
||||
let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
|
||||
let format = project.update(cx, |project, cx| {
|
||||
project.format(buffers, true, trigger, target, cx)
|
||||
});
|
||||
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
let transaction = futures::select_biased! {
|
||||
|
||||
@@ -7076,7 +7076,12 @@ async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
let format = editor
|
||||
.update(cx, |editor, cx| {
|
||||
editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
|
||||
editor.perform_format(
|
||||
project.clone(),
|
||||
FormatTrigger::Manual,
|
||||
FormatTarget::Buffer,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.unwrap();
|
||||
fake_server
|
||||
@@ -7112,7 +7117,7 @@ async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
|
||||
});
|
||||
let format = editor
|
||||
.update(cx, |editor, cx| {
|
||||
editor.perform_format(project, FormatTrigger::Manual, cx)
|
||||
editor.perform_format(project, FormatTrigger::Manual, FormatTarget::Buffer, cx)
|
||||
})
|
||||
.unwrap();
|
||||
cx.executor().advance_clock(super::FORMAT_TIMEOUT);
|
||||
@@ -10309,7 +10314,12 @@ async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
editor
|
||||
.update(cx, |editor, cx| {
|
||||
editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
|
||||
editor.perform_format(
|
||||
project.clone(),
|
||||
FormatTrigger::Manual,
|
||||
FormatTarget::Buffer,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.unwrap()
|
||||
.await;
|
||||
@@ -10323,7 +10333,12 @@ async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
|
||||
settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
|
||||
});
|
||||
let format = editor.update(cx, |editor, cx| {
|
||||
editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
|
||||
editor.perform_format(
|
||||
project.clone(),
|
||||
FormatTrigger::Manual,
|
||||
FormatTarget::Buffer,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
format.await.unwrap();
|
||||
assert_eq!(
|
||||
|
||||
@@ -376,6 +376,13 @@ impl EditorElement {
|
||||
cx.propagate();
|
||||
}
|
||||
});
|
||||
register_action(view, cx, |editor, action, cx| {
|
||||
if let Some(task) = editor.format_selections(action, cx) {
|
||||
task.detach_and_log_err(cx);
|
||||
} else {
|
||||
cx.propagate();
|
||||
}
|
||||
});
|
||||
register_action(view, cx, Editor::restart_language_server);
|
||||
register_action(view, cx, Editor::cancel_language_server_work);
|
||||
register_action(view, cx, Editor::show_character_palette);
|
||||
|
||||
@@ -27,6 +27,7 @@ use rpc::proto::{self, update_view, PeerId};
|
||||
use settings::Settings;
|
||||
use workspace::item::{Dedup, ItemSettings, SerializableItem, TabContentParams};
|
||||
|
||||
use project::lsp_store::FormatTarget;
|
||||
use std::{
|
||||
any::TypeId,
|
||||
borrow::Cow,
|
||||
@@ -722,7 +723,12 @@ impl Item for Editor {
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
if format {
|
||||
this.update(&mut cx, |editor, cx| {
|
||||
editor.perform_format(project.clone(), FormatTrigger::Save, cx)
|
||||
editor.perform_format(
|
||||
project.clone(),
|
||||
FormatTrigger::Save,
|
||||
FormatTarget::Buffer,
|
||||
cx,
|
||||
)
|
||||
})?
|
||||
.await?;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::ops::Range;
|
||||
|
||||
use crate::actions::FormatSelections;
|
||||
use crate::{
|
||||
actions::Format, selections_collection::SelectionsCollection, Copy, CopyPermalinkToLine, Cut,
|
||||
DisplayPoint, DisplaySnapshot, Editor, EditorMode, FindAllReferences, GoToDeclaration,
|
||||
@@ -8,6 +7,8 @@ use crate::{
|
||||
};
|
||||
use gpui::prelude::FluentBuilder;
|
||||
use gpui::{DismissEvent, Pixels, Point, Subscription, View, ViewContext};
|
||||
use std::ops::Range;
|
||||
use text::PointUtf16;
|
||||
use workspace::OpenInTerminal;
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -164,6 +165,12 @@ pub fn deploy_context_menu(
|
||||
} else {
|
||||
"Reveal in File Manager"
|
||||
};
|
||||
let has_selections = editor
|
||||
.selections
|
||||
.all::<PointUtf16>(cx)
|
||||
.into_iter()
|
||||
.any(|s| !s.is_empty());
|
||||
|
||||
ui::ContextMenu::build(cx, |menu, _cx| {
|
||||
let builder = menu
|
||||
.on_blur_subscription(Subscription::new(|| {}))
|
||||
@@ -175,6 +182,9 @@ pub fn deploy_context_menu(
|
||||
.separator()
|
||||
.action("Rename Symbol", Box::new(Rename))
|
||||
.action("Format Buffer", Box::new(Format))
|
||||
.when(has_selections, |cx| {
|
||||
cx.action("Format Selections", Box::new(FormatSelections))
|
||||
})
|
||||
.action(
|
||||
"Code Actions",
|
||||
Box::new(ToggleCodeActions {
|
||||
|
||||
Reference in New Issue
Block a user