mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-20 02:47:34 +03:00
Merge pull request #1647 from zed-industries/format-buffer-action
Add editor action to manually invoke buffer format
This commit is contained in:
commit
af5ad2d5ce
@ -93,6 +93,7 @@
|
|||||||
"cmd-shift-down": "editor::SelectToEnd",
|
"cmd-shift-down": "editor::SelectToEnd",
|
||||||
"cmd-a": "editor::SelectAll",
|
"cmd-a": "editor::SelectAll",
|
||||||
"cmd-l": "editor::SelectLine",
|
"cmd-l": "editor::SelectLine",
|
||||||
|
"cmd-shift-i": "editor::Format",
|
||||||
"cmd-shift-left": [
|
"cmd-shift-left": [
|
||||||
"editor::SelectToBeginningOfLine",
|
"editor::SelectToBeginningOfLine",
|
||||||
{
|
{
|
||||||
|
@ -42,21 +42,20 @@
|
|||||||
// 3. Position the dock full screen over the entire workspace"
|
// 3. Position the dock full screen over the entire workspace"
|
||||||
// "default_dock_anchor": "expanded"
|
// "default_dock_anchor": "expanded"
|
||||||
"default_dock_anchor": "right",
|
"default_dock_anchor": "right",
|
||||||
// How to auto-format modified buffers when saving them. This
|
// Whether or not to perform a buffer format before saving
|
||||||
// setting can take three values:
|
"format_on_save": true,
|
||||||
|
// How to perform a buffer format. This setting can take two values:
|
||||||
//
|
//
|
||||||
// 1. Don't format code
|
// 1. Format code using the current language server:
|
||||||
// "format_on_save": "off"
|
|
||||||
// 2. Format code using the current language server:
|
|
||||||
// "format_on_save": "language_server"
|
// "format_on_save": "language_server"
|
||||||
// 3. Format code using an external command:
|
// 2. Format code using an external command:
|
||||||
// "format_on_save": {
|
// "format_on_save": {
|
||||||
// "external": {
|
// "external": {
|
||||||
// "command": "prettier",
|
// "command": "prettier",
|
||||||
// "arguments": ["--stdin-filepath", "{buffer_path}"]
|
// "arguments": ["--stdin-filepath", "{buffer_path}"]
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
"format_on_save": "language_server",
|
"formatter": "language_server",
|
||||||
// How to soft-wrap long lines of text. This setting can take
|
// How to soft-wrap long lines of text. This setting can take
|
||||||
// three values:
|
// three values:
|
||||||
//
|
//
|
||||||
|
@ -36,7 +36,7 @@ use project::{
|
|||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use rpc::PeerId;
|
use rpc::PeerId;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use settings::{FormatOnSave, Settings};
|
use settings::{Formatter, Settings};
|
||||||
use sqlx::types::time::OffsetDateTime;
|
use sqlx::types::time::OffsetDateTime;
|
||||||
use std::{
|
use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
@ -1990,6 +1990,8 @@ async fn test_reloading_buffer_manually(cx_a: &mut TestAppContext, cx_b: &mut Te
|
|||||||
|
|
||||||
#[gpui::test(iterations = 10)]
|
#[gpui::test(iterations = 10)]
|
||||||
async fn test_formatting_buffer(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
|
async fn test_formatting_buffer(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
|
||||||
|
use project::FormatTrigger;
|
||||||
|
|
||||||
let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
|
let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
|
||||||
let client_a = server.create_client(cx_a, "user_a").await;
|
let client_a = server.create_client(cx_a, "user_a").await;
|
||||||
let client_b = server.create_client(cx_b, "user_b").await;
|
let client_b = server.create_client(cx_b, "user_b").await;
|
||||||
@ -2042,7 +2044,12 @@ async fn test_formatting_buffer(cx_a: &mut TestAppContext, cx_b: &mut TestAppCon
|
|||||||
|
|
||||||
project_b
|
project_b
|
||||||
.update(cx_b, |project, cx| {
|
.update(cx_b, |project, cx| {
|
||||||
project.format(HashSet::from_iter([buffer_b.clone()]), true, cx)
|
project.format(
|
||||||
|
HashSet::from_iter([buffer_b.clone()]),
|
||||||
|
true,
|
||||||
|
FormatTrigger::Save,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -2055,7 +2062,7 @@ async fn test_formatting_buffer(cx_a: &mut TestAppContext, cx_b: &mut TestAppCon
|
|||||||
// host's configuration is honored as opposed to using the guest's settings.
|
// host's configuration is honored as opposed to using the guest's settings.
|
||||||
cx_a.update(|cx| {
|
cx_a.update(|cx| {
|
||||||
cx.update_global(|settings: &mut Settings, _| {
|
cx.update_global(|settings: &mut Settings, _| {
|
||||||
settings.editor_defaults.format_on_save = Some(FormatOnSave::External {
|
settings.editor_defaults.formatter = Some(Formatter::External {
|
||||||
command: "awk".to_string(),
|
command: "awk".to_string(),
|
||||||
arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()],
|
arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()],
|
||||||
});
|
});
|
||||||
@ -2063,7 +2070,12 @@ async fn test_formatting_buffer(cx_a: &mut TestAppContext, cx_b: &mut TestAppCon
|
|||||||
});
|
});
|
||||||
project_b
|
project_b
|
||||||
.update(cx_b, |project, cx| {
|
.update(cx_b, |project, cx| {
|
||||||
project.format(HashSet::from_iter([buffer_b.clone()]), true, cx)
|
project.format(
|
||||||
|
HashSet::from_iter([buffer_b.clone()]),
|
||||||
|
true,
|
||||||
|
FormatTrigger::Save,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -19,6 +19,7 @@ use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
|
|||||||
pub use display_map::DisplayPoint;
|
pub use display_map::DisplayPoint;
|
||||||
use display_map::*;
|
use display_map::*;
|
||||||
pub use element::*;
|
pub use element::*;
|
||||||
|
use futures::FutureExt;
|
||||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions,
|
actions,
|
||||||
@ -49,7 +50,7 @@ pub use multi_buffer::{
|
|||||||
};
|
};
|
||||||
use multi_buffer::{MultiBufferChunks, ToOffsetUtf16};
|
use multi_buffer::{MultiBufferChunks, ToOffsetUtf16};
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use project::{LocationLink, Project, ProjectPath, ProjectTransaction};
|
use project::{FormatTrigger, LocationLink, Project, ProjectPath, ProjectTransaction};
|
||||||
use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
|
use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
@ -76,6 +77,8 @@ const MAX_LINE_LEN: usize = 1024;
|
|||||||
const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
|
const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
|
||||||
const MAX_SELECTION_HISTORY_LEN: usize = 1024;
|
const MAX_SELECTION_HISTORY_LEN: usize = 1024;
|
||||||
|
|
||||||
|
pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, PartialEq, Default)]
|
#[derive(Clone, Deserialize, PartialEq, Default)]
|
||||||
pub struct SelectNext {
|
pub struct SelectNext {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@ -204,6 +207,7 @@ actions!(
|
|||||||
OpenExcerpts,
|
OpenExcerpts,
|
||||||
RestartLanguageServer,
|
RestartLanguageServer,
|
||||||
Hover,
|
Hover,
|
||||||
|
Format,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -310,6 +314,7 @@ pub fn init(cx: &mut MutableAppContext) {
|
|||||||
cx.add_action(Editor::toggle_code_actions);
|
cx.add_action(Editor::toggle_code_actions);
|
||||||
cx.add_action(Editor::open_excerpts);
|
cx.add_action(Editor::open_excerpts);
|
||||||
cx.add_action(Editor::jump);
|
cx.add_action(Editor::jump);
|
||||||
|
cx.add_async_action(Editor::format);
|
||||||
cx.add_action(Editor::restart_language_server);
|
cx.add_action(Editor::restart_language_server);
|
||||||
cx.add_action(Editor::show_character_palette);
|
cx.add_action(Editor::show_character_palette);
|
||||||
cx.add_async_action(Editor::confirm_completion);
|
cx.add_async_action(Editor::confirm_completion);
|
||||||
@ -5173,6 +5178,51 @@ impl Editor {
|
|||||||
self.pending_rename.as_ref()
|
self.pending_rename.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn format(&mut self, _: &Format, cx: &mut ViewContext<'_, Self>) -> Option<Task<Result<()>>> {
|
||||||
|
let project = match &self.project {
|
||||||
|
Some(project) => project.clone(),
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(self.perform_format(project, cx))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn perform_format(
|
||||||
|
&mut self,
|
||||||
|
project: ModelHandle<Project>,
|
||||||
|
cx: &mut ViewContext<'_, Self>,
|
||||||
|
) -> Task<Result<()>> {
|
||||||
|
let buffer = self.buffer().clone();
|
||||||
|
let buffers = buffer.read(cx).all_buffers();
|
||||||
|
|
||||||
|
let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
|
||||||
|
let format = project.update(cx, |project, cx| {
|
||||||
|
project.format(buffers, true, FormatTrigger::Manual, cx)
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.spawn(|_, mut cx| async move {
|
||||||
|
let transaction = futures::select_biased! {
|
||||||
|
_ = timeout => {
|
||||||
|
log::warn!("timed out waiting for formatting");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
transaction = format.log_err().fuse() => transaction,
|
||||||
|
};
|
||||||
|
|
||||||
|
buffer.update(&mut cx, |buffer, cx| {
|
||||||
|
if let Some(transaction) = transaction {
|
||||||
|
if !buffer.is_singleton() {
|
||||||
|
buffer.push_transaction(&transaction.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cx.notify();
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
|
fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
|
||||||
if let Some(project) = self.project.clone() {
|
if let Some(project) = self.project.clone() {
|
||||||
self.buffer.update(cx, |multi_buffer, cx| {
|
self.buffer.update(cx, |multi_buffer, cx| {
|
||||||
@ -10087,7 +10137,7 @@ mod tests {
|
|||||||
unreachable!()
|
unreachable!()
|
||||||
});
|
});
|
||||||
let save = cx.update(|cx| editor.save(project.clone(), cx));
|
let save = cx.update(|cx| editor.save(project.clone(), cx));
|
||||||
cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
|
cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
|
||||||
cx.foreground().start_waiting();
|
cx.foreground().start_waiting();
|
||||||
save.await.unwrap();
|
save.await.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -10203,7 +10253,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
let save = cx.update(|cx| editor.save(project.clone(), cx));
|
let save = cx.update(|cx| editor.save(project.clone(), cx));
|
||||||
cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
|
cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
|
||||||
cx.foreground().start_waiting();
|
cx.foreground().start_waiting();
|
||||||
save.await.unwrap();
|
save.await.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -10241,6 +10291,87 @@ mod tests {
|
|||||||
save.await.unwrap();
|
save.await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
|
||||||
|
cx.foreground().forbid_parking();
|
||||||
|
|
||||||
|
let mut language = Language::new(
|
||||||
|
LanguageConfig {
|
||||||
|
name: "Rust".into(),
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
Some(tree_sitter_rust::language()),
|
||||||
|
);
|
||||||
|
let mut fake_servers = language
|
||||||
|
.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
|
||||||
|
capabilities: lsp::ServerCapabilities {
|
||||||
|
document_formatting_provider: Some(lsp::OneOf::Left(true)),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let fs = FakeFs::new(cx.background());
|
||||||
|
fs.insert_file("/file.rs", Default::default()).await;
|
||||||
|
|
||||||
|
let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
|
||||||
|
project.update(cx, |project, _| project.languages().add(Arc::new(language)));
|
||||||
|
let buffer = project
|
||||||
|
.update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
cx.foreground().start_waiting();
|
||||||
|
let fake_server = fake_servers.next().await.unwrap();
|
||||||
|
|
||||||
|
let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
|
||||||
|
let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
|
||||||
|
editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
|
||||||
|
|
||||||
|
let format = editor.update(cx, |editor, cx| editor.perform_format(project.clone(), cx));
|
||||||
|
fake_server
|
||||||
|
.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
|
||||||
|
assert_eq!(
|
||||||
|
params.text_document.uri,
|
||||||
|
lsp::Url::from_file_path("/file.rs").unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(params.options.tab_size, 4);
|
||||||
|
Ok(Some(vec![lsp::TextEdit::new(
|
||||||
|
lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
|
||||||
|
", ".to_string(),
|
||||||
|
)]))
|
||||||
|
})
|
||||||
|
.next()
|
||||||
|
.await;
|
||||||
|
cx.foreground().start_waiting();
|
||||||
|
format.await.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
editor.read_with(cx, |editor, cx| editor.text(cx)),
|
||||||
|
"one, two\nthree\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
|
||||||
|
// Ensure we don't lock if formatting hangs.
|
||||||
|
fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
|
||||||
|
assert_eq!(
|
||||||
|
params.text_document.uri,
|
||||||
|
lsp::Url::from_file_path("/file.rs").unwrap()
|
||||||
|
);
|
||||||
|
futures::future::pending::<()>().await;
|
||||||
|
unreachable!()
|
||||||
|
});
|
||||||
|
let format = editor.update(cx, |editor, cx| editor.perform_format(project, cx));
|
||||||
|
cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
|
||||||
|
cx.foreground().start_waiting();
|
||||||
|
format.await.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
editor.read_with(cx, |editor, cx| editor.text(cx)),
|
||||||
|
"one\ntwo\nthree\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_completion(cx: &mut gpui::TestAppContext) {
|
async fn test_completion(cx: &mut gpui::TestAppContext) {
|
||||||
let mut cx = EditorLspTestContext::new_rust(
|
let mut cx = EditorLspTestContext::new_rust(
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
display_map::ToDisplayPoint, link_go_to_definition::hide_link_definition,
|
display_map::ToDisplayPoint, link_go_to_definition::hide_link_definition,
|
||||||
movement::surrounding_word, Anchor, Autoscroll, Editor, Event, ExcerptId, MultiBuffer,
|
movement::surrounding_word, Anchor, Autoscroll, Editor, Event, ExcerptId, MultiBuffer,
|
||||||
MultiBufferSnapshot, NavigationData, ToPoint as _,
|
MultiBufferSnapshot, NavigationData, ToPoint as _, FORMAT_TIMEOUT,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
@ -10,7 +10,7 @@ use gpui::{
|
|||||||
RenderContext, Subscription, Task, View, ViewContext, ViewHandle,
|
RenderContext, Subscription, Task, View, ViewContext, ViewHandle,
|
||||||
};
|
};
|
||||||
use language::{Bias, Buffer, File as _, OffsetRangeExt, SelectionGoal};
|
use language::{Bias, Buffer, File as _, OffsetRangeExt, SelectionGoal};
|
||||||
use project::{File, Project, ProjectEntryId, ProjectPath};
|
use project::{File, FormatTrigger, Project, ProjectEntryId, ProjectPath};
|
||||||
use rpc::proto::{self, update_view};
|
use rpc::proto::{self, update_view};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
@ -20,7 +20,6 @@ use std::{
|
|||||||
fmt::Write,
|
fmt::Write,
|
||||||
ops::Range,
|
ops::Range,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
time::Duration,
|
|
||||||
};
|
};
|
||||||
use text::{Point, Selection};
|
use text::{Point, Selection};
|
||||||
use util::TryFutureExt;
|
use util::TryFutureExt;
|
||||||
@ -30,7 +29,6 @@ use workspace::{
|
|||||||
ToolbarItemLocation,
|
ToolbarItemLocation,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
|
|
||||||
pub const MAX_TAB_TITLE_LEN: usize = 24;
|
pub const MAX_TAB_TITLE_LEN: usize = 24;
|
||||||
|
|
||||||
impl FollowableItem for Editor {
|
impl FollowableItem for Editor {
|
||||||
@ -409,7 +407,9 @@ impl Item for Editor {
|
|||||||
let buffer = self.buffer().clone();
|
let buffer = self.buffer().clone();
|
||||||
let buffers = buffer.read(cx).all_buffers();
|
let buffers = buffer.read(cx).all_buffers();
|
||||||
let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
|
let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
|
||||||
let format = project.update(cx, |project, cx| project.format(buffers, true, cx));
|
let format = project.update(cx, |project, cx| {
|
||||||
|
project.format(buffers, true, FormatTrigger::Save, cx)
|
||||||
|
});
|
||||||
cx.spawn(|_, mut cx| async move {
|
cx.spawn(|_, mut cx| async move {
|
||||||
let transaction = futures::select_biased! {
|
let transaction = futures::select_biased! {
|
||||||
_ = timeout => {
|
_ = timeout => {
|
||||||
|
@ -364,6 +364,22 @@ impl ProjectEntryId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum FormatTrigger {
|
||||||
|
Save,
|
||||||
|
Manual,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FormatTrigger {
|
||||||
|
fn from_proto(value: i32) -> FormatTrigger {
|
||||||
|
match value {
|
||||||
|
0 => FormatTrigger::Save,
|
||||||
|
1 => FormatTrigger::Manual,
|
||||||
|
_ => FormatTrigger::Save,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Project {
|
impl Project {
|
||||||
pub fn init(client: &Arc<Client>) {
|
pub fn init(client: &Arc<Client>) {
|
||||||
client.add_model_message_handler(Self::handle_request_join_project);
|
client.add_model_message_handler(Self::handle_request_join_project);
|
||||||
@ -3047,6 +3063,7 @@ impl Project {
|
|||||||
&self,
|
&self,
|
||||||
buffers: HashSet<ModelHandle<Buffer>>,
|
buffers: HashSet<ModelHandle<Buffer>>,
|
||||||
push_to_history: bool,
|
push_to_history: bool,
|
||||||
|
trigger: FormatTrigger,
|
||||||
cx: &mut ModelContext<Project>,
|
cx: &mut ModelContext<Project>,
|
||||||
) -> Task<Result<ProjectTransaction>> {
|
) -> Task<Result<ProjectTransaction>> {
|
||||||
let mut local_buffers = Vec::new();
|
let mut local_buffers = Vec::new();
|
||||||
@ -3076,6 +3093,7 @@ impl Project {
|
|||||||
let response = client
|
let response = client
|
||||||
.request(proto::FormatBuffers {
|
.request(proto::FormatBuffers {
|
||||||
project_id,
|
project_id,
|
||||||
|
trigger: trigger as i32,
|
||||||
buffer_ids: remote_buffers
|
buffer_ids: remote_buffers
|
||||||
.iter()
|
.iter()
|
||||||
.map(|buffer| buffer.read_with(&cx, |buffer, _| buffer.remote_id()))
|
.map(|buffer| buffer.read_with(&cx, |buffer, _| buffer.remote_id()))
|
||||||
@ -3092,18 +3110,22 @@ impl Project {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (buffer, buffer_abs_path, language_server) in local_buffers {
|
for (buffer, buffer_abs_path, language_server) in local_buffers {
|
||||||
let (format_on_save, tab_size) = buffer.read_with(&cx, |buffer, cx| {
|
let (format_on_save, formatter, tab_size) = buffer.read_with(&cx, |buffer, cx| {
|
||||||
let settings = cx.global::<Settings>();
|
let settings = cx.global::<Settings>();
|
||||||
let language_name = buffer.language().map(|language| language.name());
|
let language_name = buffer.language().map(|language| language.name());
|
||||||
(
|
(
|
||||||
settings.format_on_save(language_name.as_deref()),
|
settings.format_on_save(language_name.as_deref()),
|
||||||
|
settings.formatter(language_name.as_deref()),
|
||||||
settings.tab_size(language_name.as_deref()),
|
settings.tab_size(language_name.as_deref()),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
let transaction = match format_on_save {
|
if trigger == FormatTrigger::Save && !format_on_save {
|
||||||
settings::FormatOnSave::Off => continue,
|
continue;
|
||||||
settings::FormatOnSave::LanguageServer => Self::format_via_lsp(
|
}
|
||||||
|
|
||||||
|
let transaction = match formatter {
|
||||||
|
settings::Formatter::LanguageServer => Self::format_via_lsp(
|
||||||
&this,
|
&this,
|
||||||
&buffer,
|
&buffer,
|
||||||
&buffer_abs_path,
|
&buffer_abs_path,
|
||||||
@ -3113,7 +3135,8 @@ impl Project {
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.context("failed to format via language server")?,
|
.context("failed to format via language server")?,
|
||||||
settings::FormatOnSave::External { command, arguments } => {
|
|
||||||
|
settings::Formatter::External { command, arguments } => {
|
||||||
Self::format_via_external_command(
|
Self::format_via_external_command(
|
||||||
&buffer,
|
&buffer,
|
||||||
&buffer_abs_path,
|
&buffer_abs_path,
|
||||||
@ -5296,7 +5319,8 @@ impl Project {
|
|||||||
.ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?,
|
.ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Ok::<_, anyhow::Error>(this.format(buffers, false, cx))
|
let trigger = FormatTrigger::from_proto(envelope.payload.trigger);
|
||||||
|
Ok::<_, anyhow::Error>(this.format(buffers, false, trigger, cx))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let project_transaction = format.await?;
|
let project_transaction = format.await?;
|
||||||
|
@ -420,9 +420,15 @@ message ReloadBuffersResponse {
|
|||||||
ProjectTransaction transaction = 1;
|
ProjectTransaction transaction = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum FormatTrigger {
|
||||||
|
Save = 0;
|
||||||
|
Manual = 1;
|
||||||
|
}
|
||||||
|
|
||||||
message FormatBuffers {
|
message FormatBuffers {
|
||||||
uint64 project_id = 1;
|
uint64 project_id = 1;
|
||||||
repeated uint64 buffer_ids = 2;
|
FormatTrigger trigger = 2;
|
||||||
|
repeated uint64 buffer_ids = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message FormatBuffersResponse {
|
message FormatBuffersResponse {
|
||||||
|
@ -6,4 +6,4 @@ pub use conn::Connection;
|
|||||||
pub use peer::*;
|
pub use peer::*;
|
||||||
mod macros;
|
mod macros;
|
||||||
|
|
||||||
pub const PROTOCOL_VERSION: u32 = 31;
|
pub const PROTOCOL_VERSION: u32 = 32;
|
||||||
|
@ -58,7 +58,8 @@ pub struct EditorSettings {
|
|||||||
pub hard_tabs: Option<bool>,
|
pub hard_tabs: Option<bool>,
|
||||||
pub soft_wrap: Option<SoftWrap>,
|
pub soft_wrap: Option<SoftWrap>,
|
||||||
pub preferred_line_length: Option<u32>,
|
pub preferred_line_length: Option<u32>,
|
||||||
pub format_on_save: Option<FormatOnSave>,
|
pub format_on_save: Option<bool>,
|
||||||
|
pub formatter: Option<Formatter>,
|
||||||
pub enable_language_server: Option<bool>,
|
pub enable_language_server: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,8 +73,7 @@ pub enum SoftWrap {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum FormatOnSave {
|
pub enum Formatter {
|
||||||
Off,
|
|
||||||
LanguageServer,
|
LanguageServer,
|
||||||
External {
|
External {
|
||||||
command: String,
|
command: String,
|
||||||
@ -207,6 +207,7 @@ impl Settings {
|
|||||||
font_cache: &FontCache,
|
font_cache: &FontCache,
|
||||||
themes: &ThemeRegistry,
|
themes: &ThemeRegistry,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
#[track_caller]
|
||||||
fn required<T>(value: Option<T>) -> Option<T> {
|
fn required<T>(value: Option<T>) -> Option<T> {
|
||||||
assert!(value.is_some(), "missing default setting value");
|
assert!(value.is_some(), "missing default setting value");
|
||||||
value
|
value
|
||||||
@ -236,6 +237,7 @@ impl Settings {
|
|||||||
soft_wrap: required(defaults.editor.soft_wrap),
|
soft_wrap: required(defaults.editor.soft_wrap),
|
||||||
preferred_line_length: required(defaults.editor.preferred_line_length),
|
preferred_line_length: required(defaults.editor.preferred_line_length),
|
||||||
format_on_save: required(defaults.editor.format_on_save),
|
format_on_save: required(defaults.editor.format_on_save),
|
||||||
|
formatter: required(defaults.editor.formatter),
|
||||||
enable_language_server: required(defaults.editor.enable_language_server),
|
enable_language_server: required(defaults.editor.enable_language_server),
|
||||||
},
|
},
|
||||||
editor_overrides: Default::default(),
|
editor_overrides: Default::default(),
|
||||||
@ -322,8 +324,12 @@ impl Settings {
|
|||||||
self.language_setting(language, |settings| settings.preferred_line_length)
|
self.language_setting(language, |settings| settings.preferred_line_length)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn format_on_save(&self, language: Option<&str>) -> FormatOnSave {
|
pub fn format_on_save(&self, language: Option<&str>) -> bool {
|
||||||
self.language_setting(language, |settings| settings.format_on_save.clone())
|
self.language_setting(language, |settings| settings.format_on_save)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn formatter(&self, language: Option<&str>) -> Formatter {
|
||||||
|
self.language_setting(language, |settings| settings.formatter.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enable_language_server(&self, language: Option<&str>) -> bool {
|
pub fn enable_language_server(&self, language: Option<&str>) -> bool {
|
||||||
@ -358,7 +364,8 @@ impl Settings {
|
|||||||
hard_tabs: Some(false),
|
hard_tabs: Some(false),
|
||||||
soft_wrap: Some(SoftWrap::None),
|
soft_wrap: Some(SoftWrap::None),
|
||||||
preferred_line_length: Some(80),
|
preferred_line_length: Some(80),
|
||||||
format_on_save: Some(FormatOnSave::LanguageServer),
|
format_on_save: Some(true),
|
||||||
|
formatter: Some(Formatter::LanguageServer),
|
||||||
enable_language_server: Some(true),
|
enable_language_server: Some(true),
|
||||||
},
|
},
|
||||||
editor_overrides: Default::default(),
|
editor_overrides: Default::default(),
|
||||||
|
Loading…
Reference in New Issue
Block a user