mirror of
https://github.com/zed-industries/zed.git
synced 2025-02-04 18:15:21 +00:00
Add an application menu item for creating/opening a project-specific settings file (#2572)
Previously, project-specific settings were not discoverable. This PR adds a `Zed > Preferences > Local Settings` application menu command that creates a `.zed/settings.json` at the root of your current worktree. This command works in both local and remote projects. Limitations: * Currently, if you have an empty project open, the command just shows a notification that there are no folders open. * The JSON-schema-based autocomplete is the same in local settings files as in your main settings, even though not all settings can be locally customized. Release Notes: - Added an application menu command - `Zed > Preferences > Local Settings` for creating a folder-specific settings file.
This commit is contained in:
commit
53906fd3da
8 changed files with 130 additions and 41 deletions
11
assets/settings/initial_local_settings.json
Normal file
11
assets/settings/initial_local_settings.json
Normal file
|
@ -0,0 +1,11 @@
|
|||
// Folder-specific Zed settings
|
||||
//
|
||||
// A subset of Zed's settings can be configured on a per-folder basis.
|
||||
//
|
||||
// For information on how to configure Zed, see the Zed
|
||||
// documentation: https://zed.dev/docs/configuring-zed
|
||||
//
|
||||
// To see all of Zed's default settings without changing your
|
||||
// custom settings, run the `open default settings` command
|
||||
// from the command palette or from `Zed` application menu.
|
||||
{}
|
|
@ -1424,7 +1424,7 @@ async fn join_project(
|
|||
)?;
|
||||
}
|
||||
|
||||
for settings_file in dbg!(worktree.settings_files) {
|
||||
for settings_file in worktree.settings_files {
|
||||
session.peer.send(
|
||||
session.connection_id,
|
||||
proto::UpdateWorktreeSettings {
|
||||
|
@ -1554,8 +1554,6 @@ async fn update_worktree_settings(
|
|||
message: proto::UpdateWorktreeSettings,
|
||||
session: Session,
|
||||
) -> Result<()> {
|
||||
dbg!(&message);
|
||||
|
||||
let guest_connection_ids = session
|
||||
.db()
|
||||
.await
|
||||
|
|
|
@ -9,10 +9,23 @@ pub use settings_store::{Setting, SettingsJsonSchemaParams, SettingsStore};
|
|||
use std::{borrow::Cow, str};
|
||||
|
||||
pub const DEFAULT_SETTINGS_ASSET_PATH: &str = "settings/default.json";
|
||||
pub const INITIAL_USER_SETTINGS_ASSET_PATH: &str = "settings/initial_user_settings.json";
|
||||
const INITIAL_USER_SETTINGS_ASSET_PATH: &str = "settings/initial_user_settings.json";
|
||||
const INITIAL_LOCAL_SETTINGS_ASSET_PATH: &str = "settings/initial_local_settings.json";
|
||||
|
||||
pub fn initial_user_settings_content(assets: &'static impl AssetSource) -> Cow<'static, str> {
|
||||
match assets.load(INITIAL_USER_SETTINGS_ASSET_PATH).unwrap() {
|
||||
pub fn default_settings() -> Cow<'static, str> {
|
||||
asset_str(&assets::Assets, DEFAULT_SETTINGS_ASSET_PATH)
|
||||
}
|
||||
|
||||
pub fn initial_user_settings_content(assets: &dyn AssetSource) -> Cow<'_, str> {
|
||||
asset_str(assets, INITIAL_USER_SETTINGS_ASSET_PATH)
|
||||
}
|
||||
|
||||
pub fn initial_local_settings_content(assets: &dyn AssetSource) -> Cow<'_, str> {
|
||||
asset_str(assets, INITIAL_LOCAL_SETTINGS_ASSET_PATH)
|
||||
}
|
||||
|
||||
fn asset_str<'a>(assets: &'a dyn AssetSource, path: &str) -> Cow<'a, str> {
|
||||
match assets.load(path).unwrap() {
|
||||
Cow::Borrowed(s) => Cow::Borrowed(str::from_utf8(s).unwrap()),
|
||||
Cow::Owned(s) => Cow::Owned(String::from_utf8(s).unwrap()),
|
||||
}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
use crate::{settings_store::SettingsStore, Setting, DEFAULT_SETTINGS_ASSET_PATH};
|
||||
use crate::{settings_store::SettingsStore, Setting};
|
||||
use anyhow::Result;
|
||||
use assets::Assets;
|
||||
use fs::Fs;
|
||||
use futures::{channel::mpsc, StreamExt};
|
||||
use gpui::{executor::Background, AppContext, AssetSource};
|
||||
use gpui::{executor::Background, AppContext};
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
io::ErrorKind,
|
||||
path::{Path, PathBuf},
|
||||
str,
|
||||
|
@ -28,19 +27,12 @@ pub fn get_local<'a, T: Setting>(location: Option<(usize, &Path)>, cx: &'a AppCo
|
|||
cx.global::<SettingsStore>().get(location)
|
||||
}
|
||||
|
||||
pub fn default_settings() -> Cow<'static, str> {
|
||||
match Assets.load(DEFAULT_SETTINGS_ASSET_PATH).unwrap() {
|
||||
Cow::Borrowed(s) => Cow::Borrowed(str::from_utf8(s).unwrap()),
|
||||
Cow::Owned(s) => Cow::Owned(String::from_utf8(s).unwrap()),
|
||||
}
|
||||
}
|
||||
|
||||
pub const EMPTY_THEME_NAME: &'static str = "empty-theme";
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn test_settings() -> String {
|
||||
let mut value = crate::settings_store::parse_json_with_comments::<serde_json::Value>(
|
||||
default_settings().as_ref(),
|
||||
crate::default_settings().as_ref(),
|
||||
)
|
||||
.unwrap();
|
||||
util::merge_non_null_json_value_into(
|
||||
|
|
|
@ -15,7 +15,6 @@ mod toolbar;
|
|||
mod workspace_settings;
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use assets::Assets;
|
||||
use call::ActiveCall;
|
||||
use client::{
|
||||
proto::{self, PeerId},
|
||||
|
@ -83,7 +82,7 @@ use status_bar::StatusBar;
|
|||
pub use status_bar::StatusItemView;
|
||||
use theme::{Theme, ThemeSettings};
|
||||
pub use toolbar::{ToolbarItemLocation, ToolbarItemView};
|
||||
use util::{async_iife, paths, ResultExt};
|
||||
use util::{async_iife, ResultExt};
|
||||
pub use workspace_settings::{AutosaveSetting, GitGutterSetting, WorkspaceSettings};
|
||||
|
||||
lazy_static! {
|
||||
|
@ -133,8 +132,6 @@ actions!(
|
|||
]
|
||||
);
|
||||
|
||||
actions!(zed, [OpenSettings]);
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct OpenPaths {
|
||||
pub paths: Vec<PathBuf>,
|
||||
|
@ -295,17 +292,6 @@ pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
|
|||
.detach();
|
||||
});
|
||||
|
||||
cx.add_action(
|
||||
move |_: &mut Workspace, _: &OpenSettings, cx: &mut ViewContext<Workspace>| {
|
||||
create_and_open_local_file(&paths::SETTINGS, cx, || {
|
||||
settings::initial_user_settings_content(&Assets)
|
||||
.as_ref()
|
||||
.into()
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
},
|
||||
);
|
||||
|
||||
let client = &app_state.client;
|
||||
client.add_view_request_handler(Workspace::handle_follow);
|
||||
client.add_view_message_handler(Workspace::handle_unfollow);
|
||||
|
|
|
@ -56,9 +56,7 @@ use fs::RealFs;
|
|||
#[cfg(debug_assertions)]
|
||||
use staff_mode::StaffMode;
|
||||
use util::{channel::RELEASE_CHANNEL, paths, ResultExt, TryFutureExt};
|
||||
use workspace::{
|
||||
item::ItemHandle, notifications::NotifyResultExt, AppState, OpenSettings, Workspace,
|
||||
};
|
||||
use workspace::{item::ItemHandle, notifications::NotifyResultExt, AppState, Workspace};
|
||||
use zed::{
|
||||
self, build_window_options, handle_keymap_file_changes, initialize_workspace, languages, menus,
|
||||
};
|
||||
|
@ -877,6 +875,6 @@ pub fn background_actions() -> &'static [(&'static str, &'static dyn Action)] {
|
|||
("Go to file", &file_finder::Toggle),
|
||||
("Open command palette", &command_palette::Toggle),
|
||||
("Open recent projects", &recent_projects::OpenRecent),
|
||||
("Change your settings", &OpenSettings),
|
||||
("Change your settings", &zed::OpenSettings),
|
||||
]
|
||||
}
|
||||
|
|
|
@ -12,10 +12,11 @@ pub fn menus() -> Vec<Menu<'static>> {
|
|||
MenuItem::submenu(Menu {
|
||||
name: "Preferences",
|
||||
items: vec![
|
||||
MenuItem::action("Open Settings", workspace::OpenSettings),
|
||||
MenuItem::action("Open Settings", super::OpenSettings),
|
||||
MenuItem::action("Open Key Bindings", super::OpenKeymap),
|
||||
MenuItem::action("Open Default Settings", super::OpenDefaultSettings),
|
||||
MenuItem::action("Open Default Key Bindings", super::OpenDefaultKeymap),
|
||||
MenuItem::action("Open Local Settings", super::OpenLocalSettings),
|
||||
MenuItem::action("Select Theme", theme_selector::Toggle),
|
||||
],
|
||||
}),
|
||||
|
|
|
@ -30,16 +30,23 @@ use project_panel::ProjectPanel;
|
|||
use search::{BufferSearchBar, ProjectSearchBar};
|
||||
use serde::Deserialize;
|
||||
use serde_json::to_string_pretty;
|
||||
use settings::{KeymapFileContent, SettingsStore, DEFAULT_SETTINGS_ASSET_PATH};
|
||||
use settings::{
|
||||
initial_local_settings_content, KeymapFileContent, SettingsStore, DEFAULT_SETTINGS_ASSET_PATH,
|
||||
};
|
||||
use std::{borrow::Cow, str, sync::Arc};
|
||||
use terminal_view::terminal_panel::{self, TerminalPanel};
|
||||
use util::{channel::ReleaseChannel, paths, ResultExt};
|
||||
use util::{
|
||||
channel::ReleaseChannel,
|
||||
paths::{self, LOCAL_SETTINGS_RELATIVE_PATH},
|
||||
ResultExt,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
use welcome::BaseKeymap;
|
||||
pub use workspace;
|
||||
use workspace::{
|
||||
create_and_open_local_file, dock::PanelHandle, open_new, AppState, NewFile, NewWindow,
|
||||
Workspace, WorkspaceSettings,
|
||||
create_and_open_local_file, dock::PanelHandle,
|
||||
notifications::simple_message_notification::MessageNotification, open_new, AppState, NewFile,
|
||||
NewWindow, Workspace, WorkspaceSettings,
|
||||
};
|
||||
|
||||
#[derive(Deserialize, Clone, PartialEq)]
|
||||
|
@ -65,6 +72,8 @@ actions!(
|
|||
OpenLicenses,
|
||||
OpenTelemetryLog,
|
||||
OpenKeymap,
|
||||
OpenSettings,
|
||||
OpenLocalSettings,
|
||||
OpenDefaultSettings,
|
||||
OpenDefaultKeymap,
|
||||
IncreaseBufferFontSize,
|
||||
|
@ -157,6 +166,17 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::AppContext) {
|
|||
create_and_open_local_file(&paths::KEYMAP, cx, Default::default).detach_and_log_err(cx);
|
||||
},
|
||||
);
|
||||
cx.add_action(
|
||||
move |_: &mut Workspace, _: &OpenSettings, cx: &mut ViewContext<Workspace>| {
|
||||
create_and_open_local_file(&paths::SETTINGS, cx, || {
|
||||
settings::initial_user_settings_content(&Assets)
|
||||
.as_ref()
|
||||
.into()
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
},
|
||||
);
|
||||
cx.add_action(open_local_settings_file);
|
||||
cx.add_action(
|
||||
move |workspace: &mut Workspace, _: &OpenDefaultKeymap, cx: &mut ViewContext<Workspace>| {
|
||||
open_bundled_file(
|
||||
|
@ -544,6 +564,76 @@ pub fn handle_keymap_file_changes(
|
|||
.detach();
|
||||
}
|
||||
|
||||
fn open_local_settings_file(
|
||||
workspace: &mut Workspace,
|
||||
_: &OpenLocalSettings,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) {
|
||||
let project = workspace.project().clone();
|
||||
let worktree = project
|
||||
.read(cx)
|
||||
.visible_worktrees(cx)
|
||||
.find_map(|tree| tree.read(cx).root_entry()?.is_dir().then_some(tree));
|
||||
if let Some(worktree) = worktree {
|
||||
let tree_id = worktree.read(cx).id();
|
||||
cx.spawn(|workspace, mut cx| async move {
|
||||
let file_path = &*LOCAL_SETTINGS_RELATIVE_PATH;
|
||||
|
||||
if let Some(dir_path) = file_path.parent() {
|
||||
if worktree.read_with(&cx, |tree, _| tree.entry_for_path(dir_path).is_none()) {
|
||||
project
|
||||
.update(&mut cx, |project, cx| {
|
||||
project.create_entry((tree_id, dir_path), true, cx)
|
||||
})
|
||||
.ok_or_else(|| anyhow!("worktree was removed"))?
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
if worktree.read_with(&cx, |tree, _| tree.entry_for_path(file_path).is_none()) {
|
||||
project
|
||||
.update(&mut cx, |project, cx| {
|
||||
project.create_entry((tree_id, file_path), false, cx)
|
||||
})
|
||||
.ok_or_else(|| anyhow!("worktree was removed"))?
|
||||
.await?;
|
||||
}
|
||||
|
||||
let editor = workspace
|
||||
.update(&mut cx, |workspace, cx| {
|
||||
workspace.open_path((tree_id, file_path), None, true, cx)
|
||||
})?
|
||||
.await?
|
||||
.downcast::<Editor>()
|
||||
.ok_or_else(|| anyhow!("unexpected item type"))?;
|
||||
|
||||
editor
|
||||
.downgrade()
|
||||
.update(&mut cx, |editor, cx| {
|
||||
if let Some(buffer) = editor.buffer().read(cx).as_singleton() {
|
||||
if buffer.read(cx).is_empty() {
|
||||
buffer.update(cx, |buffer, cx| {
|
||||
buffer.edit(
|
||||
[(0..0, initial_local_settings_content(&Assets))],
|
||||
None,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach();
|
||||
} else {
|
||||
workspace.show_notification(0, cx, |cx| {
|
||||
cx.add_view(|_| MessageNotification::new("This project has no folders open."))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn open_telemetry_log_file(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
|
||||
workspace.with_local_workspace(cx, move |workspace, cx| {
|
||||
let app_state = workspace.app_state().clone();
|
||||
|
|
Loading…
Reference in a new issue