Merge pull request #757 from zed-industries/restructure-settings
Enable language specific tab sizes
This commit is contained in:
commit
3d8e4adcde
77 changed files with 2324 additions and 1920 deletions
34
Cargo.lock
generated
34
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",
|
||||||
|
@ -1634,6 +1638,7 @@ dependencies = [
|
||||||
"futures",
|
"futures",
|
||||||
"fuzzy",
|
"fuzzy",
|
||||||
"gpui",
|
"gpui",
|
||||||
|
"indoc",
|
||||||
"itertools",
|
"itertools",
|
||||||
"language",
|
"language",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
@ -1646,6 +1651,7 @@ dependencies = [
|
||||||
"rand 0.8.3",
|
"rand 0.8.3",
|
||||||
"rpc",
|
"rpc",
|
||||||
"serde",
|
"serde",
|
||||||
|
"settings",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"smol",
|
"smol",
|
||||||
"snippet",
|
"snippet",
|
||||||
|
@ -1806,6 +1812,7 @@ dependencies = [
|
||||||
"postage",
|
"postage",
|
||||||
"project",
|
"project",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"settings",
|
||||||
"theme",
|
"theme",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
|
@ -2206,6 +2213,7 @@ dependencies = [
|
||||||
"editor",
|
"editor",
|
||||||
"gpui",
|
"gpui",
|
||||||
"postage",
|
"postage",
|
||||||
|
"settings",
|
||||||
"text",
|
"text",
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
@ -3255,6 +3263,7 @@ dependencies = [
|
||||||
"language",
|
"language",
|
||||||
"ordered-float",
|
"ordered-float",
|
||||||
"postage",
|
"postage",
|
||||||
|
"settings",
|
||||||
"smol",
|
"smol",
|
||||||
"text",
|
"text",
|
||||||
"workspace",
|
"workspace",
|
||||||
|
@ -3624,6 +3633,7 @@ dependencies = [
|
||||||
"rpc",
|
"rpc",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"settings",
|
||||||
"sha2 0.10.2",
|
"sha2 0.10.2",
|
||||||
"similar",
|
"similar",
|
||||||
"smol",
|
"smol",
|
||||||
|
@ -3643,6 +3653,7 @@ dependencies = [
|
||||||
"postage",
|
"postage",
|
||||||
"project",
|
"project",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"settings",
|
||||||
"theme",
|
"theme",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
|
@ -3659,6 +3670,7 @@ dependencies = [
|
||||||
"ordered-float",
|
"ordered-float",
|
||||||
"postage",
|
"postage",
|
||||||
"project",
|
"project",
|
||||||
|
"settings",
|
||||||
"smol",
|
"smol",
|
||||||
"text",
|
"text",
|
||||||
"util",
|
"util",
|
||||||
|
@ -4258,6 +4270,7 @@ dependencies = [
|
||||||
"postage",
|
"postage",
|
||||||
"project",
|
"project",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"settings",
|
||||||
"theme",
|
"theme",
|
||||||
"unindent",
|
"unindent",
|
||||||
"util",
|
"util",
|
||||||
|
@ -4406,6 +4419,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 +5170,7 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"postage",
|
"postage",
|
||||||
|
"settings",
|
||||||
"smol",
|
"smol",
|
||||||
"theme",
|
"theme",
|
||||||
"workspace",
|
"workspace",
|
||||||
|
@ -5719,6 +5748,7 @@ dependencies = [
|
||||||
"language",
|
"language",
|
||||||
"log",
|
"log",
|
||||||
"project",
|
"project",
|
||||||
|
"settings",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
@ -5948,9 +5978,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 +6064,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 +6130,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" }
|
||||||
|
@ -36,6 +37,7 @@ workspace = { path = "../workspace" }
|
||||||
aho-corasick = "0.7"
|
aho-corasick = "0.7"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
|
indoc = "1.0.4"
|
||||||
itertools = "0.10"
|
itertools = "0.10"
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
@ -54,6 +56,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"
|
||||||
|
|
|
@ -12,6 +12,7 @@ use gpui::{
|
||||||
Entity, ModelContext, ModelHandle,
|
Entity, ModelContext, ModelHandle,
|
||||||
};
|
};
|
||||||
use language::{Point, Subscription as BufferSubscription};
|
use language::{Point, Subscription as BufferSubscription};
|
||||||
|
use settings::Settings;
|
||||||
use std::{any::TypeId, fmt::Debug, ops::Range, sync::Arc};
|
use std::{any::TypeId, fmt::Debug, ops::Range, sync::Arc};
|
||||||
use sum_tree::{Bias, TreeMap};
|
use sum_tree::{Bias, TreeMap};
|
||||||
use tab_map::TabMap;
|
use tab_map::TabMap;
|
||||||
|
@ -46,7 +47,6 @@ impl Entity for DisplayMap {
|
||||||
impl DisplayMap {
|
impl DisplayMap {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
buffer: ModelHandle<MultiBuffer>,
|
buffer: ModelHandle<MultiBuffer>,
|
||||||
tab_size: usize,
|
|
||||||
font_id: FontId,
|
font_id: FontId,
|
||||||
font_size: f32,
|
font_size: f32,
|
||||||
wrap_width: Option<f32>,
|
wrap_width: Option<f32>,
|
||||||
|
@ -55,6 +55,8 @@ impl DisplayMap {
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let buffer_subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
|
let buffer_subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
|
||||||
|
|
||||||
|
let tab_size = Self::tab_size(&buffer, cx);
|
||||||
let (fold_map, snapshot) = FoldMap::new(buffer.read(cx).snapshot(cx));
|
let (fold_map, snapshot) = FoldMap::new(buffer.read(cx).snapshot(cx));
|
||||||
let (tab_map, snapshot) = TabMap::new(snapshot, tab_size);
|
let (tab_map, snapshot) = TabMap::new(snapshot, tab_size);
|
||||||
let (wrap_map, snapshot) = WrapMap::new(snapshot, font_id, font_size, wrap_width, cx);
|
let (wrap_map, snapshot) = WrapMap::new(snapshot, font_id, font_size, wrap_width, cx);
|
||||||
|
@ -76,7 +78,9 @@ 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);
|
||||||
let (tabs_snapshot, edits) = self.tab_map.sync(folds_snapshot.clone(), edits);
|
|
||||||
|
let tab_size = Self::tab_size(&self.buffer, cx);
|
||||||
|
let (tabs_snapshot, edits) = self.tab_map.sync(folds_snapshot.clone(), edits, tab_size);
|
||||||
let (wraps_snapshot, edits) = self
|
let (wraps_snapshot, edits) = self
|
||||||
.wrap_map
|
.wrap_map
|
||||||
.update(cx, |map, cx| map.sync(tabs_snapshot.clone(), edits, cx));
|
.update(cx, |map, cx| map.sync(tabs_snapshot.clone(), edits, cx));
|
||||||
|
@ -100,14 +104,15 @@ impl DisplayMap {
|
||||||
) {
|
) {
|
||||||
let snapshot = self.buffer.read(cx).snapshot(cx);
|
let snapshot = self.buffer.read(cx).snapshot(cx);
|
||||||
let edits = self.buffer_subscription.consume().into_inner();
|
let edits = self.buffer_subscription.consume().into_inner();
|
||||||
|
let tab_size = Self::tab_size(&self.buffer, cx);
|
||||||
let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
|
let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
|
||||||
let (snapshot, edits) = self.tab_map.sync(snapshot, edits);
|
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
|
||||||
let (snapshot, edits) = self
|
let (snapshot, edits) = self
|
||||||
.wrap_map
|
.wrap_map
|
||||||
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
|
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
|
||||||
self.block_map.read(snapshot, edits);
|
self.block_map.read(snapshot, edits);
|
||||||
let (snapshot, edits) = fold_map.fold(ranges);
|
let (snapshot, edits) = fold_map.fold(ranges);
|
||||||
let (snapshot, edits) = self.tab_map.sync(snapshot, edits);
|
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
|
||||||
let (snapshot, edits) = self
|
let (snapshot, edits) = self
|
||||||
.wrap_map
|
.wrap_map
|
||||||
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
|
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
|
||||||
|
@ -122,14 +127,15 @@ impl DisplayMap {
|
||||||
) {
|
) {
|
||||||
let snapshot = self.buffer.read(cx).snapshot(cx);
|
let snapshot = self.buffer.read(cx).snapshot(cx);
|
||||||
let edits = self.buffer_subscription.consume().into_inner();
|
let edits = self.buffer_subscription.consume().into_inner();
|
||||||
|
let tab_size = Self::tab_size(&self.buffer, cx);
|
||||||
let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
|
let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
|
||||||
let (snapshot, edits) = self.tab_map.sync(snapshot, edits);
|
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
|
||||||
let (snapshot, edits) = self
|
let (snapshot, edits) = self
|
||||||
.wrap_map
|
.wrap_map
|
||||||
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
|
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
|
||||||
self.block_map.read(snapshot, edits);
|
self.block_map.read(snapshot, edits);
|
||||||
let (snapshot, edits) = fold_map.unfold(ranges, inclusive);
|
let (snapshot, edits) = fold_map.unfold(ranges, inclusive);
|
||||||
let (snapshot, edits) = self.tab_map.sync(snapshot, edits);
|
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
|
||||||
let (snapshot, edits) = self
|
let (snapshot, edits) = self
|
||||||
.wrap_map
|
.wrap_map
|
||||||
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
|
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
|
||||||
|
@ -143,8 +149,9 @@ impl DisplayMap {
|
||||||
) -> Vec<BlockId> {
|
) -> Vec<BlockId> {
|
||||||
let snapshot = self.buffer.read(cx).snapshot(cx);
|
let snapshot = self.buffer.read(cx).snapshot(cx);
|
||||||
let edits = self.buffer_subscription.consume().into_inner();
|
let edits = self.buffer_subscription.consume().into_inner();
|
||||||
|
let tab_size = Self::tab_size(&self.buffer, cx);
|
||||||
let (snapshot, edits) = self.fold_map.read(snapshot, edits);
|
let (snapshot, edits) = self.fold_map.read(snapshot, edits);
|
||||||
let (snapshot, edits) = self.tab_map.sync(snapshot, edits);
|
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
|
||||||
let (snapshot, edits) = self
|
let (snapshot, edits) = self
|
||||||
.wrap_map
|
.wrap_map
|
||||||
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
|
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
|
||||||
|
@ -159,8 +166,9 @@ impl DisplayMap {
|
||||||
pub fn remove_blocks(&mut self, ids: HashSet<BlockId>, cx: &mut ModelContext<Self>) {
|
pub fn remove_blocks(&mut self, ids: HashSet<BlockId>, cx: &mut ModelContext<Self>) {
|
||||||
let snapshot = self.buffer.read(cx).snapshot(cx);
|
let snapshot = self.buffer.read(cx).snapshot(cx);
|
||||||
let edits = self.buffer_subscription.consume().into_inner();
|
let edits = self.buffer_subscription.consume().into_inner();
|
||||||
|
let tab_size = Self::tab_size(&self.buffer, cx);
|
||||||
let (snapshot, edits) = self.fold_map.read(snapshot, edits);
|
let (snapshot, edits) = self.fold_map.read(snapshot, edits);
|
||||||
let (snapshot, edits) = self.tab_map.sync(snapshot, edits);
|
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
|
||||||
let (snapshot, edits) = self
|
let (snapshot, edits) = self
|
||||||
.wrap_map
|
.wrap_map
|
||||||
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
|
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
|
||||||
|
@ -195,6 +203,16 @@ impl DisplayMap {
|
||||||
.update(cx, |map, cx| map.set_wrap_width(width, cx))
|
.update(cx, |map, cx| map.set_wrap_width(width, cx))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tab_size(buffer: &ModelHandle<MultiBuffer>, cx: &mut ModelContext<Self>) -> u32 {
|
||||||
|
let language_name = buffer
|
||||||
|
.read(cx)
|
||||||
|
.as_singleton()
|
||||||
|
.and_then(|buffer| buffer.read(cx).language())
|
||||||
|
.map(|language| language.name());
|
||||||
|
|
||||||
|
cx.global::<Settings>().tab_size(language_name.as_deref())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn is_rewrapping(&self, cx: &gpui::AppContext) -> bool {
|
pub fn is_rewrapping(&self, cx: &gpui::AppContext) -> bool {
|
||||||
self.wrap_map.read(cx).is_rewrapping()
|
self.wrap_map.read(cx).is_rewrapping()
|
||||||
|
@ -536,6 +554,8 @@ pub mod tests {
|
||||||
log::info!("tab size: {}", tab_size);
|
log::info!("tab size: {}", tab_size);
|
||||||
log::info!("wrap width: {:?}", wrap_width);
|
log::info!("wrap width: {:?}", wrap_width);
|
||||||
|
|
||||||
|
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||||
|
|
||||||
let buffer = cx.update(|cx| {
|
let buffer = cx.update(|cx| {
|
||||||
if rng.gen() {
|
if rng.gen() {
|
||||||
let len = rng.gen_range(0..10);
|
let len = rng.gen_range(0..10);
|
||||||
|
@ -549,7 +569,6 @@ pub mod tests {
|
||||||
let map = cx.add_model(|cx| {
|
let map = cx.add_model(|cx| {
|
||||||
DisplayMap::new(
|
DisplayMap::new(
|
||||||
buffer.clone(),
|
buffer.clone(),
|
||||||
tab_size,
|
|
||||||
font_id,
|
font_id,
|
||||||
font_size,
|
font_size,
|
||||||
wrap_width,
|
wrap_width,
|
||||||
|
@ -759,27 +778,18 @@ pub mod tests {
|
||||||
|
|
||||||
let font_cache = cx.font_cache();
|
let font_cache = cx.font_cache();
|
||||||
|
|
||||||
let tab_size = 4;
|
|
||||||
let family_id = font_cache.load_family(&["Helvetica"]).unwrap();
|
let family_id = font_cache.load_family(&["Helvetica"]).unwrap();
|
||||||
let font_id = font_cache
|
let font_id = font_cache
|
||||||
.select_font(family_id, &Default::default())
|
.select_font(family_id, &Default::default())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let font_size = 12.0;
|
let font_size = 12.0;
|
||||||
let wrap_width = Some(64.);
|
let wrap_width = Some(64.);
|
||||||
|
cx.set_global(Settings::test(cx));
|
||||||
|
|
||||||
let text = "one two three four five\nsix seven eight";
|
let text = "one two three four five\nsix seven eight";
|
||||||
let buffer = MultiBuffer::build_simple(text, cx);
|
let buffer = MultiBuffer::build_simple(text, cx);
|
||||||
let map = cx.add_model(|cx| {
|
let map = cx.add_model(|cx| {
|
||||||
DisplayMap::new(
|
DisplayMap::new(buffer.clone(), font_id, font_size, wrap_width, 1, 1, cx)
|
||||||
buffer.clone(),
|
|
||||||
tab_size,
|
|
||||||
font_id,
|
|
||||||
font_size,
|
|
||||||
wrap_width,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
|
let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
|
||||||
|
@ -847,18 +857,17 @@ pub mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_text_chunks(cx: &mut gpui::MutableAppContext) {
|
fn test_text_chunks(cx: &mut gpui::MutableAppContext) {
|
||||||
|
cx.set_global(Settings::test(cx));
|
||||||
let text = sample_text(6, 6, 'a');
|
let text = sample_text(6, 6, 'a');
|
||||||
let buffer = MultiBuffer::build_simple(&text, cx);
|
let buffer = MultiBuffer::build_simple(&text, cx);
|
||||||
let tab_size = 4;
|
|
||||||
let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
|
let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
|
||||||
let font_id = cx
|
let font_id = cx
|
||||||
.font_cache()
|
.font_cache()
|
||||||
.select_font(family_id, &Default::default())
|
.select_font(family_id, &Default::default())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let font_size = 14.0;
|
let font_size = 14.0;
|
||||||
let map = cx.add_model(|cx| {
|
let map =
|
||||||
DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, 1, 1, cx)
|
cx.add_model(|cx| DisplayMap::new(buffer.clone(), font_id, font_size, None, 1, 1, cx));
|
||||||
});
|
|
||||||
buffer.update(cx, |buffer, cx| {
|
buffer.update(cx, |buffer, cx| {
|
||||||
buffer.edit(
|
buffer.edit(
|
||||||
vec![
|
vec![
|
||||||
|
@ -923,12 +932,17 @@ pub mod tests {
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
language.set_theme(&theme);
|
language.set_theme(&theme);
|
||||||
|
cx.update(|cx| {
|
||||||
|
cx.set_global(Settings {
|
||||||
|
tab_size: 2,
|
||||||
|
..Settings::test(cx)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
|
let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
|
||||||
buffer.condition(&cx, |buf, _| !buf.is_parsing()).await;
|
buffer.condition(&cx, |buf, _| !buf.is_parsing()).await;
|
||||||
let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
|
let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
|
||||||
|
|
||||||
let tab_size = 2;
|
|
||||||
let font_cache = cx.font_cache();
|
let font_cache = cx.font_cache();
|
||||||
let family_id = font_cache.load_family(&["Helvetica"]).unwrap();
|
let family_id = font_cache.load_family(&["Helvetica"]).unwrap();
|
||||||
let font_id = font_cache
|
let font_id = font_cache
|
||||||
|
@ -936,8 +950,7 @@ pub mod tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let font_size = 14.0;
|
let font_size = 14.0;
|
||||||
|
|
||||||
let map = cx
|
let map = cx.add_model(|cx| DisplayMap::new(buffer, font_id, font_size, None, 1, 1, cx));
|
||||||
.add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, None, 1, 1, cx));
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cx.update(|cx| syntax_chunks(0..5, &map, &theme, cx)),
|
cx.update(|cx| syntax_chunks(0..5, &map, &theme, cx)),
|
||||||
vec![
|
vec![
|
||||||
|
@ -1011,22 +1024,22 @@ pub mod tests {
|
||||||
);
|
);
|
||||||
language.set_theme(&theme);
|
language.set_theme(&theme);
|
||||||
|
|
||||||
|
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||||
|
|
||||||
let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
|
let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
|
||||||
buffer.condition(&cx, |buf, _| !buf.is_parsing()).await;
|
buffer.condition(&cx, |buf, _| !buf.is_parsing()).await;
|
||||||
let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
|
let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
|
||||||
|
|
||||||
let font_cache = cx.font_cache();
|
let font_cache = cx.font_cache();
|
||||||
|
|
||||||
let tab_size = 4;
|
|
||||||
let family_id = font_cache.load_family(&["Courier"]).unwrap();
|
let family_id = font_cache.load_family(&["Courier"]).unwrap();
|
||||||
let font_id = font_cache
|
let font_id = font_cache
|
||||||
.select_font(family_id, &Default::default())
|
.select_font(family_id, &Default::default())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let font_size = 16.0;
|
let font_size = 16.0;
|
||||||
|
|
||||||
let map = cx.add_model(|cx| {
|
let map =
|
||||||
DisplayMap::new(buffer, tab_size, font_id, font_size, Some(40.0), 1, 1, cx)
|
cx.add_model(|cx| DisplayMap::new(buffer, font_id, font_size, Some(40.0), 1, 1, cx));
|
||||||
});
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cx.update(|cx| syntax_chunks(0..5, &map, &theme, cx)),
|
cx.update(|cx| syntax_chunks(0..5, &map, &theme, cx)),
|
||||||
[
|
[
|
||||||
|
@ -1058,6 +1071,7 @@ pub mod tests {
|
||||||
async fn test_chunks_with_text_highlights(cx: &mut gpui::TestAppContext) {
|
async fn test_chunks_with_text_highlights(cx: &mut gpui::TestAppContext) {
|
||||||
cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX);
|
cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX);
|
||||||
|
|
||||||
|
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||||
let theme = SyntaxTheme::new(vec![
|
let theme = SyntaxTheme::new(vec![
|
||||||
("operator".to_string(), Color::red().into()),
|
("operator".to_string(), Color::red().into()),
|
||||||
("string".to_string(), Color::green().into()),
|
("string".to_string(), Color::green().into()),
|
||||||
|
@ -1090,14 +1104,12 @@ pub mod tests {
|
||||||
let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
|
let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
|
||||||
|
|
||||||
let font_cache = cx.font_cache();
|
let font_cache = cx.font_cache();
|
||||||
let tab_size = 4;
|
|
||||||
let family_id = font_cache.load_family(&["Courier"]).unwrap();
|
let family_id = font_cache.load_family(&["Courier"]).unwrap();
|
||||||
let font_id = font_cache
|
let font_id = font_cache
|
||||||
.select_font(family_id, &Default::default())
|
.select_font(family_id, &Default::default())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let font_size = 16.0;
|
let font_size = 16.0;
|
||||||
let map = cx
|
let map = cx.add_model(|cx| DisplayMap::new(buffer, font_id, font_size, None, 1, 1, cx));
|
||||||
.add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, None, 1, 1, cx));
|
|
||||||
|
|
||||||
enum MyType {}
|
enum MyType {}
|
||||||
|
|
||||||
|
@ -1136,6 +1148,7 @@ pub mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_clip_point(cx: &mut gpui::MutableAppContext) {
|
fn test_clip_point(cx: &mut gpui::MutableAppContext) {
|
||||||
|
cx.set_global(Settings::test(cx));
|
||||||
fn assert(text: &str, shift_right: bool, bias: Bias, cx: &mut gpui::MutableAppContext) {
|
fn assert(text: &str, shift_right: bool, bias: Bias, cx: &mut gpui::MutableAppContext) {
|
||||||
let (unmarked_snapshot, mut markers) = marked_display_snapshot(text, cx);
|
let (unmarked_snapshot, mut markers) = marked_display_snapshot(text, cx);
|
||||||
|
|
||||||
|
@ -1187,6 +1200,8 @@ pub mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_clip_at_line_ends(cx: &mut gpui::MutableAppContext) {
|
fn test_clip_at_line_ends(cx: &mut gpui::MutableAppContext) {
|
||||||
|
cx.set_global(Settings::test(cx));
|
||||||
|
|
||||||
fn assert(text: &str, cx: &mut gpui::MutableAppContext) {
|
fn assert(text: &str, cx: &mut gpui::MutableAppContext) {
|
||||||
let (mut unmarked_snapshot, markers) = marked_display_snapshot(text, cx);
|
let (mut unmarked_snapshot, markers) = marked_display_snapshot(text, cx);
|
||||||
unmarked_snapshot.clip_at_line_ends = true;
|
unmarked_snapshot.clip_at_line_ends = true;
|
||||||
|
@ -1204,9 +1219,9 @@ pub mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_tabs_with_multibyte_chars(cx: &mut gpui::MutableAppContext) {
|
fn test_tabs_with_multibyte_chars(cx: &mut gpui::MutableAppContext) {
|
||||||
|
cx.set_global(Settings::test(cx));
|
||||||
let text = "✅\t\tα\nβ\t\n🏀β\t\tγ";
|
let text = "✅\t\tα\nβ\t\n🏀β\t\tγ";
|
||||||
let buffer = MultiBuffer::build_simple(text, cx);
|
let buffer = MultiBuffer::build_simple(text, cx);
|
||||||
let tab_size = 4;
|
|
||||||
let font_cache = cx.font_cache();
|
let font_cache = cx.font_cache();
|
||||||
let family_id = font_cache.load_family(&["Helvetica"]).unwrap();
|
let family_id = font_cache.load_family(&["Helvetica"]).unwrap();
|
||||||
let font_id = font_cache
|
let font_id = font_cache
|
||||||
|
@ -1214,9 +1229,8 @@ pub mod tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let font_size = 14.0;
|
let font_size = 14.0;
|
||||||
|
|
||||||
let map = cx.add_model(|cx| {
|
let map =
|
||||||
DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, 1, 1, cx)
|
cx.add_model(|cx| DisplayMap::new(buffer.clone(), font_id, font_size, None, 1, 1, cx));
|
||||||
});
|
|
||||||
let map = map.update(cx, |map, cx| map.snapshot(cx));
|
let map = map.update(cx, |map, cx| map.snapshot(cx));
|
||||||
assert_eq!(map.text(), "✅ α\nβ \n🏀β γ");
|
assert_eq!(map.text(), "✅ α\nβ \n🏀β γ");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -1264,17 +1278,16 @@ pub mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_max_point(cx: &mut gpui::MutableAppContext) {
|
fn test_max_point(cx: &mut gpui::MutableAppContext) {
|
||||||
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple("aaa\n\t\tbbb", cx);
|
let buffer = MultiBuffer::build_simple("aaa\n\t\tbbb", cx);
|
||||||
let tab_size = 4;
|
|
||||||
let font_cache = cx.font_cache();
|
let font_cache = cx.font_cache();
|
||||||
let family_id = font_cache.load_family(&["Helvetica"]).unwrap();
|
let family_id = font_cache.load_family(&["Helvetica"]).unwrap();
|
||||||
let font_id = font_cache
|
let font_id = font_cache
|
||||||
.select_font(family_id, &Default::default())
|
.select_font(family_id, &Default::default())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let font_size = 14.0;
|
let font_size = 14.0;
|
||||||
let map = cx.add_model(|cx| {
|
let map =
|
||||||
DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, 1, 1, cx)
|
cx.add_model(|cx| DisplayMap::new(buffer.clone(), font_id, font_size, None, 1, 1, cx));
|
||||||
});
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
map.update(cx, |map, cx| map.snapshot(cx)).max_point(),
|
map.update(cx, |map, cx| map.snapshot(cx)).max_point(),
|
||||||
DisplayPoint::new(1, 11)
|
DisplayPoint::new(1, 11)
|
||||||
|
|
|
@ -969,6 +969,7 @@ mod tests {
|
||||||
use crate::multi_buffer::MultiBuffer;
|
use crate::multi_buffer::MultiBuffer;
|
||||||
use gpui::{elements::Empty, Element};
|
use gpui::{elements::Empty, Element};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
use settings::Settings;
|
||||||
use std::env;
|
use std::env;
|
||||||
use text::RandomCharIter;
|
use text::RandomCharIter;
|
||||||
|
|
||||||
|
@ -988,6 +989,8 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_basic_blocks(cx: &mut gpui::MutableAppContext) {
|
fn test_basic_blocks(cx: &mut gpui::MutableAppContext) {
|
||||||
|
cx.set_global(Settings::test(cx));
|
||||||
|
|
||||||
let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
|
let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
|
||||||
let font_id = cx
|
let font_id = cx
|
||||||
.font_cache()
|
.font_cache()
|
||||||
|
@ -1157,7 +1160,7 @@ mod tests {
|
||||||
|
|
||||||
let (folds_snapshot, fold_edits) =
|
let (folds_snapshot, fold_edits) =
|
||||||
fold_map.read(buffer_snapshot, subscription.consume().into_inner());
|
fold_map.read(buffer_snapshot, subscription.consume().into_inner());
|
||||||
let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
|
let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits, 4);
|
||||||
let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
|
let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
|
||||||
wrap_map.sync(tabs_snapshot, tab_edits, cx)
|
wrap_map.sync(tabs_snapshot, tab_edits, cx)
|
||||||
});
|
});
|
||||||
|
@ -1167,6 +1170,8 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_blocks_on_wrapped_lines(cx: &mut gpui::MutableAppContext) {
|
fn test_blocks_on_wrapped_lines(cx: &mut gpui::MutableAppContext) {
|
||||||
|
cx.set_global(Settings::test(cx));
|
||||||
|
|
||||||
let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
|
let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
|
||||||
let font_id = cx
|
let font_id = cx
|
||||||
.font_cache()
|
.font_cache()
|
||||||
|
@ -1209,6 +1214,8 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test(iterations = 100)]
|
#[gpui::test(iterations = 100)]
|
||||||
fn test_random_blocks(cx: &mut gpui::MutableAppContext, mut rng: StdRng) {
|
fn test_random_blocks(cx: &mut gpui::MutableAppContext, mut rng: StdRng) {
|
||||||
|
cx.set_global(Settings::test(cx));
|
||||||
|
|
||||||
let operations = env::var("OPERATIONS")
|
let operations = env::var("OPERATIONS")
|
||||||
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
|
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
|
||||||
.unwrap_or(10);
|
.unwrap_or(10);
|
||||||
|
@ -1296,7 +1303,8 @@ mod tests {
|
||||||
|
|
||||||
let (folds_snapshot, fold_edits) =
|
let (folds_snapshot, fold_edits) =
|
||||||
fold_map.read(buffer_snapshot.clone(), vec![]);
|
fold_map.read(buffer_snapshot.clone(), vec![]);
|
||||||
let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
|
let (tabs_snapshot, tab_edits) =
|
||||||
|
tab_map.sync(folds_snapshot, fold_edits, tab_size);
|
||||||
let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
|
let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
|
||||||
wrap_map.sync(tabs_snapshot, tab_edits, cx)
|
wrap_map.sync(tabs_snapshot, tab_edits, cx)
|
||||||
});
|
});
|
||||||
|
@ -1318,7 +1326,8 @@ mod tests {
|
||||||
|
|
||||||
let (folds_snapshot, fold_edits) =
|
let (folds_snapshot, fold_edits) =
|
||||||
fold_map.read(buffer_snapshot.clone(), vec![]);
|
fold_map.read(buffer_snapshot.clone(), vec![]);
|
||||||
let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
|
let (tabs_snapshot, tab_edits) =
|
||||||
|
tab_map.sync(folds_snapshot, fold_edits, tab_size);
|
||||||
let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
|
let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
|
||||||
wrap_map.sync(tabs_snapshot, tab_edits, cx)
|
wrap_map.sync(tabs_snapshot, tab_edits, cx)
|
||||||
});
|
});
|
||||||
|
@ -1338,7 +1347,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
let (folds_snapshot, fold_edits) = fold_map.read(buffer_snapshot.clone(), buffer_edits);
|
let (folds_snapshot, fold_edits) = fold_map.read(buffer_snapshot.clone(), buffer_edits);
|
||||||
let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
|
let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits, tab_size);
|
||||||
let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
|
let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
|
||||||
wrap_map.sync(tabs_snapshot, tab_edits, cx)
|
wrap_map.sync(tabs_snapshot, tab_edits, cx)
|
||||||
});
|
});
|
||||||
|
|
|
@ -1210,6 +1210,7 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{MultiBuffer, ToPoint};
|
use crate::{MultiBuffer, ToPoint};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
use settings::Settings;
|
||||||
use std::{cmp::Reverse, env, mem, sync::Arc};
|
use std::{cmp::Reverse, env, mem, sync::Arc};
|
||||||
use sum_tree::TreeMap;
|
use sum_tree::TreeMap;
|
||||||
use text::RandomCharIter;
|
use text::RandomCharIter;
|
||||||
|
@ -1218,6 +1219,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_basic_folds(cx: &mut gpui::MutableAppContext) {
|
fn test_basic_folds(cx: &mut gpui::MutableAppContext) {
|
||||||
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx);
|
let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx);
|
||||||
let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
|
let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
|
||||||
let buffer_snapshot = buffer.read(cx).snapshot(cx);
|
let buffer_snapshot = buffer.read(cx).snapshot(cx);
|
||||||
|
@ -1291,6 +1293,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_adjacent_folds(cx: &mut gpui::MutableAppContext) {
|
fn test_adjacent_folds(cx: &mut gpui::MutableAppContext) {
|
||||||
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple("abcdefghijkl", cx);
|
let buffer = MultiBuffer::build_simple("abcdefghijkl", cx);
|
||||||
let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
|
let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
|
||||||
let buffer_snapshot = buffer.read(cx).snapshot(cx);
|
let buffer_snapshot = buffer.read(cx).snapshot(cx);
|
||||||
|
@ -1354,6 +1357,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_merging_folds_via_edit(cx: &mut gpui::MutableAppContext) {
|
fn test_merging_folds_via_edit(cx: &mut gpui::MutableAppContext) {
|
||||||
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx);
|
let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx);
|
||||||
let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
|
let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
|
||||||
let buffer_snapshot = buffer.read(cx).snapshot(cx);
|
let buffer_snapshot = buffer.read(cx).snapshot(cx);
|
||||||
|
@ -1404,6 +1408,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test(iterations = 100)]
|
#[gpui::test(iterations = 100)]
|
||||||
fn test_random_folds(cx: &mut gpui::MutableAppContext, mut rng: StdRng) {
|
fn test_random_folds(cx: &mut gpui::MutableAppContext, mut rng: StdRng) {
|
||||||
|
cx.set_global(Settings::test(cx));
|
||||||
let operations = env::var("OPERATIONS")
|
let operations = env::var("OPERATIONS")
|
||||||
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
|
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
|
||||||
.unwrap_or(10);
|
.unwrap_or(10);
|
||||||
|
|
|
@ -12,7 +12,7 @@ use text::Point;
|
||||||
pub struct TabMap(Mutex<TabSnapshot>);
|
pub struct TabMap(Mutex<TabSnapshot>);
|
||||||
|
|
||||||
impl TabMap {
|
impl TabMap {
|
||||||
pub fn new(input: FoldSnapshot, tab_size: usize) -> (Self, TabSnapshot) {
|
pub fn new(input: FoldSnapshot, tab_size: u32) -> (Self, TabSnapshot) {
|
||||||
let snapshot = TabSnapshot {
|
let snapshot = TabSnapshot {
|
||||||
fold_snapshot: input,
|
fold_snapshot: input,
|
||||||
tab_size,
|
tab_size,
|
||||||
|
@ -24,12 +24,13 @@ impl TabMap {
|
||||||
&self,
|
&self,
|
||||||
fold_snapshot: FoldSnapshot,
|
fold_snapshot: FoldSnapshot,
|
||||||
mut fold_edits: Vec<FoldEdit>,
|
mut fold_edits: Vec<FoldEdit>,
|
||||||
|
tab_size: u32,
|
||||||
) -> (TabSnapshot, Vec<TabEdit>) {
|
) -> (TabSnapshot, Vec<TabEdit>) {
|
||||||
let mut old_snapshot = self.0.lock();
|
let mut old_snapshot = self.0.lock();
|
||||||
let max_offset = old_snapshot.fold_snapshot.len();
|
let max_offset = old_snapshot.fold_snapshot.len();
|
||||||
let new_snapshot = TabSnapshot {
|
let new_snapshot = TabSnapshot {
|
||||||
fold_snapshot,
|
fold_snapshot,
|
||||||
tab_size: old_snapshot.tab_size,
|
tab_size,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut tab_edits = Vec::with_capacity(fold_edits.len());
|
let mut tab_edits = Vec::with_capacity(fold_edits.len());
|
||||||
|
@ -87,7 +88,7 @@ impl TabMap {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct TabSnapshot {
|
pub struct TabSnapshot {
|
||||||
pub fold_snapshot: FoldSnapshot,
|
pub fold_snapshot: FoldSnapshot,
|
||||||
pub tab_size: usize,
|
pub tab_size: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TabSnapshot {
|
impl TabSnapshot {
|
||||||
|
@ -234,7 +235,7 @@ impl TabSnapshot {
|
||||||
.to_buffer_point(&self.fold_snapshot)
|
.to_buffer_point(&self.fold_snapshot)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expand_tabs(chars: impl Iterator<Item = char>, column: usize, tab_size: usize) -> usize {
|
fn expand_tabs(chars: impl Iterator<Item = char>, column: usize, tab_size: u32) -> usize {
|
||||||
let mut expanded_chars = 0;
|
let mut expanded_chars = 0;
|
||||||
let mut expanded_bytes = 0;
|
let mut expanded_bytes = 0;
|
||||||
let mut collapsed_bytes = 0;
|
let mut collapsed_bytes = 0;
|
||||||
|
@ -243,7 +244,7 @@ impl TabSnapshot {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if c == '\t' {
|
if c == '\t' {
|
||||||
let tab_len = tab_size - expanded_chars % tab_size;
|
let tab_len = tab_size as usize - expanded_chars % tab_size as usize;
|
||||||
expanded_bytes += tab_len;
|
expanded_bytes += tab_len;
|
||||||
expanded_chars += tab_len;
|
expanded_chars += tab_len;
|
||||||
} else {
|
} else {
|
||||||
|
@ -259,7 +260,7 @@ impl TabSnapshot {
|
||||||
mut chars: impl Iterator<Item = char>,
|
mut chars: impl Iterator<Item = char>,
|
||||||
column: usize,
|
column: usize,
|
||||||
bias: Bias,
|
bias: Bias,
|
||||||
tab_size: usize,
|
tab_size: u32,
|
||||||
) -> (usize, usize, usize) {
|
) -> (usize, usize, usize) {
|
||||||
let mut expanded_bytes = 0;
|
let mut expanded_bytes = 0;
|
||||||
let mut expanded_chars = 0;
|
let mut expanded_chars = 0;
|
||||||
|
@ -270,7 +271,7 @@ impl TabSnapshot {
|
||||||
}
|
}
|
||||||
|
|
||||||
if c == '\t' {
|
if c == '\t' {
|
||||||
let tab_len = tab_size - (expanded_chars % tab_size);
|
let tab_len = tab_size as usize - (expanded_chars % tab_size as usize);
|
||||||
expanded_chars += tab_len;
|
expanded_chars += tab_len;
|
||||||
expanded_bytes += tab_len;
|
expanded_bytes += tab_len;
|
||||||
if expanded_bytes > column {
|
if expanded_bytes > column {
|
||||||
|
@ -383,7 +384,7 @@ pub struct TabChunks<'a> {
|
||||||
column: usize,
|
column: usize,
|
||||||
output_position: Point,
|
output_position: Point,
|
||||||
max_output_position: Point,
|
max_output_position: Point,
|
||||||
tab_size: usize,
|
tab_size: u32,
|
||||||
skip_leading_tab: bool,
|
skip_leading_tab: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -415,16 +416,16 @@ impl<'a> Iterator for TabChunks<'a> {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
self.chunk.text = &self.chunk.text[1..];
|
self.chunk.text = &self.chunk.text[1..];
|
||||||
let mut len = self.tab_size - self.column % self.tab_size;
|
let mut len = self.tab_size - self.column as u32 % self.tab_size;
|
||||||
let next_output_position = cmp::min(
|
let next_output_position = cmp::min(
|
||||||
self.output_position + Point::new(0, len as u32),
|
self.output_position + Point::new(0, len),
|
||||||
self.max_output_position,
|
self.max_output_position,
|
||||||
);
|
);
|
||||||
len = (next_output_position.column - self.output_position.column) as usize;
|
len = next_output_position.column - self.output_position.column;
|
||||||
self.column += len;
|
self.column += len as usize;
|
||||||
self.output_position = next_output_position;
|
self.output_position = next_output_position;
|
||||||
return Some(Chunk {
|
return Some(Chunk {
|
||||||
text: &SPACES[0..len],
|
text: &SPACES[0..len as usize],
|
||||||
..self.chunk
|
..self.chunk
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1014,12 +1014,14 @@ mod tests {
|
||||||
use gpui::test::observe;
|
use gpui::test::observe;
|
||||||
use language::RandomCharIter;
|
use language::RandomCharIter;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
use settings::Settings;
|
||||||
use smol::stream::StreamExt;
|
use smol::stream::StreamExt;
|
||||||
use std::{cmp, env};
|
use std::{cmp, env};
|
||||||
use text::Rope;
|
use text::Rope;
|
||||||
|
|
||||||
#[gpui::test(iterations = 100)]
|
#[gpui::test(iterations = 100)]
|
||||||
async fn test_random_wraps(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
|
async fn test_random_wraps(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
|
||||||
|
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||||
cx.foreground().set_block_on_ticks(0..=50);
|
cx.foreground().set_block_on_ticks(0..=50);
|
||||||
cx.foreground().forbid_parking();
|
cx.foreground().forbid_parking();
|
||||||
let operations = env::var("OPERATIONS")
|
let operations = env::var("OPERATIONS")
|
||||||
|
@ -1104,7 +1106,8 @@ mod tests {
|
||||||
}
|
}
|
||||||
20..=39 => {
|
20..=39 => {
|
||||||
for (folds_snapshot, fold_edits) in fold_map.randomly_mutate(&mut rng) {
|
for (folds_snapshot, fold_edits) in fold_map.randomly_mutate(&mut rng) {
|
||||||
let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
|
let (tabs_snapshot, tab_edits) =
|
||||||
|
tab_map.sync(folds_snapshot, fold_edits, tab_size);
|
||||||
let (mut snapshot, wrap_edits) =
|
let (mut snapshot, wrap_edits) =
|
||||||
wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot, tab_edits, cx));
|
wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot, tab_edits, cx));
|
||||||
snapshot.check_invariants();
|
snapshot.check_invariants();
|
||||||
|
@ -1129,7 +1132,7 @@ mod tests {
|
||||||
"Unwrapped text (unexpanded tabs): {:?}",
|
"Unwrapped text (unexpanded tabs): {:?}",
|
||||||
folds_snapshot.text()
|
folds_snapshot.text()
|
||||||
);
|
);
|
||||||
let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
|
let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits, tab_size);
|
||||||
log::info!("Unwrapped text (expanded tabs): {:?}", tabs_snapshot.text());
|
log::info!("Unwrapped text (expanded tabs): {:?}", tabs_snapshot.text());
|
||||||
|
|
||||||
let unwrapped_text = tabs_snapshot.text();
|
let unwrapped_text = tabs_snapshot.text();
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -1008,7 +1009,6 @@ impl Editor {
|
||||||
let style = build_style(&*settings, get_field_editor_theme, None, cx);
|
let style = build_style(&*settings, get_field_editor_theme, None, cx);
|
||||||
DisplayMap::new(
|
DisplayMap::new(
|
||||||
buffer.clone(),
|
buffer.clone(),
|
||||||
settings.tab_size,
|
|
||||||
style.text.font_id,
|
style.text.font_id,
|
||||||
style.text.font_size,
|
style.text.font_size,
|
||||||
None,
|
None,
|
||||||
|
@ -1130,8 +1130,12 @@ impl Editor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn language<'a>(&self, cx: &'a AppContext) -> Option<&'a Arc<Language>> {
|
pub fn language_at<'a, T: ToOffset>(
|
||||||
self.buffer.read(cx).language(cx)
|
&self,
|
||||||
|
point: T,
|
||||||
|
cx: &'a AppContext,
|
||||||
|
) -> Option<&'a Arc<Language>> {
|
||||||
|
self.buffer.read(cx).language_at(point, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn style(&self, cx: &AppContext) -> EditorStyle {
|
fn style(&self, cx: &AppContext) -> EditorStyle {
|
||||||
|
@ -2945,8 +2949,9 @@ impl Editor {
|
||||||
.buffer_line_for_row(old_head.row)
|
.buffer_line_for_row(old_head.row)
|
||||||
{
|
{
|
||||||
let indent_column = buffer.indent_column_for_line(line_buffer_range.start.row);
|
let indent_column = buffer.indent_column_for_line(line_buffer_range.start.row);
|
||||||
|
let language_name = buffer.language().map(|language| language.name());
|
||||||
|
let indent = cx.global::<Settings>().tab_size(language_name.as_deref());
|
||||||
if old_head.column <= indent_column && old_head.column > 0 {
|
if old_head.column <= indent_column && old_head.column > 0 {
|
||||||
let indent = buffer.indent_size();
|
|
||||||
new_head = cmp::min(
|
new_head = cmp::min(
|
||||||
new_head,
|
new_head,
|
||||||
Point::new(old_head.row, ((old_head.column - 1) / indent) * indent),
|
Point::new(old_head.row, ((old_head.column - 1) / indent) * indent),
|
||||||
|
@ -2991,12 +2996,15 @@ impl Editor {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let tab_size = cx.global::<Settings>().tab_size;
|
|
||||||
let mut selections = self.local_selections::<Point>(cx);
|
let mut selections = self.local_selections::<Point>(cx);
|
||||||
if selections.iter().all(|s| s.is_empty()) {
|
if selections.iter().all(|s| s.is_empty()) {
|
||||||
self.transact(cx, |this, cx| {
|
self.transact(cx, |this, cx| {
|
||||||
this.buffer.update(cx, |buffer, cx| {
|
this.buffer.update(cx, |buffer, cx| {
|
||||||
for selection in &mut selections {
|
for selection in &mut selections {
|
||||||
|
let language_name =
|
||||||
|
buffer.language_at(selection.start, cx).map(|l| l.name());
|
||||||
|
let tab_size =
|
||||||
|
cx.global::<Settings>().tab_size(language_name.as_deref());
|
||||||
let char_column = buffer
|
let char_column = buffer
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.text_for_range(
|
.text_for_range(
|
||||||
|
@ -3004,13 +3012,14 @@ impl Editor {
|
||||||
)
|
)
|
||||||
.flat_map(str::chars)
|
.flat_map(str::chars)
|
||||||
.count();
|
.count();
|
||||||
let chars_to_next_tab_stop = tab_size - (char_column % tab_size);
|
let chars_to_next_tab_stop =
|
||||||
|
tab_size - (char_column as u32 % tab_size);
|
||||||
buffer.edit(
|
buffer.edit(
|
||||||
[selection.start..selection.start],
|
[selection.start..selection.start],
|
||||||
" ".repeat(chars_to_next_tab_stop),
|
" ".repeat(chars_to_next_tab_stop as usize),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
selection.start.column += chars_to_next_tab_stop as u32;
|
selection.start.column += chars_to_next_tab_stop;
|
||||||
selection.end = selection.start;
|
selection.end = selection.start;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -3024,12 +3033,14 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
|
pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
|
||||||
let tab_size = cx.global::<Settings>().tab_size;
|
|
||||||
let mut selections = self.local_selections::<Point>(cx);
|
let mut selections = self.local_selections::<Point>(cx);
|
||||||
self.transact(cx, |this, cx| {
|
self.transact(cx, |this, cx| {
|
||||||
let mut last_indent = None;
|
let mut last_indent = None;
|
||||||
this.buffer.update(cx, |buffer, cx| {
|
this.buffer.update(cx, |buffer, cx| {
|
||||||
|
let snapshot = buffer.snapshot(cx);
|
||||||
for selection in &mut selections {
|
for selection in &mut selections {
|
||||||
|
let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
|
||||||
|
let tab_size = cx.global::<Settings>().tab_size(language_name.as_deref());
|
||||||
let mut start_row = selection.start.row;
|
let mut start_row = selection.start.row;
|
||||||
let mut end_row = selection.end.row + 1;
|
let mut end_row = selection.end.row + 1;
|
||||||
|
|
||||||
|
@ -3053,12 +3064,12 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
for row in start_row..end_row {
|
for row in start_row..end_row {
|
||||||
let indent_column = buffer.read(cx).indent_column_for_line(row) as usize;
|
let indent_column = snapshot.indent_column_for_line(row);
|
||||||
let columns_to_next_tab_stop = tab_size - (indent_column % tab_size);
|
let columns_to_next_tab_stop = tab_size - (indent_column % tab_size);
|
||||||
let row_start = Point::new(row, 0);
|
let row_start = Point::new(row, 0);
|
||||||
buffer.edit(
|
buffer.edit(
|
||||||
[row_start..row_start],
|
[row_start..row_start],
|
||||||
" ".repeat(columns_to_next_tab_stop),
|
" ".repeat(columns_to_next_tab_stop as usize),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -3080,14 +3091,16 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
|
pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
|
||||||
let tab_size = cx.global::<Settings>().tab_size;
|
|
||||||
let selections = self.local_selections::<Point>(cx);
|
let selections = self.local_selections::<Point>(cx);
|
||||||
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||||
let mut deletion_ranges = Vec::new();
|
let mut deletion_ranges = Vec::new();
|
||||||
let mut last_outdent = None;
|
let mut last_outdent = None;
|
||||||
{
|
{
|
||||||
let buffer = self.buffer.read(cx).read(cx);
|
let buffer = self.buffer.read(cx);
|
||||||
|
let snapshot = buffer.snapshot(cx);
|
||||||
for selection in &selections {
|
for selection in &selections {
|
||||||
|
let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
|
||||||
|
let tab_size = cx.global::<Settings>().tab_size(language_name.as_deref());
|
||||||
let mut rows = selection.spanned_rows(false, &display_map);
|
let mut rows = selection.spanned_rows(false, &display_map);
|
||||||
|
|
||||||
// Avoid re-outdenting a row that has already been outdented by a
|
// Avoid re-outdenting a row that has already been outdented by a
|
||||||
|
@ -3099,11 +3112,11 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
for row in rows {
|
for row in rows {
|
||||||
let column = buffer.indent_column_for_line(row) as usize;
|
let column = snapshot.indent_column_for_line(row);
|
||||||
if column > 0 {
|
if column > 0 {
|
||||||
let mut deletion_len = (column % tab_size) as u32;
|
let mut deletion_len = column % tab_size;
|
||||||
if deletion_len == 0 {
|
if deletion_len == 0 {
|
||||||
deletion_len = tab_size as u32;
|
deletion_len = tab_size;
|
||||||
}
|
}
|
||||||
deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
|
deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
|
||||||
last_outdent = Some(row);
|
last_outdent = Some(row);
|
||||||
|
@ -4243,24 +4256,26 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn toggle_comments(&mut self, _: &ToggleComments, cx: &mut ViewContext<Self>) {
|
pub fn toggle_comments(&mut self, _: &ToggleComments, cx: &mut ViewContext<Self>) {
|
||||||
// Get the line comment prefix. Split its trailing whitespace into a separate string,
|
|
||||||
// as that portion won't be used for detecting if a line is a comment.
|
|
||||||
let full_comment_prefix =
|
|
||||||
if let Some(prefix) = self.language(cx).and_then(|l| l.line_comment_prefix()) {
|
|
||||||
prefix.to_string()
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let comment_prefix = full_comment_prefix.trim_end_matches(' ');
|
|
||||||
let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
|
|
||||||
|
|
||||||
self.transact(cx, |this, cx| {
|
self.transact(cx, |this, cx| {
|
||||||
let mut selections = this.local_selections::<Point>(cx);
|
let mut selections = this.local_selections::<Point>(cx);
|
||||||
let mut all_selection_lines_are_comments = true;
|
let mut all_selection_lines_are_comments = true;
|
||||||
let mut edit_ranges = Vec::new();
|
let mut edit_ranges = Vec::new();
|
||||||
let mut last_toggled_row = None;
|
let mut last_toggled_row = None;
|
||||||
this.buffer.update(cx, |buffer, cx| {
|
this.buffer.update(cx, |buffer, cx| {
|
||||||
|
// TODO: Handle selections that cross excerpts
|
||||||
for selection in &mut selections {
|
for selection in &mut selections {
|
||||||
|
// Get the line comment prefix. Split its trailing whitespace into a separate string,
|
||||||
|
// as that portion won't be used for detecting if a line is a comment.
|
||||||
|
let full_comment_prefix = if let Some(prefix) = buffer
|
||||||
|
.language_at(selection.start, cx)
|
||||||
|
.and_then(|l| l.line_comment_prefix())
|
||||||
|
{
|
||||||
|
prefix.to_string()
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let comment_prefix = full_comment_prefix.trim_end_matches(' ');
|
||||||
|
let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
|
||||||
edit_ranges.clear();
|
edit_ranges.clear();
|
||||||
let snapshot = buffer.snapshot(cx);
|
let snapshot = buffer.snapshot(cx);
|
||||||
|
|
||||||
|
@ -5668,16 +5683,22 @@ 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_name = self
|
||||||
|
.buffer
|
||||||
|
.read(cx)
|
||||||
|
.as_singleton()
|
||||||
|
.and_then(|singleton_buffer| singleton_buffer.read(cx).language())
|
||||||
|
.map(|l| l.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_name.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_name.as_deref()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6461,14 +6482,18 @@ pub fn styled_runs_for_code_label<'a>(
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::test::{assert_text_with_selections, select_ranges};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
geometry::rect::RectF,
|
geometry::rect::RectF,
|
||||||
platform::{WindowBounds, WindowOptions},
|
platform::{WindowBounds, WindowOptions},
|
||||||
};
|
};
|
||||||
|
use indoc::indoc;
|
||||||
use language::{FakeLspAdapter, LanguageConfig};
|
use language::{FakeLspAdapter, LanguageConfig};
|
||||||
use lsp::FakeLanguageServer;
|
use lsp::FakeLanguageServer;
|
||||||
use project::FakeFs;
|
use project::FakeFs;
|
||||||
|
use settings::LanguageOverride;
|
||||||
use smol::stream::StreamExt;
|
use smol::stream::StreamExt;
|
||||||
use std::{cell::RefCell, rc::Rc, time::Instant};
|
use std::{cell::RefCell, rc::Rc, time::Instant};
|
||||||
use text::Point;
|
use text::Point;
|
||||||
|
@ -6478,7 +6503,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_edit_events(cx: &mut MutableAppContext) {
|
fn test_edit_events(cx: &mut MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
|
let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
|
||||||
|
|
||||||
let events = Rc::new(RefCell::new(Vec::new()));
|
let events = Rc::new(RefCell::new(Vec::new()));
|
||||||
|
@ -6586,7 +6611,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) {
|
fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let mut now = Instant::now();
|
let mut now = Instant::now();
|
||||||
let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
|
let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
|
||||||
let group_interval = buffer.read(cx).transaction_group_interval();
|
let group_interval = buffer.read(cx).transaction_group_interval();
|
||||||
|
@ -6655,7 +6680,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) {
|
fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
|
|
||||||
let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
|
let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
|
||||||
let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
||||||
|
@ -6720,7 +6745,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) {
|
fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
|
let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
||||||
|
|
||||||
|
@ -6752,7 +6777,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
|
fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
use workspace::Item;
|
use workspace::Item;
|
||||||
let nav_history = Rc::new(RefCell::new(workspace::NavHistory::default()));
|
let nav_history = Rc::new(RefCell::new(workspace::NavHistory::default()));
|
||||||
let buffer = MultiBuffer::build_simple(&sample_text(30, 5, 'a'), cx);
|
let buffer = MultiBuffer::build_simple(&sample_text(30, 5, 'a'), cx);
|
||||||
|
@ -6812,7 +6837,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_cancel(cx: &mut gpui::MutableAppContext) {
|
fn test_cancel(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
|
let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
||||||
|
|
||||||
|
@ -6852,7 +6877,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_fold(cx: &mut gpui::MutableAppContext) {
|
fn test_fold(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple(
|
let buffer = MultiBuffer::build_simple(
|
||||||
&"
|
&"
|
||||||
impl Foo {
|
impl Foo {
|
||||||
|
@ -6937,7 +6962,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
|
fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
|
let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
|
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
|
||||||
|
|
||||||
|
@ -7011,7 +7036,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
|
fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
|
let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
|
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
|
||||||
|
|
||||||
|
@ -7112,7 +7137,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) {
|
fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
|
let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
|
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
|
||||||
view.update(cx, |view, cx| {
|
view.update(cx, |view, cx| {
|
||||||
|
@ -7157,7 +7182,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
|
fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple("abc\n def", cx);
|
let buffer = MultiBuffer::build_simple("abc\n def", cx);
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
||||||
view.update(cx, |view, cx| {
|
view.update(cx, |view, cx| {
|
||||||
|
@ -7298,7 +7323,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
|
fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx);
|
let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx);
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
||||||
view.update(cx, |view, cx| {
|
view.update(cx, |view, cx| {
|
||||||
|
@ -7403,7 +7428,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
|
fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
|
let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
||||||
|
|
||||||
|
@ -7456,7 +7481,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) {
|
fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple("one two three four", cx);
|
let buffer = MultiBuffer::build_simple("one two three four", cx);
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
|
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
|
||||||
|
|
||||||
|
@ -7493,7 +7518,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_newline(cx: &mut gpui::MutableAppContext) {
|
fn test_newline(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx);
|
let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx);
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
|
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
|
||||||
|
|
||||||
|
@ -7514,7 +7539,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_newline_with_old_selections(cx: &mut gpui::MutableAppContext) {
|
fn test_newline_with_old_selections(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple(
|
let buffer = MultiBuffer::build_simple(
|
||||||
"
|
"
|
||||||
a
|
a
|
||||||
|
@ -7599,7 +7624,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_insert_with_old_selections(cx: &mut gpui::MutableAppContext) {
|
fn test_insert_with_old_selections(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
|
let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
|
||||||
let (_, editor) = cx.add_window(Default::default(), |cx| {
|
let (_, editor) = cx.add_window(Default::default(), |cx| {
|
||||||
let mut editor = build_editor(buffer.clone(), cx);
|
let mut editor = build_editor(buffer.clone(), cx);
|
||||||
|
@ -7626,81 +7651,226 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_indent_outdent(cx: &mut gpui::MutableAppContext) {
|
fn test_indent_outdent(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple(" one two\nthree\n four", cx);
|
let buffer = MultiBuffer::build_simple(
|
||||||
|
indoc! {"
|
||||||
|
one two
|
||||||
|
three
|
||||||
|
four"},
|
||||||
|
cx,
|
||||||
|
);
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
|
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
|
||||||
|
|
||||||
view.update(cx, |view, cx| {
|
view.update(cx, |view, cx| {
|
||||||
// two selections on the same line
|
// two selections on the same line
|
||||||
view.select_display_ranges(
|
select_ranges(
|
||||||
&[
|
view,
|
||||||
DisplayPoint::new(0, 2)..DisplayPoint::new(0, 5),
|
indoc! {"
|
||||||
DisplayPoint::new(0, 6)..DisplayPoint::new(0, 9),
|
[one] [two]
|
||||||
],
|
three
|
||||||
|
four"},
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
|
||||||
// indent from mid-tabstop to full tabstop
|
// indent from mid-tabstop to full tabstop
|
||||||
view.tab(&Tab(Direction::Next), cx);
|
view.tab(&Tab(Direction::Next), cx);
|
||||||
assert_eq!(view.text(cx), " one two\nthree\n four");
|
assert_text_with_selections(
|
||||||
assert_eq!(
|
view,
|
||||||
view.selected_display_ranges(cx),
|
indoc! {"
|
||||||
&[
|
[one] [two]
|
||||||
DisplayPoint::new(0, 4)..DisplayPoint::new(0, 7),
|
three
|
||||||
DisplayPoint::new(0, 8)..DisplayPoint::new(0, 11),
|
four"},
|
||||||
]
|
cx,
|
||||||
);
|
);
|
||||||
|
|
||||||
// outdent from 1 tabstop to 0 tabstops
|
// outdent from 1 tabstop to 0 tabstops
|
||||||
view.tab(&Tab(Direction::Prev), cx);
|
view.tab(&Tab(Direction::Prev), cx);
|
||||||
assert_eq!(view.text(cx), "one two\nthree\n four");
|
assert_text_with_selections(
|
||||||
assert_eq!(
|
view,
|
||||||
view.selected_display_ranges(cx),
|
indoc! {"
|
||||||
&[
|
[one] [two]
|
||||||
DisplayPoint::new(0, 0)..DisplayPoint::new(0, 3),
|
three
|
||||||
DisplayPoint::new(0, 4)..DisplayPoint::new(0, 7),
|
four"},
|
||||||
]
|
cx,
|
||||||
);
|
);
|
||||||
|
|
||||||
// select across line ending
|
// select across line ending
|
||||||
view.select_display_ranges(&[DisplayPoint::new(1, 1)..DisplayPoint::new(2, 0)], cx);
|
select_ranges(
|
||||||
|
view,
|
||||||
|
indoc! {"
|
||||||
|
one two
|
||||||
|
t[hree
|
||||||
|
] four"},
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
|
||||||
// indent and outdent affect only the preceding line
|
// indent and outdent affect only the preceding line
|
||||||
view.tab(&Tab(Direction::Next), cx);
|
view.tab(&Tab(Direction::Next), cx);
|
||||||
assert_eq!(view.text(cx), "one two\n three\n four");
|
assert_text_with_selections(
|
||||||
assert_eq!(
|
view,
|
||||||
view.selected_display_ranges(cx),
|
indoc! {"
|
||||||
&[DisplayPoint::new(1, 5)..DisplayPoint::new(2, 0)]
|
one two
|
||||||
|
t[hree
|
||||||
|
] four"},
|
||||||
|
cx,
|
||||||
);
|
);
|
||||||
view.tab(&Tab(Direction::Prev), cx);
|
view.tab(&Tab(Direction::Prev), cx);
|
||||||
assert_eq!(view.text(cx), "one two\nthree\n four");
|
assert_text_with_selections(
|
||||||
assert_eq!(
|
view,
|
||||||
view.selected_display_ranges(cx),
|
indoc! {"
|
||||||
&[DisplayPoint::new(1, 1)..DisplayPoint::new(2, 0)]
|
one two
|
||||||
|
t[hree
|
||||||
|
] four"},
|
||||||
|
cx,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Ensure that indenting/outdenting works when the cursor is at column 0.
|
// Ensure that indenting/outdenting works when the cursor is at column 0.
|
||||||
view.select_display_ranges(&[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)], cx);
|
select_ranges(
|
||||||
|
view,
|
||||||
|
indoc! {"
|
||||||
|
one two
|
||||||
|
[]three
|
||||||
|
four"},
|
||||||
|
cx,
|
||||||
|
);
|
||||||
view.tab(&Tab(Direction::Next), cx);
|
view.tab(&Tab(Direction::Next), cx);
|
||||||
assert_eq!(view.text(cx), "one two\n three\n four");
|
assert_text_with_selections(
|
||||||
assert_eq!(
|
view,
|
||||||
view.selected_display_ranges(cx),
|
indoc! {"
|
||||||
&[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
|
one two
|
||||||
|
[]three
|
||||||
|
four"},
|
||||||
|
cx,
|
||||||
);
|
);
|
||||||
|
|
||||||
view.select_display_ranges(&[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)], cx);
|
select_ranges(
|
||||||
|
view,
|
||||||
|
indoc! {"
|
||||||
|
one two
|
||||||
|
[] three
|
||||||
|
four"},
|
||||||
|
cx,
|
||||||
|
);
|
||||||
view.tab(&Tab(Direction::Prev), cx);
|
view.tab(&Tab(Direction::Prev), cx);
|
||||||
assert_eq!(view.text(cx), "one two\nthree\n four");
|
assert_text_with_selections(
|
||||||
assert_eq!(
|
view,
|
||||||
view.selected_display_ranges(cx),
|
indoc! {"
|
||||||
&[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
|
one two
|
||||||
|
[]three
|
||||||
|
four"},
|
||||||
|
cx,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) {
|
||||||
|
cx.set_global(
|
||||||
|
Settings::test(cx)
|
||||||
|
.with_overrides(
|
||||||
|
"TOML",
|
||||||
|
LanguageOverride {
|
||||||
|
tab_size: Some(2),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.with_overrides(
|
||||||
|
"Rust",
|
||||||
|
LanguageOverride {
|
||||||
|
tab_size: Some(4),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
let toml_language = Arc::new(Language::new(
|
||||||
|
LanguageConfig {
|
||||||
|
name: "TOML".into(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
let rust_language = Arc::new(Language::new(
|
||||||
|
LanguageConfig {
|
||||||
|
name: "Rust".into(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
|
||||||
|
let toml_buffer = cx
|
||||||
|
.add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
|
||||||
|
let rust_buffer = cx.add_model(|cx| {
|
||||||
|
Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
|
||||||
|
});
|
||||||
|
let multibuffer = cx.add_model(|cx| {
|
||||||
|
let mut multibuffer = MultiBuffer::new(0);
|
||||||
|
multibuffer.push_excerpts(
|
||||||
|
toml_buffer.clone(),
|
||||||
|
[Point::new(0, 0)..Point::new(2, 0)],
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
multibuffer.push_excerpts(
|
||||||
|
rust_buffer.clone(),
|
||||||
|
[Point::new(0, 0)..Point::new(1, 0)],
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
multibuffer
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.add_window(Default::default(), |cx| {
|
||||||
|
let mut editor = build_editor(multibuffer, cx);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
editor.text(cx),
|
||||||
|
indoc! {"
|
||||||
|
a = 1
|
||||||
|
b = 2
|
||||||
|
|
||||||
|
const c: usize = 3;
|
||||||
|
"}
|
||||||
|
);
|
||||||
|
|
||||||
|
select_ranges(
|
||||||
|
&mut editor,
|
||||||
|
indoc! {"
|
||||||
|
[a] = 1
|
||||||
|
b = 2
|
||||||
|
|
||||||
|
[const c:] usize = 3;
|
||||||
|
"},
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
|
||||||
|
editor.tab(&Tab(Direction::Next), cx);
|
||||||
|
assert_text_with_selections(
|
||||||
|
&mut editor,
|
||||||
|
indoc! {"
|
||||||
|
[a] = 1
|
||||||
|
b = 2
|
||||||
|
|
||||||
|
[const c:] usize = 3;
|
||||||
|
"},
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
editor.tab(&Tab(Direction::Prev), cx);
|
||||||
|
assert_text_with_selections(
|
||||||
|
&mut editor,
|
||||||
|
indoc! {"
|
||||||
|
[a] = 1
|
||||||
|
b = 2
|
||||||
|
|
||||||
|
[const c:] usize = 3;
|
||||||
|
"},
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
|
||||||
|
editor
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_backspace(cx: &mut gpui::MutableAppContext) {
|
fn test_backspace(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||||
build_editor(MultiBuffer::build_simple("", cx), cx)
|
build_editor(MultiBuffer::build_simple("", cx), cx)
|
||||||
});
|
});
|
||||||
|
@ -7745,7 +7915,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_delete(cx: &mut gpui::MutableAppContext) {
|
fn test_delete(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer =
|
let buffer =
|
||||||
MultiBuffer::build_simple("one two three\nfour five six\nseven eight nine\nten\n", cx);
|
MultiBuffer::build_simple("one two three\nfour five six\nseven eight nine\nten\n", cx);
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
|
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
|
||||||
|
@ -7773,7 +7943,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_delete_line(cx: &mut gpui::MutableAppContext) {
|
fn test_delete_line(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
|
let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
||||||
view.update(cx, |view, cx| {
|
view.update(cx, |view, cx| {
|
||||||
|
@ -7796,7 +7966,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
|
let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
||||||
view.update(cx, |view, cx| {
|
view.update(cx, |view, cx| {
|
||||||
|
@ -7812,7 +7982,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
|
fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
|
let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
||||||
view.update(cx, |view, cx| {
|
view.update(cx, |view, cx| {
|
||||||
|
@ -7862,7 +8032,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
|
fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
|
let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
||||||
view.update(cx, |view, cx| {
|
view.update(cx, |view, cx| {
|
||||||
|
@ -7958,7 +8128,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
|
fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
|
let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
|
||||||
let snapshot = buffer.read(cx).snapshot(cx);
|
let snapshot = buffer.read(cx).snapshot(cx);
|
||||||
let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
||||||
|
@ -7979,7 +8149,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_clipboard(cx: &mut gpui::MutableAppContext) {
|
fn test_clipboard(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple("one✅ two three four five six ", cx);
|
let buffer = MultiBuffer::build_simple("one✅ two three four five six ", cx);
|
||||||
let view = cx
|
let view = cx
|
||||||
.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx))
|
.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx))
|
||||||
|
@ -8108,7 +8278,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_select_all(cx: &mut gpui::MutableAppContext) {
|
fn test_select_all(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
|
let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
||||||
view.update(cx, |view, cx| {
|
view.update(cx, |view, cx| {
|
||||||
|
@ -8122,7 +8292,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_select_line(cx: &mut gpui::MutableAppContext) {
|
fn test_select_line(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
|
let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
||||||
view.update(cx, |view, cx| {
|
view.update(cx, |view, cx| {
|
||||||
|
@ -8167,7 +8337,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
|
fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
|
let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
||||||
view.update(cx, |view, cx| {
|
view.update(cx, |view, cx| {
|
||||||
|
@ -8233,7 +8403,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
|
fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
|
let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
|
||||||
|
|
||||||
|
@ -8417,7 +8587,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_select_next(cx: &mut gpui::MutableAppContext) {
|
fn test_select_next(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
|
|
||||||
let (text, ranges) = marked_text_ranges("[abc]\n[abc] [abc]\ndefabc\n[abc]");
|
let (text, ranges) = marked_text_ranges("[abc]\n[abc] [abc]\ndefabc\n[abc]");
|
||||||
let buffer = MultiBuffer::build_simple(&text, cx);
|
let buffer = MultiBuffer::build_simple(&text, cx);
|
||||||
|
@ -8447,7 +8617,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
|
async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
|
||||||
cx.update(populate_settings);
|
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||||
let language = Arc::new(Language::new(
|
let language = Arc::new(Language::new(
|
||||||
LanguageConfig::default(),
|
LanguageConfig::default(),
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
|
@ -8588,7 +8758,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
|
async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
|
||||||
cx.update(populate_settings);
|
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||||
let language = Arc::new(
|
let language = Arc::new(
|
||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
|
@ -8645,7 +8815,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
|
async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
|
||||||
cx.update(populate_settings);
|
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||||
let language = Arc::new(Language::new(
|
let language = Arc::new(Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
brackets: vec![
|
brackets: vec![
|
||||||
|
@ -8792,7 +8962,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_snippets(cx: &mut gpui::TestAppContext) {
|
async fn test_snippets(cx: &mut gpui::TestAppContext) {
|
||||||
cx.update(populate_settings);
|
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||||
|
|
||||||
let text = "
|
let text = "
|
||||||
a. b
|
a. b
|
||||||
|
@ -8900,7 +9070,7 @@ mod tests {
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_format_during_save(cx: &mut gpui::TestAppContext) {
|
async fn test_format_during_save(cx: &mut gpui::TestAppContext) {
|
||||||
cx.foreground().forbid_parking();
|
cx.foreground().forbid_parking();
|
||||||
cx.update(populate_settings);
|
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||||
|
|
||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
|
@ -8952,6 +9122,7 @@ mod tests {
|
||||||
params.text_document.uri,
|
params.text_document.uri,
|
||||||
lsp::Url::from_file_path("/file.rs").unwrap()
|
lsp::Url::from_file_path("/file.rs").unwrap()
|
||||||
);
|
);
|
||||||
|
assert_eq!(params.options.tab_size, 4);
|
||||||
Ok(Some(vec![lsp::TextEdit::new(
|
Ok(Some(vec![lsp::TextEdit::new(
|
||||||
lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
|
lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
|
||||||
", ".to_string(),
|
", ".to_string(),
|
||||||
|
@ -8988,11 +9159,39 @@ mod tests {
|
||||||
"one\ntwo\nthree\n"
|
"one\ntwo\nthree\n"
|
||||||
);
|
);
|
||||||
assert!(!cx.read(|cx| editor.is_dirty(cx)));
|
assert!(!cx.read(|cx| editor.is_dirty(cx)));
|
||||||
|
|
||||||
|
// Set rust language override and assert overriden tabsize is sent to language server
|
||||||
|
cx.update(|cx| {
|
||||||
|
cx.update_global::<Settings, _, _>(|settings, _| {
|
||||||
|
settings.language_overrides.insert(
|
||||||
|
"Rust".into(),
|
||||||
|
LanguageOverride {
|
||||||
|
tab_size: Some(8),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let save = cx.update(|cx| editor.save(project.clone(), cx));
|
||||||
|
fake_server
|
||||||
|
.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
|
||||||
|
assert_eq!(
|
||||||
|
params.text_document.uri,
|
||||||
|
lsp::Url::from_file_path("/file.rs").unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(params.options.tab_size, 8);
|
||||||
|
Ok(Some(vec![]))
|
||||||
|
})
|
||||||
|
.next()
|
||||||
|
.await;
|
||||||
|
cx.foreground().start_waiting();
|
||||||
|
save.await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_completion(cx: &mut gpui::TestAppContext) {
|
async fn test_completion(cx: &mut gpui::TestAppContext) {
|
||||||
cx.update(populate_settings);
|
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||||
|
|
||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
|
@ -9233,7 +9432,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
|
async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
|
||||||
cx.update(populate_settings);
|
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||||
let language = Arc::new(Language::new(
|
let language = Arc::new(Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
line_comment: Some("// ".to_string()),
|
line_comment: Some("// ".to_string()),
|
||||||
|
@ -9313,7 +9512,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
|
fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
|
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
|
||||||
let multibuffer = cx.add_model(|cx| {
|
let multibuffer = cx.add_model(|cx| {
|
||||||
let mut multibuffer = MultiBuffer::new(0);
|
let mut multibuffer = MultiBuffer::new(0);
|
||||||
|
@ -9356,7 +9555,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
|
fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
|
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
|
||||||
let multibuffer = cx.add_model(|cx| {
|
let multibuffer = cx.add_model(|cx| {
|
||||||
let mut multibuffer = MultiBuffer::new(0);
|
let mut multibuffer = MultiBuffer::new(0);
|
||||||
|
@ -9411,7 +9610,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
|
fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
|
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
|
||||||
let mut excerpt1_id = None;
|
let mut excerpt1_id = None;
|
||||||
let multibuffer = cx.add_model(|cx| {
|
let multibuffer = cx.add_model(|cx| {
|
||||||
|
@ -9489,7 +9688,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
|
fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
|
||||||
populate_settings(cx);
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
|
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
|
||||||
let mut excerpt1_id = None;
|
let mut excerpt1_id = None;
|
||||||
let multibuffer = cx.add_model(|cx| {
|
let multibuffer = cx.add_model(|cx| {
|
||||||
|
@ -9543,7 +9742,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
|
async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
|
||||||
cx.update(populate_settings);
|
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||||
let language = Arc::new(Language::new(
|
let language = Arc::new(Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
brackets: vec![
|
brackets: vec![
|
||||||
|
@ -9611,7 +9810,8 @@ mod tests {
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
|
fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
|
||||||
let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
|
let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
|
||||||
populate_settings(cx);
|
|
||||||
|
cx.set_global(Settings::test(cx));
|
||||||
let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
|
let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
|
||||||
|
|
||||||
editor.update(cx, |editor, cx| {
|
editor.update(cx, |editor, cx| {
|
||||||
|
@ -9690,7 +9890,8 @@ mod tests {
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_following(cx: &mut gpui::MutableAppContext) {
|
fn test_following(cx: &mut gpui::MutableAppContext) {
|
||||||
let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
|
let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
|
||||||
populate_settings(cx);
|
|
||||||
|
cx.set_global(Settings::test(cx));
|
||||||
|
|
||||||
let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
|
let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
|
||||||
let (_, follower) = cx.add_window(
|
let (_, follower) = cx.add_window(
|
||||||
|
@ -9857,11 +10058,6 @@ mod tests {
|
||||||
Editor::new(EditorMode::Full, buffer, None, None, cx)
|
Editor::new(EditorMode::Full, buffer, None, None, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn populate_settings(cx: &mut gpui::MutableAppContext) {
|
|
||||||
let settings = Settings::test(cx);
|
|
||||||
cx.set_global(settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn assert_selection_ranges(
|
fn assert_selection_ranges(
|
||||||
marked_text: &str,
|
marked_text: &str,
|
||||||
selection_marker_pairs: Vec<(char, char)>,
|
selection_marker_pairs: Vec<(char, char)>,
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -268,9 +268,11 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{test::marked_display_snapshot, Buffer, DisplayMap, MultiBuffer};
|
use crate::{test::marked_display_snapshot, Buffer, DisplayMap, MultiBuffer};
|
||||||
use language::Point;
|
use language::Point;
|
||||||
|
use settings::Settings;
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_previous_word_start(cx: &mut gpui::MutableAppContext) {
|
fn test_previous_word_start(cx: &mut gpui::MutableAppContext) {
|
||||||
|
cx.set_global(Settings::test(cx));
|
||||||
fn assert(marked_text: &str, cx: &mut gpui::MutableAppContext) {
|
fn assert(marked_text: &str, cx: &mut gpui::MutableAppContext) {
|
||||||
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -297,6 +299,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_previous_subword_start(cx: &mut gpui::MutableAppContext) {
|
fn test_previous_subword_start(cx: &mut gpui::MutableAppContext) {
|
||||||
|
cx.set_global(Settings::test(cx));
|
||||||
fn assert(marked_text: &str, cx: &mut gpui::MutableAppContext) {
|
fn assert(marked_text: &str, cx: &mut gpui::MutableAppContext) {
|
||||||
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -330,6 +333,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_find_preceding_boundary(cx: &mut gpui::MutableAppContext) {
|
fn test_find_preceding_boundary(cx: &mut gpui::MutableAppContext) {
|
||||||
|
cx.set_global(Settings::test(cx));
|
||||||
fn assert(
|
fn assert(
|
||||||
marked_text: &str,
|
marked_text: &str,
|
||||||
cx: &mut gpui::MutableAppContext,
|
cx: &mut gpui::MutableAppContext,
|
||||||
|
@ -361,6 +365,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_next_word_end(cx: &mut gpui::MutableAppContext) {
|
fn test_next_word_end(cx: &mut gpui::MutableAppContext) {
|
||||||
|
cx.set_global(Settings::test(cx));
|
||||||
fn assert(marked_text: &str, cx: &mut gpui::MutableAppContext) {
|
fn assert(marked_text: &str, cx: &mut gpui::MutableAppContext) {
|
||||||
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -384,6 +389,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_next_subword_end(cx: &mut gpui::MutableAppContext) {
|
fn test_next_subword_end(cx: &mut gpui::MutableAppContext) {
|
||||||
|
cx.set_global(Settings::test(cx));
|
||||||
fn assert(marked_text: &str, cx: &mut gpui::MutableAppContext) {
|
fn assert(marked_text: &str, cx: &mut gpui::MutableAppContext) {
|
||||||
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -416,6 +422,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_find_boundary(cx: &mut gpui::MutableAppContext) {
|
fn test_find_boundary(cx: &mut gpui::MutableAppContext) {
|
||||||
|
cx.set_global(Settings::test(cx));
|
||||||
fn assert(
|
fn assert(
|
||||||
marked_text: &str,
|
marked_text: &str,
|
||||||
cx: &mut gpui::MutableAppContext,
|
cx: &mut gpui::MutableAppContext,
|
||||||
|
@ -447,6 +454,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_surrounding_word(cx: &mut gpui::MutableAppContext) {
|
fn test_surrounding_word(cx: &mut gpui::MutableAppContext) {
|
||||||
|
cx.set_global(Settings::test(cx));
|
||||||
fn assert(marked_text: &str, cx: &mut gpui::MutableAppContext) {
|
fn assert(marked_text: &str, cx: &mut gpui::MutableAppContext) {
|
||||||
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -467,6 +475,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_move_up_and_down_with_excerpts(cx: &mut gpui::MutableAppContext) {
|
fn test_move_up_and_down_with_excerpts(cx: &mut gpui::MutableAppContext) {
|
||||||
|
cx.set_global(Settings::test(cx));
|
||||||
let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
|
let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
|
||||||
let font_id = cx
|
let font_id = cx
|
||||||
.font_cache()
|
.font_cache()
|
||||||
|
@ -487,7 +496,7 @@ mod tests {
|
||||||
multibuffer
|
multibuffer
|
||||||
});
|
});
|
||||||
let display_map =
|
let display_map =
|
||||||
cx.add_model(|cx| DisplayMap::new(multibuffer, 2, font_id, 14.0, None, 2, 2, cx));
|
cx.add_model(|cx| DisplayMap::new(multibuffer, font_id, 14.0, None, 2, 2, cx));
|
||||||
let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
|
let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||||
|
|
||||||
assert_eq!(snapshot.text(), "\n\nabc\ndefg\n\n\nhijkl\nmn");
|
assert_eq!(snapshot.text(), "\n\nabc\ndefg\n\n\nhijkl\nmn");
|
||||||
|
|
|
@ -11,6 +11,7 @@ use language::{
|
||||||
Language, OffsetRangeExt, Outline, OutlineItem, Selection, ToOffset as _, ToPoint as _,
|
Language, OffsetRangeExt, Outline, OutlineItem, Selection, ToOffset as _, ToPoint as _,
|
||||||
ToPointUtf16 as _, TransactionId,
|
ToPointUtf16 as _, TransactionId,
|
||||||
};
|
};
|
||||||
|
use settings::Settings;
|
||||||
use std::{
|
use std::{
|
||||||
cell::{Ref, RefCell},
|
cell::{Ref, RefCell},
|
||||||
cmp, fmt, io,
|
cmp, fmt, io,
|
||||||
|
@ -297,8 +298,10 @@ impl MultiBuffer {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|range| range.start.to_offset(&snapshot)..range.end.to_offset(&snapshot));
|
.map(|range| range.start.to_offset(&snapshot)..range.end.to_offset(&snapshot));
|
||||||
return buffer.update(cx, |buffer, cx| {
|
return buffer.update(cx, |buffer, cx| {
|
||||||
|
let language_name = buffer.language().map(|language| language.name());
|
||||||
|
let indent_size = cx.global::<Settings>().tab_size(language_name.as_deref());
|
||||||
if autoindent {
|
if autoindent {
|
||||||
buffer.edit_with_autoindent(ranges, new_text, cx);
|
buffer.edit_with_autoindent(ranges, new_text, indent_size, cx);
|
||||||
} else {
|
} else {
|
||||||
buffer.edit(ranges, new_text, cx);
|
buffer.edit(ranges, new_text, cx);
|
||||||
}
|
}
|
||||||
|
@ -392,10 +395,12 @@ impl MultiBuffer {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let language_name = buffer.language().map(|l| l.name());
|
||||||
|
let indent_size = cx.global::<Settings>().tab_size(language_name.as_deref());
|
||||||
|
|
||||||
if autoindent {
|
if autoindent {
|
||||||
buffer.edit_with_autoindent(deletions, "", cx);
|
buffer.edit_with_autoindent(deletions, "", indent_size, cx);
|
||||||
buffer.edit_with_autoindent(insertions, new_text.clone(), cx);
|
buffer.edit_with_autoindent(insertions, new_text.clone(), indent_size, cx);
|
||||||
} else {
|
} else {
|
||||||
buffer.edit(deletions, "", cx);
|
buffer.edit(deletions, "", cx);
|
||||||
buffer.edit(insertions, new_text.clone(), cx);
|
buffer.edit(insertions, new_text.clone(), cx);
|
||||||
|
@ -861,6 +866,29 @@ impl MultiBuffer {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If point is at the end of the buffer, the last excerpt is returned
|
||||||
|
pub fn point_to_buffer_offset<'a, T: ToOffset>(
|
||||||
|
&'a self,
|
||||||
|
point: T,
|
||||||
|
cx: &AppContext,
|
||||||
|
) -> Option<(ModelHandle<Buffer>, usize)> {
|
||||||
|
let snapshot = self.read(cx);
|
||||||
|
let offset = point.to_offset(&snapshot);
|
||||||
|
let mut cursor = snapshot.excerpts.cursor::<usize>();
|
||||||
|
cursor.seek(&offset, Bias::Right, &());
|
||||||
|
if cursor.item().is_none() {
|
||||||
|
cursor.prev(&());
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.item().map(|excerpt| {
|
||||||
|
let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer);
|
||||||
|
let buffer_point = excerpt_start + offset - *cursor.start();
|
||||||
|
let buffer = self.buffers.borrow()[&excerpt.buffer_id].buffer.clone();
|
||||||
|
|
||||||
|
(buffer, buffer_point)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn range_to_buffer_ranges<'a, T: ToOffset>(
|
pub fn range_to_buffer_ranges<'a, T: ToOffset>(
|
||||||
&'a self,
|
&'a self,
|
||||||
range: Range<T>,
|
range: Range<T>,
|
||||||
|
@ -1057,12 +1085,13 @@ impl MultiBuffer {
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn language<'a>(&self, cx: &'a AppContext) -> Option<&'a Arc<Language>> {
|
pub fn language_at<'a, T: ToOffset>(
|
||||||
self.buffers
|
&self,
|
||||||
.borrow()
|
point: T,
|
||||||
.values()
|
cx: &'a AppContext,
|
||||||
.next()
|
) -> Option<&'a Arc<Language>> {
|
||||||
.and_then(|state| state.buffer.read(cx).language())
|
self.point_to_buffer_offset(point, cx)
|
||||||
|
.and_then(|(buffer, _)| buffer.read(cx).language())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn file<'a>(&self, cx: &'a AppContext) -> Option<&'a dyn File> {
|
pub fn file<'a>(&self, cx: &'a AppContext) -> Option<&'a dyn File> {
|
||||||
|
@ -3760,6 +3789,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_history(cx: &mut MutableAppContext) {
|
fn test_history(cx: &mut MutableAppContext) {
|
||||||
|
cx.set_global(Settings::test(cx));
|
||||||
let buffer_1 = cx.add_model(|cx| Buffer::new(0, "1234", cx));
|
let buffer_1 = cx.add_model(|cx| Buffer::new(0, "1234", cx));
|
||||||
let buffer_2 = cx.add_model(|cx| Buffer::new(0, "5678", cx));
|
let buffer_2 = cx.add_model(|cx| Buffer::new(0, "5678", cx));
|
||||||
let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
|
let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use util::test::marked_text;
|
use gpui::ViewContext;
|
||||||
|
use util::test::{marked_text, marked_text_ranges};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint},
|
display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint},
|
||||||
DisplayPoint, MultiBuffer,
|
DisplayPoint, Editor, MultiBuffer,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -20,7 +21,6 @@ pub fn marked_display_snapshot(
|
||||||
) -> (DisplaySnapshot, Vec<DisplayPoint>) {
|
) -> (DisplaySnapshot, Vec<DisplayPoint>) {
|
||||||
let (unmarked_text, markers) = marked_text(text);
|
let (unmarked_text, markers) = marked_text(text);
|
||||||
|
|
||||||
let tab_size = 4;
|
|
||||||
let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
|
let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
|
||||||
let font_id = cx
|
let font_id = cx
|
||||||
.font_cache()
|
.font_cache()
|
||||||
|
@ -30,7 +30,7 @@ pub fn marked_display_snapshot(
|
||||||
|
|
||||||
let buffer = MultiBuffer::build_simple(&unmarked_text, cx);
|
let buffer = MultiBuffer::build_simple(&unmarked_text, cx);
|
||||||
let display_map =
|
let display_map =
|
||||||
cx.add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, None, 1, 1, cx));
|
cx.add_model(|cx| DisplayMap::new(buffer, font_id, font_size, None, 1, 1, cx));
|
||||||
let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
|
let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||||
let markers = markers
|
let markers = markers
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -39,3 +39,20 @@ pub fn marked_display_snapshot(
|
||||||
|
|
||||||
(snapshot, markers)
|
(snapshot, markers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn select_ranges(editor: &mut Editor, marked_text: &str, cx: &mut ViewContext<Editor>) {
|
||||||
|
let (umarked_text, text_ranges) = marked_text_ranges(marked_text);
|
||||||
|
assert_eq!(editor.text(cx), umarked_text);
|
||||||
|
editor.select_ranges(text_ranges, None, cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn assert_text_with_selections(
|
||||||
|
editor: &mut Editor,
|
||||||
|
marked_text: &str,
|
||||||
|
cx: &mut ViewContext<Editor>,
|
||||||
|
) {
|
||||||
|
let (unmarked_text, text_ranges) = marked_text_ranges(marked_text);
|
||||||
|
|
||||||
|
assert_eq!(editor.text(cx), unmarked_text);
|
||||||
|
assert_eq!(editor.selected_ranges(cx), text_ranges);
|
||||||
|
}
|
||||||
|
|
|
@ -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" }
|
||||||
|
|
|
@ -8,6 +8,7 @@ use gpui::{
|
||||||
ViewContext, ViewHandle, WeakViewHandle,
|
ViewContext, ViewHandle, WeakViewHandle,
|
||||||
};
|
};
|
||||||
use project::{Project, ProjectPath, WorktreeId};
|
use project::{Project, ProjectPath, WorktreeId};
|
||||||
|
use settings::Settings;
|
||||||
use std::{
|
use std::{
|
||||||
cmp,
|
cmp,
|
||||||
path::Path,
|
path::Path,
|
||||||
|
@ -19,7 +20,7 @@ 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,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct FileFinder {
|
pub struct FileFinder {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -1019,7 +1019,10 @@ impl MutableAppContext {
|
||||||
.insert(TypeId::of::<A>(), handler)
|
.insert(TypeId::of::<A>(), handler)
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
panic!("registered multiple global handlers for the same action type");
|
panic!(
|
||||||
|
"registered multiple global handlers for {}",
|
||||||
|
type_name::<A>()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2355,11 +2358,11 @@ impl AppContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn global<T: 'static>(&self) -> &T {
|
pub fn global<T: 'static>(&self) -> &T {
|
||||||
self.globals
|
if let Some(global) = self.globals.get(&TypeId::of::<T>()) {
|
||||||
.get(&TypeId::of::<T>())
|
global.downcast_ref().unwrap()
|
||||||
.expect("no app state has been added for this type")
|
} else {
|
||||||
.downcast_ref()
|
panic!("no global has been added for {}", type_name::<T>());
|
||||||
.unwrap()
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,6 @@ pub struct Buffer {
|
||||||
file_update_count: usize,
|
file_update_count: usize,
|
||||||
completion_triggers: Vec<String>,
|
completion_triggers: Vec<String>,
|
||||||
deferred_ops: OperationQueue<Operation>,
|
deferred_ops: OperationQueue<Operation>,
|
||||||
indent_size: u32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BufferSnapshot {
|
pub struct BufferSnapshot {
|
||||||
|
@ -80,7 +79,6 @@ pub struct BufferSnapshot {
|
||||||
selections_update_count: usize,
|
selections_update_count: usize,
|
||||||
language: Option<Arc<Language>>,
|
language: Option<Arc<Language>>,
|
||||||
parse_count: usize,
|
parse_count: usize,
|
||||||
indent_size: u32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -214,6 +212,7 @@ struct AutoindentRequest {
|
||||||
before_edit: BufferSnapshot,
|
before_edit: BufferSnapshot,
|
||||||
edited: Vec<Anchor>,
|
edited: Vec<Anchor>,
|
||||||
inserted: Option<Vec<Range<Anchor>>>,
|
inserted: Option<Vec<Range<Anchor>>>,
|
||||||
|
indent_size: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -427,8 +426,6 @@ impl Buffer {
|
||||||
file_update_count: 0,
|
file_update_count: 0,
|
||||||
completion_triggers: Default::default(),
|
completion_triggers: Default::default(),
|
||||||
deferred_ops: OperationQueue::new(),
|
deferred_ops: OperationQueue::new(),
|
||||||
// TODO: make this configurable
|
|
||||||
indent_size: 4,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,7 +441,6 @@ impl Buffer {
|
||||||
language: self.language.clone(),
|
language: self.language.clone(),
|
||||||
parse_count: self.parse_count,
|
parse_count: self.parse_count,
|
||||||
selections_update_count: self.selections_update_count,
|
selections_update_count: self.selections_update_count,
|
||||||
indent_size: self.indent_size,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -786,7 +782,7 @@ impl Buffer {
|
||||||
.indent_column_for_line(suggestion.basis_row)
|
.indent_column_for_line(suggestion.basis_row)
|
||||||
});
|
});
|
||||||
let delta = if suggestion.indent {
|
let delta = if suggestion.indent {
|
||||||
snapshot.indent_size
|
request.indent_size
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
@ -809,7 +805,7 @@ impl Buffer {
|
||||||
.flatten();
|
.flatten();
|
||||||
for (new_row, suggestion) in new_edited_row_range.zip(suggestions) {
|
for (new_row, suggestion) in new_edited_row_range.zip(suggestions) {
|
||||||
let delta = if suggestion.indent {
|
let delta = if suggestion.indent {
|
||||||
snapshot.indent_size
|
request.indent_size
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
@ -845,7 +841,7 @@ impl Buffer {
|
||||||
.flatten();
|
.flatten();
|
||||||
for (row, suggestion) in inserted_row_range.zip(suggestions) {
|
for (row, suggestion) in inserted_row_range.zip(suggestions) {
|
||||||
let delta = if suggestion.indent {
|
let delta = if suggestion.indent {
|
||||||
snapshot.indent_size
|
request.indent_size
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
@ -1055,7 +1051,7 @@ impl Buffer {
|
||||||
where
|
where
|
||||||
T: Into<String>,
|
T: Into<String>,
|
||||||
{
|
{
|
||||||
self.edit_internal([0..self.len()], text, false, cx)
|
self.edit_internal([0..self.len()], text, None, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn edit<I, S, T>(
|
pub fn edit<I, S, T>(
|
||||||
|
@ -1069,13 +1065,14 @@ impl Buffer {
|
||||||
S: ToOffset,
|
S: ToOffset,
|
||||||
T: Into<String>,
|
T: Into<String>,
|
||||||
{
|
{
|
||||||
self.edit_internal(ranges_iter, new_text, false, cx)
|
self.edit_internal(ranges_iter, new_text, None, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn edit_with_autoindent<I, S, T>(
|
pub fn edit_with_autoindent<I, S, T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
ranges_iter: I,
|
ranges_iter: I,
|
||||||
new_text: T,
|
new_text: T,
|
||||||
|
indent_size: u32,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Option<clock::Local>
|
) -> Option<clock::Local>
|
||||||
where
|
where
|
||||||
|
@ -1083,14 +1080,14 @@ impl Buffer {
|
||||||
S: ToOffset,
|
S: ToOffset,
|
||||||
T: Into<String>,
|
T: Into<String>,
|
||||||
{
|
{
|
||||||
self.edit_internal(ranges_iter, new_text, true, cx)
|
self.edit_internal(ranges_iter, new_text, Some(indent_size), cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn edit_internal<I, S, T>(
|
pub fn edit_internal<I, S, T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
ranges_iter: I,
|
ranges_iter: I,
|
||||||
new_text: T,
|
new_text: T,
|
||||||
autoindent: bool,
|
autoindent_size: Option<u32>,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Option<clock::Local>
|
) -> Option<clock::Local>
|
||||||
where
|
where
|
||||||
|
@ -1122,23 +1119,27 @@ impl Buffer {
|
||||||
|
|
||||||
self.start_transaction();
|
self.start_transaction();
|
||||||
self.pending_autoindent.take();
|
self.pending_autoindent.take();
|
||||||
let autoindent_request = if autoindent && self.language.is_some() {
|
let autoindent_request =
|
||||||
let before_edit = self.snapshot();
|
self.language
|
||||||
let edited = ranges
|
.as_ref()
|
||||||
.iter()
|
.and_then(|_| autoindent_size)
|
||||||
.filter_map(|range| {
|
.map(|autoindent_size| {
|
||||||
let start = range.start.to_point(self);
|
let before_edit = self.snapshot();
|
||||||
if new_text.starts_with('\n') && start.column == self.line_len(start.row) {
|
let edited = ranges
|
||||||
None
|
.iter()
|
||||||
} else {
|
.filter_map(|range| {
|
||||||
Some(self.anchor_before(range.start))
|
let start = range.start.to_point(self);
|
||||||
}
|
if new_text.starts_with('\n')
|
||||||
})
|
&& start.column == self.line_len(start.row)
|
||||||
.collect();
|
{
|
||||||
Some((before_edit, edited))
|
None
|
||||||
} else {
|
} else {
|
||||||
None
|
Some(self.anchor_before(range.start))
|
||||||
};
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
(before_edit, edited, autoindent_size)
|
||||||
|
});
|
||||||
|
|
||||||
let first_newline_ix = new_text.find('\n');
|
let first_newline_ix = new_text.find('\n');
|
||||||
let new_text_len = new_text.len();
|
let new_text_len = new_text.len();
|
||||||
|
@ -1146,7 +1147,7 @@ impl Buffer {
|
||||||
let edit = self.text.edit(ranges.iter().cloned(), new_text);
|
let edit = self.text.edit(ranges.iter().cloned(), new_text);
|
||||||
let edit_id = edit.local_timestamp();
|
let edit_id = edit.local_timestamp();
|
||||||
|
|
||||||
if let Some((before_edit, edited)) = autoindent_request {
|
if let Some((before_edit, edited, size)) = autoindent_request {
|
||||||
let mut inserted = None;
|
let mut inserted = None;
|
||||||
if let Some(first_newline_ix) = first_newline_ix {
|
if let Some(first_newline_ix) = first_newline_ix {
|
||||||
let mut delta = 0isize;
|
let mut delta = 0isize;
|
||||||
|
@ -1169,6 +1170,7 @@ impl Buffer {
|
||||||
before_edit,
|
before_edit,
|
||||||
edited,
|
edited,
|
||||||
inserted,
|
inserted,
|
||||||
|
indent_size: size,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1925,10 +1927,6 @@ impl BufferSnapshot {
|
||||||
pub fn file_update_count(&self) -> usize {
|
pub fn file_update_count(&self) -> usize {
|
||||||
self.file_update_count
|
self.file_update_count
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn indent_size(&self) -> u32 {
|
|
||||||
self.indent_size
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for BufferSnapshot {
|
impl Clone for BufferSnapshot {
|
||||||
|
@ -1944,7 +1942,6 @@ impl Clone for BufferSnapshot {
|
||||||
file_update_count: self.file_update_count,
|
file_update_count: self.file_update_count,
|
||||||
language: self.language.clone(),
|
language: self.language.clone(),
|
||||||
parse_count: self.parse_count,
|
parse_count: self.parse_count,
|
||||||
indent_size: self.indent_size,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -576,13 +576,13 @@ fn test_edit_with_autoindent(cx: &mut MutableAppContext) {
|
||||||
let text = "fn a() {}";
|
let text = "fn a() {}";
|
||||||
let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
|
let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
|
||||||
|
|
||||||
buffer.edit_with_autoindent([8..8], "\n\n", cx);
|
buffer.edit_with_autoindent([8..8], "\n\n", 4, cx);
|
||||||
assert_eq!(buffer.text(), "fn a() {\n \n}");
|
assert_eq!(buffer.text(), "fn a() {\n \n}");
|
||||||
|
|
||||||
buffer.edit_with_autoindent([Point::new(1, 4)..Point::new(1, 4)], "b()\n", cx);
|
buffer.edit_with_autoindent([Point::new(1, 4)..Point::new(1, 4)], "b()\n", 4, cx);
|
||||||
assert_eq!(buffer.text(), "fn a() {\n b()\n \n}");
|
assert_eq!(buffer.text(), "fn a() {\n b()\n \n}");
|
||||||
|
|
||||||
buffer.edit_with_autoindent([Point::new(2, 4)..Point::new(2, 4)], ".c", cx);
|
buffer.edit_with_autoindent([Point::new(2, 4)..Point::new(2, 4)], ".c", 4, cx);
|
||||||
assert_eq!(buffer.text(), "fn a() {\n b()\n .c\n}");
|
assert_eq!(buffer.text(), "fn a() {\n b()\n .c\n}");
|
||||||
|
|
||||||
buffer
|
buffer
|
||||||
|
@ -604,7 +604,12 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut Muta
|
||||||
|
|
||||||
// Lines 2 and 3 don't match the indentation suggestion. When editing these lines,
|
// Lines 2 and 3 don't match the indentation suggestion. When editing these lines,
|
||||||
// their indentation is not adjusted.
|
// their indentation is not adjusted.
|
||||||
buffer.edit_with_autoindent([empty(Point::new(1, 1)), empty(Point::new(2, 1))], "()", cx);
|
buffer.edit_with_autoindent(
|
||||||
|
[empty(Point::new(1, 1)), empty(Point::new(2, 1))],
|
||||||
|
"()",
|
||||||
|
4,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
buffer.text(),
|
buffer.text(),
|
||||||
"
|
"
|
||||||
|
@ -621,6 +626,7 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut Muta
|
||||||
buffer.edit_with_autoindent(
|
buffer.edit_with_autoindent(
|
||||||
[empty(Point::new(1, 1)), empty(Point::new(2, 1))],
|
[empty(Point::new(1, 1)), empty(Point::new(2, 1))],
|
||||||
"\n.f\n.g",
|
"\n.f\n.g",
|
||||||
|
4,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -651,7 +657,7 @@ fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut MutableAppConte
|
||||||
|
|
||||||
let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
|
let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
|
||||||
|
|
||||||
buffer.edit_with_autoindent([5..5], "\nb", cx);
|
buffer.edit_with_autoindent([5..5], "\nb", 4, cx);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
buffer.text(),
|
buffer.text(),
|
||||||
"
|
"
|
||||||
|
@ -663,7 +669,7 @@ fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut MutableAppConte
|
||||||
|
|
||||||
// The indentation suggestion changed because `@end` node (a close paren)
|
// The indentation suggestion changed because `@end` node (a close paren)
|
||||||
// is now at the beginning of the line.
|
// is now at the beginning of the line.
|
||||||
buffer.edit_with_autoindent([Point::new(1, 4)..Point::new(1, 5)], "", cx);
|
buffer.edit_with_autoindent([Point::new(1, 4)..Point::new(1, 5)], "", 4, cx);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
buffer.text(),
|
buffer.text(),
|
||||||
"
|
"
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -25,6 +25,7 @@ gpui = { path = "../gpui" }
|
||||||
language = { path = "../language" }
|
language = { path = "../language" }
|
||||||
lsp = { path = "../lsp" }
|
lsp = { path = "../lsp" }
|
||||||
rpc = { path = "../rpc" }
|
rpc = { path = "../rpc" }
|
||||||
|
settings = { path = "../settings" }
|
||||||
sum_tree = { path = "../sum_tree" }
|
sum_tree = { path = "../sum_tree" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
aho-corasick = "0.7"
|
aho-corasick = "0.7"
|
||||||
|
|
|
@ -28,6 +28,7 @@ use parking_lot::Mutex;
|
||||||
use postage::watch;
|
use postage::watch;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use search::SearchQuery;
|
use search::SearchQuery;
|
||||||
|
use settings::Settings;
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use similar::{ChangeTag, TextDiff};
|
use similar::{ChangeTag, TextDiff};
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -2173,6 +2174,10 @@ impl Project {
|
||||||
lsp::Url::from_file_path(&buffer_abs_path).unwrap(),
|
lsp::Url::from_file_path(&buffer_abs_path).unwrap(),
|
||||||
);
|
);
|
||||||
let capabilities = &language_server.capabilities();
|
let capabilities = &language_server.capabilities();
|
||||||
|
let tab_size = cx.update(|cx| {
|
||||||
|
let language_name = buffer.read(cx).language().map(|language| language.name());
|
||||||
|
cx.global::<Settings>().tab_size(language_name.as_deref())
|
||||||
|
});
|
||||||
let lsp_edits = if capabilities
|
let lsp_edits = if capabilities
|
||||||
.document_formatting_provider
|
.document_formatting_provider
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -2182,7 +2187,7 @@ impl Project {
|
||||||
.request::<lsp::request::Formatting>(lsp::DocumentFormattingParams {
|
.request::<lsp::request::Formatting>(lsp::DocumentFormattingParams {
|
||||||
text_document,
|
text_document,
|
||||||
options: lsp::FormattingOptions {
|
options: lsp::FormattingOptions {
|
||||||
tab_size: 4,
|
tab_size,
|
||||||
insert_spaces: true,
|
insert_spaces: true,
|
||||||
insert_final_newline: Some(true),
|
insert_final_newline: Some(true),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|
|
@ -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" }
|
||||||
|
|
|
@ -8,8 +8,9 @@ use gpui::{
|
||||||
};
|
};
|
||||||
use language::OffsetRangeExt;
|
use language::OffsetRangeExt;
|
||||||
use project::search::SearchQuery;
|
use project::search::SearchQuery;
|
||||||
|
use settings::Settings;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use workspace::{ItemHandle, Pane, Settings, ToolbarItemLocation, ToolbarItemView};
|
use workspace::{ItemHandle, Pane, ToolbarItemLocation, ToolbarItemView};
|
||||||
|
|
||||||
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: u32,
|
||||||
|
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<u32>,
|
||||||
|
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>) -> u32 {
|
||||||
|
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;
|
||||||
|
@ -13,6 +13,7 @@ use gpui::{
|
||||||
ViewContext, ViewHandle, WeakViewHandle,
|
ViewContext, ViewHandle, WeakViewHandle,
|
||||||
};
|
};
|
||||||
use project::{ProjectEntryId, ProjectPath};
|
use 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() {
|
||||||
|
@ -46,6 +48,20 @@ fn main() {
|
||||||
soft_wrap: Some(settings::SoftWrap::PreferredLineLength),
|
soft_wrap: Some(settings::SoftWrap::PreferredLineLength),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
)
|
||||||
|
.with_overrides(
|
||||||
|
"Rust",
|
||||||
|
settings::LanguageOverride {
|
||||||
|
tab_size: Some(4),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.with_overrides(
|
||||||
|
"TypeScript",
|
||||||
|
settings::LanguageOverride {
|
||||||
|
tab_size: Some(2),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
);
|
);
|
||||||
let settings_file = load_settings_file(&app, fs.clone());
|
let settings_file = load_settings_file(&app, fs.clone());
|
||||||
|
|
||||||
|
@ -97,7 +113,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);
|
||||||
|
@ -576,7 +578,7 @@ mod tests {
|
||||||
assert!(!editor.is_dirty(cx));
|
assert!(!editor.is_dirty(cx));
|
||||||
assert_eq!(editor.title(cx), "untitled");
|
assert_eq!(editor.title(cx), "untitled");
|
||||||
assert!(Arc::ptr_eq(
|
assert!(Arc::ptr_eq(
|
||||||
editor.language(cx).unwrap(),
|
editor.language_at(0, cx).unwrap(),
|
||||||
&languages::PLAIN_TEXT
|
&languages::PLAIN_TEXT
|
||||||
));
|
));
|
||||||
editor.handle_input(&editor::Input("hi".into()), cx);
|
editor.handle_input(&editor::Input("hi".into()), cx);
|
||||||
|
@ -600,7 +602,7 @@ mod tests {
|
||||||
editor.read_with(cx, |editor, cx| {
|
editor.read_with(cx, |editor, cx| {
|
||||||
assert!(!editor.is_dirty(cx));
|
assert!(!editor.is_dirty(cx));
|
||||||
assert_eq!(editor.title(cx), "the-new-name.rs");
|
assert_eq!(editor.title(cx), "the-new-name.rs");
|
||||||
assert_eq!(editor.language(cx).unwrap().name().as_ref(), "Rust");
|
assert_eq!(editor.language_at(0, cx).unwrap().name().as_ref(), "Rust");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Edit the file and save it again. This time, there is no filename prompt.
|
// Edit the file and save it again. This time, there is no filename prompt.
|
||||||
|
@ -666,7 +668,7 @@ mod tests {
|
||||||
|
|
||||||
editor.update(cx, |editor, cx| {
|
editor.update(cx, |editor, cx| {
|
||||||
assert!(Arc::ptr_eq(
|
assert!(Arc::ptr_eq(
|
||||||
editor.language(cx).unwrap(),
|
editor.language_at(0, cx).unwrap(),
|
||||||
&languages::PLAIN_TEXT
|
&languages::PLAIN_TEXT
|
||||||
));
|
));
|
||||||
editor.handle_input(&editor::Input("hi".into()), cx);
|
editor.handle_input(&editor::Input("hi".into()), cx);
|
||||||
|
@ -680,7 +682,7 @@ mod tests {
|
||||||
// The buffer is not dirty anymore and the language is assigned based on the path.
|
// The buffer is not dirty anymore and the language is assigned based on the path.
|
||||||
editor.read_with(cx, |editor, cx| {
|
editor.read_with(cx, |editor, cx| {
|
||||||
assert!(!editor.is_dirty(cx));
|
assert!(!editor.is_dirty(cx));
|
||||||
assert_eq!(editor.language(cx).unwrap().name().as_ref(), "Rust")
|
assert_eq!(editor.language_at(0, cx).unwrap().name().as_ref(), "Rust")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,11 @@ import snakeCase from "./utils/snakeCase";
|
||||||
|
|
||||||
const themes = [dark, light];
|
const themes = [dark, light];
|
||||||
for (let theme of themes) {
|
for (let theme of themes) {
|
||||||
let styleTree = snakeCase(app(theme));
|
let styleTree = snakeCase(app(theme));
|
||||||
let styleTreeJSON = JSON.stringify(styleTree, null, 2);
|
let styleTreeJSON = JSON.stringify(styleTree, null, 2);
|
||||||
let outPath = path.resolve(
|
let outPath = path.resolve(
|
||||||
`${__dirname}/../../crates/zed/assets/themes/${theme.name}.json`
|
`${__dirname}/../../crates/zed/assets/themes/${theme.name}.json`
|
||||||
);
|
);
|
||||||
fs.writeFileSync(outPath, styleTreeJSON);
|
fs.writeFileSync(outPath, styleTreeJSON);
|
||||||
console.log(`- ${outPath} created`);
|
console.log(`- ${outPath} created`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,80 +7,80 @@ import { colors, fontFamilies, fontSizes, fontWeights } from "./tokens";
|
||||||
|
|
||||||
// Organize theme tokens
|
// Organize theme tokens
|
||||||
function themeTokens(theme: Theme) {
|
function themeTokens(theme: Theme) {
|
||||||
return {
|
return {
|
||||||
meta: {
|
meta: {
|
||||||
themeName: theme.name,
|
themeName: theme.name,
|
||||||
},
|
},
|
||||||
text: theme.textColor,
|
text: theme.textColor,
|
||||||
icon: theme.iconColor,
|
icon: theme.iconColor,
|
||||||
background: theme.backgroundColor,
|
background: theme.backgroundColor,
|
||||||
border: theme.borderColor,
|
border: theme.borderColor,
|
||||||
editor: theme.editor,
|
editor: theme.editor,
|
||||||
syntax: {
|
syntax: {
|
||||||
primary: {
|
primary: {
|
||||||
value: theme.syntax.primary.color.value,
|
value: theme.syntax.primary.color.value,
|
||||||
type: "color",
|
type: "color",
|
||||||
},
|
},
|
||||||
comment: {
|
comment: {
|
||||||
value: theme.syntax.comment.color.value,
|
value: theme.syntax.comment.color.value,
|
||||||
type: "color",
|
type: "color",
|
||||||
},
|
},
|
||||||
keyword: {
|
keyword: {
|
||||||
value: theme.syntax.keyword.color.value,
|
value: theme.syntax.keyword.color.value,
|
||||||
type: "color",
|
type: "color",
|
||||||
},
|
},
|
||||||
function: {
|
function: {
|
||||||
value: theme.syntax.function.color.value,
|
value: theme.syntax.function.color.value,
|
||||||
type: "color",
|
type: "color",
|
||||||
},
|
},
|
||||||
type: {
|
type: {
|
||||||
value: theme.syntax.type.color.value,
|
value: theme.syntax.type.color.value,
|
||||||
type: "color",
|
type: "color",
|
||||||
},
|
},
|
||||||
variant: {
|
variant: {
|
||||||
value: theme.syntax.variant.color.value,
|
value: theme.syntax.variant.color.value,
|
||||||
type: "color",
|
type: "color",
|
||||||
},
|
},
|
||||||
property: {
|
property: {
|
||||||
value: theme.syntax.property.color.value,
|
value: theme.syntax.property.color.value,
|
||||||
type: "color",
|
type: "color",
|
||||||
},
|
},
|
||||||
enum: {
|
enum: {
|
||||||
value: theme.syntax.enum.color.value,
|
value: theme.syntax.enum.color.value,
|
||||||
type: "color",
|
type: "color",
|
||||||
},
|
},
|
||||||
operator: {
|
operator: {
|
||||||
value: theme.syntax.operator.color.value,
|
value: theme.syntax.operator.color.value,
|
||||||
type: "color",
|
type: "color",
|
||||||
},
|
},
|
||||||
string: {
|
string: {
|
||||||
value: theme.syntax.string.color.value,
|
value: theme.syntax.string.color.value,
|
||||||
type: "color",
|
type: "color",
|
||||||
},
|
},
|
||||||
number: {
|
number: {
|
||||||
value: theme.syntax.number.color.value,
|
value: theme.syntax.number.color.value,
|
||||||
type: "color",
|
type: "color",
|
||||||
},
|
},
|
||||||
boolean: {
|
boolean: {
|
||||||
value: theme.syntax.boolean.color.value,
|
value: theme.syntax.boolean.color.value,
|
||||||
type: "color",
|
type: "color",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
player: theme.player,
|
player: theme.player,
|
||||||
shadowAlpha: theme.shadowAlpha,
|
shadowAlpha: theme.shadowAlpha,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Organize core tokens
|
// Organize core tokens
|
||||||
const coreTokens = {
|
const coreTokens = {
|
||||||
color: {
|
color: {
|
||||||
...colors,
|
...colors,
|
||||||
},
|
},
|
||||||
text: {
|
text: {
|
||||||
family: fontFamilies,
|
family: fontFamilies,
|
||||||
weight: fontWeights,
|
weight: fontWeights,
|
||||||
},
|
},
|
||||||
size: fontSizes,
|
size: fontSizes,
|
||||||
};
|
};
|
||||||
|
|
||||||
const combinedTokens: any = {};
|
const combinedTokens: any = {};
|
||||||
|
@ -98,10 +98,10 @@ combinedTokens.core = coreTokens;
|
||||||
// We write `${theme}.json` as a separate file for the design team's convenience, but it isn't consumed by Figma Tokens directly.
|
// We write `${theme}.json` as a separate file for the design team's convenience, but it isn't consumed by Figma Tokens directly.
|
||||||
let themes = [dark, light];
|
let themes = [dark, light];
|
||||||
themes.forEach((theme) => {
|
themes.forEach((theme) => {
|
||||||
const themePath = `${distPath}/${theme.name}.json`
|
const themePath = `${distPath}/${theme.name}.json`
|
||||||
fs.writeFileSync(themePath, JSON.stringify(themeTokens(theme), null, 2));
|
fs.writeFileSync(themePath, JSON.stringify(themeTokens(theme), null, 2));
|
||||||
console.log(`- ${themePath} created`);
|
console.log(`- ${themePath} created`);
|
||||||
combinedTokens[theme.name] = themeTokens(theme);
|
combinedTokens[theme.name] = themeTokens(theme);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Write combined tokens to `tokens.json`. This file is consumed by the Figma Tokens plugin to keep our designs consistent with the app.
|
// Write combined tokens to `tokens.json`. This file is consumed by the Figma Tokens plugin to keep our designs consistent with the app.
|
||||||
|
|
|
@ -9,35 +9,35 @@ import selectorModal from "./selectorModal";
|
||||||
import workspace from "./workspace";
|
import workspace from "./workspace";
|
||||||
|
|
||||||
export const panel = {
|
export const panel = {
|
||||||
padding: { top: 12, left: 12, bottom: 12, right: 12 },
|
padding: { top: 12, left: 12, bottom: 12, right: 12 },
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function app(theme: Theme): Object {
|
export default function app(theme: Theme): Object {
|
||||||
return {
|
return {
|
||||||
selector: selectorModal(theme),
|
selector: selectorModal(theme),
|
||||||
workspace: workspace(theme),
|
workspace: workspace(theme),
|
||||||
editor: editor(theme),
|
editor: editor(theme),
|
||||||
projectDiagnostics: {
|
projectDiagnostics: {
|
||||||
tabIconSpacing: 4,
|
tabIconSpacing: 4,
|
||||||
tabIconWidth: 13,
|
tabIconWidth: 13,
|
||||||
tabSummarySpacing: 10,
|
tabSummarySpacing: 10,
|
||||||
emptyMessage: text(theme, "sans", "primary", { size: "lg" }),
|
emptyMessage: text(theme, "sans", "primary", { size: "lg" }),
|
||||||
statusBarItem: {
|
statusBarItem: {
|
||||||
...text(theme, "sans", "muted"),
|
...text(theme, "sans", "muted"),
|
||||||
margin: {
|
margin: {
|
||||||
right: 10,
|
right: 10,
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
projectPanel: projectPanel(theme),
|
},
|
||||||
chatPanel: chatPanel(theme),
|
},
|
||||||
contactsPanel: contactsPanel(theme),
|
projectPanel: projectPanel(theme),
|
||||||
search: search(theme),
|
chatPanel: chatPanel(theme),
|
||||||
breadcrumbs: {
|
contactsPanel: contactsPanel(theme),
|
||||||
...text(theme, "sans", "primary"),
|
search: search(theme),
|
||||||
padding: {
|
breadcrumbs: {
|
||||||
left: 6,
|
...text(theme, "sans", "primary"),
|
||||||
},
|
padding: {
|
||||||
}
|
left: 6,
|
||||||
};
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,108 +1,108 @@
|
||||||
import Theme from "../themes/theme";
|
import Theme from "../themes/theme";
|
||||||
import { panel } from "./app";
|
import { panel } from "./app";
|
||||||
import {
|
import {
|
||||||
backgroundColor,
|
backgroundColor,
|
||||||
border,
|
border,
|
||||||
player,
|
player,
|
||||||
shadow,
|
shadow,
|
||||||
text,
|
text,
|
||||||
TextColor
|
TextColor
|
||||||
} from "./components";
|
} from "./components";
|
||||||
|
|
||||||
export default function chatPanel(theme: Theme) {
|
export default function chatPanel(theme: Theme) {
|
||||||
function channelSelectItem(
|
function channelSelectItem(
|
||||||
theme: Theme,
|
theme: Theme,
|
||||||
textColor: TextColor,
|
textColor: TextColor,
|
||||||
hovered: boolean
|
hovered: boolean
|
||||||
) {
|
) {
|
||||||
return {
|
|
||||||
name: text(theme, "sans", textColor),
|
|
||||||
padding: 4,
|
|
||||||
hash: {
|
|
||||||
...text(theme, "sans", "muted"),
|
|
||||||
margin: {
|
|
||||||
right: 8,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
background: hovered ? backgroundColor(theme, 300, "hovered") : undefined,
|
|
||||||
cornerRadius: hovered ? 6 : 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const message = {
|
|
||||||
body: text(theme, "sans", "secondary"),
|
|
||||||
timestamp: text(theme, "sans", "muted", { size: "sm" }),
|
|
||||||
padding: {
|
|
||||||
bottom: 6,
|
|
||||||
},
|
|
||||||
sender: {
|
|
||||||
...text(theme, "sans", "primary", { weight: "bold" }),
|
|
||||||
margin: {
|
|
||||||
right: 8,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...panel,
|
name: text(theme, "sans", textColor),
|
||||||
channelName: text(theme, "sans", "primary", { weight: "bold" }),
|
padding: 4,
|
||||||
channelNameHash: {
|
hash: {
|
||||||
...text(theme, "sans", "muted"),
|
...text(theme, "sans", "muted"),
|
||||||
padding: {
|
margin: {
|
||||||
right: 8,
|
right: 8,
|
||||||
},
|
|
||||||
},
|
|
||||||
channelSelect: {
|
|
||||||
header: {
|
|
||||||
...channelSelectItem(theme, "primary", false),
|
|
||||||
padding: {
|
|
||||||
bottom: 4,
|
|
||||||
left: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
item: channelSelectItem(theme, "secondary", false),
|
|
||||||
hoveredItem: channelSelectItem(theme, "secondary", true),
|
|
||||||
activeItem: channelSelectItem(theme, "primary", false),
|
|
||||||
hoveredActiveItem: channelSelectItem(theme, "primary", true),
|
|
||||||
menu: {
|
|
||||||
background: backgroundColor(theme, 500),
|
|
||||||
cornerRadius: 6,
|
|
||||||
padding: 4,
|
|
||||||
border: border(theme, "primary"),
|
|
||||||
shadow: shadow(theme),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
signInPrompt: text(theme, "sans", "secondary", { underline: true }),
|
|
||||||
hoveredSignInPrompt: text(theme, "sans", "primary", { underline: true }),
|
|
||||||
message,
|
|
||||||
pendingMessage: {
|
|
||||||
...message,
|
|
||||||
body: {
|
|
||||||
...message.body,
|
|
||||||
color: theme.textColor.muted.value,
|
|
||||||
},
|
|
||||||
sender: {
|
|
||||||
...message.sender,
|
|
||||||
color: theme.textColor.muted.value,
|
|
||||||
},
|
|
||||||
timestamp: {
|
|
||||||
...message.timestamp,
|
|
||||||
color: theme.textColor.muted.value,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
inputEditor: {
|
|
||||||
background: backgroundColor(theme, 500),
|
|
||||||
cornerRadius: 6,
|
|
||||||
text: text(theme, "mono", "primary"),
|
|
||||||
placeholderText: text(theme, "mono", "placeholder", { size: "sm" }),
|
|
||||||
selection: player(theme, 1).selection,
|
|
||||||
border: border(theme, "secondary"),
|
|
||||||
padding: {
|
|
||||||
bottom: 7,
|
|
||||||
left: 8,
|
|
||||||
right: 8,
|
|
||||||
top: 7,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
background: hovered ? backgroundColor(theme, 300, "hovered") : undefined,
|
||||||
|
cornerRadius: hovered ? 6 : 0,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
body: text(theme, "sans", "secondary"),
|
||||||
|
timestamp: text(theme, "sans", "muted", { size: "sm" }),
|
||||||
|
padding: {
|
||||||
|
bottom: 6,
|
||||||
|
},
|
||||||
|
sender: {
|
||||||
|
...text(theme, "sans", "primary", { weight: "bold" }),
|
||||||
|
margin: {
|
||||||
|
right: 8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
...panel,
|
||||||
|
channelName: text(theme, "sans", "primary", { weight: "bold" }),
|
||||||
|
channelNameHash: {
|
||||||
|
...text(theme, "sans", "muted"),
|
||||||
|
padding: {
|
||||||
|
right: 8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
channelSelect: {
|
||||||
|
header: {
|
||||||
|
...channelSelectItem(theme, "primary", false),
|
||||||
|
padding: {
|
||||||
|
bottom: 4,
|
||||||
|
left: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
item: channelSelectItem(theme, "secondary", false),
|
||||||
|
hoveredItem: channelSelectItem(theme, "secondary", true),
|
||||||
|
activeItem: channelSelectItem(theme, "primary", false),
|
||||||
|
hoveredActiveItem: channelSelectItem(theme, "primary", true),
|
||||||
|
menu: {
|
||||||
|
background: backgroundColor(theme, 500),
|
||||||
|
cornerRadius: 6,
|
||||||
|
padding: 4,
|
||||||
|
border: border(theme, "primary"),
|
||||||
|
shadow: shadow(theme),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
signInPrompt: text(theme, "sans", "secondary", { underline: true }),
|
||||||
|
hoveredSignInPrompt: text(theme, "sans", "primary", { underline: true }),
|
||||||
|
message,
|
||||||
|
pendingMessage: {
|
||||||
|
...message,
|
||||||
|
body: {
|
||||||
|
...message.body,
|
||||||
|
color: theme.textColor.muted.value,
|
||||||
|
},
|
||||||
|
sender: {
|
||||||
|
...message.sender,
|
||||||
|
color: theme.textColor.muted.value,
|
||||||
|
},
|
||||||
|
timestamp: {
|
||||||
|
...message.timestamp,
|
||||||
|
color: theme.textColor.muted.value,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
inputEditor: {
|
||||||
|
background: backgroundColor(theme, 500),
|
||||||
|
cornerRadius: 6,
|
||||||
|
text: text(theme, "mono", "primary"),
|
||||||
|
placeholderText: text(theme, "mono", "placeholder", { size: "sm" }),
|
||||||
|
selection: player(theme, 1).selection,
|
||||||
|
border: border(theme, "secondary"),
|
||||||
|
padding: {
|
||||||
|
bottom: 7,
|
||||||
|
left: 8,
|
||||||
|
right: 8,
|
||||||
|
top: 7,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,89 +5,89 @@ import { Color } from "../utils/color";
|
||||||
|
|
||||||
export type TextColor = keyof Theme["textColor"];
|
export type TextColor = keyof Theme["textColor"];
|
||||||
export function text(
|
export function text(
|
||||||
theme: Theme,
|
theme: Theme,
|
||||||
fontFamily: keyof typeof fontFamilies,
|
fontFamily: keyof typeof fontFamilies,
|
||||||
color: TextColor,
|
color: TextColor,
|
||||||
properties?: {
|
properties?: {
|
||||||
size?: keyof typeof fontSizes;
|
size?: keyof typeof fontSizes;
|
||||||
weight?: FontWeight;
|
weight?: FontWeight;
|
||||||
underline?: boolean;
|
underline?: boolean;
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
let size = fontSizes[properties?.size || "sm"].value;
|
let size = fontSizes[properties?.size || "sm"].value;
|
||||||
return {
|
return {
|
||||||
family: fontFamilies[fontFamily].value,
|
family: fontFamilies[fontFamily].value,
|
||||||
color: theme.textColor[color].value,
|
color: theme.textColor[color].value,
|
||||||
...properties,
|
...properties,
|
||||||
size,
|
size,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export function textColor(theme: Theme, color: TextColor) {
|
export function textColor(theme: Theme, color: TextColor) {
|
||||||
return theme.textColor[color].value;
|
return theme.textColor[color].value;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BorderColor = keyof Theme["borderColor"];
|
export type BorderColor = keyof Theme["borderColor"];
|
||||||
export interface BorderOptions {
|
export interface BorderOptions {
|
||||||
width?: number;
|
width?: number;
|
||||||
top?: boolean;
|
top?: boolean;
|
||||||
bottom?: boolean;
|
bottom?: boolean;
|
||||||
left?: boolean;
|
left?: boolean;
|
||||||
right?: boolean;
|
right?: boolean;
|
||||||
overlay?: boolean;
|
overlay?: boolean;
|
||||||
}
|
}
|
||||||
export function border(
|
export function border(
|
||||||
theme: Theme,
|
theme: Theme,
|
||||||
color: BorderColor,
|
color: BorderColor,
|
||||||
options?: BorderOptions
|
options?: BorderOptions
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
color: borderColor(theme, color),
|
color: borderColor(theme, color),
|
||||||
width: 1,
|
width: 1,
|
||||||
...options,
|
...options,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export function borderColor(theme: Theme, color: BorderColor) {
|
export function borderColor(theme: Theme, color: BorderColor) {
|
||||||
return theme.borderColor[color].value;
|
return theme.borderColor[color].value;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type IconColor = keyof Theme["iconColor"];
|
export type IconColor = keyof Theme["iconColor"];
|
||||||
export function iconColor(theme: Theme, color: IconColor) {
|
export function iconColor(theme: Theme, color: IconColor) {
|
||||||
return theme.iconColor[color].value;
|
return theme.iconColor[color].value;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PlayerIndex = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
|
export type PlayerIndex = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
|
||||||
export interface Player {
|
export interface Player {
|
||||||
selection: {
|
selection: {
|
||||||
cursor: Color;
|
cursor: Color;
|
||||||
selection: Color;
|
selection: Color;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export function player(
|
export function player(
|
||||||
theme: Theme,
|
theme: Theme,
|
||||||
playerNumber: PlayerIndex,
|
playerNumber: PlayerIndex,
|
||||||
): Player {
|
): Player {
|
||||||
return {
|
return {
|
||||||
selection: {
|
selection: {
|
||||||
cursor: theme.player[playerNumber].cursorColor.value,
|
cursor: theme.player[playerNumber].cursorColor.value,
|
||||||
selection: theme.player[playerNumber].selectionColor.value,
|
selection: theme.player[playerNumber].selectionColor.value,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BackgroundColor = keyof Theme["backgroundColor"];
|
export type BackgroundColor = keyof Theme["backgroundColor"];
|
||||||
export type BackgroundState = keyof BackgroundColorSet;
|
export type BackgroundState = keyof BackgroundColorSet;
|
||||||
export function backgroundColor(
|
export function backgroundColor(
|
||||||
theme: Theme,
|
theme: Theme,
|
||||||
name: BackgroundColor,
|
name: BackgroundColor,
|
||||||
state?: BackgroundState,
|
state?: BackgroundState,
|
||||||
): Color {
|
): Color {
|
||||||
return theme.backgroundColor[name][state || "base"].value;
|
return theme.backgroundColor[name][state || "base"].value;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function shadow(theme: Theme) {
|
export function shadow(theme: Theme) {
|
||||||
return {
|
return {
|
||||||
blur: 16,
|
blur: 16,
|
||||||
color: chroma("black").alpha(theme.shadowAlpha.value).hex(),
|
color: chroma("black").alpha(theme.shadowAlpha.value).hex(),
|
||||||
offset: [0, 2],
|
offset: [0, 2],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,60 +3,60 @@ import { panel } from "./app";
|
||||||
import { backgroundColor, borderColor, text } from "./components";
|
import { backgroundColor, borderColor, text } from "./components";
|
||||||
|
|
||||||
export default function(theme: Theme) {
|
export default function(theme: Theme) {
|
||||||
const project = {
|
const project = {
|
||||||
guestAvatarSpacing: 4,
|
guestAvatarSpacing: 4,
|
||||||
height: 24,
|
height: 24,
|
||||||
guestAvatar: {
|
guestAvatar: {
|
||||||
cornerRadius: 8,
|
cornerRadius: 8,
|
||||||
width: 14,
|
width: 14,
|
||||||
},
|
},
|
||||||
name: {
|
name: {
|
||||||
...text(theme, "mono", "placeholder", { size: "sm" }),
|
...text(theme, "mono", "placeholder", { size: "sm" }),
|
||||||
margin: {
|
margin: {
|
||||||
right: 6,
|
right: 6,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
padding: {
|
padding: {
|
||||||
left: 8,
|
left: 8,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const sharedProject = {
|
const sharedProject = {
|
||||||
...project,
|
...project,
|
||||||
background: backgroundColor(theme, 300),
|
background: backgroundColor(theme, 300),
|
||||||
cornerRadius: 6,
|
cornerRadius: 6,
|
||||||
name: {
|
name: {
|
||||||
...project.name,
|
...project.name,
|
||||||
...text(theme, "mono", "secondary", { size: "sm" }),
|
...text(theme, "mono", "secondary", { size: "sm" }),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...panel,
|
...panel,
|
||||||
hostRowHeight: 28,
|
hostRowHeight: 28,
|
||||||
treeBranchColor: borderColor(theme, "muted"),
|
treeBranchColor: borderColor(theme, "muted"),
|
||||||
treeBranchWidth: 1,
|
treeBranchWidth: 1,
|
||||||
hostAvatar: {
|
hostAvatar: {
|
||||||
cornerRadius: 10,
|
cornerRadius: 10,
|
||||||
width: 18,
|
width: 18,
|
||||||
},
|
},
|
||||||
hostUsername: {
|
hostUsername: {
|
||||||
...text(theme, "mono", "primary", { size: "sm" }),
|
...text(theme, "mono", "primary", { size: "sm" }),
|
||||||
padding: {
|
padding: {
|
||||||
left: 8,
|
left: 8,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
project,
|
project,
|
||||||
sharedProject,
|
sharedProject,
|
||||||
hoveredSharedProject: {
|
hoveredSharedProject: {
|
||||||
...sharedProject,
|
...sharedProject,
|
||||||
background: backgroundColor(theme, 300, "hovered"),
|
background: backgroundColor(theme, 300, "hovered"),
|
||||||
cornerRadius: 6,
|
cornerRadius: 6,
|
||||||
},
|
},
|
||||||
unsharedProject: project,
|
unsharedProject: project,
|
||||||
hoveredUnsharedProject: {
|
hoveredUnsharedProject: {
|
||||||
...project,
|
...project,
|
||||||
cornerRadius: 6,
|
cornerRadius: 6,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,146 +1,146 @@
|
||||||
import Theme from "../themes/theme";
|
import Theme from "../themes/theme";
|
||||||
import {
|
import {
|
||||||
backgroundColor,
|
backgroundColor,
|
||||||
border,
|
border,
|
||||||
iconColor,
|
iconColor,
|
||||||
player,
|
player,
|
||||||
text,
|
text,
|
||||||
TextColor
|
TextColor
|
||||||
} from "./components";
|
} from "./components";
|
||||||
|
|
||||||
export default function editor(theme: Theme) {
|
export default function editor(theme: Theme) {
|
||||||
const autocompleteItem = {
|
const autocompleteItem = {
|
||||||
cornerRadius: 6,
|
cornerRadius: 6,
|
||||||
padding: {
|
padding: {
|
||||||
bottom: 2,
|
bottom: 2,
|
||||||
left: 6,
|
left: 6,
|
||||||
right: 6,
|
right: 6,
|
||||||
top: 2,
|
top: 2,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function diagnostic(theme: Theme, color: TextColor) {
|
|
||||||
return {
|
|
||||||
textScaleFactor: 0.857,
|
|
||||||
header: {
|
|
||||||
border: border(theme, "primary", {
|
|
||||||
top: true,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
message: {
|
|
||||||
text: text(theme, "sans", color, { size: "sm" }),
|
|
||||||
highlightText: text(theme, "sans", color, {
|
|
||||||
size: "sm",
|
|
||||||
weight: "bold",
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
function diagnostic(theme: Theme, color: TextColor) {
|
||||||
return {
|
return {
|
||||||
// textColor: theme.syntax.primary.color,
|
textScaleFactor: 0.857,
|
||||||
textColor: theme.syntax.primary.color.value,
|
header: {
|
||||||
background: backgroundColor(theme, 500),
|
border: border(theme, "primary", {
|
||||||
activeLineBackground: theme.editor.line.active.value,
|
top: true,
|
||||||
codeActionsIndicator: iconColor(theme, "muted"),
|
}),
|
||||||
diffBackgroundDeleted: backgroundColor(theme, "error"),
|
},
|
||||||
diffBackgroundInserted: backgroundColor(theme, "ok"),
|
message: {
|
||||||
documentHighlightReadBackground: theme.editor.highlight.occurrence.value,
|
text: text(theme, "sans", color, { size: "sm" }),
|
||||||
documentHighlightWriteBackground: theme.editor.highlight.activeOccurrence.value,
|
highlightText: text(theme, "sans", color, {
|
||||||
errorColor: theme.textColor.error.value,
|
size: "sm",
|
||||||
gutterBackground: backgroundColor(theme, 500),
|
weight: "bold",
|
||||||
gutterPaddingFactor: 3.5,
|
}),
|
||||||
highlightedLineBackground: theme.editor.line.highlighted.value,
|
},
|
||||||
lineNumber: theme.editor.gutter.primary.value,
|
|
||||||
lineNumberActive: theme.editor.gutter.active.value,
|
|
||||||
renameFade: 0.6,
|
|
||||||
unnecessaryCodeFade: 0.5,
|
|
||||||
selection: player(theme, 1).selection,
|
|
||||||
guestSelections: [
|
|
||||||
player(theme, 2).selection,
|
|
||||||
player(theme, 3).selection,
|
|
||||||
player(theme, 4).selection,
|
|
||||||
player(theme, 5).selection,
|
|
||||||
player(theme, 6).selection,
|
|
||||||
player(theme, 7).selection,
|
|
||||||
player(theme, 8).selection,
|
|
||||||
],
|
|
||||||
autocomplete: {
|
|
||||||
background: backgroundColor(theme, 500),
|
|
||||||
cornerRadius: 6,
|
|
||||||
padding: 6,
|
|
||||||
border: border(theme, "secondary"),
|
|
||||||
item: autocompleteItem,
|
|
||||||
hoveredItem: {
|
|
||||||
...autocompleteItem,
|
|
||||||
background: backgroundColor(theme, 500, "hovered"),
|
|
||||||
},
|
|
||||||
margin: {
|
|
||||||
left: -14,
|
|
||||||
},
|
|
||||||
matchHighlight: text(theme, "mono", "feature"),
|
|
||||||
selectedItem: {
|
|
||||||
...autocompleteItem,
|
|
||||||
background: backgroundColor(theme, 500, "active"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
diagnosticHeader: {
|
|
||||||
background: backgroundColor(theme, 300),
|
|
||||||
iconWidthFactor: 1.5,
|
|
||||||
textScaleFactor: 0.857, // NateQ: Will we need dynamic sizing for text? If so let's create tokens for these.
|
|
||||||
border: border(theme, "secondary", {
|
|
||||||
bottom: true,
|
|
||||||
top: true,
|
|
||||||
}),
|
|
||||||
code: {
|
|
||||||
...text(theme, "mono", "muted", { size: "sm" }),
|
|
||||||
margin: {
|
|
||||||
left: 10,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
message: {
|
|
||||||
highlightText: text(theme, "sans", "primary", {
|
|
||||||
size: "sm",
|
|
||||||
weight: "bold",
|
|
||||||
}),
|
|
||||||
text: text(theme, "sans", "secondary", { size: "sm" }),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
diagnosticPathHeader: {
|
|
||||||
background: theme.editor.line.active.value,
|
|
||||||
textScaleFactor: 0.857,
|
|
||||||
filename: text(theme, "mono", "primary", { size: "sm" }),
|
|
||||||
path: {
|
|
||||||
...text(theme, "mono", "muted", { size: "sm" }),
|
|
||||||
margin: {
|
|
||||||
left: 12,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
errorDiagnostic: diagnostic(theme, "error"),
|
|
||||||
warningDiagnostic: diagnostic(theme, "warning"),
|
|
||||||
informationDiagnostic: diagnostic(theme, "info"),
|
|
||||||
hintDiagnostic: diagnostic(theme, "info"),
|
|
||||||
invalidErrorDiagnostic: diagnostic(theme, "muted"),
|
|
||||||
invalidHintDiagnostic: diagnostic(theme, "muted"),
|
|
||||||
invalidInformationDiagnostic: diagnostic(theme, "muted"),
|
|
||||||
invalidWarningDiagnostic: diagnostic(theme, "muted"),
|
|
||||||
syntax: {
|
|
||||||
keyword: theme.syntax.keyword.color.value,
|
|
||||||
function: theme.syntax.function.color.value,
|
|
||||||
string: theme.syntax.string.color.value,
|
|
||||||
type: theme.syntax.type.color.value,
|
|
||||||
number: theme.syntax.number.color.value,
|
|
||||||
comment: theme.syntax.comment.color.value,
|
|
||||||
property: theme.syntax.property.color.value,
|
|
||||||
variant: theme.syntax.variant.color.value,
|
|
||||||
constant: theme.syntax.constant.color.value,
|
|
||||||
title: { color: theme.syntax.title.color.value, weight: "bold" },
|
|
||||||
emphasis: theme.textColor.feature.value,
|
|
||||||
"emphasis.strong": { color: theme.textColor.feature.value, weight: "bold" },
|
|
||||||
link_uri: { color: theme.syntax.linkUrl.color.value, underline: true },
|
|
||||||
link_text: { color: theme.syntax.linkText.color.value, italic: true },
|
|
||||||
list_marker: theme.syntax.punctuation.color.value,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
// textColor: theme.syntax.primary.color,
|
||||||
|
textColor: theme.syntax.primary.color.value,
|
||||||
|
background: backgroundColor(theme, 500),
|
||||||
|
activeLineBackground: theme.editor.line.active.value,
|
||||||
|
codeActionsIndicator: iconColor(theme, "muted"),
|
||||||
|
diffBackgroundDeleted: backgroundColor(theme, "error"),
|
||||||
|
diffBackgroundInserted: backgroundColor(theme, "ok"),
|
||||||
|
documentHighlightReadBackground: theme.editor.highlight.occurrence.value,
|
||||||
|
documentHighlightWriteBackground: theme.editor.highlight.activeOccurrence.value,
|
||||||
|
errorColor: theme.textColor.error.value,
|
||||||
|
gutterBackground: backgroundColor(theme, 500),
|
||||||
|
gutterPaddingFactor: 3.5,
|
||||||
|
highlightedLineBackground: theme.editor.line.highlighted.value,
|
||||||
|
lineNumber: theme.editor.gutter.primary.value,
|
||||||
|
lineNumberActive: theme.editor.gutter.active.value,
|
||||||
|
renameFade: 0.6,
|
||||||
|
unnecessaryCodeFade: 0.5,
|
||||||
|
selection: player(theme, 1).selection,
|
||||||
|
guestSelections: [
|
||||||
|
player(theme, 2).selection,
|
||||||
|
player(theme, 3).selection,
|
||||||
|
player(theme, 4).selection,
|
||||||
|
player(theme, 5).selection,
|
||||||
|
player(theme, 6).selection,
|
||||||
|
player(theme, 7).selection,
|
||||||
|
player(theme, 8).selection,
|
||||||
|
],
|
||||||
|
autocomplete: {
|
||||||
|
background: backgroundColor(theme, 500),
|
||||||
|
cornerRadius: 6,
|
||||||
|
padding: 6,
|
||||||
|
border: border(theme, "secondary"),
|
||||||
|
item: autocompleteItem,
|
||||||
|
hoveredItem: {
|
||||||
|
...autocompleteItem,
|
||||||
|
background: backgroundColor(theme, 500, "hovered"),
|
||||||
|
},
|
||||||
|
margin: {
|
||||||
|
left: -14,
|
||||||
|
},
|
||||||
|
matchHighlight: text(theme, "mono", "feature"),
|
||||||
|
selectedItem: {
|
||||||
|
...autocompleteItem,
|
||||||
|
background: backgroundColor(theme, 500, "active"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
diagnosticHeader: {
|
||||||
|
background: backgroundColor(theme, 300),
|
||||||
|
iconWidthFactor: 1.5,
|
||||||
|
textScaleFactor: 0.857, // NateQ: Will we need dynamic sizing for text? If so let's create tokens for these.
|
||||||
|
border: border(theme, "secondary", {
|
||||||
|
bottom: true,
|
||||||
|
top: true,
|
||||||
|
}),
|
||||||
|
code: {
|
||||||
|
...text(theme, "mono", "muted", { size: "sm" }),
|
||||||
|
margin: {
|
||||||
|
left: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
highlightText: text(theme, "sans", "primary", {
|
||||||
|
size: "sm",
|
||||||
|
weight: "bold",
|
||||||
|
}),
|
||||||
|
text: text(theme, "sans", "secondary", { size: "sm" }),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
diagnosticPathHeader: {
|
||||||
|
background: theme.editor.line.active.value,
|
||||||
|
textScaleFactor: 0.857,
|
||||||
|
filename: text(theme, "mono", "primary", { size: "sm" }),
|
||||||
|
path: {
|
||||||
|
...text(theme, "mono", "muted", { size: "sm" }),
|
||||||
|
margin: {
|
||||||
|
left: 12,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errorDiagnostic: diagnostic(theme, "error"),
|
||||||
|
warningDiagnostic: diagnostic(theme, "warning"),
|
||||||
|
informationDiagnostic: diagnostic(theme, "info"),
|
||||||
|
hintDiagnostic: diagnostic(theme, "info"),
|
||||||
|
invalidErrorDiagnostic: diagnostic(theme, "muted"),
|
||||||
|
invalidHintDiagnostic: diagnostic(theme, "muted"),
|
||||||
|
invalidInformationDiagnostic: diagnostic(theme, "muted"),
|
||||||
|
invalidWarningDiagnostic: diagnostic(theme, "muted"),
|
||||||
|
syntax: {
|
||||||
|
keyword: theme.syntax.keyword.color.value,
|
||||||
|
function: theme.syntax.function.color.value,
|
||||||
|
string: theme.syntax.string.color.value,
|
||||||
|
type: theme.syntax.type.color.value,
|
||||||
|
number: theme.syntax.number.color.value,
|
||||||
|
comment: theme.syntax.comment.color.value,
|
||||||
|
property: theme.syntax.property.color.value,
|
||||||
|
variant: theme.syntax.variant.color.value,
|
||||||
|
constant: theme.syntax.constant.color.value,
|
||||||
|
title: { color: theme.syntax.title.color.value, weight: "bold" },
|
||||||
|
emphasis: theme.textColor.feature.value,
|
||||||
|
"emphasis.strong": { color: theme.textColor.feature.value, weight: "bold" },
|
||||||
|
link_uri: { color: theme.syntax.linkUrl.color.value, underline: true },
|
||||||
|
link_text: { color: theme.syntax.linkText.color.value, italic: true },
|
||||||
|
list_marker: theme.syntax.punctuation.color.value,
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,34 +4,34 @@ import { panel } from "./app";
|
||||||
import { backgroundColor, iconColor, text, TextColor } from "./components";
|
import { backgroundColor, iconColor, text, TextColor } from "./components";
|
||||||
|
|
||||||
export default function projectPanel(theme: Theme) {
|
export default function projectPanel(theme: Theme) {
|
||||||
function entry(theme: Theme, textColor: TextColor, background?: Color) {
|
function entry(theme: Theme, textColor: TextColor, background?: Color) {
|
||||||
return {
|
|
||||||
height: 22,
|
|
||||||
background,
|
|
||||||
iconColor: iconColor(theme, "muted"),
|
|
||||||
iconSize: 8,
|
|
||||||
iconSpacing: 8,
|
|
||||||
text: text(theme, "mono", textColor, { size: "sm" }),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...panel,
|
height: 22,
|
||||||
entry: entry(theme, "secondary"),
|
background,
|
||||||
hoveredEntry: entry(
|
iconColor: iconColor(theme, "muted"),
|
||||||
theme,
|
iconSize: 8,
|
||||||
"secondary",
|
iconSpacing: 8,
|
||||||
backgroundColor(theme, 300, "hovered")
|
text: text(theme, "mono", textColor, { size: "sm" }),
|
||||||
),
|
|
||||||
selectedEntry: entry(theme, "primary"),
|
|
||||||
hoveredSelectedEntry: entry(
|
|
||||||
theme,
|
|
||||||
"primary",
|
|
||||||
backgroundColor(theme, 300, "hovered")
|
|
||||||
),
|
|
||||||
padding: {
|
|
||||||
top: 6,
|
|
||||||
left: 12,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...panel,
|
||||||
|
entry: entry(theme, "secondary"),
|
||||||
|
hoveredEntry: entry(
|
||||||
|
theme,
|
||||||
|
"secondary",
|
||||||
|
backgroundColor(theme, 300, "hovered")
|
||||||
|
),
|
||||||
|
selectedEntry: entry(theme, "primary"),
|
||||||
|
hoveredSelectedEntry: entry(
|
||||||
|
theme,
|
||||||
|
"primary",
|
||||||
|
backgroundColor(theme, 300, "hovered")
|
||||||
|
),
|
||||||
|
padding: {
|
||||||
|
top: 6,
|
||||||
|
left: 12,
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,78 +2,78 @@ import Theme from "../themes/theme";
|
||||||
import { backgroundColor, border, player, text } from "./components";
|
import { backgroundColor, border, player, text } from "./components";
|
||||||
|
|
||||||
export default function search(theme: Theme) {
|
export default function search(theme: Theme) {
|
||||||
const optionButton = {
|
const optionButton = {
|
||||||
...text(theme, "mono", "secondary"),
|
...text(theme, "mono", "secondary"),
|
||||||
background: backgroundColor(theme, 300),
|
background: backgroundColor(theme, 300),
|
||||||
cornerRadius: 6,
|
cornerRadius: 6,
|
||||||
border: border(theme, "primary"),
|
border: border(theme, "primary"),
|
||||||
margin: {
|
margin: {
|
||||||
left: 1,
|
left: 1,
|
||||||
right: 1,
|
right: 1,
|
||||||
},
|
},
|
||||||
padding: {
|
padding: {
|
||||||
bottom: 1,
|
bottom: 1,
|
||||||
left: 6,
|
left: 6,
|
||||||
right: 6,
|
right: 6,
|
||||||
top: 1,
|
top: 1,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const editor = {
|
const editor = {
|
||||||
background: backgroundColor(theme, 500),
|
background: backgroundColor(theme, 500),
|
||||||
cornerRadius: 6,
|
cornerRadius: 6,
|
||||||
minWidth: 200,
|
minWidth: 200,
|
||||||
maxWidth: 500,
|
maxWidth: 500,
|
||||||
placeholderText: text(theme, "mono", "placeholder"),
|
placeholderText: text(theme, "mono", "placeholder"),
|
||||||
selection: player(theme, 1).selection,
|
selection: player(theme, 1).selection,
|
||||||
text: text(theme, "mono", "primary"),
|
text: text(theme, "mono", "primary"),
|
||||||
border: border(theme, "secondary"),
|
border: border(theme, "secondary"),
|
||||||
margin: {
|
margin: {
|
||||||
right: 5,
|
right: 5,
|
||||||
},
|
},
|
||||||
padding: {
|
padding: {
|
||||||
top: 3,
|
top: 3,
|
||||||
bottom: 3,
|
bottom: 3,
|
||||||
left: 14,
|
left: 14,
|
||||||
right: 14,
|
right: 14,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
matchBackground: theme.editor.highlight.match.value,
|
matchBackground: theme.editor.highlight.match.value,
|
||||||
tabIconSpacing: 4,
|
tabIconSpacing: 4,
|
||||||
tabIconWidth: 14,
|
tabIconWidth: 14,
|
||||||
activeHoveredOptionButton: {
|
activeHoveredOptionButton: {
|
||||||
...optionButton,
|
...optionButton,
|
||||||
background: backgroundColor(theme, 100),
|
background: backgroundColor(theme, 100),
|
||||||
},
|
},
|
||||||
activeOptionButton: {
|
activeOptionButton: {
|
||||||
...optionButton,
|
...optionButton,
|
||||||
background: backgroundColor(theme, 100),
|
background: backgroundColor(theme, 100),
|
||||||
},
|
},
|
||||||
editor,
|
editor,
|
||||||
hoveredOptionButton: {
|
hoveredOptionButton: {
|
||||||
...optionButton,
|
...optionButton,
|
||||||
background: backgroundColor(theme, 100),
|
background: backgroundColor(theme, 100),
|
||||||
},
|
},
|
||||||
invalidEditor: {
|
invalidEditor: {
|
||||||
...editor,
|
...editor,
|
||||||
border: border(theme, "error"),
|
border: border(theme, "error"),
|
||||||
},
|
},
|
||||||
matchIndex: {
|
matchIndex: {
|
||||||
...text(theme, "mono", "muted"),
|
...text(theme, "mono", "muted"),
|
||||||
padding: 6,
|
padding: 6,
|
||||||
},
|
},
|
||||||
optionButton,
|
optionButton,
|
||||||
optionButtonGroup: {
|
optionButtonGroup: {
|
||||||
padding: {
|
padding: {
|
||||||
left: 2,
|
left: 2,
|
||||||
right: 2,
|
right: 2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
resultsStatus: {
|
resultsStatus: {
|
||||||
...text(theme, "mono", "primary"),
|
...text(theme, "mono", "primary"),
|
||||||
size: 18,
|
size: 18,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,58 +2,58 @@ import Theme from "../themes/theme";
|
||||||
import { backgroundColor, border, player, shadow, text } from "./components";
|
import { backgroundColor, border, player, shadow, text } from "./components";
|
||||||
|
|
||||||
export default function selectorModal(theme: Theme): Object {
|
export default function selectorModal(theme: Theme): Object {
|
||||||
const item = {
|
const item = {
|
||||||
padding: {
|
padding: {
|
||||||
bottom: 4,
|
bottom: 4,
|
||||||
left: 16,
|
left: 16,
|
||||||
right: 16,
|
right: 16,
|
||||||
top: 4,
|
top: 4,
|
||||||
},
|
},
|
||||||
cornerRadius: 6,
|
cornerRadius: 6,
|
||||||
text: text(theme, "sans", "secondary"),
|
text: text(theme, "sans", "secondary"),
|
||||||
highlightText: text(theme, "sans", "feature", { weight: "bold" }),
|
highlightText: text(theme, "sans", "feature", { weight: "bold" }),
|
||||||
};
|
};
|
||||||
|
|
||||||
const activeItem = {
|
const activeItem = {
|
||||||
...item,
|
...item,
|
||||||
background: backgroundColor(theme, 300, "active"),
|
background: backgroundColor(theme, 300, "active"),
|
||||||
text: text(theme, "sans", "primary"),
|
text: text(theme, "sans", "primary"),
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
background: backgroundColor(theme, 300),
|
background: backgroundColor(theme, 300),
|
||||||
cornerRadius: 6,
|
cornerRadius: 6,
|
||||||
padding: 8,
|
padding: 8,
|
||||||
item,
|
item,
|
||||||
activeItem,
|
activeItem,
|
||||||
border: border(theme, "primary"),
|
border: border(theme, "primary"),
|
||||||
empty: {
|
empty: {
|
||||||
text: text(theme, "sans", "placeholder"),
|
text: text(theme, "sans", "placeholder"),
|
||||||
padding: {
|
padding: {
|
||||||
bottom: 4,
|
bottom: 4,
|
||||||
left: 16,
|
left: 16,
|
||||||
right: 16,
|
right: 16,
|
||||||
top: 8,
|
top: 8,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
inputEditor: {
|
inputEditor: {
|
||||||
background: backgroundColor(theme, 500),
|
background: backgroundColor(theme, 500),
|
||||||
corner_radius: 6,
|
corner_radius: 6,
|
||||||
placeholderText: text(theme, "sans", "placeholder"),
|
placeholderText: text(theme, "sans", "placeholder"),
|
||||||
selection: player(theme, 1).selection,
|
selection: player(theme, 1).selection,
|
||||||
text: text(theme, "mono", "primary"),
|
text: text(theme, "mono", "primary"),
|
||||||
border: border(theme, "secondary"),
|
border: border(theme, "secondary"),
|
||||||
padding: {
|
padding: {
|
||||||
bottom: 7,
|
bottom: 7,
|
||||||
left: 16,
|
left: 16,
|
||||||
right: 16,
|
right: 16,
|
||||||
top: 7,
|
top: 7,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
margin: {
|
margin: {
|
||||||
bottom: 52,
|
bottom: 52,
|
||||||
top: 52,
|
top: 52,
|
||||||
},
|
},
|
||||||
shadow: shadow(theme),
|
shadow: shadow(theme),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,150 +1,150 @@
|
||||||
import Theme from "../themes/theme";
|
import Theme from "../themes/theme";
|
||||||
import { backgroundColor, border, borderColor, iconColor, text } from "./components";
|
import { backgroundColor, border, iconColor, text } from "./components";
|
||||||
|
|
||||||
export default function workspace(theme: Theme) {
|
export default function workspace(theme: Theme) {
|
||||||
const signInPrompt = {
|
const signInPrompt = {
|
||||||
...text(theme, "sans", "secondary", { size: "xs" }),
|
...text(theme, "sans", "secondary", { size: "xs" }),
|
||||||
underline: true,
|
underline: true,
|
||||||
padding: {
|
padding: {
|
||||||
right: 8,
|
right: 8,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const tab = {
|
const tab = {
|
||||||
height: 32,
|
height: 32,
|
||||||
background: backgroundColor(theme, 300),
|
background: backgroundColor(theme, 300),
|
||||||
iconClose: iconColor(theme, "muted"),
|
iconClose: iconColor(theme, "muted"),
|
||||||
iconCloseActive: iconColor(theme, "active"),
|
iconCloseActive: iconColor(theme, "active"),
|
||||||
iconConflict: iconColor(theme, "warning"),
|
iconConflict: iconColor(theme, "warning"),
|
||||||
iconDirty: iconColor(theme, "info"),
|
iconDirty: iconColor(theme, "info"),
|
||||||
iconWidth: 8,
|
iconWidth: 8,
|
||||||
spacing: 10,
|
spacing: 10,
|
||||||
text: text(theme, "mono", "secondary", { size: "sm" }),
|
text: text(theme, "mono", "secondary", { size: "sm" }),
|
||||||
border: border(theme, "primary", {
|
border: border(theme, "primary", {
|
||||||
left: true,
|
left: true,
|
||||||
bottom: true,
|
bottom: true,
|
||||||
overlay: true,
|
overlay: true,
|
||||||
}),
|
}),
|
||||||
padding: {
|
padding: {
|
||||||
left: 12,
|
left: 12,
|
||||||
right: 12,
|
right: 12,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const activeTab = {
|
const activeTab = {
|
||||||
...tab,
|
...tab,
|
||||||
background: backgroundColor(theme, 500),
|
background: backgroundColor(theme, 500),
|
||||||
text: text(theme, "mono", "active", { size: "sm" }),
|
text: text(theme, "mono", "active", { size: "sm" }),
|
||||||
|
border: {
|
||||||
|
...tab.border,
|
||||||
|
bottom: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const sidebarItem = {
|
||||||
|
height: 32,
|
||||||
|
iconColor: iconColor(theme, "secondary"),
|
||||||
|
iconSize: 18,
|
||||||
|
};
|
||||||
|
const sidebar = {
|
||||||
|
width: 30,
|
||||||
|
background: backgroundColor(theme, 300),
|
||||||
|
border: border(theme, "primary", { right: true }),
|
||||||
|
item: sidebarItem,
|
||||||
|
activeItem: {
|
||||||
|
...sidebarItem,
|
||||||
|
iconColor: iconColor(theme, "active"),
|
||||||
|
},
|
||||||
|
resizeHandle: {
|
||||||
|
background: border(theme, "primary").color,
|
||||||
|
padding: {
|
||||||
|
left: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
background: backgroundColor(theme, 300),
|
||||||
|
leaderBorderOpacity: 0.7,
|
||||||
|
leaderBorderWidth: 2.0,
|
||||||
|
tab,
|
||||||
|
activeTab,
|
||||||
|
leftSidebar: {
|
||||||
|
...sidebar,
|
||||||
|
border: border(theme, "primary", { right: true }),
|
||||||
|
},
|
||||||
|
rightSidebar: {
|
||||||
|
...sidebar,
|
||||||
|
border: border(theme, "primary", { left: true }),
|
||||||
|
},
|
||||||
|
paneDivider: {
|
||||||
|
color: border(theme, "secondary").color,
|
||||||
|
width: 1,
|
||||||
|
},
|
||||||
|
status_bar: {
|
||||||
|
height: 24,
|
||||||
|
itemSpacing: 8,
|
||||||
|
padding: {
|
||||||
|
left: 6,
|
||||||
|
right: 6,
|
||||||
|
},
|
||||||
|
border: border(theme, "primary", { top: true, overlay: true }),
|
||||||
|
cursorPosition: text(theme, "sans", "muted"),
|
||||||
|
diagnosticMessage: text(theme, "sans", "muted"),
|
||||||
|
lspMessage: text(theme, "sans", "muted"),
|
||||||
|
},
|
||||||
|
titlebar: {
|
||||||
|
avatarWidth: 18,
|
||||||
|
height: 32,
|
||||||
|
background: backgroundColor(theme, 100),
|
||||||
|
shareIconColor: iconColor(theme, "secondary"),
|
||||||
|
shareIconActiveColor: iconColor(theme, "feature"),
|
||||||
|
title: text(theme, "sans", "primary"),
|
||||||
|
avatar: {
|
||||||
|
cornerRadius: 10,
|
||||||
border: {
|
border: {
|
||||||
...tab.border,
|
color: "#00000088",
|
||||||
bottom: false,
|
width: 1,
|
||||||
},
|
},
|
||||||
};
|
},
|
||||||
|
avatarRibbon: {
|
||||||
const sidebarItem = {
|
height: 3,
|
||||||
height: 32,
|
width: 12,
|
||||||
iconColor: iconColor(theme, "secondary"),
|
// TODO: The background for this ideally should be
|
||||||
iconSize: 18,
|
// set with a token, not hardcoded in rust
|
||||||
};
|
},
|
||||||
const sidebar = {
|
border: border(theme, "primary", { bottom: true }),
|
||||||
width: 30,
|
signInPrompt,
|
||||||
background: backgroundColor(theme, 300),
|
hoveredSignInPrompt: {
|
||||||
border: border(theme, "primary", { right: true }),
|
...signInPrompt,
|
||||||
item: sidebarItem,
|
...text(theme, "sans", "active", { size: "xs" }),
|
||||||
activeItem: {
|
},
|
||||||
...sidebarItem,
|
offlineIcon: {
|
||||||
iconColor: iconColor(theme, "active"),
|
color: iconColor(theme, "secondary"),
|
||||||
|
width: 16,
|
||||||
|
padding: {
|
||||||
|
right: 4,
|
||||||
},
|
},
|
||||||
resizeHandle: {
|
},
|
||||||
background: border(theme, "primary").color,
|
outdatedWarning: {
|
||||||
padding: {
|
...text(theme, "sans", "warning"),
|
||||||
left: 1,
|
size: 13,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
toolbar: {
|
||||||
|
height: 34,
|
||||||
return {
|
background: backgroundColor(theme, 500),
|
||||||
background: backgroundColor(theme, 300),
|
border: border(theme, "secondary", { bottom: true }),
|
||||||
leaderBorderOpacity: 0.7,
|
itemSpacing: 8,
|
||||||
leaderBorderWidth: 2.0,
|
padding: { left: 16, right: 8, top: 4, bottom: 4 },
|
||||||
tab,
|
},
|
||||||
activeTab,
|
breadcrumbs: {
|
||||||
leftSidebar: {
|
...text(theme, "mono", "secondary"),
|
||||||
...sidebar,
|
padding: { left: 6 },
|
||||||
border: border(theme, "primary", { right: true }),
|
},
|
||||||
},
|
disconnectedOverlay: {
|
||||||
rightSidebar: {
|
...text(theme, "sans", "active"),
|
||||||
...sidebar,
|
background: "#000000aa",
|
||||||
border: border(theme, "primary", { left: true }),
|
},
|
||||||
},
|
};
|
||||||
paneDivider: {
|
|
||||||
color: border(theme, "secondary").color,
|
|
||||||
width: 1,
|
|
||||||
},
|
|
||||||
status_bar: {
|
|
||||||
height: 24,
|
|
||||||
itemSpacing: 8,
|
|
||||||
padding: {
|
|
||||||
left: 6,
|
|
||||||
right: 6,
|
|
||||||
},
|
|
||||||
border: border(theme, "primary", { top: true, overlay: true }),
|
|
||||||
cursorPosition: text(theme, "sans", "muted"),
|
|
||||||
diagnosticMessage: text(theme, "sans", "muted"),
|
|
||||||
lspMessage: text(theme, "sans", "muted"),
|
|
||||||
},
|
|
||||||
titlebar: {
|
|
||||||
avatarWidth: 18,
|
|
||||||
height: 32,
|
|
||||||
background: backgroundColor(theme, 100),
|
|
||||||
shareIconColor: iconColor(theme, "secondary"),
|
|
||||||
shareIconActiveColor: iconColor(theme, "feature"),
|
|
||||||
title: text(theme, "sans", "primary"),
|
|
||||||
avatar: {
|
|
||||||
cornerRadius: 10,
|
|
||||||
border: {
|
|
||||||
color: "#00000088",
|
|
||||||
width: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
avatarRibbon: {
|
|
||||||
height: 3,
|
|
||||||
width: 12,
|
|
||||||
// TODO: The background for this ideally should be
|
|
||||||
// set with a token, not hardcoded in rust
|
|
||||||
},
|
|
||||||
border: border(theme, "primary", { bottom: true }),
|
|
||||||
signInPrompt,
|
|
||||||
hoveredSignInPrompt: {
|
|
||||||
...signInPrompt,
|
|
||||||
...text(theme, "sans", "active", { size: "xs" }),
|
|
||||||
},
|
|
||||||
offlineIcon: {
|
|
||||||
color: iconColor(theme, "secondary"),
|
|
||||||
width: 16,
|
|
||||||
padding: {
|
|
||||||
right: 4,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
outdatedWarning: {
|
|
||||||
...text(theme, "sans", "warning"),
|
|
||||||
size: 13,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
toolbar: {
|
|
||||||
height: 34,
|
|
||||||
background: backgroundColor(theme, 500),
|
|
||||||
border: border(theme, "secondary", { bottom: true }),
|
|
||||||
itemSpacing: 8,
|
|
||||||
padding: { left: 16, right: 8, top: 4, bottom: 4 },
|
|
||||||
},
|
|
||||||
breadcrumbs: {
|
|
||||||
...text(theme, "mono", "secondary"),
|
|
||||||
padding: { left: 6 },
|
|
||||||
},
|
|
||||||
disconnectedOverlay: {
|
|
||||||
...text(theme, "sans", "active"),
|
|
||||||
background: "#000000aa",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,227 +3,227 @@ import { withOpacity } from "../utils/color";
|
||||||
import Theme, { buildPlayer, Syntax } from "./theme";
|
import Theme, { buildPlayer, Syntax } from "./theme";
|
||||||
|
|
||||||
const backgroundColor = {
|
const backgroundColor = {
|
||||||
100: {
|
100: {
|
||||||
base: colors.neutral[750],
|
base: colors.neutral[750],
|
||||||
hovered: colors.neutral[725],
|
hovered: colors.neutral[725],
|
||||||
active: colors.neutral[800],
|
active: colors.neutral[800],
|
||||||
focused: colors.neutral[675],
|
focused: colors.neutral[675],
|
||||||
},
|
},
|
||||||
300: {
|
300: {
|
||||||
base: colors.neutral[800],
|
base: colors.neutral[800],
|
||||||
hovered: colors.neutral[775],
|
hovered: colors.neutral[775],
|
||||||
active: colors.neutral[750],
|
active: colors.neutral[750],
|
||||||
focused: colors.neutral[775],
|
focused: colors.neutral[775],
|
||||||
},
|
},
|
||||||
500: {
|
500: {
|
||||||
base: colors.neutral[900],
|
base: colors.neutral[900],
|
||||||
hovered: withOpacity(colors.neutral[0], 0.08),
|
hovered: withOpacity(colors.neutral[0], 0.08),
|
||||||
active: withOpacity(colors.neutral[0], 0.12),
|
active: withOpacity(colors.neutral[0], 0.12),
|
||||||
focused: colors.neutral[825],
|
focused: colors.neutral[825],
|
||||||
},
|
},
|
||||||
ok: {
|
ok: {
|
||||||
base: colors.green[600],
|
base: colors.green[600],
|
||||||
hovered: colors.green[600],
|
hovered: colors.green[600],
|
||||||
active: colors.green[600],
|
active: colors.green[600],
|
||||||
focused: colors.green[600],
|
focused: colors.green[600],
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
base: colors.red[400],
|
base: colors.red[400],
|
||||||
hovered: colors.red[400],
|
hovered: colors.red[400],
|
||||||
active: colors.red[400],
|
active: colors.red[400],
|
||||||
focused: colors.red[400],
|
focused: colors.red[400],
|
||||||
},
|
},
|
||||||
warning: {
|
warning: {
|
||||||
base: colors.amber[300],
|
base: colors.amber[300],
|
||||||
hovered: colors.amber[300],
|
hovered: colors.amber[300],
|
||||||
active: colors.amber[300],
|
active: colors.amber[300],
|
||||||
focused: colors.amber[300],
|
focused: colors.amber[300],
|
||||||
},
|
},
|
||||||
info: {
|
info: {
|
||||||
base: colors.blue[500],
|
base: colors.blue[500],
|
||||||
hovered: colors.blue[500],
|
hovered: colors.blue[500],
|
||||||
active: colors.blue[500],
|
active: colors.blue[500],
|
||||||
focused: colors.blue[500],
|
focused: colors.blue[500],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const borderColor = {
|
const borderColor = {
|
||||||
primary: colors.neutral[875],
|
primary: colors.neutral[875],
|
||||||
secondary: colors.neutral[775],
|
secondary: colors.neutral[775],
|
||||||
muted: colors.neutral[675],
|
muted: colors.neutral[675],
|
||||||
focused: colors.neutral[500],
|
focused: colors.neutral[500],
|
||||||
active: colors.neutral[900],
|
active: colors.neutral[900],
|
||||||
ok: colors.green[500],
|
ok: colors.green[500],
|
||||||
error: colors.red[500],
|
error: colors.red[500],
|
||||||
warning: colors.amber[500],
|
warning: colors.amber[500],
|
||||||
info: colors.blue[500],
|
info: colors.blue[500],
|
||||||
};
|
};
|
||||||
|
|
||||||
const textColor = {
|
const textColor = {
|
||||||
primary: colors.neutral[50],
|
primary: colors.neutral[50],
|
||||||
secondary: colors.neutral[350],
|
secondary: colors.neutral[350],
|
||||||
muted: colors.neutral[450],
|
muted: colors.neutral[450],
|
||||||
placeholder: colors.neutral[650],
|
placeholder: colors.neutral[650],
|
||||||
active: colors.neutral[0],
|
active: colors.neutral[0],
|
||||||
//TODO: (design) define feature and it's correct value
|
//TODO: (design) define feature and it's correct value
|
||||||
feature: colors.sky[500],
|
feature: colors.sky[500],
|
||||||
ok: colors.green[600],
|
ok: colors.green[600],
|
||||||
error: colors.red[400],
|
error: colors.red[400],
|
||||||
warning: colors.amber[300],
|
warning: colors.amber[300],
|
||||||
info: colors.blue[500],
|
info: colors.blue[500],
|
||||||
};
|
};
|
||||||
|
|
||||||
const iconColor = {
|
const iconColor = {
|
||||||
primary: colors.neutral[200],
|
primary: colors.neutral[200],
|
||||||
secondary: colors.neutral[350],
|
secondary: colors.neutral[350],
|
||||||
muted: colors.neutral[600],
|
muted: colors.neutral[600],
|
||||||
placeholder: colors.neutral[700],
|
placeholder: colors.neutral[700],
|
||||||
active: colors.neutral[0],
|
active: colors.neutral[0],
|
||||||
//TODO: (design) define feature and it's correct value
|
//TODO: (design) define feature and it's correct value
|
||||||
feature: colors.blue[500],
|
feature: colors.blue[500],
|
||||||
ok: colors.green[600],
|
ok: colors.green[600],
|
||||||
error: colors.red[500],
|
error: colors.red[500],
|
||||||
warning: colors.amber[400],
|
warning: colors.amber[400],
|
||||||
info: colors.blue[600],
|
info: colors.blue[600],
|
||||||
};
|
};
|
||||||
|
|
||||||
const player = {
|
const player = {
|
||||||
1: buildPlayer(colors.blue[500]),
|
1: buildPlayer(colors.blue[500]),
|
||||||
2: buildPlayer(colors.lime[500]),
|
2: buildPlayer(colors.lime[500]),
|
||||||
3: buildPlayer(colors.fuschia[500]),
|
3: buildPlayer(colors.fuschia[500]),
|
||||||
4: buildPlayer(colors.orange[500]),
|
4: buildPlayer(colors.orange[500]),
|
||||||
5: buildPlayer(colors.purple[500]),
|
5: buildPlayer(colors.purple[500]),
|
||||||
6: buildPlayer(colors.teal[400]),
|
6: buildPlayer(colors.teal[400]),
|
||||||
7: buildPlayer(colors.pink[400]),
|
7: buildPlayer(colors.pink[400]),
|
||||||
8: buildPlayer(colors.yellow[400]),
|
8: buildPlayer(colors.yellow[400]),
|
||||||
};
|
};
|
||||||
|
|
||||||
const editor = {
|
const editor = {
|
||||||
background: backgroundColor[500].base,
|
background: backgroundColor[500].base,
|
||||||
indent_guide: borderColor.muted,
|
indent_guide: borderColor.muted,
|
||||||
indent_guide_active: borderColor.secondary,
|
indent_guide_active: borderColor.secondary,
|
||||||
line: {
|
line: {
|
||||||
active: withOpacity(colors.neutral[0], 0.07),
|
active: withOpacity(colors.neutral[0], 0.07),
|
||||||
highlighted: withOpacity(colors.neutral[0], 0.12),
|
highlighted: withOpacity(colors.neutral[0], 0.12),
|
||||||
inserted: backgroundColor.ok.active,
|
inserted: backgroundColor.ok.active,
|
||||||
deleted: backgroundColor.error.active,
|
deleted: backgroundColor.error.active,
|
||||||
modified: backgroundColor.info.active,
|
modified: backgroundColor.info.active,
|
||||||
},
|
},
|
||||||
highlight: {
|
highlight: {
|
||||||
selection: player[1].selectionColor,
|
selection: player[1].selectionColor,
|
||||||
occurrence: withOpacity(colors.neutral[0], 0.12),
|
occurrence: withOpacity(colors.neutral[0], 0.12),
|
||||||
activeOccurrence: withOpacity(colors.neutral[0], 0.16), // TODO: This is not correctly hooked up to occurences on the rust side
|
activeOccurrence: withOpacity(colors.neutral[0], 0.16), // TODO: This is not correctly hooked up to occurences on the rust side
|
||||||
matchingBracket: backgroundColor[500].active,
|
matchingBracket: backgroundColor[500].active,
|
||||||
match: withOpacity(colors.sky[500], 0.16),
|
match: withOpacity(colors.sky[500], 0.16),
|
||||||
activeMatch: withOpacity(colors.sky[800], 0.32),
|
activeMatch: withOpacity(colors.sky[800], 0.32),
|
||||||
related: backgroundColor[500].focused,
|
related: backgroundColor[500].focused,
|
||||||
},
|
},
|
||||||
gutter: {
|
gutter: {
|
||||||
primary: textColor.placeholder,
|
primary: textColor.placeholder,
|
||||||
active: textColor.active,
|
active: textColor.active,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const syntax: Syntax = {
|
const syntax: Syntax = {
|
||||||
primary: {
|
primary: {
|
||||||
color: colors.neutral[150],
|
color: colors.neutral[150],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
comment: {
|
comment: {
|
||||||
color: colors.neutral[300],
|
color: colors.neutral[300],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
punctuation: {
|
punctuation: {
|
||||||
color: colors.neutral[200],
|
color: colors.neutral[200],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
constant: {
|
constant: {
|
||||||
color: colors.neutral[150],
|
color: colors.neutral[150],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
keyword: {
|
keyword: {
|
||||||
color: colors.blue[400],
|
color: colors.blue[400],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
function: {
|
function: {
|
||||||
color: colors.yellow[200],
|
color: colors.yellow[200],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
type: {
|
type: {
|
||||||
color: colors.teal[300],
|
color: colors.teal[300],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
variant: {
|
variant: {
|
||||||
color: colors.sky[300],
|
color: colors.sky[300],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
property: {
|
property: {
|
||||||
color: colors.blue[400],
|
color: colors.blue[400],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
enum: {
|
enum: {
|
||||||
color: colors.orange[500],
|
color: colors.orange[500],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
operator: {
|
operator: {
|
||||||
color: colors.orange[500],
|
color: colors.orange[500],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
string: {
|
string: {
|
||||||
color: colors.orange[300],
|
color: colors.orange[300],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
number: {
|
number: {
|
||||||
color: colors.lime[300],
|
color: colors.lime[300],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
boolean: {
|
boolean: {
|
||||||
color: colors.lime[300],
|
color: colors.lime[300],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
predictive: {
|
predictive: {
|
||||||
color: textColor.muted,
|
color: textColor.muted,
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
color: colors.amber[500],
|
color: colors.amber[500],
|
||||||
weight: fontWeights.bold,
|
weight: fontWeights.bold,
|
||||||
},
|
},
|
||||||
emphasis: {
|
emphasis: {
|
||||||
color: textColor.active,
|
color: textColor.active,
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
emphasisStrong: {
|
emphasisStrong: {
|
||||||
color: textColor.active,
|
color: textColor.active,
|
||||||
weight: fontWeights.bold,
|
weight: fontWeights.bold,
|
||||||
},
|
},
|
||||||
linkUrl: {
|
linkUrl: {
|
||||||
color: colors.lime[500],
|
color: colors.lime[500],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
// TODO: add underline
|
// TODO: add underline
|
||||||
},
|
},
|
||||||
linkText: {
|
linkText: {
|
||||||
color: colors.orange[500],
|
color: colors.orange[500],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
// TODO: add italic
|
// TODO: add italic
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const shadowAlpha: NumberToken = {
|
const shadowAlpha: NumberToken = {
|
||||||
value: 0.32,
|
value: 0.32,
|
||||||
type: "number",
|
type: "number",
|
||||||
};
|
};
|
||||||
|
|
||||||
const theme: Theme = {
|
const theme: Theme = {
|
||||||
name: "dark",
|
name: "dark",
|
||||||
backgroundColor,
|
backgroundColor,
|
||||||
borderColor,
|
borderColor,
|
||||||
textColor,
|
textColor,
|
||||||
iconColor,
|
iconColor,
|
||||||
editor,
|
editor,
|
||||||
syntax,
|
syntax,
|
||||||
player,
|
player,
|
||||||
shadowAlpha,
|
shadowAlpha,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default theme;
|
export default theme;
|
||||||
|
|
|
@ -3,225 +3,225 @@ import { withOpacity } from "../utils/color";
|
||||||
import Theme, { buildPlayer, Syntax } from "./theme";
|
import Theme, { buildPlayer, Syntax } from "./theme";
|
||||||
|
|
||||||
const backgroundColor = {
|
const backgroundColor = {
|
||||||
100: {
|
100: {
|
||||||
base: colors.neutral[75],
|
base: colors.neutral[75],
|
||||||
hovered: colors.neutral[100],
|
hovered: colors.neutral[100],
|
||||||
active: colors.neutral[150],
|
active: colors.neutral[150],
|
||||||
focused: colors.neutral[100],
|
focused: colors.neutral[100],
|
||||||
},
|
},
|
||||||
300: {
|
300: {
|
||||||
base: colors.neutral[25],
|
base: colors.neutral[25],
|
||||||
hovered: colors.neutral[75],
|
hovered: colors.neutral[75],
|
||||||
active: colors.neutral[125],
|
active: colors.neutral[125],
|
||||||
focused: colors.neutral[75],
|
focused: colors.neutral[75],
|
||||||
},
|
},
|
||||||
500: {
|
500: {
|
||||||
base: colors.neutral[0],
|
base: colors.neutral[0],
|
||||||
hovered: withOpacity(colors.neutral[900], 0.03),
|
hovered: withOpacity(colors.neutral[900], 0.03),
|
||||||
active: withOpacity(colors.neutral[900], 0.06),
|
active: withOpacity(colors.neutral[900], 0.06),
|
||||||
focused: colors.neutral[50],
|
focused: colors.neutral[50],
|
||||||
},
|
},
|
||||||
ok: {
|
ok: {
|
||||||
base: colors.green[100],
|
base: colors.green[100],
|
||||||
hovered: colors.green[100],
|
hovered: colors.green[100],
|
||||||
active: colors.green[100],
|
active: colors.green[100],
|
||||||
focused: colors.green[100],
|
focused: colors.green[100],
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
base: colors.red[100],
|
base: colors.red[100],
|
||||||
hovered: colors.red[100],
|
hovered: colors.red[100],
|
||||||
active: colors.red[100],
|
active: colors.red[100],
|
||||||
focused: colors.red[100],
|
focused: colors.red[100],
|
||||||
},
|
},
|
||||||
warning: {
|
warning: {
|
||||||
base: colors.yellow[100],
|
base: colors.yellow[100],
|
||||||
hovered: colors.yellow[100],
|
hovered: colors.yellow[100],
|
||||||
active: colors.yellow[100],
|
active: colors.yellow[100],
|
||||||
focused: colors.yellow[100],
|
focused: colors.yellow[100],
|
||||||
},
|
},
|
||||||
info: {
|
info: {
|
||||||
base: colors.blue[100],
|
base: colors.blue[100],
|
||||||
hovered: colors.blue[100],
|
hovered: colors.blue[100],
|
||||||
active: colors.blue[100],
|
active: colors.blue[100],
|
||||||
focused: colors.blue[100],
|
focused: colors.blue[100],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const borderColor = {
|
const borderColor = {
|
||||||
primary: colors.neutral[150],
|
primary: colors.neutral[150],
|
||||||
secondary: colors.neutral[150],
|
secondary: colors.neutral[150],
|
||||||
muted: colors.neutral[100],
|
muted: colors.neutral[100],
|
||||||
focused: colors.neutral[100],
|
focused: colors.neutral[100],
|
||||||
active: colors.neutral[250],
|
active: colors.neutral[250],
|
||||||
ok: colors.green[200],
|
ok: colors.green[200],
|
||||||
error: colors.red[200],
|
error: colors.red[200],
|
||||||
warning: colors.yellow[200],
|
warning: colors.yellow[200],
|
||||||
info: colors.blue[200],
|
info: colors.blue[200],
|
||||||
};
|
};
|
||||||
|
|
||||||
const textColor = {
|
const textColor = {
|
||||||
primary: colors.neutral[750],
|
primary: colors.neutral[750],
|
||||||
secondary: colors.neutral[650],
|
secondary: colors.neutral[650],
|
||||||
muted: colors.neutral[550],
|
muted: colors.neutral[550],
|
||||||
placeholder: colors.neutral[450],
|
placeholder: colors.neutral[450],
|
||||||
active: colors.neutral[900],
|
active: colors.neutral[900],
|
||||||
feature: colors.indigo[600],
|
feature: colors.indigo[600],
|
||||||
ok: colors.green[500],
|
ok: colors.green[500],
|
||||||
error: colors.red[500],
|
error: colors.red[500],
|
||||||
warning: colors.yellow[500],
|
warning: colors.yellow[500],
|
||||||
info: colors.blue[500],
|
info: colors.blue[500],
|
||||||
};
|
};
|
||||||
|
|
||||||
const iconColor = {
|
const iconColor = {
|
||||||
primary: colors.neutral[700],
|
primary: colors.neutral[700],
|
||||||
secondary: colors.neutral[500],
|
secondary: colors.neutral[500],
|
||||||
muted: colors.neutral[350],
|
muted: colors.neutral[350],
|
||||||
placeholder: colors.neutral[300],
|
placeholder: colors.neutral[300],
|
||||||
active: colors.neutral[900],
|
active: colors.neutral[900],
|
||||||
feature: colors.indigo[500],
|
feature: colors.indigo[500],
|
||||||
ok: colors.green[600],
|
ok: colors.green[600],
|
||||||
error: colors.red[600],
|
error: colors.red[600],
|
||||||
warning: colors.yellow[400],
|
warning: colors.yellow[400],
|
||||||
info: colors.blue[600],
|
info: colors.blue[600],
|
||||||
};
|
};
|
||||||
|
|
||||||
const player = {
|
const player = {
|
||||||
1: buildPlayer(colors.blue[500]),
|
1: buildPlayer(colors.blue[500]),
|
||||||
2: buildPlayer(colors.emerald[400]),
|
2: buildPlayer(colors.emerald[400]),
|
||||||
3: buildPlayer(colors.fuschia[400]),
|
3: buildPlayer(colors.fuschia[400]),
|
||||||
4: buildPlayer(colors.orange[400]),
|
4: buildPlayer(colors.orange[400]),
|
||||||
5: buildPlayer(colors.purple[400]),
|
5: buildPlayer(colors.purple[400]),
|
||||||
6: buildPlayer(colors.teal[400]),
|
6: buildPlayer(colors.teal[400]),
|
||||||
7: buildPlayer(colors.pink[400]),
|
7: buildPlayer(colors.pink[400]),
|
||||||
8: buildPlayer(colors.yellow[400]),
|
8: buildPlayer(colors.yellow[400]),
|
||||||
};
|
};
|
||||||
|
|
||||||
const editor = {
|
const editor = {
|
||||||
background: backgroundColor[500].base,
|
background: backgroundColor[500].base,
|
||||||
indent_guide: borderColor.muted,
|
indent_guide: borderColor.muted,
|
||||||
indent_guide_active: borderColor.secondary,
|
indent_guide_active: borderColor.secondary,
|
||||||
line: {
|
line: {
|
||||||
active: withOpacity(colors.neutral[900], 0.06),
|
active: withOpacity(colors.neutral[900], 0.06),
|
||||||
highlighted: withOpacity(colors.neutral[900], 0.12),
|
highlighted: withOpacity(colors.neutral[900], 0.12),
|
||||||
inserted: backgroundColor.ok.active,
|
inserted: backgroundColor.ok.active,
|
||||||
deleted: backgroundColor.error.active,
|
deleted: backgroundColor.error.active,
|
||||||
modified: backgroundColor.info.active,
|
modified: backgroundColor.info.active,
|
||||||
},
|
},
|
||||||
highlight: {
|
highlight: {
|
||||||
selection: player[1].selectionColor,
|
selection: player[1].selectionColor,
|
||||||
occurrence: withOpacity(colors.neutral[900], 0.06),
|
occurrence: withOpacity(colors.neutral[900], 0.06),
|
||||||
activeOccurrence: withOpacity(colors.neutral[900], 0.16), // TODO: This is not hooked up to occurences on the rust side
|
activeOccurrence: withOpacity(colors.neutral[900], 0.16), // TODO: This is not hooked up to occurences on the rust side
|
||||||
matchingBracket: colors.neutral[0],
|
matchingBracket: colors.neutral[0],
|
||||||
match: withOpacity(colors.red[500], 0.2),
|
match: withOpacity(colors.red[500], 0.2),
|
||||||
activeMatch: withOpacity(colors.indigo[400], 0.36), // TODO: This is not hooked up to occurences on the rust side
|
activeMatch: withOpacity(colors.indigo[400], 0.36), // TODO: This is not hooked up to occurences on the rust side
|
||||||
related: colors.neutral[0],
|
related: colors.neutral[0],
|
||||||
},
|
},
|
||||||
gutter: {
|
gutter: {
|
||||||
primary: colors.neutral[300],
|
primary: colors.neutral[300],
|
||||||
active: textColor.active,
|
active: textColor.active,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const syntax: Syntax = {
|
const syntax: Syntax = {
|
||||||
primary: {
|
primary: {
|
||||||
color: colors.neutral[800],
|
color: colors.neutral[800],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
comment: {
|
comment: {
|
||||||
color: colors.neutral[500],
|
color: colors.neutral[500],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
punctuation: {
|
punctuation: {
|
||||||
color: colors.neutral[600],
|
color: colors.neutral[600],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
constant: {
|
constant: {
|
||||||
color: colors.neutral[800],
|
color: colors.neutral[800],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
keyword: {
|
keyword: {
|
||||||
color: colors.indigo[700],
|
color: colors.indigo[700],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
function: {
|
function: {
|
||||||
color: colors.orange[600],
|
color: colors.orange[600],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
type: {
|
type: {
|
||||||
color: colors.yellow[600],
|
color: colors.yellow[600],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
variant: {
|
variant: {
|
||||||
color: colors.rose[700],
|
color: colors.rose[700],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
property: {
|
property: {
|
||||||
color: colors.emerald[700],
|
color: colors.emerald[700],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
enum: {
|
enum: {
|
||||||
color: colors.red[500],
|
color: colors.red[500],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
operator: {
|
operator: {
|
||||||
color: colors.red[500],
|
color: colors.red[500],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
string: {
|
string: {
|
||||||
color: colors.red[500],
|
color: colors.red[500],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
number: {
|
number: {
|
||||||
color: colors.indigo[500],
|
color: colors.indigo[500],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
boolean: {
|
boolean: {
|
||||||
color: colors.red[500],
|
color: colors.red[500],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
predictive: {
|
predictive: {
|
||||||
color: textColor.placeholder,
|
color: textColor.placeholder,
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
color: colors.sky[500],
|
color: colors.sky[500],
|
||||||
weight: fontWeights.bold,
|
weight: fontWeights.bold,
|
||||||
},
|
},
|
||||||
emphasis: {
|
emphasis: {
|
||||||
color: textColor.active,
|
color: textColor.active,
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
},
|
},
|
||||||
emphasisStrong: {
|
emphasisStrong: {
|
||||||
color: textColor.active,
|
color: textColor.active,
|
||||||
weight: fontWeights.bold,
|
weight: fontWeights.bold,
|
||||||
},
|
},
|
||||||
linkUrl: {
|
linkUrl: {
|
||||||
color: colors.lime[500],
|
color: colors.lime[500],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
// TODO: add underline
|
// TODO: add underline
|
||||||
},
|
},
|
||||||
linkText: {
|
linkText: {
|
||||||
color: colors.red[500],
|
color: colors.red[500],
|
||||||
weight: fontWeights.normal,
|
weight: fontWeights.normal,
|
||||||
// TODO: add italic
|
// TODO: add italic
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const shadowAlpha: NumberToken = {
|
const shadowAlpha: NumberToken = {
|
||||||
value: 0.12,
|
value: 0.12,
|
||||||
type: "number",
|
type: "number",
|
||||||
};
|
};
|
||||||
|
|
||||||
const theme: Theme = {
|
const theme: Theme = {
|
||||||
name: "light",
|
name: "light",
|
||||||
backgroundColor,
|
backgroundColor,
|
||||||
borderColor,
|
borderColor,
|
||||||
textColor,
|
textColor,
|
||||||
iconColor,
|
iconColor,
|
||||||
editor,
|
editor,
|
||||||
syntax,
|
syntax,
|
||||||
player,
|
player,
|
||||||
shadowAlpha,
|
shadowAlpha,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default theme;
|
export default theme;
|
||||||
|
|
|
@ -2,144 +2,144 @@ import { ColorToken, FontWeightToken, NumberToken } from "../tokens";
|
||||||
import { withOpacity } from "../utils/color";
|
import { withOpacity } from "../utils/color";
|
||||||
|
|
||||||
export interface SyntaxHighlightStyle {
|
export interface SyntaxHighlightStyle {
|
||||||
color: ColorToken;
|
color: ColorToken;
|
||||||
weight: FontWeightToken;
|
weight: FontWeightToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Player {
|
export interface Player {
|
||||||
baseColor: ColorToken;
|
baseColor: ColorToken;
|
||||||
cursorColor: ColorToken;
|
cursorColor: ColorToken;
|
||||||
selectionColor: ColorToken;
|
selectionColor: ColorToken;
|
||||||
borderColor: ColorToken;
|
borderColor: ColorToken;
|
||||||
}
|
}
|
||||||
export function buildPlayer(
|
export function buildPlayer(
|
||||||
color: ColorToken,
|
color: ColorToken,
|
||||||
cursorOpacity?: number,
|
cursorOpacity?: number,
|
||||||
selectionOpacity?: number,
|
selectionOpacity?: number,
|
||||||
borderOpacity?: number
|
borderOpacity?: number
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
baseColor: color,
|
baseColor: color,
|
||||||
cursorColor: withOpacity(color, cursorOpacity || 1.0),
|
cursorColor: withOpacity(color, cursorOpacity || 1.0),
|
||||||
selectionColor: withOpacity(color, selectionOpacity || 0.24),
|
selectionColor: withOpacity(color, selectionOpacity || 0.24),
|
||||||
borderColor: withOpacity(color, borderOpacity || 0.8),
|
borderColor: withOpacity(color, borderOpacity || 0.8),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BackgroundColorSet {
|
export interface BackgroundColorSet {
|
||||||
base: ColorToken;
|
base: ColorToken;
|
||||||
hovered: ColorToken;
|
hovered: ColorToken;
|
||||||
active: ColorToken;
|
active: ColorToken;
|
||||||
focused: ColorToken;
|
focused: ColorToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Syntax {
|
export interface Syntax {
|
||||||
primary: SyntaxHighlightStyle;
|
primary: SyntaxHighlightStyle;
|
||||||
comment: SyntaxHighlightStyle;
|
comment: SyntaxHighlightStyle;
|
||||||
punctuation: SyntaxHighlightStyle;
|
punctuation: SyntaxHighlightStyle;
|
||||||
constant: SyntaxHighlightStyle;
|
constant: SyntaxHighlightStyle;
|
||||||
keyword: SyntaxHighlightStyle;
|
keyword: SyntaxHighlightStyle;
|
||||||
function: SyntaxHighlightStyle;
|
function: SyntaxHighlightStyle;
|
||||||
type: SyntaxHighlightStyle;
|
type: SyntaxHighlightStyle;
|
||||||
variant: SyntaxHighlightStyle;
|
variant: SyntaxHighlightStyle;
|
||||||
property: SyntaxHighlightStyle;
|
property: SyntaxHighlightStyle;
|
||||||
enum: SyntaxHighlightStyle;
|
enum: SyntaxHighlightStyle;
|
||||||
operator: SyntaxHighlightStyle;
|
operator: SyntaxHighlightStyle;
|
||||||
string: SyntaxHighlightStyle;
|
string: SyntaxHighlightStyle;
|
||||||
number: SyntaxHighlightStyle;
|
number: SyntaxHighlightStyle;
|
||||||
boolean: SyntaxHighlightStyle;
|
boolean: SyntaxHighlightStyle;
|
||||||
predictive: SyntaxHighlightStyle;
|
predictive: SyntaxHighlightStyle;
|
||||||
// TODO: Either move the following or rename
|
// TODO: Either move the following or rename
|
||||||
title: SyntaxHighlightStyle;
|
title: SyntaxHighlightStyle;
|
||||||
emphasis: SyntaxHighlightStyle;
|
emphasis: SyntaxHighlightStyle;
|
||||||
emphasisStrong: SyntaxHighlightStyle;
|
emphasisStrong: SyntaxHighlightStyle;
|
||||||
linkUrl: SyntaxHighlightStyle;
|
linkUrl: SyntaxHighlightStyle;
|
||||||
linkText: SyntaxHighlightStyle;
|
linkText: SyntaxHighlightStyle;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default interface Theme {
|
export default interface Theme {
|
||||||
name: string;
|
name: string;
|
||||||
backgroundColor: {
|
backgroundColor: {
|
||||||
100: BackgroundColorSet;
|
100: BackgroundColorSet;
|
||||||
300: BackgroundColorSet;
|
300: BackgroundColorSet;
|
||||||
500: BackgroundColorSet;
|
500: BackgroundColorSet;
|
||||||
ok: BackgroundColorSet;
|
ok: BackgroundColorSet;
|
||||||
error: BackgroundColorSet;
|
error: BackgroundColorSet;
|
||||||
warning: BackgroundColorSet;
|
warning: BackgroundColorSet;
|
||||||
info: BackgroundColorSet;
|
info: BackgroundColorSet;
|
||||||
|
};
|
||||||
|
borderColor: {
|
||||||
|
primary: ColorToken;
|
||||||
|
secondary: ColorToken;
|
||||||
|
muted: ColorToken;
|
||||||
|
focused: ColorToken;
|
||||||
|
active: ColorToken;
|
||||||
|
ok: ColorToken;
|
||||||
|
error: ColorToken;
|
||||||
|
warning: ColorToken;
|
||||||
|
info: ColorToken;
|
||||||
|
};
|
||||||
|
textColor: {
|
||||||
|
primary: ColorToken;
|
||||||
|
secondary: ColorToken;
|
||||||
|
muted: ColorToken;
|
||||||
|
placeholder: ColorToken;
|
||||||
|
active: ColorToken;
|
||||||
|
feature: ColorToken;
|
||||||
|
ok: ColorToken;
|
||||||
|
error: ColorToken;
|
||||||
|
warning: ColorToken;
|
||||||
|
info: ColorToken;
|
||||||
|
};
|
||||||
|
iconColor: {
|
||||||
|
primary: ColorToken;
|
||||||
|
secondary: ColorToken;
|
||||||
|
muted: ColorToken;
|
||||||
|
placeholder: ColorToken;
|
||||||
|
active: ColorToken;
|
||||||
|
feature: ColorToken;
|
||||||
|
ok: ColorToken;
|
||||||
|
error: ColorToken;
|
||||||
|
warning: ColorToken;
|
||||||
|
info: ColorToken;
|
||||||
|
};
|
||||||
|
editor: {
|
||||||
|
background: ColorToken;
|
||||||
|
indent_guide: ColorToken;
|
||||||
|
indent_guide_active: ColorToken;
|
||||||
|
line: {
|
||||||
|
active: ColorToken;
|
||||||
|
highlighted: ColorToken;
|
||||||
|
inserted: ColorToken;
|
||||||
|
deleted: ColorToken;
|
||||||
|
modified: ColorToken;
|
||||||
};
|
};
|
||||||
borderColor: {
|
highlight: {
|
||||||
primary: ColorToken;
|
selection: ColorToken;
|
||||||
secondary: ColorToken;
|
occurrence: ColorToken;
|
||||||
muted: ColorToken;
|
activeOccurrence: ColorToken;
|
||||||
focused: ColorToken;
|
matchingBracket: ColorToken;
|
||||||
active: ColorToken;
|
match: ColorToken;
|
||||||
ok: ColorToken;
|
activeMatch: ColorToken;
|
||||||
error: ColorToken;
|
related: ColorToken;
|
||||||
warning: ColorToken;
|
|
||||||
info: ColorToken;
|
|
||||||
};
|
};
|
||||||
textColor: {
|
gutter: {
|
||||||
primary: ColorToken;
|
primary: ColorToken;
|
||||||
secondary: ColorToken;
|
active: ColorToken;
|
||||||
muted: ColorToken;
|
|
||||||
placeholder: ColorToken;
|
|
||||||
active: ColorToken;
|
|
||||||
feature: ColorToken;
|
|
||||||
ok: ColorToken;
|
|
||||||
error: ColorToken;
|
|
||||||
warning: ColorToken;
|
|
||||||
info: ColorToken;
|
|
||||||
};
|
|
||||||
iconColor: {
|
|
||||||
primary: ColorToken;
|
|
||||||
secondary: ColorToken;
|
|
||||||
muted: ColorToken;
|
|
||||||
placeholder: ColorToken;
|
|
||||||
active: ColorToken;
|
|
||||||
feature: ColorToken;
|
|
||||||
ok: ColorToken;
|
|
||||||
error: ColorToken;
|
|
||||||
warning: ColorToken;
|
|
||||||
info: ColorToken;
|
|
||||||
};
|
|
||||||
editor: {
|
|
||||||
background: ColorToken;
|
|
||||||
indent_guide: ColorToken;
|
|
||||||
indent_guide_active: ColorToken;
|
|
||||||
line: {
|
|
||||||
active: ColorToken;
|
|
||||||
highlighted: ColorToken;
|
|
||||||
inserted: ColorToken;
|
|
||||||
deleted: ColorToken;
|
|
||||||
modified: ColorToken;
|
|
||||||
};
|
|
||||||
highlight: {
|
|
||||||
selection: ColorToken;
|
|
||||||
occurrence: ColorToken;
|
|
||||||
activeOccurrence: ColorToken;
|
|
||||||
matchingBracket: ColorToken;
|
|
||||||
match: ColorToken;
|
|
||||||
activeMatch: ColorToken;
|
|
||||||
related: ColorToken;
|
|
||||||
};
|
|
||||||
gutter: {
|
|
||||||
primary: ColorToken;
|
|
||||||
active: ColorToken;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
syntax: Syntax,
|
syntax: Syntax,
|
||||||
|
|
||||||
player: {
|
player: {
|
||||||
1: Player;
|
1: Player;
|
||||||
2: Player;
|
2: Player;
|
||||||
3: Player;
|
3: Player;
|
||||||
4: Player;
|
4: Player;
|
||||||
5: Player;
|
5: Player;
|
||||||
6: Player;
|
6: Player;
|
||||||
7: Player;
|
7: Player;
|
||||||
8: Player;
|
8: Player;
|
||||||
};
|
};
|
||||||
shadowAlpha: NumberToken;
|
shadowAlpha: NumberToken;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ export const fontSizes = {
|
||||||
xl: fontSize(20),
|
xl: fontSize(20),
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FontWeight =
|
export type FontWeight =
|
||||||
| "thin"
|
| "thin"
|
||||||
| "extra_light"
|
| "extra_light"
|
||||||
| "light"
|
| "light"
|
||||||
|
|
|
@ -4,49 +4,49 @@ import { ColorToken } from "../tokens";
|
||||||
export type Color = string;
|
export type Color = string;
|
||||||
export type ColorRampStep = { value: Color; type: "color"; step: number };
|
export type ColorRampStep = { value: Color; type: "color"; step: number };
|
||||||
export type ColorRamp = {
|
export type ColorRamp = {
|
||||||
[index: number]: ColorRampStep;
|
[index: number]: ColorRampStep;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function colorRamp(
|
export function colorRamp(
|
||||||
color: Color | [Color, Color],
|
color: Color | [Color, Color],
|
||||||
options?: { steps?: number; increment?: number; }
|
options?: { steps?: number; increment?: number; }
|
||||||
): ColorRamp {
|
): ColorRamp {
|
||||||
let scale: Scale;
|
let scale: Scale;
|
||||||
if (Array.isArray(color)) {
|
if (Array.isArray(color)) {
|
||||||
const [startColor, endColor] = color;
|
const [startColor, endColor] = color;
|
||||||
scale = chroma.scale([startColor, endColor]);
|
scale = chroma.scale([startColor, endColor]);
|
||||||
} else {
|
} else {
|
||||||
let hue = Math.round(chroma(color).hsl()[0]);
|
let hue = Math.round(chroma(color).hsl()[0]);
|
||||||
let startColor = chroma.hsl(hue, 0.88, 0.96);
|
let startColor = chroma.hsl(hue, 0.88, 0.96);
|
||||||
let endColor = chroma.hsl(hue, 0.68, 0.12);
|
let endColor = chroma.hsl(hue, 0.68, 0.12);
|
||||||
scale = chroma
|
scale = chroma
|
||||||
.scale([startColor, color, endColor])
|
.scale([startColor, color, endColor])
|
||||||
.domain([0, 0.5, 1])
|
.domain([0, 0.5, 1])
|
||||||
.mode("hsl")
|
.mode("hsl")
|
||||||
.gamma(1)
|
.gamma(1)
|
||||||
// .correctLightness(true)
|
// .correctLightness(true)
|
||||||
.padding([0, 0]);
|
.padding([0, 0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ramp: ColorRamp = {};
|
const ramp: ColorRamp = {};
|
||||||
const steps = options?.steps || 10;
|
const steps = options?.steps || 10;
|
||||||
const increment = options?.increment || 100;
|
const increment = options?.increment || 100;
|
||||||
|
|
||||||
scale.colors(steps, "hex").forEach((color, ix) => {
|
scale.colors(steps, "hex").forEach((color, ix) => {
|
||||||
const step = ix * increment;
|
const step = ix * increment;
|
||||||
ramp[step] = {
|
ramp[step] = {
|
||||||
value: color,
|
value: color,
|
||||||
step,
|
step,
|
||||||
type: "color",
|
type: "color",
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
return ramp;
|
return ramp;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function withOpacity(color: ColorToken, opacity: number): ColorToken {
|
export function withOpacity(color: ColorToken, opacity: number): ColorToken {
|
||||||
return {
|
return {
|
||||||
...color,
|
...color,
|
||||||
value: chroma(color.value).alpha(opacity).hex()
|
value: chroma(color.value).alpha(opacity).hex()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,32 +4,32 @@ import { snakeCase } from "case-anything";
|
||||||
|
|
||||||
// Typescript magic to convert any string from camelCase to snake_case at compile time
|
// Typescript magic to convert any string from camelCase to snake_case at compile time
|
||||||
type SnakeCase<S> =
|
type SnakeCase<S> =
|
||||||
S extends string ?
|
S extends string ?
|
||||||
S extends `${infer T}${infer U}` ?
|
S extends `${infer T}${infer U}` ?
|
||||||
`${T extends Capitalize<T> ? "_" : ""}${Lowercase<T>}${SnakeCase<U>}` :
|
`${T extends Capitalize<T> ? "_" : ""}${Lowercase<T>}${SnakeCase<U>}` :
|
||||||
S :
|
S :
|
||||||
S;
|
S;
|
||||||
|
|
||||||
type SnakeCased<Type> = {
|
type SnakeCased<Type> = {
|
||||||
[Property in keyof Type as SnakeCase<Property>]: SnakeCased<Type[Property]>
|
[Property in keyof Type as SnakeCase<Property>]: SnakeCased<Type[Property]>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function snakeCaseTree<T>(object: T): SnakeCased<T> {
|
export default function snakeCaseTree<T>(object: T): SnakeCased<T> {
|
||||||
const snakeObject: any = {};
|
const snakeObject: any = {};
|
||||||
for (const key in object) {
|
for (const key in object) {
|
||||||
snakeObject[snakeCase(key)] = snakeCaseValue(object[key]);
|
snakeObject[snakeCase(key)] = snakeCaseValue(object[key]);
|
||||||
}
|
}
|
||||||
return snakeObject;
|
return snakeObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
function snakeCaseValue(value: any): any {
|
function snakeCaseValue(value: any): any {
|
||||||
if (typeof value === "object") {
|
if (typeof value === "object") {
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
return value.map(snakeCaseValue);
|
return value.map(snakeCaseValue);
|
||||||
} else {
|
|
||||||
return snakeCaseTree(value);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return value;
|
return snakeCaseTree(value);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue