Move Settings to its own crate
Co-authored-by: Keith Simmons <keith@zed.dev>
This commit is contained in:
parent
664f17f92b
commit
866ffdd4ae
48 changed files with 465 additions and 369 deletions
32
Cargo.lock
generated
32
Cargo.lock
generated
|
@ -729,6 +729,7 @@ dependencies = [
|
||||||
"language",
|
"language",
|
||||||
"project",
|
"project",
|
||||||
"search",
|
"search",
|
||||||
|
"settings",
|
||||||
"theme",
|
"theme",
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
@ -881,6 +882,7 @@ dependencies = [
|
||||||
"editor",
|
"editor",
|
||||||
"gpui",
|
"gpui",
|
||||||
"postage",
|
"postage",
|
||||||
|
"settings",
|
||||||
"theme",
|
"theme",
|
||||||
"time 0.3.7",
|
"time 0.3.7",
|
||||||
"util",
|
"util",
|
||||||
|
@ -1131,6 +1133,7 @@ dependencies = [
|
||||||
"client",
|
"client",
|
||||||
"gpui",
|
"gpui",
|
||||||
"postage",
|
"postage",
|
||||||
|
"settings",
|
||||||
"theme",
|
"theme",
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
@ -1480,6 +1483,7 @@ dependencies = [
|
||||||
"postage",
|
"postage",
|
||||||
"project",
|
"project",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"settings",
|
||||||
"theme",
|
"theme",
|
||||||
"unindent",
|
"unindent",
|
||||||
"util",
|
"util",
|
||||||
|
@ -1646,6 +1650,7 @@ dependencies = [
|
||||||
"rand 0.8.3",
|
"rand 0.8.3",
|
||||||
"rpc",
|
"rpc",
|
||||||
"serde",
|
"serde",
|
||||||
|
"settings",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"smol",
|
"smol",
|
||||||
"snippet",
|
"snippet",
|
||||||
|
@ -1806,6 +1811,7 @@ dependencies = [
|
||||||
"postage",
|
"postage",
|
||||||
"project",
|
"project",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"settings",
|
||||||
"theme",
|
"theme",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
|
@ -2206,6 +2212,7 @@ dependencies = [
|
||||||
"editor",
|
"editor",
|
||||||
"gpui",
|
"gpui",
|
||||||
"postage",
|
"postage",
|
||||||
|
"settings",
|
||||||
"text",
|
"text",
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
@ -3255,6 +3262,7 @@ dependencies = [
|
||||||
"language",
|
"language",
|
||||||
"ordered-float",
|
"ordered-float",
|
||||||
"postage",
|
"postage",
|
||||||
|
"settings",
|
||||||
"smol",
|
"smol",
|
||||||
"text",
|
"text",
|
||||||
"workspace",
|
"workspace",
|
||||||
|
@ -3643,6 +3651,7 @@ dependencies = [
|
||||||
"postage",
|
"postage",
|
||||||
"project",
|
"project",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"settings",
|
||||||
"theme",
|
"theme",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
|
@ -3659,6 +3668,7 @@ dependencies = [
|
||||||
"ordered-float",
|
"ordered-float",
|
||||||
"postage",
|
"postage",
|
||||||
"project",
|
"project",
|
||||||
|
"settings",
|
||||||
"smol",
|
"smol",
|
||||||
"text",
|
"text",
|
||||||
"util",
|
"util",
|
||||||
|
@ -4258,6 +4268,7 @@ dependencies = [
|
||||||
"postage",
|
"postage",
|
||||||
"project",
|
"project",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"settings",
|
||||||
"theme",
|
"theme",
|
||||||
"unindent",
|
"unindent",
|
||||||
"util",
|
"util",
|
||||||
|
@ -4406,6 +4417,21 @@ dependencies = [
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "settings"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"gpui",
|
||||||
|
"schemars",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_path_to_error",
|
||||||
|
"theme",
|
||||||
|
"toml",
|
||||||
|
"util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha-1"
|
name = "sha-1"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
|
@ -5142,6 +5168,7 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"postage",
|
"postage",
|
||||||
|
"settings",
|
||||||
"smol",
|
"smol",
|
||||||
"theme",
|
"theme",
|
||||||
"workspace",
|
"workspace",
|
||||||
|
@ -5719,6 +5746,7 @@ dependencies = [
|
||||||
"language",
|
"language",
|
||||||
"log",
|
"log",
|
||||||
"project",
|
"project",
|
||||||
|
"settings",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
@ -5948,9 +5976,9 @@ dependencies = [
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"postage",
|
"postage",
|
||||||
"project",
|
"project",
|
||||||
"schemars",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"settings",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"theme",
|
"theme",
|
||||||
"util",
|
"util",
|
||||||
|
@ -6034,6 +6062,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_path_to_error",
|
"serde_path_to_error",
|
||||||
|
"settings",
|
||||||
"simplelog",
|
"simplelog",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"smol",
|
"smol",
|
||||||
|
@ -6099,6 +6128,7 @@ dependencies = [
|
||||||
"scrypt",
|
"scrypt",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"settings",
|
||||||
"sha-1 0.9.6",
|
"sha-1 0.9.6",
|
||||||
"sqlx 0.5.5",
|
"sqlx 0.5.5",
|
||||||
"surf",
|
"surf",
|
||||||
|
|
|
@ -14,6 +14,7 @@ gpui = { path = "../gpui" }
|
||||||
language = { path = "../language" }
|
language = { path = "../language" }
|
||||||
project = { path = "../project" }
|
project = { path = "../project" }
|
||||||
search = { path = "../search" }
|
search = { path = "../search" }
|
||||||
|
settings = { path = "../settings" }
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,9 @@ use gpui::{
|
||||||
use language::{Buffer, OutlineItem};
|
use language::{Buffer, OutlineItem};
|
||||||
use project::Project;
|
use project::Project;
|
||||||
use search::ProjectSearchView;
|
use search::ProjectSearchView;
|
||||||
|
use settings::Settings;
|
||||||
use theme::SyntaxTheme;
|
use theme::SyntaxTheme;
|
||||||
use workspace::{ItemHandle, Settings, ToolbarItemLocation, ToolbarItemView};
|
use workspace::{ItemHandle, ToolbarItemLocation, ToolbarItemView};
|
||||||
|
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
UpdateLocation,
|
UpdateLocation,
|
||||||
|
|
|
@ -11,6 +11,7 @@ doctest = false
|
||||||
client = { path = "../client" }
|
client = { path = "../client" }
|
||||||
editor = { path = "../editor" }
|
editor = { path = "../editor" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
|
settings = { path = "../settings" }
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
|
|
|
@ -13,10 +13,10 @@ use gpui::{
|
||||||
ViewContext, ViewHandle,
|
ViewContext, ViewHandle,
|
||||||
};
|
};
|
||||||
use postage::prelude::Stream;
|
use postage::prelude::Stream;
|
||||||
|
use settings::{Settings, SoftWrap};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use time::{OffsetDateTime, UtcOffset};
|
use time::{OffsetDateTime, UtcOffset};
|
||||||
use util::{ResultExt, TryFutureExt};
|
use util::{ResultExt, TryFutureExt};
|
||||||
use workspace::{settings::SoftWrap, Settings};
|
|
||||||
|
|
||||||
const MESSAGE_LOADING_THRESHOLD: usize = 50;
|
const MESSAGE_LOADING_THRESHOLD: usize = 50;
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ doctest = false
|
||||||
[dependencies]
|
[dependencies]
|
||||||
client = { path = "../client" }
|
client = { path = "../client" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
|
settings = { path = "../settings" }
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
postage = { version = "0.4.1", features = ["futures-traits"] }
|
postage = { version = "0.4.1", features = ["futures-traits"] }
|
||||||
|
|
|
@ -8,7 +8,8 @@ use gpui::{
|
||||||
Element, ElementBox, Entity, LayoutContext, ModelHandle, RenderContext, Subscription, View,
|
Element, ElementBox, Entity, LayoutContext, ModelHandle, RenderContext, Subscription, View,
|
||||||
ViewContext,
|
ViewContext,
|
||||||
};
|
};
|
||||||
use workspace::{AppState, JoinProject, JoinProjectParams, Settings};
|
use workspace::{AppState, JoinProject, JoinProjectParams};
|
||||||
|
use settings::Settings;
|
||||||
|
|
||||||
pub struct ContactsPanel {
|
pub struct ContactsPanel {
|
||||||
contacts: ListState,
|
contacts: ListState,
|
||||||
|
|
|
@ -14,6 +14,7 @@ editor = { path = "../editor" }
|
||||||
language = { path = "../language" }
|
language = { path = "../language" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
project = { path = "../project" }
|
project = { path = "../project" }
|
||||||
|
settings = { path = "../settings" }
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
|
|
|
@ -25,7 +25,8 @@ use std::{
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
use util::TryFutureExt;
|
use util::TryFutureExt;
|
||||||
use workspace::{ItemHandle as _, ItemNavHistory, Settings, Workspace};
|
use workspace::{ItemHandle as _, ItemNavHistory, Workspace};
|
||||||
|
use settings::Settings;
|
||||||
|
|
||||||
action!(Deploy);
|
action!(Deploy);
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,8 @@ use gpui::{
|
||||||
elements::*, platform::CursorStyle, Entity, ModelHandle, RenderContext, View, ViewContext,
|
elements::*, platform::CursorStyle, Entity, ModelHandle, RenderContext, View, ViewContext,
|
||||||
};
|
};
|
||||||
use project::Project;
|
use project::Project;
|
||||||
use workspace::{Settings, StatusItemView};
|
use workspace::{StatusItemView};
|
||||||
|
use settings::Settings;
|
||||||
|
|
||||||
pub struct DiagnosticSummary {
|
pub struct DiagnosticSummary {
|
||||||
summary: project::DiagnosticSummary,
|
summary: project::DiagnosticSummary,
|
||||||
|
|
|
@ -28,6 +28,7 @@ language = { path = "../language" }
|
||||||
lsp = { path = "../lsp" }
|
lsp = { path = "../lsp" }
|
||||||
project = { path = "../project" }
|
project = { path = "../project" }
|
||||||
rpc = { path = "../rpc" }
|
rpc = { path = "../rpc" }
|
||||||
|
settings = { path = "../settings" }
|
||||||
snippet = { path = "../snippet" }
|
snippet = { path = "../snippet" }
|
||||||
sum_tree = { path = "../sum_tree" }
|
sum_tree = { path = "../sum_tree" }
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
|
@ -54,6 +55,7 @@ lsp = { path = "../lsp", features = ["test-support"] }
|
||||||
gpui = { path = "../gpui", features = ["test-support"] }
|
gpui = { path = "../gpui", features = ["test-support"] }
|
||||||
util = { path = "../util", features = ["test-support"] }
|
util = { path = "../util", features = ["test-support"] }
|
||||||
project = { path = "../project", features = ["test-support"] }
|
project = { path = "../project", features = ["test-support"] }
|
||||||
|
settings = { path = "../settings", features = ["test-support"] }
|
||||||
workspace = { path = "../workspace", features = ["test-support"] }
|
workspace = { path = "../workspace", features = ["test-support"] }
|
||||||
ctor = "0.1"
|
ctor = "0.1"
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
|
|
|
@ -46,6 +46,7 @@ impl Entity for DisplayMap {
|
||||||
impl DisplayMap {
|
impl DisplayMap {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
buffer: ModelHandle<MultiBuffer>,
|
buffer: ModelHandle<MultiBuffer>,
|
||||||
|
// TODO - remove. read tab_size from settings inside
|
||||||
tab_size: usize,
|
tab_size: usize,
|
||||||
font_id: FontId,
|
font_id: FontId,
|
||||||
font_size: f32,
|
font_size: f32,
|
||||||
|
@ -76,6 +77,8 @@ impl DisplayMap {
|
||||||
let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
|
let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
|
||||||
let edits = self.buffer_subscription.consume().into_inner();
|
let edits = self.buffer_subscription.consume().into_inner();
|
||||||
let (folds_snapshot, edits) = self.fold_map.read(buffer_snapshot, edits);
|
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 (tabs_snapshot, edits) = self.tab_map.sync(folds_snapshot.clone(), edits);
|
||||||
let (wraps_snapshot, edits) = self
|
let (wraps_snapshot, edits) = self
|
||||||
.wrap_map
|
.wrap_map
|
||||||
|
|
|
@ -41,6 +41,7 @@ pub use multi_buffer::{
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use project::{Project, ProjectTransaction};
|
use project::{Project, ProjectTransaction};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use settings::Settings;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use smol::Timer;
|
use smol::Timer;
|
||||||
use snippet::Snippet;
|
use snippet::Snippet;
|
||||||
|
@ -57,7 +58,7 @@ pub use sum_tree::Bias;
|
||||||
use text::rope::TextDimension;
|
use text::rope::TextDimension;
|
||||||
use theme::DiagnosticStyle;
|
use theme::DiagnosticStyle;
|
||||||
use util::{post_inc, ResultExt, TryFutureExt};
|
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 CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
|
||||||
const MAX_LINE_LEN: usize = 1024;
|
const MAX_LINE_LEN: usize = 1024;
|
||||||
|
@ -5669,16 +5670,16 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
|
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 settings = cx.global::<Settings>();
|
||||||
let mode = self
|
let mode = self
|
||||||
.soft_wrap_mode_override
|
.soft_wrap_mode_override
|
||||||
.unwrap_or_else(|| settings.soft_wrap(language));
|
.unwrap_or_else(|| settings.soft_wrap(language.as_deref()));
|
||||||
match mode {
|
match mode {
|
||||||
settings::SoftWrap::None => SoftWrap::None,
|
settings::SoftWrap::None => SoftWrap::None,
|
||||||
settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
|
settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
|
||||||
settings::SoftWrap::PreferredLineLength => {
|
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},
|
display_map::{BlockDisposition, BlockProperties},
|
||||||
Editor, MultiBuffer,
|
Editor, MultiBuffer,
|
||||||
};
|
};
|
||||||
|
use settings::Settings;
|
||||||
use util::test::sample_text;
|
use util::test::sample_text;
|
||||||
use workspace::Settings;
|
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_layout_line_numbers(cx: &mut gpui::MutableAppContext) {
|
fn test_layout_line_numbers(cx: &mut gpui::MutableAppContext) {
|
||||||
|
|
|
@ -8,12 +8,11 @@ use gpui::{
|
||||||
use language::{Bias, Buffer, Diagnostic, File as _, SelectionGoal};
|
use language::{Bias, Buffer, Diagnostic, File as _, SelectionGoal};
|
||||||
use project::{File, Project, ProjectEntryId, ProjectPath};
|
use project::{File, Project, ProjectEntryId, ProjectPath};
|
||||||
use rpc::proto::{self, update_view};
|
use rpc::proto::{self, update_view};
|
||||||
|
use settings::Settings;
|
||||||
use std::{fmt::Write, path::PathBuf, time::Duration};
|
use std::{fmt::Write, path::PathBuf, time::Duration};
|
||||||
use text::{Point, Selection};
|
use text::{Point, Selection};
|
||||||
use util::TryFutureExt;
|
use util::TryFutureExt;
|
||||||
use workspace::{
|
use workspace::{FollowableItem, Item, ItemHandle, ItemNavHistory, ProjectItem, StatusItemView};
|
||||||
FollowableItem, Item, ItemHandle, ItemNavHistory, ProjectItem, Settings, StatusItemView,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
|
pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ editor = { path = "../editor" }
|
||||||
fuzzy = { path = "../fuzzy" }
|
fuzzy = { path = "../fuzzy" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
project = { path = "../project" }
|
project = { path = "../project" }
|
||||||
|
settings = { path = "../settings" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
|
|
|
@ -19,8 +19,9 @@ use std::{
|
||||||
use util::post_inc;
|
use util::post_inc;
|
||||||
use workspace::{
|
use workspace::{
|
||||||
menu::{Confirm, SelectNext, SelectPrev},
|
menu::{Confirm, SelectNext, SelectPrev},
|
||||||
Settings, Workspace,
|
Workspace,
|
||||||
};
|
};
|
||||||
|
use settings::Settings;
|
||||||
|
|
||||||
pub struct FileFinder {
|
pub struct FileFinder {
|
||||||
handle: WeakViewHandle<Self>,
|
handle: WeakViewHandle<Self>,
|
||||||
|
|
|
@ -11,5 +11,6 @@ doctest = false
|
||||||
text = { path = "../text" }
|
text = { path = "../text" }
|
||||||
editor = { path = "../editor" }
|
editor = { path = "../editor" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
|
settings = { path = "../settings" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
postage = { version = "0.4", features = ["futures-traits"] }
|
postage = { version = "0.4", features = ["futures-traits"] }
|
||||||
|
|
|
@ -3,8 +3,9 @@ use gpui::{
|
||||||
action, elements::*, geometry::vector::Vector2F, keymap::Binding, Axis, Entity,
|
action, elements::*, geometry::vector::Vector2F, keymap::Binding, Axis, Entity,
|
||||||
MutableAppContext, RenderContext, View, ViewContext, ViewHandle,
|
MutableAppContext, RenderContext, View, ViewContext, ViewHandle,
|
||||||
};
|
};
|
||||||
|
use settings::Settings;
|
||||||
use text::{Bias, Point};
|
use text::{Bias, Point};
|
||||||
use workspace::{Settings, Workspace};
|
use workspace::Workspace;
|
||||||
|
|
||||||
action!(Toggle);
|
action!(Toggle);
|
||||||
action!(Confirm);
|
action!(Confirm);
|
||||||
|
|
|
@ -12,6 +12,7 @@ editor = { path = "../editor" }
|
||||||
fuzzy = { path = "../fuzzy" }
|
fuzzy = { path = "../fuzzy" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
language = { path = "../language" }
|
language = { path = "../language" }
|
||||||
|
settings = { path = "../settings" }
|
||||||
text = { path = "../text" }
|
text = { path = "../text" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
ordered-float = "2.1.1"
|
ordered-float = "2.1.1"
|
||||||
|
|
|
@ -13,10 +13,11 @@ use gpui::{
|
||||||
};
|
};
|
||||||
use language::Outline;
|
use language::Outline;
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
|
use settings::Settings;
|
||||||
use std::cmp::{self, Reverse};
|
use std::cmp::{self, Reverse};
|
||||||
use workspace::{
|
use workspace::{
|
||||||
menu::{Confirm, SelectFirst, SelectLast, SelectNext, SelectPrev},
|
menu::{Confirm, SelectFirst, SelectLast, SelectNext, SelectPrev},
|
||||||
Settings, Workspace,
|
Workspace,
|
||||||
};
|
};
|
||||||
|
|
||||||
action!(Toggle);
|
action!(Toggle);
|
||||||
|
|
|
@ -10,6 +10,7 @@ doctest = false
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
project = { path = "../project" }
|
project = { path = "../project" }
|
||||||
|
settings = { path = "../settings" }
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
|
|
|
@ -10,6 +10,7 @@ use gpui::{
|
||||||
ViewHandle, WeakViewHandle,
|
ViewHandle, WeakViewHandle,
|
||||||
};
|
};
|
||||||
use project::{Project, ProjectEntryId, ProjectPath, Worktree, WorktreeId};
|
use project::{Project, ProjectEntryId, ProjectPath, Worktree, WorktreeId};
|
||||||
|
use settings::Settings;
|
||||||
use std::{
|
use std::{
|
||||||
collections::{hash_map, HashMap},
|
collections::{hash_map, HashMap},
|
||||||
ffi::OsStr,
|
ffi::OsStr,
|
||||||
|
@ -17,7 +18,7 @@ use std::{
|
||||||
};
|
};
|
||||||
use workspace::{
|
use workspace::{
|
||||||
menu::{SelectNext, SelectPrev},
|
menu::{SelectNext, SelectPrev},
|
||||||
Settings, Workspace,
|
Workspace,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ProjectPanel {
|
pub struct ProjectPanel {
|
||||||
|
|
|
@ -13,6 +13,7 @@ fuzzy = { path = "../fuzzy" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
project = { path = "../project" }
|
project = { path = "../project" }
|
||||||
text = { path = "../text" }
|
text = { path = "../text" }
|
||||||
|
settings = { path = "../settings" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
anyhow = "1.0.38"
|
anyhow = "1.0.38"
|
||||||
|
|
|
@ -11,6 +11,7 @@ use gpui::{
|
||||||
};
|
};
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use project::{Project, Symbol};
|
use project::{Project, Symbol};
|
||||||
|
use settings::Settings;
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
cmp::{self, Reverse},
|
cmp::{self, Reverse},
|
||||||
|
@ -18,7 +19,7 @@ use std::{
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use workspace::{
|
use workspace::{
|
||||||
menu::{Confirm, SelectFirst, SelectLast, SelectNext, SelectPrev},
|
menu::{Confirm, SelectFirst, SelectLast, SelectNext, SelectPrev},
|
||||||
Settings, Workspace,
|
Workspace,
|
||||||
};
|
};
|
||||||
|
|
||||||
action!(Toggle);
|
action!(Toggle);
|
||||||
|
|
|
@ -13,6 +13,7 @@ editor = { path = "../editor" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
language = { path = "../language" }
|
language = { path = "../language" }
|
||||||
project = { path = "../project" }
|
project = { path = "../project" }
|
||||||
|
settings = { path = "../settings" }
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
|
|
|
@ -9,7 +9,8 @@ use gpui::{
|
||||||
use language::OffsetRangeExt;
|
use language::OffsetRangeExt;
|
||||||
use project::search::SearchQuery;
|
use project::search::SearchQuery;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use workspace::{ItemHandle, Pane, Settings, ToolbarItemLocation, ToolbarItemView};
|
use workspace::{ItemHandle, Pane, ToolbarItemLocation, ToolbarItemView};
|
||||||
|
use settings::Settings;
|
||||||
|
|
||||||
action!(Deploy, bool);
|
action!(Deploy, bool);
|
||||||
action!(Dismiss);
|
action!(Dismiss);
|
||||||
|
|
|
@ -10,15 +10,14 @@ use gpui::{
|
||||||
ViewContext, ViewHandle, WeakModelHandle, WeakViewHandle,
|
ViewContext, ViewHandle, WeakModelHandle, WeakViewHandle,
|
||||||
};
|
};
|
||||||
use project::{search::SearchQuery, Project};
|
use project::{search::SearchQuery, Project};
|
||||||
|
use settings::Settings;
|
||||||
use std::{
|
use std::{
|
||||||
any::{Any, TypeId},
|
any::{Any, TypeId},
|
||||||
ops::Range,
|
ops::Range,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
};
|
};
|
||||||
use util::ResultExt as _;
|
use util::ResultExt as _;
|
||||||
use workspace::{
|
use workspace::{Item, ItemNavHistory, Pane, ToolbarItemLocation, ToolbarItemView, Workspace};
|
||||||
Item, ItemNavHistory, Pane, Settings, ToolbarItemLocation, ToolbarItemView, Workspace,
|
|
||||||
};
|
|
||||||
|
|
||||||
action!(Deploy);
|
action!(Deploy);
|
||||||
action!(Search);
|
action!(Search);
|
||||||
|
|
|
@ -14,6 +14,7 @@ required-features = ["seed-support"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
collections = { path = "../collections" }
|
collections = { path = "../collections" }
|
||||||
|
settings = { path = "../settings" }
|
||||||
rpc = { path = "../rpc" }
|
rpc = { path = "../rpc" }
|
||||||
anyhow = "1.0.40"
|
anyhow = "1.0.40"
|
||||||
async-io = "1.3"
|
async-io = "1.3"
|
||||||
|
|
|
@ -1104,6 +1104,7 @@ mod tests {
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use rpc::PeerId;
|
use rpc::PeerId;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
use settings::Settings;
|
||||||
use sqlx::types::time::OffsetDateTime;
|
use sqlx::types::time::OffsetDateTime;
|
||||||
use std::{
|
use std::{
|
||||||
cell::Cell,
|
cell::Cell,
|
||||||
|
@ -1117,7 +1118,7 @@ mod tests {
|
||||||
},
|
},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
use workspace::{Item, Settings, SplitDirection, Workspace, WorkspaceParams};
|
use workspace::{Item, SplitDirection, Workspace, WorkspaceParams};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[ctor::ctor]
|
#[ctor::ctor]
|
||||||
|
|
22
crates/settings/Cargo.toml
Normal file
22
crates/settings/Cargo.toml
Normal file
|
@ -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"
|
171
crates/settings/src/settings.rs
Normal file
171
crates/settings/src/settings.rs
Normal file
|
@ -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" }
|
fuzzy = { path = "../fuzzy" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
|
settings = { path = "../settings" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
parking_lot = "0.11.1"
|
parking_lot = "0.11.1"
|
||||||
|
|
|
@ -9,9 +9,10 @@ use gpui::{
|
||||||
};
|
};
|
||||||
use std::{cmp, sync::Arc};
|
use std::{cmp, sync::Arc};
|
||||||
use theme::{Theme, ThemeRegistry};
|
use theme::{Theme, ThemeRegistry};
|
||||||
|
use settings::Settings;
|
||||||
use workspace::{
|
use workspace::{
|
||||||
menu::{Confirm, SelectNext, SelectPrev},
|
menu::{Confirm, SelectNext, SelectPrev},
|
||||||
Settings, Workspace,
|
Workspace,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ThemeSelector {
|
pub struct ThemeSelector {
|
||||||
|
|
|
@ -12,6 +12,7 @@ collections = { path = "../collections" }
|
||||||
editor = { path = "../editor" }
|
editor = { path = "../editor" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
language = { path = "../language" }
|
language = { path = "../language" }
|
||||||
|
settings = { path = "../settings" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
|
||||||
|
@ -19,7 +20,8 @@ log = "0.4"
|
||||||
indoc = "1.0.4"
|
indoc = "1.0.4"
|
||||||
editor = { path = "../editor", features = ["test-support"] }
|
editor = { path = "../editor", features = ["test-support"] }
|
||||||
gpui = { path = "../gpui", features = ["test-support"] }
|
gpui = { path = "../gpui", features = ["test-support"] }
|
||||||
project = { path = "../project", features = ["test-support"] }
|
|
||||||
language = { path = "../language", features = ["test-support"] }
|
language = { path = "../language", features = ["test-support"] }
|
||||||
|
project = { path = "../project", features = ["test-support"] }
|
||||||
util = { path = "../util", features = ["test-support"] }
|
util = { path = "../util", features = ["test-support"] }
|
||||||
|
settings = { path = "../settings" }
|
||||||
workspace = { path = "../workspace", features = ["test-support"] }
|
workspace = { path = "../workspace", features = ["test-support"] }
|
|
@ -10,7 +10,8 @@ use editor::{CursorShape, Editor};
|
||||||
use gpui::{action, MutableAppContext, ViewContext, WeakViewHandle};
|
use gpui::{action, MutableAppContext, ViewContext, WeakViewHandle};
|
||||||
|
|
||||||
use mode::Mode;
|
use mode::Mode;
|
||||||
use workspace::{self, Settings, Workspace};
|
use settings::Settings;
|
||||||
|
use workspace::{self, Workspace};
|
||||||
|
|
||||||
action!(SwitchMode, Mode);
|
action!(SwitchMode, Mode);
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ collections = { path = "../collections" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
language = { path = "../language" }
|
language = { path = "../language" }
|
||||||
project = { path = "../project" }
|
project = { path = "../project" }
|
||||||
|
settings = { path = "../settings" }
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
anyhow = "1.0.38"
|
anyhow = "1.0.38"
|
||||||
|
@ -24,7 +25,6 @@ futures = "0.3"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
parking_lot = "0.11.1"
|
parking_lot = "0.11.1"
|
||||||
postage = { version = "0.4.1", features = ["futures-traits"] }
|
postage = { version = "0.4.1", features = ["futures-traits"] }
|
||||||
schemars = "0.8"
|
|
||||||
serde = { version = "1", features = ["derive", "rc"] }
|
serde = { version = "1", features = ["derive", "rc"] }
|
||||||
serde_json = { version = "1", features = ["preserve_order"] }
|
serde_json = { version = "1", features = ["preserve_order"] }
|
||||||
smallvec = { version = "1.6", features = ["union"] }
|
smallvec = { version = "1.6", features = ["union"] }
|
||||||
|
@ -33,3 +33,4 @@ smallvec = { version = "1.6", features = ["union"] }
|
||||||
client = { path = "../client", features = ["test-support"] }
|
client = { path = "../client", features = ["test-support"] }
|
||||||
gpui = { path = "../gpui", features = ["test-support"] }
|
gpui = { path = "../gpui", features = ["test-support"] }
|
||||||
project = { path = "../project", 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 futures::StreamExt;
|
||||||
use gpui::AppContext;
|
use gpui::AppContext;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
|
@ -7,6 +7,7 @@ use gpui::{
|
||||||
};
|
};
|
||||||
use language::{LanguageRegistry, LanguageServerBinaryStatus};
|
use language::{LanguageRegistry, LanguageServerBinaryStatus};
|
||||||
use project::{LanguageServerProgress, Project};
|
use project::{LanguageServerProgress, Project};
|
||||||
|
use settings::Settings;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::cmp::Reverse;
|
use std::cmp::Reverse;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use super::{ItemHandle, SplitDirection};
|
use super::{ItemHandle, SplitDirection};
|
||||||
use crate::{toolbar::Toolbar, Item, Settings, WeakItemHandle, Workspace};
|
use crate::{toolbar::Toolbar, Item, WeakItemHandle, Workspace};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use collections::{HashMap, VecDeque};
|
use collections::{HashMap, VecDeque};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
|
@ -12,7 +12,8 @@ use gpui::{
|
||||||
AppContext, Entity, MutableAppContext, PromptLevel, Quad, RenderContext, Task, View,
|
AppContext, Entity, MutableAppContext, PromptLevel, Quad, RenderContext, Task, View,
|
||||||
ViewContext, ViewHandle, WeakViewHandle,
|
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 std::{any::Any, cell::RefCell, cmp, mem, path::Path, rc::Rc};
|
||||||
use util::ResultExt;
|
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::{
|
use gpui::{
|
||||||
elements::*, AnyViewHandle, ElementBox, Entity, MutableAppContext, RenderContext, Subscription,
|
elements::*, AnyViewHandle, ElementBox, Entity, MutableAppContext, RenderContext, Subscription,
|
||||||
View, ViewContext, ViewHandle,
|
View, ViewContext, ViewHandle,
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use crate::{ItemHandle, Settings};
|
use crate::ItemHandle;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
elements::*, AnyViewHandle, AppContext, ElementBox, Entity, MutableAppContext, RenderContext,
|
elements::*, AnyViewHandle, AppContext, ElementBox, Entity, MutableAppContext, RenderContext,
|
||||||
View, ViewContext, ViewHandle,
|
View, ViewContext, ViewHandle,
|
||||||
};
|
};
|
||||||
|
use settings::Settings;
|
||||||
|
|
||||||
pub trait ToolbarItemView: View {
|
pub trait ToolbarItemView: View {
|
||||||
fn set_active_pane_item(
|
fn set_active_pane_item(
|
||||||
|
|
|
@ -2,7 +2,6 @@ pub mod lsp_status;
|
||||||
pub mod menu;
|
pub mod menu;
|
||||||
pub mod pane;
|
pub mod pane;
|
||||||
pub mod pane_group;
|
pub mod pane_group;
|
||||||
pub mod settings;
|
|
||||||
pub mod sidebar;
|
pub mod sidebar;
|
||||||
mod status_bar;
|
mod status_bar;
|
||||||
mod toolbar;
|
mod toolbar;
|
||||||
|
@ -31,7 +30,7 @@ pub use pane::*;
|
||||||
pub use pane_group::*;
|
pub use pane_group::*;
|
||||||
use postage::prelude::Stream;
|
use postage::prelude::Stream;
|
||||||
use project::{fs, Fs, Project, ProjectEntryId, ProjectPath, Worktree};
|
use project::{fs, Fs, Project, ProjectEntryId, ProjectPath, Worktree};
|
||||||
pub use settings::Settings;
|
use settings::Settings;
|
||||||
use sidebar::{Side, Sidebar, SidebarItemId, ToggleSidebarItem, ToggleSidebarItemFocus};
|
use sidebar::{Side, Sidebar, SidebarItemId, ToggleSidebarItem, ToggleSidebarItemFocus};
|
||||||
use status_bar::StatusBar;
|
use status_bar::StatusBar;
|
||||||
pub use status_bar::StatusItemView;
|
pub use status_bar::StatusItemView;
|
||||||
|
|
|
@ -51,6 +51,7 @@ project = { path = "../project" }
|
||||||
project_panel = { path = "../project_panel" }
|
project_panel = { path = "../project_panel" }
|
||||||
project_symbols = { path = "../project_symbols" }
|
project_symbols = { path = "../project_symbols" }
|
||||||
rpc = { path = "../rpc" }
|
rpc = { path = "../rpc" }
|
||||||
|
settings = { path = "../settings" }
|
||||||
sum_tree = { path = "../sum_tree" }
|
sum_tree = { path = "../sum_tree" }
|
||||||
text = { path = "../text" }
|
text = { path = "../text" }
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
|
@ -111,6 +112,7 @@ lsp = { path = "../lsp", features = ["test-support"] }
|
||||||
project = { path = "../project", features = ["test-support"] }
|
project = { path = "../project", features = ["test-support"] }
|
||||||
rpc = { path = "../rpc", features = ["test-support"] }
|
rpc = { path = "../rpc", features = ["test-support"] }
|
||||||
client = { path = "../client", features = ["test-support"] }
|
client = { path = "../client", features = ["test-support"] }
|
||||||
|
settings = { path = "../settings", features = ["test-support"] }
|
||||||
util = { path = "../util", features = ["test-support"] }
|
util = { path = "../util", features = ["test-support"] }
|
||||||
workspace = { path = "../workspace", features = ["test-support"] }
|
workspace = { path = "../workspace", features = ["test-support"] }
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
|
|
|
@ -9,17 +9,19 @@ use gpui::{App, AssetSource, Task};
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use project::Fs;
|
use project::Fs;
|
||||||
|
use settings::{self, Settings};
|
||||||
use smol::process::Command;
|
use smol::process::Command;
|
||||||
use std::{env, fs, path::PathBuf, sync::Arc};
|
use std::{env, fs, path::PathBuf, sync::Arc};
|
||||||
use theme::{ThemeRegistry, DEFAULT_THEME_NAME};
|
use theme::{ThemeRegistry, DEFAULT_THEME_NAME};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use workspace::{
|
use workspace::{self, AppState, OpenNew, OpenParams, OpenPaths};
|
||||||
self,
|
|
||||||
settings::{self, SettingsFile},
|
|
||||||
AppState, OpenNew, OpenParams, OpenPaths, Settings,
|
|
||||||
};
|
|
||||||
use zed::{
|
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() {
|
fn main() {
|
||||||
|
@ -97,7 +99,7 @@ fn main() {
|
||||||
.detach_and_log_err(cx);
|
.detach_and_log_err(cx);
|
||||||
|
|
||||||
let settings_file = cx.background().block(settings_file).unwrap();
|
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,
|
default_settings,
|
||||||
vec![settings_file],
|
vec![settings_file],
|
||||||
themes.clone(),
|
themes.clone(),
|
||||||
|
|
157
crates/zed/src/settings_file.rs
Normal file
157
crates/zed/src/settings_file.rs
Normal file
|
@ -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 gpui::MutableAppContext;
|
||||||
use language::LanguageRegistry;
|
use language::LanguageRegistry;
|
||||||
use project::fs::FakeFs;
|
use project::fs::FakeFs;
|
||||||
|
use settings::Settings;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use theme::ThemeRegistry;
|
use theme::ThemeRegistry;
|
||||||
use workspace::Settings;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[ctor::ctor]
|
#[ctor::ctor]
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
pub mod assets;
|
pub mod assets;
|
||||||
pub mod languages;
|
pub mod languages;
|
||||||
pub mod menus;
|
pub mod menus;
|
||||||
|
pub mod settings_file;
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
pub mod test;
|
pub mod test;
|
||||||
|
|
||||||
|
@ -23,9 +24,10 @@ use project::Project;
|
||||||
pub use project::{self, fs};
|
pub use project::{self, fs};
|
||||||
use project_panel::ProjectPanel;
|
use project_panel::ProjectPanel;
|
||||||
use search::{BufferSearchBar, ProjectSearchBar};
|
use search::{BufferSearchBar, ProjectSearchBar};
|
||||||
|
use settings::Settings;
|
||||||
use std::{path::PathBuf, sync::Arc};
|
use std::{path::PathBuf, sync::Arc};
|
||||||
pub use workspace;
|
pub use workspace;
|
||||||
use workspace::{AppState, Settings, Workspace, WorkspaceParams};
|
use workspace::{AppState, Workspace, WorkspaceParams};
|
||||||
|
|
||||||
action!(About);
|
action!(About);
|
||||||
action!(Quit);
|
action!(Quit);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue