Detailed changes
@@ -729,6 +729,7 @@ dependencies = [
"language",
"project",
"search",
+ "settings",
"theme",
"workspace",
]
@@ -881,6 +882,7 @@ dependencies = [
"editor",
"gpui",
"postage",
+ "settings",
"theme",
"time 0.3.7",
"util",
@@ -1131,6 +1133,7 @@ dependencies = [
"client",
"gpui",
"postage",
+ "settings",
"theme",
"workspace",
]
@@ -1480,6 +1483,7 @@ dependencies = [
"postage",
"project",
"serde_json",
+ "settings",
"theme",
"unindent",
"util",
@@ -1646,6 +1650,7 @@ dependencies = [
"rand 0.8.3",
"rpc",
"serde",
+ "settings",
"smallvec",
"smol",
"snippet",
@@ -1806,6 +1811,7 @@ dependencies = [
"postage",
"project",
"serde_json",
+ "settings",
"theme",
"util",
"workspace",
@@ -2206,6 +2212,7 @@ dependencies = [
"editor",
"gpui",
"postage",
+ "settings",
"text",
"workspace",
]
@@ -3255,6 +3262,7 @@ dependencies = [
"language",
"ordered-float",
"postage",
+ "settings",
"smol",
"text",
"workspace",
@@ -3643,6 +3651,7 @@ dependencies = [
"postage",
"project",
"serde_json",
+ "settings",
"theme",
"util",
"workspace",
@@ -3659,6 +3668,7 @@ dependencies = [
"ordered-float",
"postage",
"project",
+ "settings",
"smol",
"text",
"util",
@@ -4258,6 +4268,7 @@ dependencies = [
"postage",
"project",
"serde_json",
+ "settings",
"theme",
"unindent",
"util",
@@ -4406,6 +4417,21 @@ dependencies = [
"pkg-config",
]
+[[package]]
+name = "settings"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "gpui",
+ "schemars",
+ "serde",
+ "serde_json",
+ "serde_path_to_error",
+ "theme",
+ "toml",
+ "util",
+]
+
[[package]]
name = "sha-1"
version = "0.8.2"
@@ -5142,6 +5168,7 @@ dependencies = [
"log",
"parking_lot",
"postage",
+ "settings",
"smol",
"theme",
"workspace",
@@ -5719,6 +5746,7 @@ dependencies = [
"language",
"log",
"project",
+ "settings",
"util",
"workspace",
]
@@ -5948,9 +5976,9 @@ dependencies = [
"parking_lot",
"postage",
"project",
- "schemars",
"serde",
"serde_json",
+ "settings",
"smallvec",
"theme",
"util",
@@ -6034,6 +6062,7 @@ dependencies = [
"serde",
"serde_json",
"serde_path_to_error",
+ "settings",
"simplelog",
"smallvec",
"smol",
@@ -6099,6 +6128,7 @@ dependencies = [
"scrypt",
"serde",
"serde_json",
+ "settings",
"sha-1 0.9.6",
"sqlx 0.5.5",
"surf",
@@ -14,6 +14,7 @@ gpui = { path = "../gpui" }
language = { path = "../language" }
project = { path = "../project" }
search = { path = "../search" }
+settings = { path = "../settings" }
theme = { path = "../theme" }
workspace = { path = "../workspace" }
@@ -6,8 +6,9 @@ use gpui::{
use language::{Buffer, OutlineItem};
use project::Project;
use search::ProjectSearchView;
+use settings::Settings;
use theme::SyntaxTheme;
-use workspace::{ItemHandle, Settings, ToolbarItemLocation, ToolbarItemView};
+use workspace::{ItemHandle, ToolbarItemLocation, ToolbarItemView};
pub enum Event {
UpdateLocation,
@@ -11,6 +11,7 @@ doctest = false
client = { path = "../client" }
editor = { path = "../editor" }
gpui = { path = "../gpui" }
+settings = { path = "../settings" }
theme = { path = "../theme" }
util = { path = "../util" }
workspace = { path = "../workspace" }
@@ -13,10 +13,10 @@ use gpui::{
ViewContext, ViewHandle,
};
use postage::prelude::Stream;
+use settings::{Settings, SoftWrap};
use std::sync::Arc;
use time::{OffsetDateTime, UtcOffset};
use util::{ResultExt, TryFutureExt};
-use workspace::{settings::SoftWrap, Settings};
const MESSAGE_LOADING_THRESHOLD: usize = 50;
@@ -10,6 +10,7 @@ doctest = false
[dependencies]
client = { path = "../client" }
gpui = { path = "../gpui" }
+settings = { path = "../settings" }
theme = { path = "../theme" }
workspace = { path = "../workspace" }
postage = { version = "0.4.1", features = ["futures-traits"] }
@@ -8,7 +8,8 @@ use gpui::{
Element, ElementBox, Entity, LayoutContext, ModelHandle, RenderContext, Subscription, View,
ViewContext,
};
-use workspace::{AppState, JoinProject, JoinProjectParams, Settings};
+use workspace::{AppState, JoinProject, JoinProjectParams};
+use settings::Settings;
pub struct ContactsPanel {
contacts: ListState,
@@ -14,6 +14,7 @@ editor = { path = "../editor" }
language = { path = "../language" }
gpui = { path = "../gpui" }
project = { path = "../project" }
+settings = { path = "../settings" }
theme = { path = "../theme" }
util = { path = "../util" }
workspace = { path = "../workspace" }
@@ -25,7 +25,8 @@ use std::{
sync::Arc,
};
use util::TryFutureExt;
-use workspace::{ItemHandle as _, ItemNavHistory, Settings, Workspace};
+use workspace::{ItemHandle as _, ItemNavHistory, Workspace};
+use settings::Settings;
action!(Deploy);
@@ -3,7 +3,8 @@ use gpui::{
elements::*, platform::CursorStyle, Entity, ModelHandle, RenderContext, View, ViewContext,
};
use project::Project;
-use workspace::{Settings, StatusItemView};
+use workspace::{StatusItemView};
+use settings::Settings;
pub struct DiagnosticSummary {
summary: project::DiagnosticSummary,
@@ -28,6 +28,7 @@ language = { path = "../language" }
lsp = { path = "../lsp" }
project = { path = "../project" }
rpc = { path = "../rpc" }
+settings = { path = "../settings" }
snippet = { path = "../snippet" }
sum_tree = { path = "../sum_tree" }
theme = { path = "../theme" }
@@ -54,6 +55,7 @@ lsp = { path = "../lsp", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
util = { path = "../util", features = ["test-support"] }
project = { path = "../project", features = ["test-support"] }
+settings = { path = "../settings", features = ["test-support"] }
workspace = { path = "../workspace", features = ["test-support"] }
ctor = "0.1"
env_logger = "0.8"
@@ -46,6 +46,7 @@ impl Entity for DisplayMap {
impl DisplayMap {
pub fn new(
buffer: ModelHandle<MultiBuffer>,
+ // TODO - remove. read tab_size from settings inside
tab_size: usize,
font_id: FontId,
font_size: f32,
@@ -76,6 +77,8 @@ impl DisplayMap {
let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
let edits = self.buffer_subscription.consume().into_inner();
let (folds_snapshot, edits) = self.fold_map.read(buffer_snapshot, edits);
+
+ // TODO: Pull tabsize out of cx and pass it to sync
let (tabs_snapshot, edits) = self.tab_map.sync(folds_snapshot.clone(), edits);
let (wraps_snapshot, edits) = self
.wrap_map
@@ -41,6 +41,7 @@ pub use multi_buffer::{
use ordered_float::OrderedFloat;
use project::{Project, ProjectTransaction};
use serde::{Deserialize, Serialize};
+use settings::Settings;
use smallvec::SmallVec;
use smol::Timer;
use snippet::Snippet;
@@ -57,7 +58,7 @@ pub use sum_tree::Bias;
use text::rope::TextDimension;
use theme::DiagnosticStyle;
use util::{post_inc, ResultExt, TryFutureExt};
-use workspace::{settings, ItemNavHistory, Settings, Workspace};
+use workspace::{ItemNavHistory, Workspace};
const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
const MAX_LINE_LEN: usize = 1024;
@@ -5669,16 +5670,16 @@ impl Editor {
}
pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
- let language = self.language(cx);
+ let language = self.language(cx).map(|language| language.name());
let settings = cx.global::<Settings>();
let mode = self
.soft_wrap_mode_override
- .unwrap_or_else(|| settings.soft_wrap(language));
+ .unwrap_or_else(|| settings.soft_wrap(language.as_deref()));
match mode {
settings::SoftWrap::None => SoftWrap::None,
settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
settings::SoftWrap::PreferredLineLength => {
- SoftWrap::Column(settings.preferred_line_length(language))
+ SoftWrap::Column(settings.preferred_line_length(language.as_deref()))
}
}
}
@@ -1494,8 +1494,8 @@ mod tests {
display_map::{BlockDisposition, BlockProperties},
Editor, MultiBuffer,
};
+ use settings::Settings;
use util::test::sample_text;
- use workspace::Settings;
#[gpui::test]
fn test_layout_line_numbers(cx: &mut gpui::MutableAppContext) {
@@ -8,12 +8,11 @@ use gpui::{
use language::{Bias, Buffer, Diagnostic, File as _, SelectionGoal};
use project::{File, Project, ProjectEntryId, ProjectPath};
use rpc::proto::{self, update_view};
+use settings::Settings;
use std::{fmt::Write, path::PathBuf, time::Duration};
use text::{Point, Selection};
use util::TryFutureExt;
-use workspace::{
- FollowableItem, Item, ItemHandle, ItemNavHistory, ProjectItem, Settings, StatusItemView,
-};
+use workspace::{FollowableItem, Item, ItemHandle, ItemNavHistory, ProjectItem, StatusItemView};
pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
@@ -12,6 +12,7 @@ editor = { path = "../editor" }
fuzzy = { path = "../fuzzy" }
gpui = { path = "../gpui" }
project = { path = "../project" }
+settings = { path = "../settings" }
util = { path = "../util" }
theme = { path = "../theme" }
workspace = { path = "../workspace" }
@@ -19,8 +19,9 @@ use std::{
use util::post_inc;
use workspace::{
menu::{Confirm, SelectNext, SelectPrev},
- Settings, Workspace,
+ Workspace,
};
+use settings::Settings;
pub struct FileFinder {
handle: WeakViewHandle<Self>,
@@ -11,5 +11,6 @@ doctest = false
text = { path = "../text" }
editor = { path = "../editor" }
gpui = { path = "../gpui" }
+settings = { path = "../settings" }
workspace = { path = "../workspace" }
postage = { version = "0.4", features = ["futures-traits"] }
@@ -3,8 +3,9 @@ use gpui::{
action, elements::*, geometry::vector::Vector2F, keymap::Binding, Axis, Entity,
MutableAppContext, RenderContext, View, ViewContext, ViewHandle,
};
+use settings::Settings;
use text::{Bias, Point};
-use workspace::{Settings, Workspace};
+use workspace::Workspace;
action!(Toggle);
action!(Confirm);
@@ -12,6 +12,7 @@ editor = { path = "../editor" }
fuzzy = { path = "../fuzzy" }
gpui = { path = "../gpui" }
language = { path = "../language" }
+settings = { path = "../settings" }
text = { path = "../text" }
workspace = { path = "../workspace" }
ordered-float = "2.1.1"
@@ -13,10 +13,11 @@ use gpui::{
};
use language::Outline;
use ordered_float::OrderedFloat;
+use settings::Settings;
use std::cmp::{self, Reverse};
use workspace::{
menu::{Confirm, SelectFirst, SelectLast, SelectNext, SelectPrev},
- Settings, Workspace,
+ Workspace,
};
action!(Toggle);
@@ -10,6 +10,7 @@ doctest = false
[dependencies]
gpui = { path = "../gpui" }
project = { path = "../project" }
+settings = { path = "../settings" }
theme = { path = "../theme" }
util = { path = "../util" }
workspace = { path = "../workspace" }
@@ -10,6 +10,7 @@ use gpui::{
ViewHandle, WeakViewHandle,
};
use project::{Project, ProjectEntryId, ProjectPath, Worktree, WorktreeId};
+use settings::Settings;
use std::{
collections::{hash_map, HashMap},
ffi::OsStr,
@@ -17,7 +18,7 @@ use std::{
};
use workspace::{
menu::{SelectNext, SelectPrev},
- Settings, Workspace,
+ Workspace,
};
pub struct ProjectPanel {
@@ -13,6 +13,7 @@ fuzzy = { path = "../fuzzy" }
gpui = { path = "../gpui" }
project = { path = "../project" }
text = { path = "../text" }
+settings = { path = "../settings" }
workspace = { path = "../workspace" }
util = { path = "../util" }
anyhow = "1.0.38"
@@ -11,6 +11,7 @@ use gpui::{
};
use ordered_float::OrderedFloat;
use project::{Project, Symbol};
+use settings::Settings;
use std::{
borrow::Cow,
cmp::{self, Reverse},
@@ -18,7 +19,7 @@ use std::{
use util::ResultExt;
use workspace::{
menu::{Confirm, SelectFirst, SelectLast, SelectNext, SelectPrev},
- Settings, Workspace,
+ Workspace,
};
action!(Toggle);
@@ -13,6 +13,7 @@ editor = { path = "../editor" }
gpui = { path = "../gpui" }
language = { path = "../language" }
project = { path = "../project" }
+settings = { path = "../settings" }
theme = { path = "../theme" }
util = { path = "../util" }
workspace = { path = "../workspace" }
@@ -9,7 +9,8 @@ use gpui::{
use language::OffsetRangeExt;
use project::search::SearchQuery;
use std::ops::Range;
-use workspace::{ItemHandle, Pane, Settings, ToolbarItemLocation, ToolbarItemView};
+use workspace::{ItemHandle, Pane, ToolbarItemLocation, ToolbarItemView};
+use settings::Settings;
action!(Deploy, bool);
action!(Dismiss);
@@ -10,15 +10,14 @@ use gpui::{
ViewContext, ViewHandle, WeakModelHandle, WeakViewHandle,
};
use project::{search::SearchQuery, Project};
+use settings::Settings;
use std::{
any::{Any, TypeId},
ops::Range,
path::PathBuf,
};
use util::ResultExt as _;
-use workspace::{
- Item, ItemNavHistory, Pane, Settings, ToolbarItemLocation, ToolbarItemView, Workspace,
-};
+use workspace::{Item, ItemNavHistory, Pane, ToolbarItemLocation, ToolbarItemView, Workspace};
action!(Deploy);
action!(Search);
@@ -14,6 +14,7 @@ required-features = ["seed-support"]
[dependencies]
collections = { path = "../collections" }
+settings = { path = "../settings" }
rpc = { path = "../rpc" }
anyhow = "1.0.40"
async-io = "1.3"
@@ -1104,6 +1104,7 @@ mod tests {
use rand::prelude::*;
use rpc::PeerId;
use serde_json::json;
+ use settings::Settings;
use sqlx::types::time::OffsetDateTime;
use std::{
cell::Cell,
@@ -1117,7 +1118,7 @@ mod tests {
},
time::Duration,
};
- use workspace::{Item, Settings, SplitDirection, Workspace, WorkspaceParams};
+ use workspace::{Item, SplitDirection, Workspace, WorkspaceParams};
#[cfg(test)]
#[ctor::ctor]
@@ -0,0 +1,22 @@
+[package]
+name = "settings"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+path = "src/settings.rs"
+doctest = false
+
+[features]
+test-support = []
+
+[dependencies]
+gpui = { path = "../gpui" }
+theme = { path = "../theme" }
+util = { path = "../util" }
+anyhow = "1.0.38"
+schemars = "0.8"
+serde = { version = "1", features = ["derive", "rc"] }
+serde_json = { version = "1.0.64", features = ["preserve_order"] }
+serde_path_to_error = "0.1.4"
+toml = "0.5"
@@ -0,0 +1,171 @@
+use anyhow::Result;
+use gpui::font_cache::{FamilyId, FontCache};
+use schemars::{schema_for, JsonSchema};
+use serde::Deserialize;
+use std::{collections::HashMap, sync::Arc};
+use theme::{Theme, ThemeRegistry};
+use util::ResultExt as _;
+
+#[derive(Clone)]
+pub struct Settings {
+ pub buffer_font_family: FamilyId,
+ pub buffer_font_size: f32,
+ pub vim_mode: bool,
+ pub tab_size: usize,
+ pub soft_wrap: SoftWrap,
+ pub preferred_line_length: u32,
+ pub language_overrides: HashMap<Arc<str>, LanguageOverride>,
+ pub theme: Arc<Theme>,
+}
+
+#[derive(Clone, Debug, Default, Deserialize, JsonSchema)]
+pub struct LanguageOverride {
+ pub tab_size: Option<usize>,
+ pub soft_wrap: Option<SoftWrap>,
+ pub preferred_line_length: Option<u32>,
+}
+
+#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum SoftWrap {
+ None,
+ EditorWidth,
+ PreferredLineLength,
+}
+
+#[derive(Clone, Debug, Default, Deserialize, JsonSchema)]
+pub struct SettingsFileContent {
+ #[serde(default)]
+ pub buffer_font_family: Option<String>,
+ #[serde(default)]
+ pub buffer_font_size: Option<f32>,
+ #[serde(default)]
+ pub vim_mode: Option<bool>,
+ #[serde(flatten)]
+ pub editor: LanguageOverride,
+ #[serde(default)]
+ pub language_overrides: HashMap<Arc<str>, LanguageOverride>,
+ #[serde(default)]
+ pub theme: Option<String>,
+}
+
+impl Settings {
+ pub fn new(
+ buffer_font_family: &str,
+ font_cache: &FontCache,
+ theme: Arc<Theme>,
+ ) -> Result<Self> {
+ Ok(Self {
+ buffer_font_family: font_cache.load_family(&[buffer_font_family])?,
+ buffer_font_size: 15.,
+ vim_mode: false,
+ tab_size: 4,
+ soft_wrap: SoftWrap::None,
+ preferred_line_length: 80,
+ language_overrides: Default::default(),
+ theme,
+ })
+ }
+
+ pub fn file_json_schema() -> serde_json::Value {
+ serde_json::to_value(schema_for!(SettingsFileContent)).unwrap()
+ }
+
+ pub fn with_overrides(
+ mut self,
+ language_name: impl Into<Arc<str>>,
+ overrides: LanguageOverride,
+ ) -> Self {
+ self.language_overrides
+ .insert(language_name.into(), overrides);
+ self
+ }
+
+ pub fn tab_size(&self, language: Option<&str>) -> usize {
+ language
+ .and_then(|language| self.language_overrides.get(language))
+ .and_then(|settings| settings.tab_size)
+ .unwrap_or(self.tab_size)
+ }
+
+ pub fn soft_wrap(&self, language: Option<&str>) -> SoftWrap {
+ language
+ .and_then(|language| self.language_overrides.get(language))
+ .and_then(|settings| settings.soft_wrap)
+ .unwrap_or(self.soft_wrap)
+ }
+
+ pub fn preferred_line_length(&self, language: Option<&str>) -> u32 {
+ language
+ .and_then(|language| self.language_overrides.get(language))
+ .and_then(|settings| settings.preferred_line_length)
+ .unwrap_or(self.preferred_line_length)
+ }
+
+ #[cfg(any(test, feature = "test-support"))]
+ pub fn test(cx: &gpui::AppContext) -> Settings {
+ Settings {
+ buffer_font_family: cx.font_cache().load_family(&["Monaco"]).unwrap(),
+ buffer_font_size: 14.,
+ vim_mode: false,
+ tab_size: 4,
+ soft_wrap: SoftWrap::None,
+ preferred_line_length: 80,
+ language_overrides: Default::default(),
+ theme: gpui::fonts::with_font_cache(cx.font_cache().clone(), || Default::default()),
+ }
+ }
+
+ pub fn merge(
+ &mut self,
+ data: &SettingsFileContent,
+ theme_registry: &ThemeRegistry,
+ font_cache: &FontCache,
+ ) {
+ if let Some(value) = &data.buffer_font_family {
+ if let Some(id) = font_cache.load_family(&[value]).log_err() {
+ self.buffer_font_family = id;
+ }
+ }
+ if let Some(value) = &data.theme {
+ if let Some(theme) = theme_registry.get(value).log_err() {
+ self.theme = theme;
+ }
+ }
+
+ merge(&mut self.buffer_font_size, data.buffer_font_size);
+ merge(&mut self.vim_mode, data.vim_mode);
+ merge(&mut self.soft_wrap, data.editor.soft_wrap);
+ merge(&mut self.tab_size, data.editor.tab_size);
+ merge(
+ &mut self.preferred_line_length,
+ data.editor.preferred_line_length,
+ );
+
+ for (language_name, settings) in &data.language_overrides {
+ let target = self
+ .language_overrides
+ .entry(language_name.clone())
+ .or_default();
+
+ merge_option(&mut target.tab_size, settings.tab_size);
+ merge_option(&mut target.soft_wrap, settings.soft_wrap);
+ merge_option(
+ &mut target.preferred_line_length,
+ settings.preferred_line_length,
+ );
+ }
+ }
+}
+
+fn merge<T: Copy>(target: &mut T, value: Option<T>) {
+ if let Some(value) = value {
+ *target = value;
+ }
+}
+
+fn merge_option<T: Copy>(target: &mut Option<T>, value: Option<T>) {
+ if value.is_some() {
+ *target = value;
+ }
+}
@@ -12,6 +12,7 @@ editor = { path = "../editor" }
fuzzy = { path = "../fuzzy" }
gpui = { path = "../gpui" }
theme = { path = "../theme" }
+settings = { path = "../settings" }
workspace = { path = "../workspace" }
log = "0.4"
parking_lot = "0.11.1"
@@ -9,9 +9,10 @@ use gpui::{
};
use std::{cmp, sync::Arc};
use theme::{Theme, ThemeRegistry};
+use settings::Settings;
use workspace::{
menu::{Confirm, SelectNext, SelectPrev},
- Settings, Workspace,
+ Workspace,
};
pub struct ThemeSelector {
@@ -12,6 +12,7 @@ collections = { path = "../collections" }
editor = { path = "../editor" }
gpui = { path = "../gpui" }
language = { path = "../language" }
+settings = { path = "../settings" }
workspace = { path = "../workspace" }
log = "0.4"
@@ -19,7 +20,8 @@ log = "0.4"
indoc = "1.0.4"
editor = { path = "../editor", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
-project = { path = "../project", features = ["test-support"] }
language = { path = "../language", features = ["test-support"] }
+project = { path = "../project", features = ["test-support"] }
util = { path = "../util", features = ["test-support"] }
+settings = { path = "../settings" }
workspace = { path = "../workspace", features = ["test-support"] }
@@ -10,7 +10,8 @@ use editor::{CursorShape, Editor};
use gpui::{action, MutableAppContext, ViewContext, WeakViewHandle};
use mode::Mode;
-use workspace::{self, Settings, Workspace};
+use settings::Settings;
+use workspace::{self, Workspace};
action!(SwitchMode, Mode);
@@ -17,6 +17,7 @@ collections = { path = "../collections" }
gpui = { path = "../gpui" }
language = { path = "../language" }
project = { path = "../project" }
+settings = { path = "../settings" }
theme = { path = "../theme" }
util = { path = "../util" }
anyhow = "1.0.38"
@@ -24,7 +25,6 @@ futures = "0.3"
log = "0.4"
parking_lot = "0.11.1"
postage = { version = "0.4.1", features = ["futures-traits"] }
-schemars = "0.8"
serde = { version = "1", features = ["derive", "rc"] }
serde_json = { version = "1", features = ["preserve_order"] }
smallvec = { version = "1.6", features = ["union"] }
@@ -33,3 +33,4 @@ smallvec = { version = "1.6", features = ["union"] }
client = { path = "../client", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
project = { path = "../project", features = ["test-support"] }
+settings = { path = "../settings", features = ["test-support"] }
@@ -1,4 +1,4 @@
-use crate::{ItemHandle, Settings, StatusItemView};
+use crate::{ItemHandle, StatusItemView};
use futures::StreamExt;
use gpui::AppContext;
use gpui::{
@@ -7,6 +7,7 @@ use gpui::{
};
use language::{LanguageRegistry, LanguageServerBinaryStatus};
use project::{LanguageServerProgress, Project};
+use settings::Settings;
use smallvec::SmallVec;
use std::cmp::Reverse;
use std::fmt::Write;
@@ -1,5 +1,5 @@
use super::{ItemHandle, SplitDirection};
-use crate::{toolbar::Toolbar, Item, Settings, WeakItemHandle, Workspace};
+use crate::{toolbar::Toolbar, Item, WeakItemHandle, Workspace};
use anyhow::Result;
use collections::{HashMap, VecDeque};
use futures::StreamExt;
@@ -12,7 +12,8 @@ use gpui::{
AppContext, Entity, MutableAppContext, PromptLevel, Quad, RenderContext, Task, View,
ViewContext, ViewHandle, WeakViewHandle,
};
-use project::{ProjectEntryId, ProjectPath};
+use project::{Project, ProjectEntryId, ProjectPath};
+use settings::Settings;
use std::{any::Any, cell::RefCell, cmp, mem, path::Path, rc::Rc};
use util::ResultExt;
@@ -1,325 +0,0 @@
-use anyhow::Result;
-use futures::{stream, SinkExt, StreamExt as _};
-use gpui::{
- executor,
- font_cache::{FamilyId, FontCache},
-};
-use language::Language;
-use postage::{prelude::Stream, watch};
-use project::Fs;
-use schemars::{schema_for, JsonSchema};
-use serde::Deserialize;
-use std::{collections::HashMap, path::Path, sync::Arc, time::Duration};
-use theme::{Theme, ThemeRegistry};
-use util::ResultExt;
-
-#[derive(Clone)]
-pub struct Settings {
- pub buffer_font_family: FamilyId,
- pub buffer_font_size: f32,
- pub vim_mode: bool,
- pub tab_size: usize,
- pub soft_wrap: SoftWrap,
- pub preferred_line_length: u32,
- pub language_overrides: HashMap<Arc<str>, LanguageOverride>,
- pub theme: Arc<Theme>,
-}
-
-#[derive(Clone, Debug, Default, Deserialize, JsonSchema)]
-pub struct LanguageOverride {
- pub tab_size: Option<usize>,
- pub soft_wrap: Option<SoftWrap>,
- pub preferred_line_length: Option<u32>,
-}
-
-#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)]
-#[serde(rename_all = "snake_case")]
-pub enum SoftWrap {
- None,
- EditorWidth,
- PreferredLineLength,
-}
-
-#[derive(Clone)]
-pub struct SettingsFile(watch::Receiver<SettingsFileContent>);
-
-#[derive(Clone, Debug, Default, Deserialize, JsonSchema)]
-struct SettingsFileContent {
- #[serde(default)]
- buffer_font_family: Option<String>,
- #[serde(default)]
- buffer_font_size: Option<f32>,
- #[serde(default)]
- vim_mode: Option<bool>,
- #[serde(flatten)]
- editor: LanguageOverride,
- #[serde(default)]
- language_overrides: HashMap<Arc<str>, LanguageOverride>,
- #[serde(default)]
- theme: Option<String>,
-}
-
-impl SettingsFile {
- pub async fn new(
- fs: Arc<dyn Fs>,
- executor: &executor::Background,
- path: impl Into<Arc<Path>>,
- ) -> Self {
- let path = path.into();
- let settings = Self::load(fs.clone(), &path).await.unwrap_or_default();
- let mut events = fs.watch(&path, Duration::from_millis(500)).await;
- let (mut tx, rx) = watch::channel_with(settings);
- executor
- .spawn(async move {
- while events.next().await.is_some() {
- if let Some(settings) = Self::load(fs.clone(), &path).await {
- if tx.send(settings).await.is_err() {
- break;
- }
- }
- }
- })
- .detach();
- Self(rx)
- }
-
- async fn load(fs: Arc<dyn Fs>, path: &Path) -> Option<SettingsFileContent> {
- if fs.is_file(&path).await {
- fs.load(&path)
- .await
- .log_err()
- .and_then(|data| serde_json::from_str(&data).log_err())
- } else {
- Some(SettingsFileContent::default())
- }
- }
-}
-
-impl Settings {
- pub fn file_json_schema() -> serde_json::Value {
- serde_json::to_value(schema_for!(SettingsFileContent)).unwrap()
- }
-
- pub fn from_files(
- defaults: Self,
- sources: Vec<SettingsFile>,
- theme_registry: Arc<ThemeRegistry>,
- font_cache: Arc<FontCache>,
- ) -> impl futures::stream::Stream<Item = Self> {
- stream::select_all(sources.iter().enumerate().map(|(i, source)| {
- let mut rx = source.0.clone();
- // Consume the initial item from all of the constituent file watches but one.
- // This way, the stream will yield exactly one item for the files' initial
- // state, and won't return any more items until the files change.
- if i > 0 {
- rx.try_recv().ok();
- }
- rx
- }))
- .map(move |_| {
- let mut settings = defaults.clone();
- for source in &sources {
- settings.merge(&*source.0.borrow(), &theme_registry, &font_cache);
- }
- settings
- })
- }
-
- pub fn new(
- buffer_font_family: &str,
- font_cache: &FontCache,
- theme: Arc<Theme>,
- ) -> Result<Self> {
- Ok(Self {
- buffer_font_family: font_cache.load_family(&[buffer_font_family])?,
- buffer_font_size: 15.,
- vim_mode: false,
- tab_size: 4,
- soft_wrap: SoftWrap::None,
- preferred_line_length: 80,
- language_overrides: Default::default(),
- theme,
- })
- }
-
- pub fn with_overrides(
- mut self,
- language_name: impl Into<Arc<str>>,
- overrides: LanguageOverride,
- ) -> Self {
- self.language_overrides
- .insert(language_name.into(), overrides);
- self
- }
-
- pub fn tab_size(&self, language: Option<&Arc<Language>>) -> usize {
- language
- .and_then(|language| self.language_overrides.get(language.name().as_ref()))
- .and_then(|settings| settings.tab_size)
- .unwrap_or(self.tab_size)
- }
-
- pub fn soft_wrap(&self, language: Option<&Arc<Language>>) -> SoftWrap {
- language
- .and_then(|language| self.language_overrides.get(language.name().as_ref()))
- .and_then(|settings| settings.soft_wrap)
- .unwrap_or(self.soft_wrap)
- }
-
- pub fn preferred_line_length(&self, language: Option<&Arc<Language>>) -> u32 {
- language
- .and_then(|language| self.language_overrides.get(language.name().as_ref()))
- .and_then(|settings| settings.preferred_line_length)
- .unwrap_or(self.preferred_line_length)
- }
-
- #[cfg(any(test, feature = "test-support"))]
- pub fn test(cx: &gpui::AppContext) -> Settings {
- Settings {
- buffer_font_family: cx.font_cache().load_family(&["Monaco"]).unwrap(),
- buffer_font_size: 14.,
- vim_mode: false,
- tab_size: 4,
- soft_wrap: SoftWrap::None,
- preferred_line_length: 80,
- language_overrides: Default::default(),
- theme: gpui::fonts::with_font_cache(cx.font_cache().clone(), || Default::default()),
- }
- }
-
- fn merge(
- &mut self,
- data: &SettingsFileContent,
- theme_registry: &ThemeRegistry,
- font_cache: &FontCache,
- ) {
- if let Some(value) = &data.buffer_font_family {
- if let Some(id) = font_cache.load_family(&[value]).log_err() {
- self.buffer_font_family = id;
- }
- }
- if let Some(value) = &data.theme {
- if let Some(theme) = theme_registry.get(value).log_err() {
- self.theme = theme;
- }
- }
-
- merge(&mut self.buffer_font_size, data.buffer_font_size);
- merge(&mut self.vim_mode, data.vim_mode);
- merge(&mut self.soft_wrap, data.editor.soft_wrap);
- merge(&mut self.tab_size, data.editor.tab_size);
- merge(
- &mut self.preferred_line_length,
- data.editor.preferred_line_length,
- );
-
- for (language_name, settings) in &data.language_overrides {
- let target = self
- .language_overrides
- .entry(language_name.clone())
- .or_default();
-
- merge_option(&mut target.tab_size, settings.tab_size);
- merge_option(&mut target.soft_wrap, settings.soft_wrap);
- merge_option(
- &mut target.preferred_line_length,
- settings.preferred_line_length,
- );
- }
- }
-}
-
-fn merge<T: Copy>(target: &mut T, value: Option<T>) {
- if let Some(value) = value {
- *target = value;
- }
-}
-
-fn merge_option<T: Copy>(target: &mut Option<T>, value: Option<T>) {
- if value.is_some() {
- *target = value;
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use project::FakeFs;
-
- #[gpui::test]
- async fn test_settings_from_files(cx: &mut gpui::TestAppContext) {
- let executor = cx.background();
- let fs = FakeFs::new(executor.clone());
-
- fs.save(
- "/settings1.json".as_ref(),
- &r#"
- {
- "buffer_font_size": 24,
- "soft_wrap": "editor_width",
- "language_overrides": {
- "Markdown": {
- "preferred_line_length": 100,
- "soft_wrap": "preferred_line_length"
- }
- }
- }
- "#
- .into(),
- )
- .await
- .unwrap();
-
- let source1 = SettingsFile::new(fs.clone(), &executor, "/settings1.json".as_ref()).await;
- let source2 = SettingsFile::new(fs.clone(), &executor, "/settings2.json".as_ref()).await;
- let source3 = SettingsFile::new(fs.clone(), &executor, "/settings3.json".as_ref()).await;
-
- let mut settings_rx = Settings::from_files(
- cx.read(Settings::test),
- vec![source1, source2, source3],
- ThemeRegistry::new((), cx.font_cache()),
- cx.font_cache(),
- );
-
- let settings = settings_rx.next().await.unwrap();
- let md_settings = settings.language_overrides.get("Markdown").unwrap();
- assert_eq!(settings.soft_wrap, SoftWrap::EditorWidth);
- assert_eq!(settings.buffer_font_size, 24.0);
- assert_eq!(settings.tab_size, 4);
- assert_eq!(md_settings.soft_wrap, Some(SoftWrap::PreferredLineLength));
- assert_eq!(md_settings.preferred_line_length, Some(100));
-
- fs.save(
- "/settings2.json".as_ref(),
- &r#"
- {
- "tab_size": 2,
- "soft_wrap": "none",
- "language_overrides": {
- "Markdown": {
- "preferred_line_length": 120
- }
- }
- }
- "#
- .into(),
- )
- .await
- .unwrap();
-
- let settings = settings_rx.next().await.unwrap();
- let md_settings = settings.language_overrides.get("Markdown").unwrap();
- assert_eq!(settings.soft_wrap, SoftWrap::None);
- assert_eq!(settings.buffer_font_size, 24.0);
- assert_eq!(settings.tab_size, 2);
- assert_eq!(md_settings.soft_wrap, Some(SoftWrap::PreferredLineLength));
- assert_eq!(md_settings.preferred_line_length, Some(120));
-
- fs.remove_file("/settings2.json".as_ref(), Default::default())
- .await
- .unwrap();
-
- let settings = settings_rx.next().await.unwrap();
- assert_eq!(settings.tab_size, 4);
- }
-}
@@ -1,4 +1,5 @@
-use crate::{ItemHandle, Pane, Settings};
+use crate::{ItemHandle, Pane};
+use settings::Settings;
use gpui::{
elements::*, AnyViewHandle, ElementBox, Entity, MutableAppContext, RenderContext, Subscription,
View, ViewContext, ViewHandle,
@@ -1,8 +1,9 @@
-use crate::{ItemHandle, Settings};
+use crate::ItemHandle;
use gpui::{
elements::*, AnyViewHandle, AppContext, ElementBox, Entity, MutableAppContext, RenderContext,
View, ViewContext, ViewHandle,
};
+use settings::Settings;
pub trait ToolbarItemView: View {
fn set_active_pane_item(
@@ -2,7 +2,6 @@ pub mod lsp_status;
pub mod menu;
pub mod pane;
pub mod pane_group;
-pub mod settings;
pub mod sidebar;
mod status_bar;
mod toolbar;
@@ -31,7 +30,7 @@ pub use pane::*;
pub use pane_group::*;
use postage::prelude::Stream;
use project::{fs, Fs, Project, ProjectEntryId, ProjectPath, Worktree};
-pub use settings::Settings;
+use settings::Settings;
use sidebar::{Side, Sidebar, SidebarItemId, ToggleSidebarItem, ToggleSidebarItemFocus};
use status_bar::StatusBar;
pub use status_bar::StatusItemView;
@@ -51,6 +51,7 @@ project = { path = "../project" }
project_panel = { path = "../project_panel" }
project_symbols = { path = "../project_symbols" }
rpc = { path = "../rpc" }
+settings = { path = "../settings" }
sum_tree = { path = "../sum_tree" }
text = { path = "../text" }
theme = { path = "../theme" }
@@ -111,6 +112,7 @@ lsp = { path = "../lsp", features = ["test-support"] }
project = { path = "../project", features = ["test-support"] }
rpc = { path = "../rpc", features = ["test-support"] }
client = { path = "../client", features = ["test-support"] }
+settings = { path = "../settings", features = ["test-support"] }
util = { path = "../util", features = ["test-support"] }
workspace = { path = "../workspace", features = ["test-support"] }
env_logger = "0.8"
@@ -9,17 +9,19 @@ use gpui::{App, AssetSource, Task};
use log::LevelFilter;
use parking_lot::Mutex;
use project::Fs;
+use settings::{self, Settings};
use smol::process::Command;
use std::{env, fs, path::PathBuf, sync::Arc};
use theme::{ThemeRegistry, DEFAULT_THEME_NAME};
use util::ResultExt;
-use workspace::{
- self,
- settings::{self, SettingsFile},
- AppState, OpenNew, OpenParams, OpenPaths, Settings,
-};
+use workspace::{self, AppState, OpenNew, OpenParams, OpenPaths};
use zed::{
- self, assets::Assets, build_window_options, build_workspace, fs::RealFs, languages, menus,
+ self,
+ assets::Assets,
+ build_window_options, build_workspace,
+ fs::RealFs,
+ languages, menus,
+ settings_file::{settings_from_files, SettingsFile},
};
fn main() {
@@ -97,7 +99,7 @@ fn main() {
.detach_and_log_err(cx);
let settings_file = cx.background().block(settings_file).unwrap();
- let mut settings_rx = Settings::from_files(
+ let mut settings_rx = settings_from_files(
default_settings,
vec![settings_file],
themes.clone(),
@@ -0,0 +1,157 @@
+use futures::{stream, StreamExt};
+use gpui::{executor, FontCache};
+use postage::sink::Sink as _;
+use postage::{prelude::Stream, watch};
+use project::Fs;
+use settings::{Settings, SettingsFileContent};
+use std::{path::Path, sync::Arc, time::Duration};
+use theme::ThemeRegistry;
+use util::ResultExt;
+
+#[derive(Clone)]
+pub struct SettingsFile(watch::Receiver<SettingsFileContent>);
+
+impl SettingsFile {
+ pub async fn new(
+ fs: Arc<dyn Fs>,
+ executor: &executor::Background,
+ path: impl Into<Arc<Path>>,
+ ) -> Self {
+ let path = path.into();
+ let settings = Self::load(fs.clone(), &path).await.unwrap_or_default();
+ let mut events = fs.watch(&path, Duration::from_millis(500)).await;
+ let (mut tx, rx) = watch::channel_with(settings);
+ executor
+ .spawn(async move {
+ while events.next().await.is_some() {
+ if let Some(settings) = Self::load(fs.clone(), &path).await {
+ if tx.send(settings).await.is_err() {
+ break;
+ }
+ }
+ }
+ })
+ .detach();
+ Self(rx)
+ }
+
+ async fn load(fs: Arc<dyn Fs>, path: &Path) -> Option<SettingsFileContent> {
+ if fs.is_file(&path).await {
+ fs.load(&path)
+ .await
+ .log_err()
+ .and_then(|data| serde_json::from_str(&data).log_err())
+ } else {
+ Some(SettingsFileContent::default())
+ }
+ }
+}
+
+pub fn settings_from_files(
+ defaults: Settings,
+ sources: Vec<SettingsFile>,
+ theme_registry: Arc<ThemeRegistry>,
+ font_cache: Arc<FontCache>,
+) -> impl futures::stream::Stream<Item = Settings> {
+ stream::select_all(sources.iter().enumerate().map(|(i, source)| {
+ let mut rx = source.0.clone();
+ // Consume the initial item from all of the constituent file watches but one.
+ // This way, the stream will yield exactly one item for the files' initial
+ // state, and won't return any more items until the files change.
+ if i > 0 {
+ rx.try_recv().ok();
+ }
+ rx
+ }))
+ .map(move |_| {
+ let mut settings = defaults.clone();
+ for source in &sources {
+ settings.merge(&*source.0.borrow(), &theme_registry, &font_cache);
+ }
+ settings
+ })
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use project::FakeFs;
+ use settings::SoftWrap;
+
+ #[gpui::test]
+ async fn test_settings_from_files(cx: &mut gpui::TestAppContext) {
+ let executor = cx.background();
+ let fs = FakeFs::new(executor.clone());
+
+ fs.save(
+ "/settings1.json".as_ref(),
+ &r#"
+ {
+ "buffer_font_size": 24,
+ "soft_wrap": "editor_width",
+ "language_overrides": {
+ "Markdown": {
+ "preferred_line_length": 100,
+ "soft_wrap": "preferred_line_length"
+ }
+ }
+ }
+ "#
+ .into(),
+ )
+ .await
+ .unwrap();
+
+ let source1 = SettingsFile::new(fs.clone(), &executor, "/settings1.json".as_ref()).await;
+ let source2 = SettingsFile::new(fs.clone(), &executor, "/settings2.json".as_ref()).await;
+ let source3 = SettingsFile::new(fs.clone(), &executor, "/settings3.json".as_ref()).await;
+
+ let mut settings_rx = settings_from_files(
+ cx.read(Settings::test),
+ vec![source1, source2, source3],
+ ThemeRegistry::new((), cx.font_cache()),
+ cx.font_cache(),
+ );
+
+ let settings = settings_rx.next().await.unwrap();
+ let md_settings = settings.language_overrides.get("Markdown").unwrap();
+ assert_eq!(settings.soft_wrap, SoftWrap::EditorWidth);
+ assert_eq!(settings.buffer_font_size, 24.0);
+ assert_eq!(settings.tab_size, 4);
+ assert_eq!(md_settings.soft_wrap, Some(SoftWrap::PreferredLineLength));
+ assert_eq!(md_settings.preferred_line_length, Some(100));
+
+ fs.save(
+ "/settings2.json".as_ref(),
+ &r#"
+ {
+ "tab_size": 2,
+ "soft_wrap": "none",
+ "language_overrides": {
+ "Markdown": {
+ "preferred_line_length": 120
+ }
+ }
+ }
+ "#
+ .into(),
+ )
+ .await
+ .unwrap();
+
+ let settings = settings_rx.next().await.unwrap();
+ let md_settings = settings.language_overrides.get("Markdown").unwrap();
+ assert_eq!(settings.soft_wrap, SoftWrap::None);
+ assert_eq!(settings.buffer_font_size, 24.0);
+ assert_eq!(settings.tab_size, 2);
+ assert_eq!(md_settings.soft_wrap, Some(SoftWrap::PreferredLineLength));
+ assert_eq!(md_settings.preferred_line_length, Some(120));
+
+ fs.remove_file("/settings2.json".as_ref(), Default::default())
+ .await
+ .unwrap();
+
+ let settings = settings_rx.next().await.unwrap();
+ assert_eq!(settings.tab_size, 4);
+ }
+}
@@ -3,9 +3,9 @@ use client::{test::FakeHttpClient, ChannelList, Client, UserStore};
use gpui::MutableAppContext;
use language::LanguageRegistry;
use project::fs::FakeFs;
+use settings::Settings;
use std::sync::Arc;
use theme::ThemeRegistry;
-use workspace::Settings;
#[cfg(test)]
#[ctor::ctor]
@@ -1,6 +1,7 @@
pub mod assets;
pub mod languages;
pub mod menus;
+pub mod settings_file;
#[cfg(any(test, feature = "test-support"))]
pub mod test;
@@ -23,9 +24,10 @@ use project::Project;
pub use project::{self, fs};
use project_panel::ProjectPanel;
use search::{BufferSearchBar, ProjectSearchBar};
+use settings::Settings;
use std::{path::PathBuf, sync::Arc};
pub use workspace;
-use workspace::{AppState, Settings, Workspace, WorkspaceParams};
+use workspace::{AppState, Workspace, WorkspaceParams};
action!(About);
action!(Quit);