Define workspace settings in workspace crate

This commit is contained in:
Max Brunsfeld 2023-05-16 20:25:18 -07:00
parent cbd4771f10
commit 6403bb86e1
22 changed files with 253 additions and 243 deletions

1
Cargo.lock generated
View file

@ -8676,6 +8676,7 @@ dependencies = [
"parking_lot 0.11.2",
"postage",
"project",
"schemars",
"serde",
"serde_derive",
"serde_json",

View file

@ -6,7 +6,7 @@ use gpui::{
platform::{Appearance, MouseButton},
AnyElement, AppContext, Element, Entity, View, ViewContext,
};
use settings::Settings;
use workspace::WorkspaceSettings;
pub fn init(cx: &mut AppContext) {
let active_call = ActiveCall::global(cx);
@ -15,7 +15,9 @@ pub fn init(cx: &mut AppContext) {
cx.observe(&active_call, move |call, cx| {
if let Some(room) = call.read(cx).room() {
if room.read(cx).is_screen_sharing() {
if status_indicator.is_none() && cx.global::<Settings>().show_call_status_icon {
if status_indicator.is_none()
&& settings::get_setting::<WorkspaceSettings>(None, cx).show_call_status_icon
{
status_indicator = Some(cx.add_status_bar_item(|_| SharingStatusIndicator));
}
} else if let Some((window_id, _)) = status_indicator.take() {

View file

@ -1499,6 +1499,7 @@ mod tests {
cx.set_global(Settings::test(cx));
cx.set_global(SettingsStore::test(cx));
language::init(cx);
workspace::init_settings(cx);
});
}

View file

@ -6685,6 +6685,7 @@ pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsC
language::init(cx);
crate::init(cx);
Project::init_settings(cx);
workspace::init_settings(cx);
});
update_test_settings(cx, f);

View file

@ -40,7 +40,7 @@ use language::{
Selection,
};
use project::ProjectPath;
use settings::{GitGutter, Settings};
use settings::Settings;
use smallvec::SmallVec;
use std::{
borrow::Cow,
@ -50,7 +50,7 @@ use std::{
ops::Range,
sync::Arc,
};
use workspace::item::Item;
use workspace::{item::Item, GitGutterSetting, WorkspaceSettings};
enum FoldMarkers {}
@ -550,11 +550,11 @@ impl EditorElement {
let scroll_top = scroll_position.y() * line_height;
let show_gutter = matches!(
&cx.global::<Settings>()
.git_overrides
settings::get_setting::<WorkspaceSettings>(None, cx)
.git
.git_gutter
.unwrap_or_default(),
GitGutter::TrackedFiles
GitGutterSetting::TrackedFiles
);
if show_gutter {

View file

@ -41,6 +41,7 @@ impl<'a> EditorLspTestContext<'a> {
crate::init(cx);
pane::init(cx);
Project::init_settings(cx);
workspace::init_settings(cx);
});
let file_name = format!(

View file

@ -658,6 +658,7 @@ mod tests {
language::init(cx);
super::init(cx);
editor::init(cx);
workspace::init_settings(cx);
state
})
}

View file

@ -1936,6 +1936,7 @@ mod tests {
cx.set_global(SettingsStore::test(cx));
language::init(cx);
editor::init_settings(cx);
workspace::init_settings(cx);
});
}
}

View file

@ -375,6 +375,7 @@ mod tests {
cx.set_global(SettingsStore::test(cx));
language::init(cx);
Project::init_settings(cx);
workspace::init_settings(cx);
});
}

View file

@ -1288,6 +1288,7 @@ pub mod tests {
language::init(cx);
editor::init_settings(cx);
workspace::init_settings(cx);
});
}
}

View file

@ -3,7 +3,7 @@ mod keymap_file;
mod settings_file;
mod settings_store;
use anyhow::{bail, Result};
use anyhow::Result;
use gpui::{
font_cache::{FamilyId, FontCache},
fonts, AppContext, AssetSource,
@ -15,10 +15,6 @@ use schemars::{
};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use sqlez::{
bindable::{Bind, Column, StaticColumnCount},
statement::Statement,
};
use std::{borrow::Cow, str, sync::Arc};
use theme::{Theme, ThemeRegistry};
use util::ResultExt as _;
@ -37,13 +33,6 @@ pub struct Settings {
pub buffer_font_features: fonts::Features,
pub buffer_font_family: FamilyId,
pub buffer_font_size: f32,
pub active_pane_magnification: f32,
pub confirm_quit: bool,
pub show_call_status_icon: bool,
pub autosave: Autosave,
pub default_dock_anchor: DockAnchor,
pub git: GitSettings,
pub git_overrides: GitSettings,
pub theme: Arc<Theme>,
pub base_keymap: BaseKeymap,
}
@ -72,13 +61,6 @@ impl Setting for Settings {
buffer_font_family_name: defaults.buffer_font_family.clone().unwrap(),
buffer_font_features,
buffer_font_size: defaults.buffer_font_size.unwrap(),
active_pane_magnification: defaults.active_pane_magnification.unwrap(),
confirm_quit: defaults.confirm_quit.unwrap(),
show_call_status_icon: defaults.show_call_status_icon.unwrap(),
autosave: defaults.autosave.unwrap(),
default_dock_anchor: defaults.default_dock_anchor.unwrap(),
git: defaults.git.unwrap(),
git_overrides: Default::default(),
theme: themes.get(defaults.theme.as_ref().unwrap()).unwrap(),
base_keymap: Default::default(),
};
@ -201,65 +183,6 @@ impl BaseKeymap {
.unwrap_or_default()
}
}
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
pub struct GitSettings {
pub git_gutter: Option<GitGutter>,
pub gutter_debounce: Option<u64>,
}
#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum GitGutter {
#[default]
TrackedFiles,
Hide,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum Autosave {
Off,
AfterDelay { milliseconds: u64 },
OnFocusChange,
OnWindowChange,
}
#[derive(PartialEq, Eq, Debug, Default, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum DockAnchor {
#[default]
Bottom,
Right,
Expanded,
}
impl StaticColumnCount for DockAnchor {}
impl Bind for DockAnchor {
fn bind(&self, statement: &Statement, start_index: i32) -> anyhow::Result<i32> {
match self {
DockAnchor::Bottom => "Bottom",
DockAnchor::Right => "Right",
DockAnchor::Expanded => "Expanded",
}
.bind(statement, start_index)
}
}
impl Column for DockAnchor {
fn column(statement: &mut Statement, start_index: i32) -> anyhow::Result<(Self, i32)> {
String::column(statement, start_index).and_then(|(anchor_text, next_index)| {
Ok((
match anchor_text.as_ref() {
"Bottom" => DockAnchor::Bottom,
"Right" => DockAnchor::Right,
"Expanded" => DockAnchor::Expanded,
_ => bail!("Stored dock anchor is incorrect"),
},
next_index,
))
})
}
}
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
pub struct SettingsFileContent {
@ -270,24 +193,6 @@ pub struct SettingsFileContent {
#[serde(default)]
pub buffer_font_features: Option<fonts::Features>,
#[serde(default)]
pub active_pane_magnification: Option<f32>,
#[serde(default)]
pub cursor_blink: Option<bool>,
#[serde(default)]
pub confirm_quit: Option<bool>,
#[serde(default)]
pub hover_popover_enabled: Option<bool>,
#[serde(default)]
pub show_completions_on_input: Option<bool>,
#[serde(default)]
pub show_call_status_icon: Option<bool>,
#[serde(default)]
pub autosave: Option<Autosave>,
#[serde(default)]
pub default_dock_anchor: Option<DockAnchor>,
#[serde(default)]
pub git: Option<GitSettings>,
#[serde(default)]
pub theme: Option<String>,
#[serde(default)]
pub base_keymap: Option<BaseKeymap>,
@ -323,13 +228,6 @@ impl Settings {
buffer_font_family_name: defaults.buffer_font_family.unwrap(),
buffer_font_features,
buffer_font_size: defaults.buffer_font_size.unwrap(),
active_pane_magnification: defaults.active_pane_magnification.unwrap(),
confirm_quit: defaults.confirm_quit.unwrap(),
show_call_status_icon: defaults.show_call_status_icon.unwrap(),
autosave: defaults.autosave.unwrap(),
default_dock_anchor: defaults.default_dock_anchor.unwrap(),
git: defaults.git.unwrap(),
git_overrides: Default::default(),
theme: themes.get(&defaults.theme.unwrap()).unwrap(),
base_keymap: Default::default(),
}
@ -367,24 +265,7 @@ impl Settings {
}
merge(&mut self.buffer_font_size, data.buffer_font_size);
merge(
&mut self.active_pane_magnification,
data.active_pane_magnification,
);
merge(&mut self.confirm_quit, data.confirm_quit);
merge(&mut self.autosave, data.autosave);
merge(&mut self.default_dock_anchor, data.default_dock_anchor);
merge(&mut self.base_keymap, data.base_keymap);
self.git_overrides = data.git.unwrap_or_default();
}
pub fn git_gutter(&self) -> GitGutter {
self.git_overrides.git_gutter.unwrap_or_else(|| {
self.git
.git_gutter
.expect("git_gutter should be some by setting setup")
})
}
#[cfg(any(test, feature = "test-support"))]
@ -397,13 +278,6 @@ impl Settings {
.load_family(&["Monaco"], &Default::default())
.unwrap(),
buffer_font_size: 14.,
active_pane_magnification: 1.,
confirm_quit: false,
show_call_status_icon: true,
autosave: Autosave::Off,
default_dock_anchor: DockAnchor::Bottom,
git: Default::default(),
git_overrides: Default::default(),
theme: gpui::fonts::with_font_cache(cx.font_cache().clone(), Default::default),
base_keymap: Default::default(),
}

View file

@ -787,22 +787,18 @@ fn get_path_from_wt(wt: &LocalWorktree) -> Option<PathBuf> {
#[cfg(test)]
mod tests {
use super::*;
use gpui::TestAppContext;
use project::{Entry, Project, ProjectPath, Worktree};
use std::path::Path;
use workspace::AppState;
use std::path::Path;
// Working directory calculation tests
///Working directory calculation tests
///No Worktrees in project -> home_dir()
// No Worktrees in project -> home_dir()
#[gpui::test]
async fn no_worktree(cx: &mut TestAppContext) {
//Setup variables
let (project, workspace) = blank_workspace(cx).await;
//Test
let (project, workspace) = init_test(cx).await;
cx.read(|cx| {
let workspace = workspace.read(cx);
let active_entry = project.read(cx).active_entry();
@ -818,14 +814,12 @@ mod tests {
});
}
///No active entry, but a worktree, worktree is a file -> home_dir()
// No active entry, but a worktree, worktree is a file -> home_dir()
#[gpui::test]
async fn no_active_entry_worktree_is_file(cx: &mut TestAppContext) {
//Setup variables
let (project, workspace) = init_test(cx).await;
let (project, workspace) = blank_workspace(cx).await;
create_file_wt(project.clone(), "/root.txt", cx).await;
cx.read(|cx| {
let workspace = workspace.read(cx);
let active_entry = project.read(cx).active_entry();
@ -841,14 +835,12 @@ mod tests {
});
}
//No active entry, but a worktree, worktree is a folder -> worktree_folder
// No active entry, but a worktree, worktree is a folder -> worktree_folder
#[gpui::test]
async fn no_active_entry_worktree_is_dir(cx: &mut TestAppContext) {
//Setup variables
let (project, workspace) = blank_workspace(cx).await;
let (_wt, _entry) = create_folder_wt(project.clone(), "/root/", cx).await;
let (project, workspace) = init_test(cx).await;
//Test
let (_wt, _entry) = create_folder_wt(project.clone(), "/root/", cx).await;
cx.update(|cx| {
let workspace = workspace.read(cx);
let active_entry = project.read(cx).active_entry();
@ -863,17 +855,15 @@ mod tests {
});
}
//Active entry with a work tree, worktree is a file -> home_dir()
// Active entry with a work tree, worktree is a file -> home_dir()
#[gpui::test]
async fn active_entry_worktree_is_file(cx: &mut TestAppContext) {
//Setup variables
let (project, workspace) = init_test(cx).await;
let (project, workspace) = blank_workspace(cx).await;
let (_wt, _entry) = create_folder_wt(project.clone(), "/root1/", cx).await;
let (wt2, entry2) = create_file_wt(project.clone(), "/root2.txt", cx).await;
insert_active_entry_for(wt2, entry2, project.clone(), cx);
//Test
cx.update(|cx| {
let workspace = workspace.read(cx);
let active_entry = project.read(cx).active_entry();
@ -887,16 +877,15 @@ mod tests {
});
}
//Active entry, with a worktree, worktree is a folder -> worktree_folder
// Active entry, with a worktree, worktree is a folder -> worktree_folder
#[gpui::test]
async fn active_entry_worktree_is_dir(cx: &mut TestAppContext) {
//Setup variables
let (project, workspace) = blank_workspace(cx).await;
let (project, workspace) = init_test(cx).await;
let (_wt, _entry) = create_folder_wt(project.clone(), "/root1/", cx).await;
let (wt2, entry2) = create_folder_wt(project.clone(), "/root2/", cx).await;
insert_active_entry_for(wt2, entry2, project.clone(), cx);
//Test
cx.update(|cx| {
let workspace = workspace.read(cx);
let active_entry = project.read(cx).active_entry();
@ -910,8 +899,8 @@ mod tests {
});
}
///Creates a worktree with 1 file: /root.txt
pub async fn blank_workspace(
/// Creates a worktree with 1 file: /root.txt
pub async fn init_test(
cx: &mut TestAppContext,
) -> (ModelHandle<Project>, ViewHandle<Workspace>) {
let params = cx.update(AppState::test);
@ -922,7 +911,7 @@ mod tests {
(project, workspace)
}
///Creates a worktree with 1 folder: /root{suffix}/
/// Creates a worktree with 1 folder: /root{suffix}/
async fn create_folder_wt(
project: ModelHandle<Project>,
path: impl AsRef<Path>,
@ -931,7 +920,7 @@ mod tests {
create_wt(project, true, path, cx).await
}
///Creates a worktree with 1 file: /root{suffix}.txt
/// Creates a worktree with 1 file: /root{suffix}.txt
async fn create_file_wt(
project: ModelHandle<Project>,
path: impl AsRef<Path>,

View file

@ -45,6 +45,7 @@ lazy_static.workspace = true
log.workspace = true
parking_lot.workspace = true
postage.workspace = true
schemars.workspace = true
serde.workspace = true
serde_derive.workspace = true
serde_json.workspace = true

View file

@ -1,5 +1,9 @@
mod toggle_dock_button;
use crate::{
sidebar::SidebarSide, BackgroundActions, DockAnchor, ItemHandle, Pane, Workspace,
WorkspaceSettings,
};
use collections::HashMap;
use gpui::{
actions,
@ -8,10 +12,7 @@ use gpui::{
platform::{CursorStyle, MouseButton},
AnyElement, AppContext, Border, Element, SizeConstraint, ViewContext, ViewHandle,
};
use settings::{DockAnchor, Settings};
use theme::Theme;
use crate::{sidebar::SidebarSide, BackgroundActions, ItemHandle, Pane, Workspace};
pub use toggle_dock_button::ToggleDockButton;
actions!(
@ -171,7 +172,9 @@ impl Dock {
background_actions: BackgroundActions,
cx: &mut ViewContext<Workspace>,
) -> Self {
let position = DockPosition::Hidden(cx.global::<Settings>().default_dock_anchor);
let position = DockPosition::Hidden(
settings::get_setting::<WorkspaceSettings>(None, cx).default_dock_anchor,
);
let workspace = cx.weak_handle();
let pane =
cx.add_view(|cx| Pane::new(workspace, Some(position.anchor()), background_actions, cx));
@ -405,7 +408,6 @@ mod tests {
use gpui::{AppContext, BorrowWindowContext, TestAppContext, ViewContext, WindowContext};
use project::{FakeFs, Project};
use settings::Settings;
use theme::ThemeRegistry;
use super::*;
@ -417,6 +419,7 @@ mod tests {
},
register_deserializable_item,
sidebar::Sidebar,
tests::init_test,
AppState, ItemHandle, Workspace,
};
@ -429,8 +432,7 @@ mod tests {
#[gpui::test]
async fn test_dock_workspace_infinite_loop(cx: &mut TestAppContext) {
cx.foreground().forbid_parking();
Settings::test_async(cx);
init_test(cx);
cx.update(|cx| {
register_deserializable_item::<item::test::TestItem>(cx);
@ -598,7 +600,7 @@ mod tests {
impl<'a> DockTestContext<'a> {
pub async fn new(cx: &'a mut TestAppContext) -> DockTestContext<'a> {
Settings::test_async(cx);
init_test(cx);
let fs = FakeFs::new(cx.background());
cx.update(|cx| init(cx));

View file

@ -3,6 +3,7 @@ use crate::{
FollowableItemBuilders, ItemNavHistory, Pane, ToolbarItemLocation, ViewId, Workspace,
WorkspaceId,
};
use crate::{AutosaveSetting, WorkspaceSettings};
use anyhow::Result;
use client::{proto, Client};
use gpui::{
@ -10,7 +11,6 @@ use gpui::{
ViewContext, ViewHandle, WeakViewHandle, WindowContext,
};
use project::{Project, ProjectEntryId, ProjectPath};
use settings::{Autosave, Settings};
use smallvec::SmallVec;
use std::{
any::{Any, TypeId},
@ -450,8 +450,11 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
}
ItemEvent::Edit => {
if let Autosave::AfterDelay { milliseconds } =
cx.global::<Settings>().autosave
let settings = settings::get_setting::<WorkspaceSettings>(None, cx);
let debounce_delay = settings.git.gutter_debounce;
if let AutosaveSetting::AfterDelay { milliseconds } =
settings.autosave
{
let delay = Duration::from_millis(milliseconds);
let item = item.clone();
@ -460,9 +463,6 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
});
}
let settings = cx.global::<Settings>();
let debounce_delay = settings.git_overrides.gutter_debounce;
let item = item.clone();
if let Some(delay) = debounce_delay {
@ -500,7 +500,10 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
}));
cx.observe_focus(self, move |workspace, item, focused, cx| {
if !focused && cx.global::<Settings>().autosave == Autosave::OnFocusChange {
if !focused
&& settings::get_setting::<WorkspaceSettings>(None, cx).autosave
== AutosaveSetting::OnFocusChange
{
Pane::autosave_item(&item, workspace.project.clone(), cx)
.detach_and_log_err(cx);
}

View file

@ -5,7 +5,8 @@ use crate::{
dock::{icon_for_dock_anchor, AnchorDockBottom, AnchorDockRight, Dock, ExpandDock},
item::WeakItemHandle,
toolbar::Toolbar,
Item, NewFile, NewSearch, NewTerminal, Workspace,
AutosaveSetting, DockAnchor, Item, NewFile, NewSearch, NewTerminal, Workspace,
WorkspaceSettings,
};
use anyhow::{anyhow, Result};
use collections::{HashMap, HashSet, VecDeque};
@ -29,7 +30,7 @@ use gpui::{
};
use project::{Project, ProjectEntryId, ProjectPath};
use serde::Deserialize;
use settings::{Autosave, DockAnchor, Settings};
use settings::Settings;
use std::{any::Any, cell::RefCell, cmp, mem, path::Path, rc::Rc};
use theme::Theme;
use util::ResultExt;
@ -1024,8 +1025,8 @@ impl Pane {
} else if is_dirty && (can_save || is_singleton) {
let will_autosave = cx.read(|cx| {
matches!(
cx.global::<Settings>().autosave,
Autosave::OnFocusChange | Autosave::OnWindowChange
settings::get_setting::<WorkspaceSettings>(None, cx).autosave,
AutosaveSetting::OnFocusChange | AutosaveSetting::OnWindowChange
) && Self::can_autosave_item(&*item, cx)
});
let should_save = if should_prompt_for_save && !will_autosave {
@ -2087,10 +2088,11 @@ mod tests {
use crate::item::test::{TestItem, TestProjectItem};
use gpui::{executor::Deterministic, TestAppContext};
use project::FakeFs;
use settings::SettingsStore;
#[gpui::test]
async fn test_remove_active_empty(cx: &mut TestAppContext) {
Settings::test_async(cx);
init_test(cx);
let fs = FakeFs::new(cx.background());
let project = Project::test(fs, None, cx).await;
@ -2104,7 +2106,7 @@ mod tests {
#[gpui::test]
async fn test_add_item_with_new_item(cx: &mut TestAppContext) {
cx.foreground().forbid_parking();
Settings::test_async(cx);
init_test(cx);
let fs = FakeFs::new(cx.background());
let project = Project::test(fs, None, cx).await;
@ -2192,7 +2194,7 @@ mod tests {
#[gpui::test]
async fn test_add_item_with_existing_item(cx: &mut TestAppContext) {
cx.foreground().forbid_parking();
Settings::test_async(cx);
init_test(cx);
let fs = FakeFs::new(cx.background());
let project = Project::test(fs, None, cx).await;
@ -2268,7 +2270,7 @@ mod tests {
#[gpui::test]
async fn test_add_item_with_same_project_entries(cx: &mut TestAppContext) {
cx.foreground().forbid_parking();
Settings::test_async(cx);
init_test(cx);
let fs = FakeFs::new(cx.background());
let project = Project::test(fs, None, cx).await;
@ -2377,7 +2379,7 @@ mod tests {
#[gpui::test]
async fn test_remove_item_ordering(deterministic: Arc<Deterministic>, cx: &mut TestAppContext) {
Settings::test_async(cx);
init_test(cx);
let fs = FakeFs::new(cx.background());
let project = Project::test(fs, None, cx).await;
@ -2424,7 +2426,7 @@ mod tests {
#[gpui::test]
async fn test_close_inactive_items(deterministic: Arc<Deterministic>, cx: &mut TestAppContext) {
Settings::test_async(cx);
init_test(cx);
let fs = FakeFs::new(cx.background());
let project = Project::test(fs, None, cx).await;
@ -2443,7 +2445,7 @@ mod tests {
#[gpui::test]
async fn test_close_clean_items(deterministic: Arc<Deterministic>, cx: &mut TestAppContext) {
Settings::test_async(cx);
init_test(cx);
let fs = FakeFs::new(cx.background());
let project = Project::test(fs, None, cx).await;
@ -2470,7 +2472,7 @@ mod tests {
deterministic: Arc<Deterministic>,
cx: &mut TestAppContext,
) {
Settings::test_async(cx);
init_test(cx);
let fs = FakeFs::new(cx.background());
let project = Project::test(fs, None, cx).await;
@ -2492,7 +2494,7 @@ mod tests {
deterministic: Arc<Deterministic>,
cx: &mut TestAppContext,
) {
Settings::test_async(cx);
init_test(cx);
let fs = FakeFs::new(cx.background());
let project = Project::test(fs, None, cx).await;
@ -2511,7 +2513,7 @@ mod tests {
#[gpui::test]
async fn test_close_all_items(deterministic: Arc<Deterministic>, cx: &mut TestAppContext) {
Settings::test_async(cx);
init_test(cx);
let fs = FakeFs::new(cx.background());
let project = Project::test(fs, None, cx).await;
@ -2531,6 +2533,14 @@ mod tests {
assert_item_labels(&pane, [], cx);
}
fn init_test(cx: &mut TestAppContext) {
cx.update(|cx| {
cx.set_global(SettingsStore::test(cx));
cx.set_global(settings::Settings::test(cx));
crate::init_settings(cx);
});
}
fn add_labeled_item(
workspace: &ViewHandle<Workspace>,
pane: &ViewHandle<Pane>,

View file

@ -1,6 +1,6 @@
use std::sync::Arc;
use crate::{AppState, FollowerStatesByLeader, Pane, Workspace};
use crate::{AppState, FollowerStatesByLeader, Pane, Workspace, WorkspaceSettings};
use anyhow::{anyhow, Result};
use call::{ActiveCall, ParticipantLocation};
use gpui::{
@ -11,7 +11,6 @@ use gpui::{
};
use project::Project;
use serde::Deserialize;
use settings::Settings;
use theme::Theme;
#[derive(Clone, Debug, Eq, PartialEq)]
@ -380,7 +379,8 @@ impl PaneAxis {
.with_children(self.members.iter().enumerate().map(|(ix, member)| {
let mut flex = 1.0;
if member.contains(active_pane) {
flex = cx.global::<Settings>().active_pane_magnification;
flex = settings::get_setting::<WorkspaceSettings>(None, cx)
.active_pane_magnification;
}
let mut member = member.render(

View file

@ -497,13 +497,10 @@ impl WorkspaceDb {
#[cfg(test)]
mod tests {
use std::sync::Arc;
use db::open_test_db;
use settings::DockAnchor;
use super::*;
use crate::DockAnchor;
use db::open_test_db;
use std::sync::Arc;
#[gpui::test]
async fn test_next_id_stability() {

View file

@ -1,5 +1,6 @@
use crate::{
dock::DockPosition, ItemDeserializers, Member, Pane, PaneAxis, Workspace, WorkspaceId,
dock::DockPosition, DockAnchor, ItemDeserializers, Member, Pane, PaneAxis, Workspace,
WorkspaceId,
};
use anyhow::{anyhow, Context, Result};
use async_recursion::async_recursion;
@ -11,7 +12,6 @@ use gpui::{
platform::WindowBounds, AsyncAppContext, Axis, ModelHandle, Task, ViewHandle, WeakViewHandle,
};
use project::Project;
use settings::DockAnchor;
use std::{
path::{Path, PathBuf},
sync::Arc,
@ -305,10 +305,9 @@ impl Column for DockPosition {
#[cfg(test)]
mod tests {
use db::sqlez::connection::Connection;
use settings::DockAnchor;
use super::WorkspaceLocation;
use crate::DockAnchor;
use db::sqlez::connection::Connection;
#[test]
fn test_workspace_round_trips() {

View file

@ -13,6 +13,7 @@ pub mod shared_screen;
pub mod sidebar;
mod status_bar;
mod toolbar;
mod workspace_settings;
use anyhow::{anyhow, Context, Result};
use assets::Assets;
@ -75,7 +76,7 @@ pub use persistence::{
use postage::prelude::Stream;
use project::{Project, ProjectEntryId, ProjectPath, Worktree, WorktreeId};
use serde::Deserialize;
use settings::{Autosave, DockAnchor, Settings};
use settings::Settings;
use shared_screen::SharedScreen;
use sidebar::{Sidebar, SidebarButtons, SidebarSide, ToggleSidebarItem};
use status_bar::StatusBar;
@ -83,6 +84,7 @@ pub use status_bar::StatusItemView;
use theme::{Theme, ThemeRegistry};
pub use toolbar::{ToolbarItemLocation, ToolbarItemView};
use util::{paths, ResultExt};
pub use workspace_settings::{AutosaveSetting, DockAnchor, GitGutterSetting, WorkspaceSettings};
lazy_static! {
static ref ZED_WINDOW_SIZE: Option<Vector2F> = env::var("ZED_WINDOW_SIZE")
@ -183,7 +185,12 @@ pub type WorkspaceId = i64;
impl_actions!(workspace, [ActivatePane]);
pub fn init_settings(cx: &mut AppContext) {
settings::register_setting::<WorkspaceSettings>(cx);
}
pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
init_settings(cx);
pane::init(cx);
dock::init(cx);
notifications::init(cx);
@ -384,6 +391,7 @@ impl AppState {
let themes = ThemeRegistry::new((), cx.font_cache().clone());
client::init(&client, cx);
crate::init_settings(cx);
Arc::new(Self {
client,
@ -672,7 +680,9 @@ impl Workspace {
Self::load_from_serialized_workspace(weak_handle, serialized_workspace, cx)
});
} else if project.read(cx).is_local() {
if cx.global::<Settings>().default_dock_anchor != DockAnchor::Expanded {
if settings::get_setting::<WorkspaceSettings>(None, cx).default_dock_anchor
!= DockAnchor::Expanded
{
Dock::show(&mut this, false, cx);
}
}
@ -2406,8 +2416,8 @@ impl Workspace {
item.workspace_deactivated(cx);
}
if matches!(
cx.global::<Settings>().autosave,
Autosave::OnWindowChange | Autosave::OnFocusChange
settings::get_setting::<WorkspaceSettings>(None, cx).autosave,
AutosaveSetting::OnWindowChange | AutosaveSetting::OnFocusChange
) {
for item in pane.items() {
Pane::autosave_item(item.as_ref(), self.project.clone(), cx)
@ -3067,7 +3077,7 @@ pub fn join_remote_project(
}
pub fn restart(_: &Restart, cx: &mut AppContext) {
let should_confirm = cx.global::<Settings>().confirm_quit;
let should_confirm = settings::get_setting::<WorkspaceSettings>(None, cx).confirm_quit;
cx.spawn(|mut cx| async move {
let mut workspaces = cx
.window_ids()
@ -3128,20 +3138,18 @@ fn parse_pixel_position_env_var(value: &str) -> Option<Vector2F> {
#[cfg(test)]
mod tests {
use std::{cell::RefCell, rc::Rc};
use crate::item::test::{TestItem, TestItemEvent, TestProjectItem};
use super::*;
use crate::item::test::{TestItem, TestItemEvent, TestProjectItem};
use fs::FakeFs;
use gpui::{executor::Deterministic, TestAppContext};
use project::{Project, ProjectEntryId};
use serde_json::json;
use settings::SettingsStore;
use std::{cell::RefCell, rc::Rc};
#[gpui::test]
async fn test_tab_disambiguation(cx: &mut TestAppContext) {
cx.foreground().forbid_parking();
Settings::test_async(cx);
init_test(cx);
let fs = FakeFs::new(cx.background());
let project = Project::test(fs, [], cx).await;
@ -3189,8 +3197,8 @@ mod tests {
#[gpui::test]
async fn test_tracking_active_path(cx: &mut TestAppContext) {
cx.foreground().forbid_parking();
Settings::test_async(cx);
init_test(cx);
let fs = FakeFs::new(cx.background());
fs.insert_tree(
"/root1",
@ -3293,8 +3301,8 @@ mod tests {
#[gpui::test]
async fn test_close_window(cx: &mut TestAppContext) {
cx.foreground().forbid_parking();
Settings::test_async(cx);
init_test(cx);
let fs = FakeFs::new(cx.background());
fs.insert_tree("/root", json!({ "one": "" })).await;
@ -3329,8 +3337,8 @@ mod tests {
#[gpui::test]
async fn test_close_pane_items(cx: &mut TestAppContext) {
cx.foreground().forbid_parking();
Settings::test_async(cx);
init_test(cx);
let fs = FakeFs::new(cx.background());
let project = Project::test(fs, None, cx).await;
@ -3436,8 +3444,8 @@ mod tests {
#[gpui::test]
async fn test_prompting_to_save_only_on_last_item_for_entry(cx: &mut TestAppContext) {
cx.foreground().forbid_parking();
Settings::test_async(cx);
init_test(cx);
let fs = FakeFs::new(cx.background());
let project = Project::test(fs, [], cx).await;
@ -3542,9 +3550,8 @@ mod tests {
#[gpui::test]
async fn test_autosave(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
deterministic.forbid_parking();
init_test(cx);
Settings::test_async(cx);
let fs = FakeFs::new(cx.background());
let project = Project::test(fs, [], cx).await;
@ -3560,8 +3567,10 @@ mod tests {
// Autosave on window change.
item.update(cx, |item, cx| {
cx.update_global(|settings: &mut Settings, _| {
settings.autosave = Autosave::OnWindowChange;
cx.update_global(|settings: &mut SettingsStore, cx| {
settings.update_user_settings::<WorkspaceSettings>(cx, |settings| {
settings.autosave = Some(AutosaveSetting::OnWindowChange);
})
});
item.is_dirty = true;
});
@ -3574,8 +3583,10 @@ mod tests {
// Autosave on focus change.
item.update(cx, |item, cx| {
cx.focus_self();
cx.update_global(|settings: &mut Settings, _| {
settings.autosave = Autosave::OnFocusChange;
cx.update_global(|settings: &mut SettingsStore, cx| {
settings.update_user_settings::<WorkspaceSettings>(cx, |settings| {
settings.autosave = Some(AutosaveSetting::OnFocusChange);
})
});
item.is_dirty = true;
});
@ -3598,8 +3609,10 @@ mod tests {
// Autosave after delay.
item.update(cx, |item, cx| {
cx.update_global(|settings: &mut Settings, _| {
settings.autosave = Autosave::AfterDelay { milliseconds: 500 };
cx.update_global(|settings: &mut SettingsStore, cx| {
settings.update_user_settings::<WorkspaceSettings>(cx, |settings| {
settings.autosave = Some(AutosaveSetting::AfterDelay { milliseconds: 500 });
})
});
item.is_dirty = true;
cx.emit(TestItemEvent::Edit);
@ -3615,8 +3628,10 @@ mod tests {
// Autosave on focus change, ensuring closing the tab counts as such.
item.update(cx, |item, cx| {
cx.update_global(|settings: &mut Settings, _| {
settings.autosave = Autosave::OnFocusChange;
cx.update_global(|settings: &mut SettingsStore, cx| {
settings.update_user_settings::<WorkspaceSettings>(cx, |settings| {
settings.autosave = Some(AutosaveSetting::OnFocusChange);
})
});
item.is_dirty = true;
});
@ -3656,12 +3671,9 @@ mod tests {
}
#[gpui::test]
async fn test_pane_navigation(
deterministic: Arc<Deterministic>,
cx: &mut gpui::TestAppContext,
) {
deterministic.forbid_parking();
Settings::test_async(cx);
async fn test_pane_navigation(cx: &mut gpui::TestAppContext) {
init_test(cx);
let fs = FakeFs::new(cx.background());
let project = Project::test(fs, [], cx).await;
@ -3713,4 +3725,14 @@ mod tests {
assert!(pane.can_navigate_forward());
});
}
pub fn init_test(cx: &mut TestAppContext) {
cx.foreground().forbid_parking();
cx.update(|cx| {
cx.set_global(SettingsStore::test(cx));
cx.set_global(Settings::test(cx));
language::init(cx);
crate::init_settings(cx);
});
}
}

View file

@ -0,0 +1,103 @@
use anyhow::bail;
use db::sqlez::{
bindable::{Bind, Column, StaticColumnCount},
statement::Statement,
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::Setting;
#[derive(Deserialize)]
pub struct WorkspaceSettings {
pub active_pane_magnification: f32,
pub confirm_quit: bool,
pub show_call_status_icon: bool,
pub autosave: AutosaveSetting,
pub default_dock_anchor: DockAnchor,
pub git: GitSettings,
}
#[derive(Clone, Serialize, Deserialize, JsonSchema)]
pub struct WorkspaceSettingsContent {
pub active_pane_magnification: Option<f32>,
pub confirm_quit: Option<bool>,
pub show_call_status_icon: Option<bool>,
pub autosave: Option<AutosaveSetting>,
pub default_dock_anchor: Option<DockAnchor>,
pub git: Option<GitSettings>,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum AutosaveSetting {
Off,
AfterDelay { milliseconds: u64 },
OnFocusChange,
OnWindowChange,
}
#[derive(PartialEq, Eq, Debug, Default, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum DockAnchor {
#[default]
Bottom,
Right,
Expanded,
}
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
pub struct GitSettings {
pub git_gutter: Option<GitGutterSetting>,
pub gutter_debounce: Option<u64>,
}
#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum GitGutterSetting {
#[default]
TrackedFiles,
Hide,
}
impl StaticColumnCount for DockAnchor {}
impl Bind for DockAnchor {
fn bind(&self, statement: &Statement, start_index: i32) -> anyhow::Result<i32> {
match self {
DockAnchor::Bottom => "Bottom",
DockAnchor::Right => "Right",
DockAnchor::Expanded => "Expanded",
}
.bind(statement, start_index)
}
}
impl Column for DockAnchor {
fn column(statement: &mut Statement, start_index: i32) -> anyhow::Result<(Self, i32)> {
String::column(statement, start_index).and_then(|(anchor_text, next_index)| {
Ok((
match anchor_text.as_ref() {
"Bottom" => DockAnchor::Bottom,
"Right" => DockAnchor::Right,
"Expanded" => DockAnchor::Expanded,
_ => bail!("Stored dock anchor is incorrect"),
},
next_index,
))
})
}
}
impl Setting for WorkspaceSettings {
const KEY: Option<&'static str> = None;
type FileContent = WorkspaceSettingsContent;
fn load(
default_value: &Self::FileContent,
user_values: &[&Self::FileContent],
_: &gpui::AppContext,
) -> anyhow::Result<Self> {
Self::load_via_json_merge(default_value, user_values)
}
}

View file

@ -37,7 +37,7 @@ use uuid::Uuid;
pub use workspace;
use workspace::{
create_and_open_local_file, open_new, sidebar::SidebarSide, AppState, NewFile, NewWindow,
Workspace,
Workspace, WorkspaceSettings,
};
#[derive(Deserialize, Clone, PartialEq)]
@ -367,7 +367,7 @@ pub fn build_window_options(
}
fn quit(_: &Quit, cx: &mut gpui::AppContext) {
let should_confirm = cx.global::<Settings>().confirm_quit;
let should_confirm = settings::get_setting::<WorkspaceSettings>(None, cx).confirm_quit;
cx.spawn(|mut cx| async move {
let mut workspaces = cx
.window_ids()