Merge branch 'main' into following-tests

This commit is contained in:
Conrad Irwin
2024-01-05 16:14:12 -07:00
224 changed files with 12664 additions and 23906 deletions
Generated
+11 -9
View File
@@ -576,8 +576,9 @@ dependencies = [
[[package]]
name = "async-task"
version = "4.0.3"
source = "git+https://github.com/zed-industries/async-task?rev=341b57d6de98cdfd7b418567b8de2022ca993a6e#341b57d6de98cdfd7b418567b8de2022ca993a6e"
version = "4.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799"
[[package]]
name = "async-tls"
@@ -1440,7 +1441,7 @@ dependencies = [
[[package]]
name = "collab"
version = "0.33.0"
version = "0.34.0"
dependencies = [
"anyhow",
"async-trait",
@@ -1925,11 +1926,12 @@ dependencies = [
[[package]]
name = "ctor"
version = "0.1.20"
source = "git+https://github.com/zed-industries/rust-ctor?rev=7f824cf6a7943885a649b579f33f9ac53f0d1db6#7f824cf6a7943885a649b579f33f9ac53f0d1db6"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30d2b3721e861707777e3195b0158f950ae6dc4a27e4d02ff9f67e3eb3de199e"
dependencies = [
"quote",
"syn 1.0.109",
"syn 2.0.37",
]
[[package]]
@@ -3064,7 +3066,6 @@ dependencies = [
"slotmap",
"smallvec",
"smol",
"sqlez",
"sum_tree",
"taffy",
"thiserror",
@@ -6845,7 +6846,6 @@ dependencies = [
"serde_json",
"serde_json_lenient",
"smallvec",
"sqlez",
"toml 0.5.11",
"tree-sitter",
"tree-sitter-json 0.19.0",
@@ -7837,6 +7837,7 @@ dependencies = [
"convert_case 0.6.0",
"gpui",
"indexmap 1.9.3",
"indoc",
"json_comments",
"log",
"palette",
@@ -8488,7 +8489,7 @@ dependencies = [
[[package]]
name = "tree-sitter-nu"
version = "0.0.1"
source = "git+https://github.com/nushell/tree-sitter-nu?rev=a0b80b2e21e5e39571252dc799e19eb89f1fc912#a0b80b2e21e5e39571252dc799e19eb89f1fc912"
source = "git+https://github.com/nushell/tree-sitter-nu?rev=26bbaecda0039df4067861ab38ea8ea169f7f5aa#26bbaecda0039df4067861ab38ea8ea169f7f5aa"
dependencies = [
"cc",
"tree-sitter",
@@ -9438,6 +9439,7 @@ dependencies = [
"serde_json",
"settings",
"smallvec",
"sqlez",
"terminal",
"theme",
"ui",
+2 -6
View File
@@ -92,10 +92,7 @@ resolver = "2"
anyhow = { version = "1.0.57" }
async-trait = { version = "0.1" }
async-compression = { version = "0.4", features = ["gzip", "futures-io"] }
# TODO: Switch back to the published version of `ctor` once:
# 1. A new version of `ctor` is published with this change: https://github.com/mmastrac/rust-ctor/pull/295
# 2. We've confirmed it's fine to update to the latest version of `ctor` (we're currently on v0.1.20).
ctor = { git = "https://github.com/zed-industries/rust-ctor", rev = "7f824cf6a7943885a649b579f33f9ac53f0d1db6" }
ctor = "0.2.6"
derive_more = { version = "0.99.17" }
env_logger = { version = "0.9" }
futures = { version = "0.3" }
@@ -158,13 +155,12 @@ tree-sitter-racket = { git = "https://github.com/zed-industries/tree-sitter-rack
tree-sitter-yaml = { git = "https://github.com/zed-industries/tree-sitter-yaml", rev = "f545a41f57502e1b5ddf2a6668896c1b0620f930"}
tree-sitter-lua = "0.0.14"
tree-sitter-nix = { git = "https://github.com/nix-community/tree-sitter-nix", rev = "66e3e9ce9180ae08fc57372061006ef83f0abde7" }
tree-sitter-nu = { git = "https://github.com/nushell/tree-sitter-nu", rev = "a0b80b2e21e5e39571252dc799e19eb89f1fc912"}
tree-sitter-nu = { git = "https://github.com/nushell/tree-sitter-nu", rev = "26bbaecda0039df4067861ab38ea8ea169f7f5aa"}
tree-sitter-vue = {git = "https://github.com/zed-industries/tree-sitter-vue", rev = "6608d9d60c386f19d80af7d8132322fa11199c42"}
tree-sitter-uiua = {git = "https://github.com/shnarazk/tree-sitter-uiua", rev = "9260f11be5900beda4ee6d1a24ab8ddfaf5a19b2"}
[patch.crates-io]
tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "31c40449749c4263a91a43593831b82229049a4c" }
async-task = { git = "https://github.com/zed-industries/async-task", rev = "341b57d6de98cdfd7b418567b8de2022ca993a6e" }
# wasmtime = { git = "https://github.com/bytecodealliance/wasmtime", rev = "v16.0.0" }
# TODO - Remove when a version is released with this PR: https://github.com/servo/core-foundation-rs/pull/457
+1 -1
View File
@@ -402,7 +402,7 @@
"cmd-r": "workspace::ToggleRightDock",
"cmd-j": "workspace::ToggleBottomDock",
"alt-cmd-y": "workspace::CloseAllDocks",
"cmd-shift-f": "workspace::NewSearch",
"cmd-shift-f": "workspace::DeploySearch",
"cmd-k cmd-t": "theme_selector::Toggle",
"cmd-k cmd-s": "zed::OpenKeymap",
"cmd-t": "project_symbols::Toggle",
+5 -5
View File
@@ -1286,8 +1286,8 @@ impl Panel for AssistantPanel {
}
}
fn icon(&self, _cx: &WindowContext) -> Option<Icon> {
Some(Icon::Ai)
fn icon(&self, cx: &WindowContext) -> Option<Icon> {
Some(Icon::Ai).filter(|_| AssistantSettings::get_global(cx).button)
}
fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> {
@@ -2827,8 +2827,8 @@ impl InlineAssistant {
fn handle_codegen_changed(&mut self, _: Model<Codegen>, cx: &mut ViewContext<Self>) {
let is_read_only = !self.codegen.read(cx).idle();
self.prompt_editor.update(cx, |editor, _cx| {
let was_read_only = editor.read_only();
self.prompt_editor.update(cx, |editor, cx| {
let was_read_only = editor.read_only(cx);
if was_read_only != is_read_only {
if is_read_only {
editor.set_read_only(true);
@@ -3063,7 +3063,7 @@ impl InlineAssistant {
fn render_prompt_editor(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let settings = ThemeSettings::get_global(cx);
let text_style = TextStyle {
color: if self.prompt_editor.read(cx).read_only() {
color: if self.prompt_editor.read(cx).read_only(cx) {
cx.theme().colors().text_disabled
} else {
cx.theme().colors().text
+2
View File
@@ -36,12 +36,14 @@ impl ParticipantLocation {
pub struct LocalParticipant {
pub projects: Vec<proto::ParticipantProject>,
pub active_project: Option<WeakModel<Project>>,
pub role: proto::ChannelRole,
}
#[derive(Clone, Debug)]
pub struct RemoteParticipant {
pub user: Arc<User>,
pub peer_id: proto::PeerId,
pub role: proto::ChannelRole,
pub projects: Vec<proto::ParticipantProject>,
pub location: ParticipantLocation,
pub participant_index: ParticipantIndex,
+52 -6
View File
@@ -247,14 +247,18 @@ impl Room {
let response = client.request(proto::CreateRoom {}).await?;
let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?;
let room = cx.new_model(|cx| {
Self::new(
let mut room = Self::new(
room_proto.id,
None,
response.live_kit_connection_info,
client,
user_store,
cx,
)
);
if let Some(participant) = room_proto.participants.first() {
room.local_participant.role = participant.role()
}
room
})?;
let initial_project_id = if let Some(initial_project) = initial_project {
@@ -606,6 +610,16 @@ impl Room {
.find(|p| p.peer_id == peer_id)
}
pub fn role_for_user(&self, user_id: u64) -> Option<proto::ChannelRole> {
self.remote_participants
.get(&user_id)
.map(|participant| participant.role)
}
pub fn local_participant_is_admin(&self) -> bool {
self.local_participant.role == proto::ChannelRole::Admin
}
pub fn pending_participants(&self) -> &[Arc<User>] {
&self.pending_participants
}
@@ -710,7 +724,20 @@ impl Room {
this.participant_user_ids.clear();
if let Some(participant) = local_participant {
let role = participant.role();
this.local_participant.projects = participant.projects;
if this.local_participant.role != role {
this.local_participant.role = role;
this.joined_projects.retain(|project| {
if let Some(project) = project.upgrade() {
project.update(cx, |project, _| project.set_role(role));
true
} else {
false
}
});
}
} else {
this.local_participant.projects.clear();
}
@@ -766,6 +793,7 @@ impl Room {
});
}
let role = participant.role();
let location = ParticipantLocation::from_proto(participant.location)
.unwrap_or(ParticipantLocation::External);
if let Some(remote_participant) =
@@ -774,8 +802,11 @@ impl Room {
remote_participant.peer_id = peer_id;
remote_participant.projects = participant.projects;
remote_participant.participant_index = participant_index;
if location != remote_participant.location {
if location != remote_participant.location
|| role != remote_participant.role
{
remote_participant.location = location;
remote_participant.role = role;
cx.emit(Event::ParticipantLocationChanged {
participant_id: peer_id,
});
@@ -789,6 +820,7 @@ impl Room {
peer_id,
projects: participant.projects,
location,
role,
muted: true,
speaking: false,
video_tracks: Default::default(),
@@ -1091,15 +1123,24 @@ impl Room {
) -> Task<Result<Model<Project>>> {
let client = self.client.clone();
let user_store = self.user_store.clone();
let role = self.local_participant.role;
cx.emit(Event::RemoteProjectJoined { project_id: id });
cx.spawn(move |this, mut cx| async move {
let project =
Project::remote(id, client, user_store, language_registry, fs, cx.clone()).await?;
let project = Project::remote(
id,
client,
user_store,
language_registry,
fs,
role,
cx.clone(),
)
.await?;
this.update(&mut cx, |this, cx| {
this.joined_projects.retain(|project| {
if let Some(project) = project.upgrade() {
!project.read(cx).is_read_only()
!project.read(cx).is_disconnected()
} else {
false
}
@@ -1224,6 +1265,11 @@ impl Room {
.unwrap_or(false)
}
pub fn read_only(&self) -> bool {
!(self.local_participant().role == proto::ChannelRole::Member
|| self.local_participant().role == proto::ChannelRole::Admin)
}
pub fn is_speaking(&self) -> bool {
self.live_kit
.as_ref()
+6 -1
View File
@@ -62,7 +62,12 @@ impl ChannelBuffer {
.collect::<Result<Vec<_>, _>>()?;
let buffer = cx.new_model(|_| {
language::Buffer::remote(response.buffer_id, response.replica_id as u16, base_text)
language::Buffer::remote(
response.buffer_id,
response.replica_id as u16,
channel.channel_buffer_capability(),
base_text,
)
})?;
buffer.update(&mut cx, |buffer, cx| buffer.apply_ops(operations, cx))??;
+7 -2
View File
@@ -11,6 +11,7 @@ use gpui::{
AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, SharedString, Task,
WeakModel,
};
use language::Capability;
use rpc::{
proto::{self, ChannelVisibility},
TypedEnvelope,
@@ -74,8 +75,12 @@ impl Channel {
slug.trim_matches(|c| c == '-').to_string()
}
pub fn can_edit_notes(&self) -> bool {
self.role == proto::ChannelRole::Member || self.role == proto::ChannelRole::Admin
pub fn channel_buffer_capability(&self) -> Capability {
if self.role == proto::ChannelRole::Member || self.role == proto::ChannelRole::Admin {
Capability::ReadWrite
} else {
Capability::ReadOnly
}
}
}
+1 -1
View File
@@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathan@zed.dev>"]
default-run = "collab"
edition = "2021"
name = "collab"
version = "0.33.0"
version = "0.34.0"
publish = false
[[bin]]
+1
View File
@@ -13,6 +13,7 @@ metadata:
annotations:
service.beta.kubernetes.io/do-loadbalancer-tls-ports: "443"
service.beta.kubernetes.io/do-loadbalancer-certificate-id: ${ZED_DO_CERTIFICATE_ID}
service.beta.kubernetes.io/do-loadbalancer-disable-lets-encrypt-dns-records: "true"
spec:
type: LoadBalancer
selector:
+1
View File
@@ -7,6 +7,7 @@ metadata:
annotations:
service.beta.kubernetes.io/do-loadbalancer-tls-ports: "443"
service.beta.kubernetes.io/do-loadbalancer-certificate-id: ${ZED_DO_CERTIFICATE_ID}
service.beta.kubernetes.io/do-loadbalancer-disable-lets-encrypt-dns-records: "true"
spec:
type: LoadBalancer
selector:
@@ -161,7 +161,8 @@ CREATE TABLE "room_participants" (
"calling_user_id" INTEGER NOT NULL REFERENCES users (id),
"calling_connection_id" INTEGER NOT NULL,
"calling_connection_server_id" INTEGER REFERENCES servers (id) ON DELETE SET NULL,
"participant_index" INTEGER
"participant_index" INTEGER,
"role" TEXT
);
CREATE UNIQUE INDEX "index_room_participants_on_user_id" ON "room_participants" ("user_id");
CREATE INDEX "index_room_participants_on_room_id" ON "room_participants" ("room_id");
@@ -0,0 +1 @@
ALTER TABLE room_participants ADD COLUMN role TEXT;
+8
View File
@@ -132,6 +132,14 @@ impl ChannelRole {
Admin | Member | Banned => false,
}
}
pub fn can_share_projects(&self) -> bool {
use ChannelRole::*;
match self {
Admin | Member => true,
Guest | Banned => false,
}
}
}
impl From<proto::ChannelRole> for ChannelRole {
+30 -29
View File
@@ -129,51 +129,52 @@ impl Database {
.await?,
);
debug_assert!(
self.channel_role_for_user(&channel, user_id, &*tx).await? == role
);
} else if channel.visibility == ChannelVisibility::Public {
role = Some(ChannelRole::Guest);
let channel_to_join = self
.public_ancestors_including_self(&channel, &*tx)
.await?
.first()
.cloned()
.unwrap_or(channel.clone());
channel_member::Entity::insert(channel_member::ActiveModel {
id: ActiveValue::NotSet,
channel_id: ActiveValue::Set(channel_to_join.id),
user_id: ActiveValue::Set(user_id),
accepted: ActiveValue::Set(true),
role: ActiveValue::Set(ChannelRole::Guest),
})
.exec(&*tx)
.await?;
accept_invite_result = Some(
self.calculate_membership_updated(&channel_to_join, user_id, &*tx)
.await?,
);
debug_assert!(
self.channel_role_for_user(&channel, user_id, &*tx).await? == role
);
}
}
if channel.visibility == ChannelVisibility::Public {
role = Some(ChannelRole::Guest);
let channel_to_join = self
.public_ancestors_including_self(&channel, &*tx)
.await?
.first()
.cloned()
.unwrap_or(channel.clone());
channel_member::Entity::insert(channel_member::ActiveModel {
id: ActiveValue::NotSet,
channel_id: ActiveValue::Set(channel_to_join.id),
user_id: ActiveValue::Set(user_id),
accepted: ActiveValue::Set(true),
role: ActiveValue::Set(ChannelRole::Guest),
})
.exec(&*tx)
.await?;
accept_invite_result = Some(
self.calculate_membership_updated(&channel_to_join, user_id, &*tx)
.await?,
);
debug_assert!(self.channel_role_for_user(&channel, user_id, &*tx).await? == role);
}
if role.is_none() || role == Some(ChannelRole::Banned) {
Err(anyhow!("not allowed"))?
}
let role = role.unwrap();
let live_kit_room = format!("channel-{}", nanoid::nanoid!(30));
let room_id = self
.get_or_create_channel_room(channel_id, &live_kit_room, environment, &*tx)
.await?;
self.join_channel_room_internal(room_id, user_id, connection, &*tx)
self.join_channel_room_internal(room_id, user_id, connection, role, &*tx)
.await
.map(|jr| (jr, accept_invite_result, role.unwrap()))
.map(|jr| (jr, accept_invite_result, role))
})
.await
}
+7
View File
@@ -46,6 +46,13 @@ impl Database {
if participant.room_id != room_id {
return Err(anyhow!("shared project on unexpected room"))?;
}
if !participant
.role
.unwrap_or(ChannelRole::Member)
.can_share_projects()
{
return Err(anyhow!("guests cannot share projects"))?;
}
let project = project::ActiveModel {
room_id: ActiveValue::set(participant.room_id),
+37 -3
View File
@@ -131,7 +131,12 @@ impl Database {
connection.owner_id as i32,
))),
participant_index: ActiveValue::set(Some(0)),
..Default::default()
role: ActiveValue::set(Some(ChannelRole::Admin)),
id: ActiveValue::NotSet,
location_kind: ActiveValue::NotSet,
location_project_id: ActiveValue::NotSet,
initial_project_id: ActiveValue::NotSet,
}
.insert(&*tx)
.await?;
@@ -151,6 +156,22 @@ impl Database {
initial_project_id: Option<ProjectId>,
) -> Result<RoomGuard<(proto::Room, proto::IncomingCall)>> {
self.room_transaction(room_id, |tx| async move {
let caller = room_participant::Entity::find()
.filter(
room_participant::Column::UserId
.eq(calling_user_id)
.and(room_participant::Column::RoomId.eq(room_id)),
)
.one(&*tx)
.await?
.ok_or_else(|| anyhow!("user is not in the room"))?;
let called_user_role = match caller.role.unwrap_or(ChannelRole::Member) {
ChannelRole::Admin | ChannelRole::Member => ChannelRole::Member,
ChannelRole::Guest => ChannelRole::Guest,
ChannelRole::Banned => return Err(anyhow!("banned users cannot invite").into()),
};
room_participant::ActiveModel {
room_id: ActiveValue::set(room_id),
user_id: ActiveValue::set(called_user_id),
@@ -162,7 +183,13 @@ impl Database {
calling_connection.owner_id as i32,
))),
initial_project_id: ActiveValue::set(initial_project_id),
..Default::default()
role: ActiveValue::set(Some(called_user_role)),
id: ActiveValue::NotSet,
answering_connection_id: ActiveValue::NotSet,
answering_connection_server_id: ActiveValue::NotSet,
location_kind: ActiveValue::NotSet,
location_project_id: ActiveValue::NotSet,
}
.insert(&*tx)
.await?;
@@ -384,6 +411,7 @@ impl Database {
room_id: RoomId,
user_id: UserId,
connection: ConnectionId,
role: ChannelRole,
tx: &DatabaseTransaction,
) -> Result<JoinRoom> {
let participant_index = self
@@ -404,7 +432,11 @@ impl Database {
connection.owner_id as i32,
))),
participant_index: ActiveValue::Set(Some(participant_index)),
..Default::default()
role: ActiveValue::set(Some(role)),
id: ActiveValue::NotSet,
location_kind: ActiveValue::NotSet,
location_project_id: ActiveValue::NotSet,
initial_project_id: ActiveValue::NotSet,
}])
.on_conflict(
OnConflict::columns([room_participant::Column::UserId])
@@ -413,6 +445,7 @@ impl Database {
room_participant::Column::AnsweringConnectionServerId,
room_participant::Column::AnsweringConnectionLost,
room_participant::Column::ParticipantIndex,
room_participant::Column::Role,
])
.to_owned(),
)
@@ -1134,6 +1167,7 @@ impl Database {
projects: Default::default(),
location: Some(proto::ParticipantLocation { variant: location }),
participant_index: participant_index as u32,
role: db_participant.role.unwrap_or(ChannelRole::Member).into(),
},
);
} else {
@@ -1,4 +1,4 @@
use crate::db::{ProjectId, RoomId, RoomParticipantId, ServerId, UserId};
use crate::db::{ChannelRole, ProjectId, RoomId, RoomParticipantId, ServerId, UserId};
use rpc::ConnectionId;
use sea_orm::entity::prelude::*;
@@ -19,6 +19,7 @@ pub struct Model {
pub calling_connection_id: i32,
pub calling_connection_server_id: Option<ServerId>,
pub participant_index: Option<i32>,
pub role: Option<ChannelRole>,
}
impl Model {
+1
View File
@@ -2,6 +2,7 @@ use call::Room;
use gpui::{Model, TestAppContext};
mod channel_buffer_tests;
mod channel_guest_tests;
mod channel_message_tests;
mod channel_tests;
mod editor_tests;
@@ -0,0 +1,86 @@
use crate::tests::TestServer;
use call::ActiveCall;
use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext};
use rpc::proto;
use workspace::Workspace;
#[gpui::test]
async fn test_channel_guests(
executor: BackgroundExecutor,
cx_a: &mut TestAppContext,
cx_b: &mut TestAppContext,
) {
let mut server = TestServer::start(executor.clone()).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
let channel_id = server
.make_channel("the-channel", None, (&client_a, cx_a), &mut [])
.await;
client_a
.channel_store()
.update(cx_a, |channel_store, cx| {
channel_store.set_channel_visibility(channel_id, proto::ChannelVisibility::Public, cx)
})
.await
.unwrap();
client_a
.fs()
.insert_tree(
"/a",
serde_json::json!({
"a.txt": "a-contents",
}),
)
.await;
let active_call_a = cx_a.read(ActiveCall::global);
// Client A shares a project in the channel
active_call_a
.update(cx_a, |call, cx| call.join_channel(channel_id, cx))
.await
.unwrap();
let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
let project_id = active_call_a
.update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
.await
.unwrap();
cx_a.executor().run_until_parked();
// Client B joins channel A as a guest
cx_b.update(|cx| workspace::join_channel(channel_id, client_b.app_state.clone(), None, cx))
.await
.unwrap();
// b should be following a in the shared project.
// B is a guest,
cx_a.executor().run_until_parked();
// todo!() the test window does not call activation handlers
// correctly yet, so this API does not work.
// let project_b = active_call_b.read_with(cx_b, |call, _| {
// call.location()
// .unwrap()
// .upgrade()
// .expect("should not be weak")
// });
let window_b = cx_b.update(|cx| cx.active_window().unwrap());
let cx_b = &mut VisualTestContext::from_window(window_b, cx_b);
let workspace_b = window_b
.downcast::<Workspace>()
.unwrap()
.root_view(cx_b)
.unwrap();
let project_b = workspace_b.update(cx_b, |workspace, _| workspace.project().clone());
assert_eq!(
project_b.read_with(cx_b, |project, _| project.remote_id()),
Some(project_id),
);
assert!(project_b.read_with(cx_b, |project, _| project.is_read_only()))
}
+14 -12
View File
@@ -19,6 +19,7 @@ use project::{
search::SearchQuery, DiagnosticSummary, FormatTrigger, HoverBlockKind, Project, ProjectPath,
};
use rand::prelude::*;
use rpc::proto::ChannelRole;
use serde_json::json;
use settings::SettingsStore;
use std::{
@@ -1380,7 +1381,7 @@ async fn test_unshare_project(
.unwrap();
executor.run_until_parked();
assert!(project_b.read_with(cx_b, |project, _| project.is_read_only()));
assert!(project_b.read_with(cx_b, |project, _| project.is_disconnected()));
// Client C opens the project.
let project_c = client_c.build_remote_project(project_id, cx_c).await;
@@ -1393,7 +1394,7 @@ async fn test_unshare_project(
assert!(worktree_a.read_with(cx_a, |tree, _| !tree.as_local().unwrap().is_shared()));
assert!(project_c.read_with(cx_c, |project, _| project.is_read_only()));
assert!(project_c.read_with(cx_c, |project, _| project.is_disconnected()));
// Client C can open the project again after client A re-shares.
let project_id = active_call_a
@@ -1419,7 +1420,7 @@ async fn test_unshare_project(
project_a.read_with(cx_a, |project, _| assert!(!project.is_shared()));
project_c2.read_with(cx_c, |project, _| {
assert!(project.is_read_only());
assert!(project.is_disconnected());
assert!(project.collaborators().is_empty());
});
}
@@ -1551,7 +1552,7 @@ async fn test_project_reconnect(
});
project_b1.read_with(cx_b, |project, _| {
assert!(!project.is_read_only());
assert!(!project.is_disconnected());
assert_eq!(project.collaborators().len(), 1);
});
@@ -1653,7 +1654,7 @@ async fn test_project_reconnect(
});
project_b1.read_with(cx_b, |project, cx| {
assert!(!project.is_read_only());
assert!(!project.is_disconnected());
assert_eq!(
project
.worktree_for_id(worktree1_id, cx)
@@ -1687,9 +1688,9 @@ async fn test_project_reconnect(
);
});
project_b2.read_with(cx_b, |project, _| assert!(project.is_read_only()));
project_b2.read_with(cx_b, |project, _| assert!(project.is_disconnected()));
project_b3.read_with(cx_b, |project, _| assert!(!project.is_read_only()));
project_b3.read_with(cx_b, |project, _| assert!(!project.is_disconnected()));
buffer_a1.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "WaZ"));
@@ -1746,7 +1747,7 @@ async fn test_project_reconnect(
executor.run_until_parked();
project_b1.read_with(cx_b, |project, cx| {
assert!(!project.is_read_only());
assert!(!project.is_disconnected());
assert_eq!(
project
.worktree_for_id(worktree1_id, cx)
@@ -1780,7 +1781,7 @@ async fn test_project_reconnect(
);
});
project_b3.read_with(cx_b, |project, _| assert!(project.is_read_only()));
project_b3.read_with(cx_b, |project, _| assert!(project.is_disconnected()));
buffer_a1.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "WXaYZ"));
@@ -3535,7 +3536,7 @@ async fn test_leaving_project(
});
project_b2.read_with(cx_b, |project, _| {
assert!(project.is_read_only());
assert!(project.is_disconnected());
});
project_c.read_with(cx_c, |project, _| {
@@ -3550,6 +3551,7 @@ async fn test_leaving_project(
client_b.user_store().clone(),
client_b.language_registry().clone(),
FakeFs::new(cx.background_executor().clone()),
ChannelRole::Member,
cx,
)
})
@@ -3568,11 +3570,11 @@ async fn test_leaving_project(
});
project_b2.read_with(cx_b, |project, _| {
assert!(project.is_read_only());
assert!(project.is_disconnected());
});
project_c.read_with(cx_c, |project, _| {
assert!(project.is_read_only());
assert!(project.is_disconnected());
});
}
@@ -1149,7 +1149,7 @@ impl RandomizedTest for ProjectCollaborationTest {
Some((project, cx))
});
if !guest_project.is_read_only() {
if !guest_project.is_disconnected() {
if let Some((host_project, host_cx)) = host_project {
let host_worktree_snapshots =
host_project.read_with(host_cx, |host_project, cx| {
@@ -1236,7 +1236,7 @@ impl RandomizedTest for ProjectCollaborationTest {
let buffers = client.buffers().clone();
for (guest_project, guest_buffers) in &buffers {
let project_id = if guest_project.read_with(client_cx, |project, _| {
project.is_local() || project.is_read_only()
project.is_local() || project.is_disconnected()
}) {
continue;
} else {
@@ -518,7 +518,7 @@ impl<T: RandomizedTest> TestPlan<T> {
for project in client.remote_projects().iter() {
project.read_with(&client_cx, |project, _| {
assert!(
project.is_read_only(),
project.is_disconnected(),
"project {:?} should be read only",
project.remote_id()
)
+4 -11
View File
@@ -138,12 +138,6 @@ impl ChannelView {
editor.set_collaboration_hub(Box::new(ChannelBufferCollaborationHub(
channel_buffer.clone(),
)));
editor.set_read_only(
!channel_buffer
.read(cx)
.channel(cx)
.is_some_and(|c| c.can_edit_notes()),
);
editor
});
let _editor_event_subscription =
@@ -178,8 +172,7 @@ impl ChannelView {
cx.notify();
}),
ChannelBufferEvent::ChannelChanged => {
self.editor.update(cx, |editor, cx| {
editor.set_read_only(!self.channel(cx).is_some_and(|c| c.can_edit_notes()));
self.editor.update(cx, |_, cx| {
cx.emit(editor::EditorEvent::TitleChanged);
cx.notify()
});
@@ -254,11 +247,11 @@ impl Item for ChannelView {
fn tab_content(&self, _: Option<usize>, selected: bool, cx: &WindowContext) -> AnyElement {
let label = if let Some(channel) = self.channel(cx) {
match (
channel.can_edit_notes(),
self.channel_buffer.read(cx).buffer().read(cx).read_only(),
self.channel_buffer.read(cx).is_connected(),
) {
(true, true) => format!("#{}", channel.name),
(false, true) => format!("#{} (read-only)", channel.name),
(false, true) => format!("#{}", channel.name),
(true, true) => format!("#{} (read-only)", channel.name),
(_, false) => format!("#{} (disconnected)", channel.name),
}
} else {
+6 -2
View File
@@ -607,8 +607,12 @@ impl Panel for ChatPanel {
"ChatPanel"
}
fn icon(&self, _cx: &WindowContext) -> Option<ui::Icon> {
Some(ui::Icon::MessageBubbles)
fn icon(&self, cx: &WindowContext) -> Option<ui::Icon> {
if !is_channels_feature_enabled(cx) {
return None;
}
Some(ui::Icon::MessageBubbles).filter(|_| ChatPanelSettings::get_global(cx).button)
}
fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> {
+95 -10
View File
@@ -151,6 +151,10 @@ enum ListEntry {
peer_id: Option<PeerId>,
is_last: bool,
},
GuestCount {
count: usize,
has_visible_participants: bool,
},
IncomingRequest(Arc<User>),
OutgoingRequest(Arc<User>),
ChannelInvite(Arc<Channel>),
@@ -380,10 +384,14 @@ impl CollabPanel {
if !self.collapsed_sections.contains(&Section::ActiveCall) {
let room = room.read(cx);
let mut guest_count_ix = 0;
let mut guest_count = if room.read_only() { 1 } else { 0 };
let mut non_guest_count = if room.read_only() { 0 } else { 1 };
if let Some(channel_id) = room.channel_id() {
self.entries.push(ListEntry::ChannelNotes { channel_id });
self.entries.push(ListEntry::ChannelChat { channel_id })
self.entries.push(ListEntry::ChannelChat { channel_id });
guest_count_ix = self.entries.len();
}
// Populate the active user.
@@ -402,7 +410,7 @@ impl CollabPanel {
&Default::default(),
executor.clone(),
));
if !matches.is_empty() {
if !matches.is_empty() && !room.read_only() {
let user_id = user.id;
self.entries.push(ListEntry::CallParticipant {
user,
@@ -430,13 +438,23 @@ impl CollabPanel {
// Populate remote participants.
self.match_candidates.clear();
self.match_candidates
.extend(room.remote_participants().iter().map(|(_, participant)| {
StringMatchCandidate {
id: participant.user.id as usize,
string: participant.user.github_login.clone(),
char_bag: participant.user.github_login.chars().collect(),
}
}));
.extend(
room.remote_participants()
.iter()
.filter_map(|(_, participant)| {
if participant.role == proto::ChannelRole::Guest {
guest_count += 1;
return None;
} else {
non_guest_count += 1;
}
Some(StringMatchCandidate {
id: participant.user.id as usize,
string: participant.user.github_login.clone(),
char_bag: participant.user.github_login.chars().collect(),
})
}),
);
let matches = executor.block(match_strings(
&self.match_candidates,
&query,
@@ -470,6 +488,15 @@ impl CollabPanel {
});
}
}
if guest_count > 0 {
self.entries.insert(
guest_count_ix,
ListEntry::GuestCount {
count: guest_count,
has_visible_participants: non_guest_count > 0,
},
);
}
// Populate pending participants.
self.match_candidates.clear();
@@ -959,6 +986,41 @@ impl CollabPanel {
.tooltip(move |cx| Tooltip::text("Open Chat", cx))
}
fn render_guest_count(
&self,
count: usize,
has_visible_participants: bool,
is_selected: bool,
cx: &mut ViewContext<Self>,
) -> impl IntoElement {
let manageable_channel_id = ActiveCall::global(cx).read(cx).room().and_then(|room| {
let room = room.read(cx);
if room.local_participant_is_admin() {
room.channel_id()
} else {
None
}
});
ListItem::new("guest_count")
.selected(is_selected)
.start_slot(
h_stack()
.gap_1()
.child(render_tree_branch(!has_visible_participants, cx))
.child(""),
)
.child(Label::new(if count == 1 {
format!("{} guest", count)
} else {
format!("{} guests", count)
}))
.when_some(manageable_channel_id, |el, channel_id| {
el.tooltip(move |cx| Tooltip::text("Manage Members", cx))
.on_click(cx.listener(move |this, _, cx| this.manage_members(channel_id, cx)))
})
}
fn has_subchannels(&self, ix: usize) -> bool {
self.entries.get(ix).map_or(false, |entry| {
if let ListEntry::Channel { has_children, .. } = entry {
@@ -1180,6 +1242,18 @@ impl CollabPanel {
});
}
}
ListEntry::GuestCount { .. } => {
let Some(room) = ActiveCall::global(cx).read(cx).room() else {
return;
};
let room = room.read(cx);
let Some(channel_id) = room.channel_id() else {
return;
};
if room.local_participant_is_admin() {
self.manage_members(channel_id, cx)
}
}
ListEntry::Channel { channel, .. } => {
let is_active = maybe!({
let call_channel = ActiveCall::global(cx)
@@ -1735,6 +1809,12 @@ impl CollabPanel {
ListEntry::ParticipantScreen { peer_id, is_last } => self
.render_participant_screen(*peer_id, *is_last, is_selected, cx)
.into_any_element(),
ListEntry::GuestCount {
count,
has_visible_participants,
} => self
.render_guest_count(*count, *has_visible_participants, is_selected, cx)
.into_any_element(),
ListEntry::ChannelNotes { channel_id } => self
.render_channel_notes(*channel_id, is_selected, cx)
.into_any_element(),
@@ -1766,7 +1846,7 @@ impl CollabPanel {
) -> impl IntoElement {
let settings = ThemeSettings::get_global(cx);
let text_style = TextStyle {
color: if editor.read(cx).read_only() {
color: if editor.read(cx).read_only(cx) {
cx.theme().colors().text_disabled
} else {
cx.theme().colors().text
@@ -2538,6 +2618,11 @@ impl PartialEq for ListEntry {
return true;
}
}
ListEntry::GuestCount { .. } => {
if let ListEntry::GuestCount { .. } = other {
return true;
}
}
}
false
}
@@ -162,8 +162,7 @@ impl Render for ChannelModal {
v_stack()
.px_2()
.py_1()
.rounded_t(px(8.))
.bg(cx.theme().colors().element_background)
.gap_2()
.child(
h_stack()
.w_px()
@@ -175,7 +174,9 @@ impl Render for ChannelModal {
.child(
h_stack()
.w_full()
.h(rems(22. / 16.))
.justify_between()
.line_height(rems(1.25))
.child(
h_stack()
.gap_2()
@@ -190,38 +191,54 @@ impl Render for ChannelModal {
)
.on_click(cx.listener(Self::set_channel_visiblity)),
)
.child(Label::new("Public")),
.child(Label::new("Public").size(LabelSize::Small)),
)
.children(if visibility == ChannelVisibility::Public {
Some(Button::new("copy-link", "Copy Link").on_click(cx.listener(
move |this, _, cx| {
if let Some(channel) =
this.channel_store.read(cx).channel_for_id(channel_id)
{
let item = ClipboardItem::new(channel.link());
cx.write_to_clipboard(item);
}
},
)))
} else {
None
}),
.children(
Some(
Button::new("copy-link", "Copy Link")
.label_size(LabelSize::Small)
.on_click(cx.listener(move |this, _, cx| {
if let Some(channel) = this
.channel_store
.read(cx)
.channel_for_id(channel_id)
{
let item = ClipboardItem::new(channel.link());
cx.write_to_clipboard(item);
}
})),
)
.filter(|_| visibility == ChannelVisibility::Public),
),
)
.child(
div()
.w_full()
.flex()
.flex_row()
h_stack()
.child(
Button::new("manage-members", "Manage Members")
.selected(mode == Mode::ManageMembers)
div()
.id("manage-members")
.px_2()
.py_1()
.cursor_pointer()
.border_b_2()
.when(mode == Mode::ManageMembers, |this| {
this.border_color(cx.theme().colors().border)
})
.child(Label::new("Manage Members"))
.on_click(cx.listener(|this, _, cx| {
this.set_mode(Mode::ManageMembers, cx);
})),
)
.child(
Button::new("invite-members", "Invite Members")
.selected(mode == Mode::InviteMembers)
div()
.id("invite-members")
.px_2()
.py_1()
.cursor_pointer()
.border_b_2()
.when(mode == Mode::InviteMembers, |this| {
this.border_color(cx.theme().colors().border)
})
.child(Label::new("Invite Members"))
.on_click(cx.listener(|this, _, cx| {
this.set_mode(Mode::InviteMembers, cx);
})),
+66 -41
View File
@@ -10,11 +10,12 @@ use gpui::{
};
use project::{Project, RepositoryEntry};
use recent_projects::RecentProjects;
use rpc::proto;
use std::sync::Arc;
use theme::{ActiveTheme, PlayerColors};
use ui::{
h_stack, popover_menu, prelude::*, Avatar, Button, ButtonLike, ButtonStyle, ContextMenu, Icon,
IconButton, IconElement, Tooltip,
IconButton, IconElement, TintColor, Tooltip,
};
use util::ResultExt;
use vcs_menu::{build_branch_list, BranchList, OpenRecent as ToggleVcsMenu};
@@ -175,14 +176,17 @@ impl Render for CollabTitlebarItem {
let is_muted = room.is_muted(cx);
let is_deafened = room.is_deafened().unwrap_or(false);
let is_screen_sharing = room.is_screen_sharing();
let read_only = room.read_only();
this.when(is_local, |this| {
this.when(is_local && !read_only, |this| {
this.child(
Button::new(
"toggle_sharing",
if is_shared { "Unshare" } else { "Share" },
)
.style(ButtonStyle::Subtle)
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
.selected(is_shared)
.label_size(LabelSize::Small)
.on_click(cx.listener(
move |this, _, cx| {
@@ -205,20 +209,23 @@ impl Render for CollabTitlebarItem {
.detach_and_log_err(cx);
}),
)
.child(
IconButton::new(
"mute-microphone",
if is_muted {
ui::Icon::MicMute
} else {
ui::Icon::Mic
},
.when(!read_only, |this| {
this.child(
IconButton::new(
"mute-microphone",
if is_muted {
ui::Icon::MicMute
} else {
ui::Icon::Mic
},
)
.style(ButtonStyle::Subtle)
.icon_size(IconSize::Small)
.selected(is_muted)
.selected_style(ButtonStyle::Tinted(TintColor::Negative))
.on_click(move |_, cx| crate::toggle_mute(&Default::default(), cx)),
)
.style(ButtonStyle::Subtle)
.icon_size(IconSize::Small)
.selected(is_muted)
.on_click(move |_, cx| crate::toggle_mute(&Default::default(), cx)),
)
})
.child(
IconButton::new(
"mute-sound",
@@ -229,22 +236,35 @@ impl Render for CollabTitlebarItem {
},
)
.style(ButtonStyle::Subtle)
.selected_style(ButtonStyle::Tinted(TintColor::Negative))
.icon_size(IconSize::Small)
.selected(is_deafened)
.tooltip(move |cx| {
Tooltip::with_meta("Deafen Audio", None, "Mic will be muted", cx)
if !read_only {
Tooltip::with_meta(
"Deafen Audio",
None,
"Mic will be muted",
cx,
)
} else {
Tooltip::text("Deafen Audio", cx)
}
})
.on_click(move |_, cx| crate::toggle_mute(&Default::default(), cx)),
)
.child(
IconButton::new("screen-share", ui::Icon::Screen)
.style(ButtonStyle::Subtle)
.icon_size(IconSize::Small)
.selected(is_screen_sharing)
.on_click(move |_, cx| {
crate::toggle_screen_sharing(&Default::default(), cx)
}),
.on_click(move |_, cx| crate::toggle_deafen(&Default::default(), cx)),
)
.when(!read_only, |this| {
this.child(
IconButton::new("screen-share", ui::Icon::Screen)
.style(ButtonStyle::Subtle)
.icon_size(IconSize::Small)
.selected(is_screen_sharing)
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
.on_click(move |_, cx| {
crate::toggle_screen_sharing(&Default::default(), cx)
}),
)
})
})
.map(|el| {
let status = self.client.status();
@@ -409,6 +429,10 @@ impl CollabTitlebarItem {
current_user: &Arc<User>,
cx: &ViewContext<Self>,
) -> Option<FacePile> {
if room.role_for_user(user.id) == Some(proto::ChannelRole::Guest) {
return None;
}
let followers = project_id.map_or(&[] as &[_], |id| room.followers_for(peer_id, id));
let pile = FacePile::default()
@@ -504,8 +528,7 @@ impl CollabTitlebarItem {
| client::Status::ReconnectionError { .. } => Some(
div()
.id("disconnected")
.bg(gpui::red()) // todo!() @nate
.child(IconElement::new(Icon::Disconnected))
.child(IconElement::new(Icon::Disconnected).size(IconSize::Small))
.tooltip(|cx| Tooltip::text("Disconnected", cx))
.into_any_element(),
),
@@ -522,9 +545,9 @@ impl CollabTitlebarItem {
};
Some(
div()
.bg(gpui::red()) // todo!() @nate
.child(Button::new("connection-status", label).on_click(|_, cx| {
Button::new("connection-status", label)
.label_size(LabelSize::Small)
.on_click(|_, cx| {
if let Some(auto_updater) = auto_update::AutoUpdater::get(cx) {
if auto_updater.read(cx).status() == AutoUpdateStatus::Updated {
workspace::restart(&Default::default(), cx);
@@ -532,7 +555,7 @@ impl CollabTitlebarItem {
}
}
auto_update::check(&Default::default(), cx);
}))
})
.into_any_element(),
)
}
@@ -542,16 +565,18 @@ impl CollabTitlebarItem {
pub fn render_sign_in_button(&mut self, _: &mut ViewContext<Self>) -> Button {
let client = self.client.clone();
Button::new("sign_in", "Sign in").on_click(move |_, cx| {
let client = client.clone();
cx.spawn(move |mut cx| async move {
client
.authenticate_and_connect(true, &cx)
.await
.notify_async_err(&mut cx);
Button::new("sign_in", "Sign in")
.label_size(LabelSize::Small)
.on_click(move |_, cx| {
let client = client.clone();
cx.spawn(move |mut cx| async move {
client
.authenticate_and_connect(true, &cx)
.await
.notify_async_err(&mut cx);
})
.detach();
})
.detach();
})
}
pub fn render_user_menu_button(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+7 -1
View File
@@ -151,7 +151,12 @@ impl ProjectDiagnosticsEditor {
let focus_in_subscription =
cx.on_focus_in(&focus_handle, |diagnostics, cx| diagnostics.focus_in(cx));
let excerpts = cx.new_model(|cx| MultiBuffer::new(project_handle.read(cx).replica_id()));
let excerpts = cx.new_model(|cx| {
MultiBuffer::new(
project_handle.read(cx).replica_id(),
project_handle.read(cx).capability(),
)
});
let editor = cx.new_view(|cx| {
let mut editor =
Editor::for_multibuffer(excerpts.clone(), Some(project_handle.clone()), cx);
@@ -1572,6 +1577,7 @@ mod tests {
workspace::init_settings(cx);
Project::init_settings(cx);
crate::init(cx);
editor::init(cx);
});
}
+17 -14
View File
@@ -54,10 +54,10 @@ use itertools::Itertools;
pub use language::{char_kind, CharKind};
use language::{
language_settings::{self, all_language_settings, InlayHintSettings},
markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel,
Completion, CursorShape, Diagnostic, Documentation, IndentKind, IndentSize, Language,
LanguageRegistry, LanguageServerName, OffsetRangeExt, Point, Selection, SelectionGoal,
TransactionId,
markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CodeAction,
CodeLabel, Completion, CursorShape, Diagnostic, Documentation, IndentKind, IndentSize,
Language, LanguageRegistry, LanguageServerName, OffsetRangeExt, Point, Selection,
SelectionGoal, TransactionId,
};
use link_go_to_definition::{GoToDefinitionLink, InlayHighlight, LinkGoToDefinitionState};
@@ -2049,8 +2049,8 @@ impl Editor {
}
}
pub fn read_only(&self) -> bool {
self.read_only
pub fn read_only(&self, cx: &AppContext) -> bool {
self.read_only || self.buffer.read(cx).read_only()
}
pub fn set_read_only(&mut self, read_only: bool) {
@@ -2199,7 +2199,7 @@ impl Editor {
S: ToOffset,
T: Into<Arc<str>>,
{
if self.read_only {
if self.read_only(cx) {
return;
}
@@ -2213,7 +2213,7 @@ impl Editor {
S: ToOffset,
T: Into<Arc<str>>,
{
if self.read_only {
if self.read_only(cx) {
return;
}
@@ -2232,7 +2232,7 @@ impl Editor {
S: ToOffset,
T: Into<Arc<str>>,
{
if self.read_only {
if self.read_only(cx) {
return;
}
@@ -2596,7 +2596,7 @@ impl Editor {
pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
let text: Arc<str> = text.into();
if self.read_only {
if self.read_only(cx) {
return;
}
@@ -3049,7 +3049,7 @@ impl Editor {
autoindent_mode: Option<AutoindentMode>,
cx: &mut ViewContext<Self>,
) {
if self.read_only {
if self.read_only(cx) {
return;
}
@@ -3786,7 +3786,8 @@ impl Editor {
let mut ranges_to_highlight = Vec::new();
let excerpt_buffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
let mut multibuffer =
MultiBuffer::new(replica_id, Capability::ReadWrite).with_title(title);
for (buffer_handle, transaction) in &entries {
let buffer = buffer_handle.read(cx);
ranges_to_highlight.extend(
@@ -7491,9 +7492,10 @@ impl Editor {
locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
let mut locations = locations.into_iter().peekable();
let mut ranges_to_highlight = Vec::new();
let capability = workspace.project().read(cx).capability();
let excerpt_buffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(replica_id);
let mut multibuffer = MultiBuffer::new(replica_id, capability);
while let Some(location) = locations.next() {
let buffer = location.buffer.read(cx);
let mut ranges_for_buffer = Vec::new();
@@ -8608,7 +8610,8 @@ impl Editor {
}
pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
self.blink_manager.read(cx).visible() && self.focus_handle.is_focused(cx)
(self.read_only(cx) || self.blink_manager.read(cx).visible())
&& self.focus_handle.is_focused(cx)
}
fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
+11 -10
View File
@@ -17,8 +17,9 @@ use gpui::{
use indoc::indoc;
use language::{
language_settings::{AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent},
BracketPairConfig, FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageRegistry,
Override, Point,
BracketPairConfig,
Capability::ReadWrite,
FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageRegistry, Override, Point,
};
use parking_lot::Mutex;
use project::project_settings::{LspSettings, ProjectSettings};
@@ -2355,7 +2356,7 @@ fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
.with_language(rust_language, cx)
});
let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0);
let mut multibuffer = MultiBuffer::new(0, ReadWrite);
multibuffer.push_excerpts(
toml_buffer.clone(),
[ExcerptRange {
@@ -6019,7 +6020,7 @@ fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(3, 4, 'a')));
let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0);
let mut multibuffer = MultiBuffer::new(0, ReadWrite);
multibuffer.push_excerpts(
buffer.clone(),
[
@@ -6103,7 +6104,7 @@ fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
});
let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), initial_text));
let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0);
let mut multibuffer = MultiBuffer::new(0, ReadWrite);
multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
multibuffer
});
@@ -6162,7 +6163,7 @@ fn test_refresh_selections(cx: &mut TestAppContext) {
let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(3, 4, 'a')));
let mut excerpt1_id = None;
let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0);
let mut multibuffer = MultiBuffer::new(0, ReadWrite);
excerpt1_id = multibuffer
.push_excerpts(
buffer.clone(),
@@ -6247,7 +6248,7 @@ fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(3, 4, 'a')));
let mut excerpt1_id = None;
let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0);
let mut multibuffer = MultiBuffer::new(0, ReadWrite);
excerpt1_id = multibuffer
.push_excerpts(
buffer.clone(),
@@ -6636,7 +6637,7 @@ async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
let leader = pane.update(cx, |_, cx| {
let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
let multibuffer = cx.new_model(|_| MultiBuffer::new(0, ReadWrite));
cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
});
@@ -7425,7 +7426,7 @@ async fn test_copilot_multibuffer(executor: BackgroundExecutor, cx: &mut gpui::T
let buffer_1 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "a = 1\nb = 2\n"));
let buffer_2 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "c = 3\nd = 4\n"));
let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0);
let mut multibuffer = MultiBuffer::new(0, ReadWrite);
multibuffer.push_excerpts(
buffer_1.clone(),
[ExcerptRange {
@@ -7552,7 +7553,7 @@ async fn test_copilot_disabled_globs(executor: BackgroundExecutor, cx: &mut gpui
.unwrap();
let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0);
let mut multibuffer = MultiBuffer::new(0, ReadWrite);
multibuffer.push_excerpts(
private_buffer.clone(),
[ExcerptRange {
+7 -1
View File
@@ -1910,7 +1910,13 @@ impl EditorElement {
layouts.push(layout);
}
selections.push((style.local_player, layouts));
let player = if editor.read_only(cx) {
cx.theme().players().read_only()
} else {
style.local_player
};
selections.push((player, layouts));
}
if let Some(collaboration_hub) = &editor.collaboration_hub {
+2 -1
View File
@@ -93,6 +93,7 @@ mod tests {
use crate::editor_tests::init_test;
use crate::Point;
use gpui::{Context, TestAppContext};
use language::Capability::ReadWrite;
use multi_buffer::{ExcerptRange, MultiBuffer};
use project::{FakeFs, Project};
use unindent::Unindent;
@@ -183,7 +184,7 @@ mod tests {
cx.background_executor.run_until_parked();
let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0);
let mut multibuffer = MultiBuffer::new(0, ReadWrite);
multibuffer.push_excerpts(
buffer_1.clone(),
[
+1 -7
View File
@@ -515,34 +515,28 @@ impl DiagnosticPopover {
};
struct DiagnosticColors {
pub text: Hsla,
pub background: Hsla,
pub border: Hsla,
}
let diagnostic_colors = match self.local_diagnostic.diagnostic.severity {
DiagnosticSeverity::ERROR => DiagnosticColors {
text: style.status.error,
background: style.status.error_background,
border: style.status.error_border,
},
DiagnosticSeverity::WARNING => DiagnosticColors {
text: style.status.warning,
background: style.status.warning_background,
border: style.status.warning_border,
},
DiagnosticSeverity::INFORMATION => DiagnosticColors {
text: style.status.info,
background: style.status.info_background,
border: style.status.info_border,
},
DiagnosticSeverity::HINT => DiagnosticColors {
text: style.status.hint,
background: style.status.hint_background,
border: style.status.hint_border,
},
_ => DiagnosticColors {
text: style.status.ignored,
background: style.status.ignored_background,
border: style.status.ignored_border,
},
@@ -554,7 +548,7 @@ impl DiagnosticPopover {
.px_2()
.py_1()
.bg(diagnostic_colors.background)
.text_color(diagnostic_colors.text)
.text_color(style.text.color)
.border_1()
.border_color(diagnostic_colors.border)
.rounded_md()
+4 -3
View File
@@ -1206,7 +1206,8 @@ pub mod tests {
use gpui::{Context, TestAppContext, WindowHandle};
use itertools::Itertools;
use language::{
language_settings::AllLanguageSettingsContent, FakeLspAdapter, Language, LanguageConfig,
language_settings::AllLanguageSettingsContent, Capability, FakeLspAdapter, Language,
LanguageConfig,
};
use lsp::FakeLanguageServer;
use parking_lot::Mutex;
@@ -2459,7 +2460,7 @@ pub mod tests {
.await
.unwrap();
let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0);
let mut multibuffer = MultiBuffer::new(0, Capability::ReadWrite);
multibuffer.push_excerpts(
buffer_1.clone(),
[
@@ -2798,7 +2799,7 @@ pub mod tests {
})
.await
.unwrap();
let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
let (buffer_1_excerpts, buffer_2_excerpts) = multibuffer.update(cx, |multibuffer, cx| {
let buffer_1_excerpts = multibuffer.push_excerpts(
buffer_1.clone(),
+28 -8
View File
@@ -15,9 +15,11 @@ use language::{
proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, CharKind, OffsetRangeExt,
Point, SelectionGoal,
};
use project::repository::GitFileStatus;
use project::{search::SearchQuery, FormatTrigger, Item as _, Project, ProjectPath};
use rpc::proto::{self, update_view, PeerId};
use settings::Settings;
use workspace::item::ItemSettings;
use std::fmt::Write;
use std::{
@@ -29,7 +31,7 @@ use std::{
sync::Arc,
};
use text::Selection;
use theme::{ActiveTheme, Theme};
use theme::Theme;
use ui::{h_stack, prelude::*, Label};
use util::{paths::PathExt, paths::FILE_ROW_COLUMN_DELIMITER, ResultExt, TryFutureExt};
use workspace::{
@@ -101,7 +103,8 @@ impl FollowableItem for Editor {
if state.singleton && buffers.len() == 1 {
multibuffer = MultiBuffer::singleton(buffers.pop().unwrap(), cx)
} else {
multibuffer = MultiBuffer::new(replica_id);
multibuffer =
MultiBuffer::new(replica_id, project.read(cx).capability());
let mut excerpts = state.excerpts.into_iter().peekable();
while let Some(excerpt) = excerpts.peek() {
let buffer_id = excerpt.buffer_id;
@@ -579,7 +582,28 @@ impl Item for Editor {
}
fn tab_content(&self, detail: Option<usize>, selected: bool, cx: &WindowContext) -> AnyElement {
let _theme = cx.theme();
let git_status = if ItemSettings::get_global(cx).git_status {
self.buffer()
.read(cx)
.as_singleton()
.and_then(|buffer| buffer.read(cx).project_path(cx))
.and_then(|path| self.project.as_ref()?.read(cx).entry_for_path(&path, cx))
.and_then(|entry| entry.git_status())
} else {
None
};
let label_color = match git_status {
Some(GitFileStatus::Added) => Color::Created,
Some(GitFileStatus::Modified) => Color::Modified,
Some(GitFileStatus::Conflict) => Color::Conflict,
None => {
if selected {
Color::Default
} else {
Color::Muted
}
}
};
let description = detail.and_then(|detail| {
let path = path_for_buffer(&self.buffer, detail, false, cx)?;
@@ -595,11 +619,7 @@ impl Item for Editor {
h_stack()
.gap_2()
.child(Label::new(self.title(cx).to_string()).color(if selected {
Color::Default
} else {
Color::Muted
}))
.child(Label::new(self.title(cx).to_string()).color(label_color))
.when_some(description, |this, description| {
this.child(
Label::new(description)
+1 -4
View File
@@ -930,10 +930,7 @@ mod tests {
fn do_work() { «test»(); }
"});
// Deactivating the window dismisses the highlight
cx.update_workspace(|workspace, cx| {
workspace.on_window_activation_changed(cx);
});
cx.cx.cx.deactivate_window();
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
fn test() { do_work(); }
fn do_work() { test(); }
+2 -1
View File
@@ -461,6 +461,7 @@ mod tests {
Buffer, DisplayMap, ExcerptRange, InlayId, MultiBuffer,
};
use gpui::{font, Context as _};
use language::Capability;
use project::Project;
use settings::SettingsStore;
use util::post_inc;
@@ -766,7 +767,7 @@ mod tests {
let buffer =
cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abc\ndefg\nhijkl\nmn"));
let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0);
let mut multibuffer = MultiBuffer::new(0, Capability::ReadWrite);
multibuffer.push_excerpts(
buffer.clone(),
[
+15 -1
View File
@@ -17,7 +17,7 @@ use regex::Regex;
use serde_derive::Serialize;
use ui::{prelude::*, Button, ButtonStyle, IconPosition, Tooltip};
use util::ResultExt;
use workspace::{ModalView, Workspace};
use workspace::{ModalView, Toast, Workspace};
use crate::{system_specs::SystemSpecs, GiveFeedback, OpenZedCommunityRepo};
@@ -125,6 +125,20 @@ impl FeedbackModal {
.language_for_name("Markdown");
let project = workspace.project().clone();
let is_local_project = project.read(cx).is_local();
if !is_local_project {
const TOAST_ID: usize = 0xdeadbeef;
workspace.show_toast(
Toast::new(
TOAST_ID,
"You can only submit feedback in your own project.",
),
cx,
);
return;
}
cx.spawn(|workspace, mut cx| async move {
let markdown = markdown.await.log_err();
+1 -2
View File
@@ -18,8 +18,7 @@ collections = { path = "../collections" }
gpui_macros = { path = "../gpui_macros" }
util = { path = "../util" }
sum_tree = { path = "../sum_tree" }
sqlez = { path = "../sqlez" }
async-task = "4.0.3"
async-task = "4.7"
backtrace = { version = "0.3", optional = true }
ctor.workspace = true
linkme = "0.3"
+4 -3
View File
@@ -1,8 +1,6 @@
use crate::{private::Sealed, AppContext, Context, Entity, ModelContext};
use anyhow::{anyhow, Result};
use collections::HashMap;
use derive_more::{Deref, DerefMut};
use lazy_static::lazy_static;
use parking_lot::{RwLock, RwLockUpgradableReadGuard};
use slotmap::{SecondaryMap, SlotMap};
use std::{
@@ -18,6 +16,9 @@ use std::{
thread::panicking,
};
#[cfg(any(test, feature = "test-support"))]
use collections::HashMap;
slotmap::new_key_type! { pub struct EntityId; }
impl EntityId {
@@ -600,7 +601,7 @@ impl<T> PartialEq<Model<T>> for WeakModel<T> {
}
#[cfg(any(test, feature = "test-support"))]
lazy_static! {
lazy_static::lazy_static! {
static ref LEAK_BACKTRACE: bool =
std::env::var("LEAK_BACKTRACE").map_or(false, |b| !b.is_empty());
}
+25 -110
View File
@@ -1,14 +1,13 @@
use crate::{
div, Action, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext,
BackgroundExecutor, Bounds, ClipboardItem, Context, Entity, EventEmitter, ForegroundExecutor,
InputEvent, IntoElement, KeyDownEvent, Keystroke, Model, ModelContext, Pixels, Platform,
PlatformWindow, Point, Render, Result, Size, Task, TestDispatcher, TestPlatform, TestWindow,
TestWindowHandlers, TextSystem, View, ViewContext, VisualContext, WindowBounds, WindowContext,
WindowHandle, WindowOptions,
BackgroundExecutor, ClipboardItem, Context, Entity, EventEmitter, ForegroundExecutor,
IntoElement, Keystroke, Model, ModelContext, Pixels, Platform, Render, Result, Size, Task,
TestDispatcher, TestPlatform, TestWindow, TextSystem, View, ViewContext, VisualContext,
WindowContext, WindowHandle, WindowOptions,
};
use anyhow::{anyhow, bail};
use futures::{Stream, StreamExt};
use std::{future::Future, mem, ops::Deref, rc::Rc, sync::Arc, time::Duration};
use std::{future::Future, ops::Deref, rc::Rc, sync::Arc, time::Duration};
#[derive(Clone)]
pub struct TestAppContext {
@@ -185,42 +184,7 @@ impl TestAppContext {
}
pub fn simulate_window_resize(&self, window_handle: AnyWindowHandle, size: Size<Pixels>) {
let (mut handlers, scale_factor) = self
.app
.borrow_mut()
.update_window(window_handle, |_, cx| {
let platform_window = cx.window.platform_window.as_test().unwrap();
let scale_factor = platform_window.scale_factor();
match &mut platform_window.bounds {
WindowBounds::Fullscreen | WindowBounds::Maximized => {
platform_window.bounds = WindowBounds::Fixed(Bounds {
origin: Point::default(),
size: size.map(|pixels| f64::from(pixels).into()),
});
}
WindowBounds::Fixed(bounds) => {
bounds.size = size.map(|pixels| f64::from(pixels).into());
}
}
(
mem::take(&mut platform_window.handlers.lock().resize),
scale_factor,
)
})
.unwrap();
for handler in &mut handlers {
handler(size, scale_factor);
}
self.app
.borrow_mut()
.update_window(window_handle, |_, cx| {
let platform_window = cx.window.platform_window.as_test().unwrap();
platform_window.handlers.lock().resize = handlers;
})
.unwrap();
self.test_window(window_handle).simulate_resize(size);
}
pub fn windows(&self) -> Vec<AnyWindowHandle> {
@@ -317,41 +281,22 @@ impl TestAppContext {
keystroke: Keystroke,
is_held: bool,
) {
let keystroke2 = keystroke.clone();
let handled = window
.update(self, |_, cx| {
cx.dispatch_event(InputEvent::KeyDown(KeyDownEvent { keystroke, is_held }))
})
.is_ok_and(|handled| handled);
if handled {
return;
}
let input_handler = self.update_test_window(window, |window| window.input_handler.clone());
let Some(input_handler) = input_handler else {
panic!(
"dispatch_keystroke {:?} failed to dispatch action or input",
&keystroke2
);
};
let text = keystroke2.ime_key.unwrap_or(keystroke2.key);
input_handler.lock().replace_text_in_range(None, &text);
self.test_window(window)
.simulate_keystroke(keystroke, is_held)
}
pub fn update_test_window<R>(
&mut self,
window: AnyWindowHandle,
f: impl FnOnce(&mut TestWindow) -> R,
) -> R {
window
.update(self, |_, cx| {
f(cx.window
.platform_window
.as_any_mut()
.downcast_mut::<TestWindow>()
.unwrap())
})
pub fn test_window(&self, window: AnyWindowHandle) -> TestWindow {
self.app
.borrow_mut()
.windows
.get_mut(window.id)
.unwrap()
.as_mut()
.unwrap()
.platform_window
.as_test()
.unwrap()
.clone()
}
pub fn notifications<T: 'static>(&mut self, entity: &impl Entity<T>) -> impl Stream<Item = ()> {
@@ -567,11 +512,7 @@ impl<'a> VisualTestContext<'a> {
}
pub fn window_title(&mut self) -> Option<String> {
self.cx
.update_window(self.window, |_, cx| {
cx.window.platform_window.as_test().unwrap().title.clone()
})
.unwrap()
self.cx.test_window(self.window).0.lock().title.clone()
}
pub fn simulate_keystrokes(&mut self, keystrokes: &str) {
@@ -582,37 +523,11 @@ impl<'a> VisualTestContext<'a> {
self.cx.simulate_input(self.window, input)
}
pub fn simulate_activation(&mut self) {
self.simulate_window_events(&mut |handlers| {
handlers
.active_status_change
.iter_mut()
.for_each(|f| f(true));
})
}
pub fn simulate_deactivation(&mut self) {
self.simulate_window_events(&mut |handlers| {
handlers
.active_status_change
.iter_mut()
.for_each(|f| f(false));
})
}
fn simulate_window_events(&mut self, f: &mut dyn FnMut(&mut TestWindowHandlers)) {
let handlers = self
.cx
.update_window(self.window, |_, cx| {
cx.window
.platform_window
.as_test()
.unwrap()
.handlers
.clone()
})
.unwrap();
f(&mut *handlers.lock());
pub fn deactivate_window(&mut self) {
if Some(self.window) == self.test_platform.active_window() {
self.test_platform.set_active_window(None)
}
self.background_executor.run_until_parked();
}
}
+9
View File
@@ -339,6 +339,15 @@ impl Hsla {
}
}
pub fn grayscale(&self) -> Self {
Hsla {
h: self.h,
s: 0.,
l: self.l,
a: self.a,
}
}
/// Fade out the color by a given factor. This factor should be between 0.0 and 1.0.
/// Where 0.0 will leave the color unchanged, and 1.0 will completely fade out the color.
pub fn fade_out(&mut self, factor: f32) {
+3 -2
View File
@@ -44,8 +44,9 @@ pub trait IntoElement: Sized {
}
/// Convert into an element, then draw in the current window at the given origin.
/// The provided available space is provided to the layout engine to determine the size of the root element.
/// Once the element is drawn, its associated element staet is yielded to the given callback.
/// The available space argument is provided to the layout engine to determine the size of the
// root element. Once the element is drawn, its associated element state is yielded to the
// given callback.
fn draw_and_update_state<T, R>(
self,
origin: Point<Pixels>,
-45
View File
@@ -31,39 +31,6 @@ pub trait Along {
fn apply_along(&self, axis: Axis, f: impl FnOnce(Self::Unit) -> Self::Unit) -> Self;
}
impl sqlez::bindable::StaticColumnCount for Axis {}
impl sqlez::bindable::Bind for Axis {
fn bind(
&self,
statement: &sqlez::statement::Statement,
start_index: i32,
) -> anyhow::Result<i32> {
match self {
Axis::Horizontal => "Horizontal",
Axis::Vertical => "Vertical",
}
.bind(statement, start_index)
}
}
impl sqlez::bindable::Column for Axis {
fn column(
statement: &mut sqlez::statement::Statement,
start_index: i32,
) -> anyhow::Result<(Self, i32)> {
String::column(statement, start_index).and_then(|(axis_text, next_index)| {
Ok((
match axis_text.as_str() {
"Horizontal" => Axis::Horizontal,
"Vertical" => Axis::Vertical,
_ => anyhow::bail!("Stored serialized item kind is incorrect"),
},
next_index,
))
})
}
}
/// Describes a location in a 2D cartesian coordinate space.
///
/// It holds two public fields, `x` and `y`, which represent the coordinates in the space.
@@ -2296,18 +2263,6 @@ impl From<f64> for GlobalPixels {
}
}
impl sqlez::bindable::StaticColumnCount for GlobalPixels {}
impl sqlez::bindable::Bind for GlobalPixels {
fn bind(
&self,
statement: &sqlez::statement::Statement,
start_index: i32,
) -> anyhow::Result<i32> {
self.0.bind(statement, start_index)
}
}
/// Represents a length in rems, a unit based on the font-size of the window, which can be assigned with [WindowContext::set_rem_size].
///
/// Rems are used for defining lengths that are scalable and consistent across different UI elements.
+6 -8
View File
@@ -188,15 +188,13 @@ impl DispatchTree {
action: &dyn Action,
context_stack: &Vec<KeyContext>,
) -> Vec<KeyBinding> {
self.keymap
.lock()
.bindings_for_action(action.type_id())
.filter(|candidate| {
if !candidate.action.partial_eq(action) {
return false;
}
let keymap = self.keymap.lock();
keymap
.bindings_for_action(action)
.filter(|binding| {
for i in 1..context_stack.len() {
if candidate.matches_context(&context_stack[0..=i]) {
let context = &context_stack[0..i];
if keymap.binding_enabled(binding, context) {
return true;
}
}
+13 -28
View File
@@ -1,4 +1,4 @@
use crate::{Action, KeyBindingContextPredicate, KeyContext, KeyMatch, Keystroke};
use crate::{Action, KeyBindingContextPredicate, KeyMatch, Keystroke};
use anyhow::Result;
use smallvec::SmallVec;
@@ -42,21 +42,8 @@ impl KeyBinding {
})
}
pub fn matches_context(&self, contexts: &[KeyContext]) -> bool {
self.context_predicate
.as_ref()
.map(|predicate| predicate.eval(contexts))
.unwrap_or(true)
}
pub fn match_keystrokes(
&self,
pending_keystrokes: &[Keystroke],
contexts: &[KeyContext],
) -> KeyMatch {
if self.keystrokes.as_ref().starts_with(pending_keystrokes)
&& self.matches_context(contexts)
{
pub fn match_keystrokes(&self, pending_keystrokes: &[Keystroke]) -> KeyMatch {
if self.keystrokes.as_ref().starts_with(pending_keystrokes) {
// If the binding is completed, push it onto the matches list
if self.keystrokes.as_ref().len() == pending_keystrokes.len() {
KeyMatch::Some(vec![self.action.boxed_clone()])
@@ -68,18 +55,6 @@ impl KeyBinding {
}
}
pub fn keystrokes_for_action(
&self,
action: &dyn Action,
contexts: &[KeyContext],
) -> Option<SmallVec<[Keystroke; 2]>> {
if self.action.partial_eq(action) && self.matches_context(contexts) {
Some(self.keystrokes.clone())
} else {
None
}
}
pub fn keystrokes(&self) -> &[Keystroke] {
self.keystrokes.as_slice()
}
@@ -88,3 +63,13 @@ impl KeyBinding {
self.action.as_ref()
}
}
impl std::fmt::Debug for KeyBinding {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("KeyBinding")
.field("keystrokes", &self.keystrokes)
.field("context_predicate", &self.context_predicate)
.field("action", &self.action.name())
.finish()
}
}
+101 -331
View File
@@ -1,4 +1,4 @@
use crate::{KeyBinding, KeyBindingContextPredicate, Keystroke, NoAction};
use crate::{Action, KeyBinding, KeyBindingContextPredicate, KeyContext, Keystroke, NoAction};
use collections::HashSet;
use smallvec::SmallVec;
use std::{
@@ -29,54 +29,22 @@ impl Keymap {
self.version
}
pub fn bindings_for_action(&self, action_id: TypeId) -> impl Iterator<Item = &'_ KeyBinding> {
self.binding_indices_by_action_id
.get(&action_id)
.map(SmallVec::as_slice)
.unwrap_or(&[])
.iter()
.map(|ix| &self.bindings[*ix])
.filter(|binding| !self.binding_disabled(binding))
}
pub fn add_bindings<T: IntoIterator<Item = KeyBinding>>(&mut self, bindings: T) {
let no_action_id = &(NoAction {}).type_id();
let mut new_bindings = Vec::new();
let mut has_new_disabled_keystrokes = false;
let no_action_id = (NoAction {}).type_id();
for binding in bindings {
if binding.action.type_id() == *no_action_id {
has_new_disabled_keystrokes |= self
.disabled_keystrokes
let action_id = binding.action().as_any().type_id();
if action_id == no_action_id {
self.disabled_keystrokes
.entry(binding.keystrokes)
.or_default()
.insert(binding.context_predicate);
} else {
new_bindings.push(binding);
}
}
if has_new_disabled_keystrokes {
self.binding_indices_by_action_id.retain(|_, indices| {
indices.retain(|ix| {
let binding = &self.bindings[*ix];
match self.disabled_keystrokes.get(&binding.keystrokes) {
Some(disabled_predicates) => {
!disabled_predicates.contains(&binding.context_predicate)
}
None => true,
}
});
!indices.is_empty()
});
}
for new_binding in new_bindings {
if !self.binding_disabled(&new_binding) {
self.binding_indices_by_action_id
.entry(new_binding.action().as_any().type_id())
.entry(action_id)
.or_default()
.push(self.bindings.len());
self.bindings.push(new_binding);
self.bindings.push(binding);
}
}
@@ -90,311 +58,113 @@ impl Keymap {
self.version.0 += 1;
}
pub fn bindings(&self) -> Vec<&KeyBinding> {
self.bindings
.iter()
.filter(|binding| !self.binding_disabled(binding))
.collect()
/// Iterate over all bindings, in the order they were added.
pub fn bindings(&self) -> impl Iterator<Item = &KeyBinding> + DoubleEndedIterator {
self.bindings.iter()
}
fn binding_disabled(&self, binding: &KeyBinding) -> bool {
match self.disabled_keystrokes.get(&binding.keystrokes) {
Some(disabled_predicates) => disabled_predicates.contains(&binding.context_predicate),
None => false,
/// Iterate over all bindings for the given action, in the order they were added.
pub fn bindings_for_action<'a>(
&'a self,
action: &'a dyn Action,
) -> impl 'a + Iterator<Item = &'a KeyBinding> + DoubleEndedIterator {
let action_id = action.type_id();
self.binding_indices_by_action_id
.get(&action_id)
.map_or(&[] as _, SmallVec::as_slice)
.iter()
.map(|ix| &self.bindings[*ix])
.filter(move |binding| binding.action().partial_eq(action))
}
pub fn binding_enabled(&self, binding: &KeyBinding, context: &[KeyContext]) -> bool {
// If binding has a context predicate, it must match the current context,
if let Some(predicate) = &binding.context_predicate {
if !predicate.eval(context) {
return false;
}
}
if let Some(disabled_predicates) = self.disabled_keystrokes.get(&binding.keystrokes) {
for disabled_predicate in disabled_predicates {
match disabled_predicate {
// The binding must not be globally disabled.
None => return false,
// The binding must not be disabled in the current context.
Some(predicate) => {
if predicate.eval(context) {
return false;
}
}
}
}
}
true
}
}
// #[cfg(test)]
// mod tests {
// use crate::actions;
#[cfg(test)]
mod tests {
use super::*;
use crate as gpui;
use gpui::actions;
// use super::*;
actions!(
keymap_test,
[ActionAlpha, ActionBeta, ActionGamma, ActionDelta,]
);
// actions!(
// keymap_test,
// [Present1, Present2, Present3, Duplicate, Missing]
// );
#[test]
fn test_keymap() {
let bindings = [
KeyBinding::new("ctrl-a", ActionAlpha {}, None),
KeyBinding::new("ctrl-a", ActionBeta {}, Some("pane")),
KeyBinding::new("ctrl-a", ActionGamma {}, Some("editor && mode==full")),
];
// #[test]
// fn regular_keymap() {
// let present_1 = Binding::new("ctrl-q", Present1 {}, None);
// let present_2 = Binding::new("ctrl-w", Present2 {}, Some("pane"));
// let present_3 = Binding::new("ctrl-e", Present3 {}, Some("editor"));
// let keystroke_duplicate_to_1 = Binding::new("ctrl-q", Duplicate {}, None);
// let full_duplicate_to_2 = Binding::new("ctrl-w", Present2 {}, Some("pane"));
// let missing = Binding::new("ctrl-r", Missing {}, None);
// let all_bindings = [
// &present_1,
// &present_2,
// &present_3,
// &keystroke_duplicate_to_1,
// &full_duplicate_to_2,
// &missing,
// ];
let mut keymap = Keymap::default();
keymap.add_bindings(bindings.clone());
// let mut keymap = Keymap::default();
// assert_absent(&keymap, &all_bindings);
// assert!(keymap.bindings().is_empty());
// global bindings are enabled in all contexts
assert!(keymap.binding_enabled(&bindings[0], &[]));
assert!(keymap.binding_enabled(&bindings[0], &[KeyContext::parse("terminal").unwrap()]));
// keymap.add_bindings([present_1.clone(), present_2.clone(), present_3.clone()]);
// assert_absent(&keymap, &[&keystroke_duplicate_to_1, &missing]);
// assert_present(
// &keymap,
// &[(&present_1, "q"), (&present_2, "w"), (&present_3, "e")],
// );
// contextual bindings are enabled in contexts that match their predicate
assert!(!keymap.binding_enabled(&bindings[1], &[KeyContext::parse("barf x=y").unwrap()]));
assert!(keymap.binding_enabled(&bindings[1], &[KeyContext::parse("pane x=y").unwrap()]));
// keymap.add_bindings([
// keystroke_duplicate_to_1.clone(),
// full_duplicate_to_2.clone(),
// ]);
// assert_absent(&keymap, &[&missing]);
// assert!(
// !keymap.binding_disabled(&keystroke_duplicate_to_1),
// "Duplicate binding 1 was added and should not be disabled"
// );
// assert!(
// !keymap.binding_disabled(&full_duplicate_to_2),
// "Duplicate binding 2 was added and should not be disabled"
// );
assert!(!keymap.binding_enabled(&bindings[2], &[KeyContext::parse("editor").unwrap()]));
assert!(keymap.binding_enabled(
&bindings[2],
&[KeyContext::parse("editor mode=full").unwrap()]
));
}
// assert_eq!(
// keymap
// .bindings_for_action(keystroke_duplicate_to_1.action().id())
// .map(|binding| &binding.keystrokes)
// .flatten()
// .collect::<Vec<_>>(),
// vec![&Keystroke {
// ctrl: true,
// alt: false,
// shift: false,
// cmd: false,
// function: false,
// key: "q".to_string(),
// ime_key: None,
// }],
// "{keystroke_duplicate_to_1:?} should have the expected keystroke in the keymap"
// );
// assert_eq!(
// keymap
// .bindings_for_action(full_duplicate_to_2.action().id())
// .map(|binding| &binding.keystrokes)
// .flatten()
// .collect::<Vec<_>>(),
// vec![
// &Keystroke {
// ctrl: true,
// alt: false,
// shift: false,
// cmd: false,
// function: false,
// key: "w".to_string(),
// ime_key: None,
// },
// &Keystroke {
// ctrl: true,
// alt: false,
// shift: false,
// cmd: false,
// function: false,
// key: "w".to_string(),
// ime_key: None,
// }
// ],
// "{full_duplicate_to_2:?} should have a duplicated keystroke in the keymap"
// );
#[test]
fn test_keymap_disabled() {
let bindings = [
KeyBinding::new("ctrl-a", ActionAlpha {}, Some("editor")),
KeyBinding::new("ctrl-b", ActionAlpha {}, Some("editor")),
KeyBinding::new("ctrl-a", NoAction {}, Some("editor && mode==full")),
KeyBinding::new("ctrl-b", NoAction {}, None),
];
// let updated_bindings = keymap.bindings();
// let expected_updated_bindings = vec![
// &present_1,
// &present_2,
// &present_3,
// &keystroke_duplicate_to_1,
// &full_duplicate_to_2,
// ];
// assert_eq!(
// updated_bindings.len(),
// expected_updated_bindings.len(),
// "Unexpected updated keymap bindings {updated_bindings:?}"
// );
// for (i, expected) in expected_updated_bindings.iter().enumerate() {
// let keymap_binding = &updated_bindings[i];
// assert_eq!(
// keymap_binding.context_predicate, expected.context_predicate,
// "Unexpected context predicate for keymap {i} element: {keymap_binding:?}"
// );
// assert_eq!(
// keymap_binding.keystrokes, expected.keystrokes,
// "Unexpected keystrokes for keymap {i} element: {keymap_binding:?}"
// );
// }
let mut keymap = Keymap::default();
keymap.add_bindings(bindings.clone());
// keymap.clear();
// assert_absent(&keymap, &all_bindings);
// assert!(keymap.bindings().is_empty());
// }
// binding is only enabled in a specific context
assert!(!keymap.binding_enabled(&bindings[0], &[KeyContext::parse("barf").unwrap()]));
assert!(keymap.binding_enabled(&bindings[0], &[KeyContext::parse("editor").unwrap()]));
// #[test]
// fn keymap_with_ignored() {
// let present_1 = Binding::new("ctrl-q", Present1 {}, None);
// let present_2 = Binding::new("ctrl-w", Present2 {}, Some("pane"));
// let present_3 = Binding::new("ctrl-e", Present3 {}, Some("editor"));
// let keystroke_duplicate_to_1 = Binding::new("ctrl-q", Duplicate {}, None);
// let full_duplicate_to_2 = Binding::new("ctrl-w", Present2 {}, Some("pane"));
// let ignored_1 = Binding::new("ctrl-q", NoAction {}, None);
// let ignored_2 = Binding::new("ctrl-w", NoAction {}, Some("pane"));
// let ignored_3_with_other_context =
// Binding::new("ctrl-e", NoAction {}, Some("other_context"));
// binding is disabled in a more specific context
assert!(!keymap.binding_enabled(
&bindings[0],
&[KeyContext::parse("editor mode=full").unwrap()]
));
// let mut keymap = Keymap::default();
// keymap.add_bindings([
// ignored_1.clone(),
// ignored_2.clone(),
// ignored_3_with_other_context.clone(),
// ]);
// assert_absent(&keymap, &[&present_3]);
// assert_disabled(
// &keymap,
// &[
// &present_1,
// &present_2,
// &ignored_1,
// &ignored_2,
// &ignored_3_with_other_context,
// ],
// );
// assert!(keymap.bindings().is_empty());
// keymap.clear();
// keymap.add_bindings([
// present_1.clone(),
// present_2.clone(),
// present_3.clone(),
// ignored_1.clone(),
// ignored_2.clone(),
// ignored_3_with_other_context.clone(),
// ]);
// assert_present(&keymap, &[(&present_3, "e")]);
// assert_disabled(
// &keymap,
// &[
// &present_1,
// &present_2,
// &ignored_1,
// &ignored_2,
// &ignored_3_with_other_context,
// ],
// );
// keymap.clear();
// keymap.add_bindings([
// present_1.clone(),
// present_2.clone(),
// present_3.clone(),
// ignored_1.clone(),
// ]);
// assert_present(&keymap, &[(&present_2, "w"), (&present_3, "e")]);
// assert_disabled(&keymap, &[&present_1, &ignored_1]);
// assert_absent(&keymap, &[&ignored_2, &ignored_3_with_other_context]);
// keymap.clear();
// keymap.add_bindings([
// present_1.clone(),
// present_2.clone(),
// present_3.clone(),
// keystroke_duplicate_to_1.clone(),
// full_duplicate_to_2.clone(),
// ignored_1.clone(),
// ignored_2.clone(),
// ignored_3_with_other_context.clone(),
// ]);
// assert_present(&keymap, &[(&present_3, "e")]);
// assert_disabled(
// &keymap,
// &[
// &present_1,
// &present_2,
// &keystroke_duplicate_to_1,
// &full_duplicate_to_2,
// &ignored_1,
// &ignored_2,
// &ignored_3_with_other_context,
// ],
// );
// keymap.clear();
// }
// #[track_caller]
// fn assert_present(keymap: &Keymap, expected_bindings: &[(&Binding, &str)]) {
// let keymap_bindings = keymap.bindings();
// assert_eq!(
// expected_bindings.len(),
// keymap_bindings.len(),
// "Unexpected keymap bindings {keymap_bindings:?}"
// );
// for (i, (expected, expected_key)) in expected_bindings.iter().enumerate() {
// assert!(
// !keymap.binding_disabled(expected),
// "{expected:?} should not be disabled as it was added into keymap for element {i}"
// );
// assert_eq!(
// keymap
// .bindings_for_action(expected.action().id())
// .map(|binding| &binding.keystrokes)
// .flatten()
// .collect::<Vec<_>>(),
// vec![&Keystroke {
// ctrl: true,
// alt: false,
// shift: false,
// cmd: false,
// function: false,
// key: expected_key.to_string(),
// ime_key: None,
// }],
// "{expected:?} should have the expected keystroke with key '{expected_key}' in the keymap for element {i}"
// );
// let keymap_binding = &keymap_bindings[i];
// assert_eq!(
// keymap_binding.context_predicate, expected.context_predicate,
// "Unexpected context predicate for keymap {i} element: {keymap_binding:?}"
// );
// assert_eq!(
// keymap_binding.keystrokes, expected.keystrokes,
// "Unexpected keystrokes for keymap {i} element: {keymap_binding:?}"
// );
// }
// }
// #[track_caller]
// fn assert_absent(keymap: &Keymap, bindings: &[&Binding]) {
// for binding in bindings.iter() {
// assert!(
// !keymap.binding_disabled(binding),
// "{binding:?} should not be disabled in the keymap where was not added"
// );
// assert_eq!(
// keymap.bindings_for_action(binding.action().id()).count(),
// 0,
// "{binding:?} should have no actions in the keymap where was not added"
// );
// }
// }
// #[track_caller]
// fn assert_disabled(keymap: &Keymap, bindings: &[&Binding]) {
// for binding in bindings.iter() {
// assert!(
// keymap.binding_disabled(binding),
// "{binding:?} should be disabled in the keymap"
// );
// assert_eq!(
// keymap.bindings_for_action(binding.action().id()).count(),
// 0,
// "{binding:?} should have no actions in the keymap where it was disabled"
// );
// }
// }
// }
// binding is globally disabled
assert!(!keymap.binding_enabled(&bindings[1], &[KeyContext::parse("barf").unwrap()]));
}
}
+6 -16
View File
@@ -1,6 +1,5 @@
use crate::{Action, KeyContext, Keymap, KeymapVersion, Keystroke};
use parking_lot::Mutex;
use smallvec::SmallVec;
use std::sync::Arc;
pub struct KeystrokeMatcher {
@@ -51,10 +50,14 @@ impl KeystrokeMatcher {
let mut pending_key = None;
let mut found_actions = Vec::new();
for binding in keymap.bindings().iter().rev() {
for binding in keymap.bindings().rev() {
if !keymap.binding_enabled(binding, context_stack) {
continue;
}
for candidate in keystroke.match_candidates() {
self.pending_keystrokes.push(candidate.clone());
match binding.match_keystrokes(&self.pending_keystrokes, context_stack) {
match binding.match_keystrokes(&self.pending_keystrokes) {
KeyMatch::Some(mut actions) => {
found_actions.append(&mut actions);
}
@@ -82,19 +85,6 @@ impl KeystrokeMatcher {
KeyMatch::Pending
}
}
pub fn keystrokes_for_action(
&self,
action: &dyn Action,
contexts: &[KeyContext],
) -> Option<SmallVec<[Keystroke; 2]>> {
self.keymap
.lock()
.bindings()
.iter()
.rev()
.find_map(|binding| binding.keystrokes_for_action(action, contexts))
}
}
#[derive(Debug)]
+5 -68
View File
@@ -6,19 +6,17 @@ mod mac;
mod test;
use crate::{
point, size, Action, AnyWindowHandle, BackgroundExecutor, Bounds, DevicePixels, Font, FontId,
FontMetrics, FontRun, ForegroundExecutor, GlobalPixels, GlyphId, InputEvent, Keymap,
LineLayout, Pixels, Point, RenderGlyphParams, RenderImageParams, RenderSvgParams, Result,
Scene, SharedString, Size, TaskLabel,
Action, AnyWindowHandle, BackgroundExecutor, Bounds, DevicePixels, Font, FontId, FontMetrics,
FontRun, ForegroundExecutor, GlobalPixels, GlyphId, InputEvent, Keymap, LineLayout, Pixels,
Point, RenderGlyphParams, RenderImageParams, RenderSvgParams, Result, Scene, SharedString,
Size, TaskLabel,
};
use anyhow::{anyhow, bail};
use anyhow::anyhow;
use async_task::Runnable;
use futures::channel::oneshot;
use parking::Unparker;
use seahash::SeaHasher;
use serde::{Deserialize, Serialize};
use sqlez::bindable::{Bind, Column, StaticColumnCount};
use sqlez::statement::Statement;
use std::borrow::Cow;
use std::hash::{Hash, Hasher};
use std::time::Duration;
@@ -396,67 +394,6 @@ pub enum WindowBounds {
Fixed(Bounds<GlobalPixels>),
}
impl StaticColumnCount for WindowBounds {
fn column_count() -> usize {
5
}
}
impl Bind for WindowBounds {
fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
let (region, next_index) = match self {
WindowBounds::Fullscreen => {
let next_index = statement.bind(&"Fullscreen", start_index)?;
(None, next_index)
}
WindowBounds::Maximized => {
let next_index = statement.bind(&"Maximized", start_index)?;
(None, next_index)
}
WindowBounds::Fixed(region) => {
let next_index = statement.bind(&"Fixed", start_index)?;
(Some(*region), next_index)
}
};
statement.bind(
&region.map(|region| {
(
region.origin.x,
region.origin.y,
region.size.width,
region.size.height,
)
}),
next_index,
)
}
}
impl Column for WindowBounds {
fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
let (window_state, next_index) = String::column(statement, start_index)?;
let bounds = match window_state.as_str() {
"Fullscreen" => WindowBounds::Fullscreen,
"Maximized" => WindowBounds::Maximized,
"Fixed" => {
let ((x, y, width, height), _) = Column::column(statement, next_index)?;
let x: f64 = x;
let y: f64 = y;
let width: f64 = width;
let height: f64 = height;
WindowBounds::Fixed(Bounds {
origin: point(x.into(), y.into()),
size: size(width.into(), height.into()),
})
}
_ => bail!("Window State did not have a valid string"),
};
Ok((bounds, next_index + 4))
}
}
#[derive(Copy, Clone, Debug)]
pub enum WindowAppearance {
Light,
+5 -5
View File
@@ -11,7 +11,7 @@ use objc::{
};
use parking::{Parker, Unparker};
use parking_lot::Mutex;
use std::{ffi::c_void, sync::Arc, time::Duration};
use std::{ffi::c_void, ptr::NonNull, sync::Arc, time::Duration};
include!(concat!(env!("OUT_DIR"), "/dispatch_sys.rs"));
@@ -47,7 +47,7 @@ impl PlatformDispatcher for MacDispatcher {
unsafe {
dispatch_async_f(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT.try_into().unwrap(), 0),
runnable.into_raw() as *mut c_void,
runnable.into_raw().as_ptr() as *mut c_void,
Some(trampoline),
);
}
@@ -57,7 +57,7 @@ impl PlatformDispatcher for MacDispatcher {
unsafe {
dispatch_async_f(
dispatch_get_main_queue(),
runnable.into_raw() as *mut c_void,
runnable.into_raw().as_ptr() as *mut c_void,
Some(trampoline),
);
}
@@ -71,7 +71,7 @@ impl PlatformDispatcher for MacDispatcher {
dispatch_after_f(
when,
queue,
runnable.into_raw() as *mut c_void,
runnable.into_raw().as_ptr() as *mut c_void,
Some(trampoline),
);
}
@@ -91,6 +91,6 @@ impl PlatformDispatcher for MacDispatcher {
}
extern "C" fn trampoline(runnable: *mut c_void) {
let task = unsafe { Runnable::from_raw(runnable as *mut ()) };
let task = unsafe { Runnable::<()>::from_raw(NonNull::new_unchecked(runnable as *mut ())) };
task.run();
}
+2 -2
View File
@@ -260,8 +260,8 @@ impl MacPlatform {
os_action,
} => {
let keystrokes = keymap
.bindings_for_action(action.type_id())
.find(|binding| binding.action().partial_eq(action.as_ref()))
.bindings_for_action(action.as_ref())
.next()
.map(|binding| binding.keystrokes());
let selector = match os_action {
+34 -6
View File
@@ -19,7 +19,7 @@ pub struct TestPlatform {
background_executor: BackgroundExecutor,
foreground_executor: ForegroundExecutor,
active_window: Arc<Mutex<Option<AnyWindowHandle>>>,
pub(crate) active_window: RefCell<Option<TestWindow>>,
active_display: Rc<dyn PlatformDisplay>,
active_cursor: Mutex<CursorStyle>,
current_clipboard_item: Mutex<Option<ClipboardItem>>,
@@ -79,6 +79,28 @@ impl TestPlatform {
self.prompts.borrow_mut().multiple_choice.push_back(tx);
rx
}
pub(crate) fn set_active_window(&self, window: Option<TestWindow>) {
let executor = self.foreground_executor().clone();
let previous_window = self.active_window.borrow_mut().take();
*self.active_window.borrow_mut() = window.clone();
executor
.spawn(async move {
if let Some(previous_window) = previous_window {
if let Some(window) = window.as_ref() {
if Arc::ptr_eq(&previous_window.0, &window.0) {
return;
}
}
previous_window.simulate_active_status_change(false);
}
if let Some(window) = window {
window.simulate_active_status_change(true);
}
})
.detach();
}
}
// todo!("implement out what our tests needed in GPUI 1")
@@ -105,7 +127,9 @@ impl Platform for TestPlatform {
unimplemented!()
}
fn activate(&self, _ignoring_other_apps: bool) {}
fn activate(&self, _ignoring_other_apps: bool) {
//
}
fn hide(&self) {
unimplemented!()
@@ -128,7 +152,10 @@ impl Platform for TestPlatform {
}
fn active_window(&self) -> Option<crate::AnyWindowHandle> {
self.active_window.lock().clone()
self.active_window
.borrow()
.as_ref()
.map(|window| window.0.lock().handle)
}
fn open_window(
@@ -137,12 +164,13 @@ impl Platform for TestPlatform {
options: WindowOptions,
_draw: Box<dyn FnMut() -> Result<Scene>>,
) -> Box<dyn crate::PlatformWindow> {
*self.active_window.lock() = Some(handle);
Box::new(TestWindow::new(
let window = TestWindow::new(
options,
handle,
self.weak.clone(),
self.active_display.clone(),
))
);
Box::new(window)
}
fn set_display_link_output_callback(
+112 -31
View File
@@ -1,7 +1,7 @@
use crate::{
px, AtlasKey, AtlasTextureId, AtlasTile, Pixels, PlatformAtlas, PlatformDisplay,
PlatformInputHandler, PlatformWindow, Point, Size, TestPlatform, TileId, WindowAppearance,
WindowBounds, WindowOptions,
px, AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, Bounds, InputEvent, KeyDownEvent,
Keystroke, Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point,
Size, TestPlatform, TileId, WindowAppearance, WindowBounds, WindowOptions,
};
use collections::HashMap;
use parking_lot::Mutex;
@@ -10,51 +10,122 @@ use std::{
sync::{self, Arc},
};
#[derive(Default)]
pub(crate) struct TestWindowHandlers {
pub(crate) active_status_change: Vec<Box<dyn FnMut(bool)>>,
pub(crate) input: Vec<Box<dyn FnMut(crate::InputEvent) -> bool>>,
pub(crate) moved: Vec<Box<dyn FnMut()>>,
pub(crate) resize: Vec<Box<dyn FnMut(Size<Pixels>, f32)>>,
}
pub struct TestWindow {
pub struct TestWindowState {
pub(crate) bounds: WindowBounds,
pub(crate) handle: AnyWindowHandle,
display: Rc<dyn PlatformDisplay>,
pub(crate) title: Option<String>,
pub(crate) edited: bool,
pub(crate) input_handler: Option<Arc<Mutex<Box<dyn PlatformInputHandler>>>>,
pub(crate) handlers: Arc<Mutex<TestWindowHandlers>>,
platform: Weak<TestPlatform>,
sprite_atlas: Arc<dyn PlatformAtlas>,
input_callback: Option<Box<dyn FnMut(InputEvent) -> bool>>,
active_status_change_callback: Option<Box<dyn FnMut(bool)>>,
resize_callback: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
moved_callback: Option<Box<dyn FnMut()>>,
input_handler: Option<Box<dyn PlatformInputHandler>>,
}
#[derive(Clone)]
pub struct TestWindow(pub(crate) Arc<Mutex<TestWindowState>>);
impl TestWindow {
pub fn new(
options: WindowOptions,
handle: AnyWindowHandle,
platform: Weak<TestPlatform>,
display: Rc<dyn PlatformDisplay>,
) -> Self {
Self {
Self(Arc::new(Mutex::new(TestWindowState {
bounds: options.bounds,
display,
platform,
input_handler: None,
handle,
sprite_atlas: Arc::new(TestAtlas::new()),
handlers: Default::default(),
title: Default::default(),
edited: false,
input_callback: None,
active_status_change_callback: None,
resize_callback: None,
moved_callback: None,
input_handler: None,
})))
}
pub fn simulate_resize(&mut self, size: Size<Pixels>) {
let scale_factor = self.scale_factor();
let mut lock = self.0.lock();
let Some(mut callback) = lock.resize_callback.take() else {
return;
};
match &mut lock.bounds {
WindowBounds::Fullscreen | WindowBounds::Maximized => {
lock.bounds = WindowBounds::Fixed(Bounds {
origin: Point::default(),
size: size.map(|pixels| f64::from(pixels).into()),
});
}
WindowBounds::Fixed(bounds) => {
bounds.size = size.map(|pixels| f64::from(pixels).into());
}
}
drop(lock);
callback(size, scale_factor);
self.0.lock().resize_callback = Some(callback);
}
pub(crate) fn simulate_active_status_change(&self, active: bool) {
let mut lock = self.0.lock();
let Some(mut callback) = lock.active_status_change_callback.take() else {
return;
};
drop(lock);
callback(active);
self.0.lock().active_status_change_callback = Some(callback);
}
pub fn simulate_input(&mut self, event: InputEvent) -> bool {
let mut lock = self.0.lock();
let Some(mut callback) = lock.input_callback.take() else {
return false;
};
drop(lock);
let result = callback(event);
self.0.lock().input_callback = Some(callback);
result
}
pub fn simulate_keystroke(&mut self, keystroke: Keystroke, is_held: bool) {
if self.simulate_input(InputEvent::KeyDown(KeyDownEvent {
keystroke: keystroke.clone(),
is_held,
})) {
return;
}
let mut lock = self.0.lock();
let Some(mut input_handler) = lock.input_handler.take() else {
panic!(
"simulate_keystroke {:?} input event was not handled and there was no active input",
&keystroke
);
};
drop(lock);
let text = keystroke.ime_key.unwrap_or(keystroke.key);
input_handler.replace_text_in_range(None, &text);
self.0.lock().input_handler = Some(input_handler);
}
}
impl PlatformWindow for TestWindow {
fn bounds(&self) -> WindowBounds {
self.bounds
self.0.lock().bounds
}
fn content_size(&self) -> Size<Pixels> {
let bounds = match self.bounds {
let bounds = match self.bounds() {
WindowBounds::Fixed(bounds) => bounds,
WindowBounds::Maximized | WindowBounds::Fullscreen => self.display().bounds(),
};
@@ -74,7 +145,7 @@ impl PlatformWindow for TestWindow {
}
fn display(&self) -> std::rc::Rc<dyn crate::PlatformDisplay> {
self.display.clone()
self.0.lock().display.clone()
}
fn mouse_position(&self) -> Point<Pixels> {
@@ -90,11 +161,11 @@ impl PlatformWindow for TestWindow {
}
fn set_input_handler(&mut self, input_handler: Box<dyn crate::PlatformInputHandler>) {
self.input_handler = Some(Arc::new(Mutex::new(input_handler)));
self.0.lock().input_handler = Some(input_handler);
}
fn clear_input_handler(&mut self) {
self.input_handler = None;
self.0.lock().input_handler = None;
}
fn prompt(
@@ -103,19 +174,29 @@ impl PlatformWindow for TestWindow {
_msg: &str,
_answers: &[&str],
) -> futures::channel::oneshot::Receiver<usize> {
self.platform.upgrade().expect("platform dropped").prompt()
self.0
.lock()
.platform
.upgrade()
.expect("platform dropped")
.prompt()
}
fn activate(&self) {
unimplemented!()
self.0
.lock()
.platform
.upgrade()
.unwrap()
.set_active_window(Some(self.clone()))
}
fn set_title(&mut self, title: &str) {
self.title = Some(title.to_owned());
self.0.lock().title = Some(title.to_owned());
}
fn set_edited(&mut self, edited: bool) {
self.edited = edited;
self.0.lock().edited = edited;
}
fn show_character_palette(&self) {
@@ -135,15 +216,15 @@ impl PlatformWindow for TestWindow {
}
fn on_input(&self, callback: Box<dyn FnMut(crate::InputEvent) -> bool>) {
self.handlers.lock().input.push(callback)
self.0.lock().input_callback = Some(callback)
}
fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
self.handlers.lock().active_status_change.push(callback)
self.0.lock().active_status_change_callback = Some(callback)
}
fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
self.handlers.lock().resize.push(callback)
self.0.lock().resize_callback = Some(callback)
}
fn on_fullscreen(&self, _callback: Box<dyn FnMut(bool)>) {
@@ -151,7 +232,7 @@ impl PlatformWindow for TestWindow {
}
fn on_moved(&self, callback: Box<dyn FnMut()>) {
self.handlers.lock().moved.push(callback)
self.0.lock().moved_callback = Some(callback)
}
fn on_should_close(&self, _callback: Box<dyn FnMut() -> bool>) {
@@ -175,7 +256,7 @@ impl PlatformWindow for TestWindow {
}
fn sprite_atlas(&self) -> sync::Arc<dyn crate::PlatformAtlas> {
self.sprite_atlas.clone()
self.0.lock().sprite_atlas.clone()
}
fn as_test(&mut self) -> Option<&mut TestWindow> {
+27 -1
View File
@@ -57,6 +57,12 @@ lazy_static! {
pub static ref BUFFER_DIFF_TASK: TaskLabel = TaskLabel::new();
}
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum Capability {
ReadWrite,
ReadOnly,
}
pub struct Buffer {
text: TextBuffer,
diff_base: Option<String>,
@@ -90,6 +96,7 @@ pub struct Buffer {
completion_triggers: Vec<String>,
completion_triggers_timestamp: clock::Lamport,
deferred_ops: OperationQueue<Operation>,
capability: Capability,
}
pub struct BufferSnapshot {
@@ -405,19 +412,27 @@ impl Buffer {
TextBuffer::new(replica_id, id, base_text.into()),
None,
None,
Capability::ReadWrite,
)
}
pub fn remote(remote_id: u64, replica_id: ReplicaId, base_text: String) -> Self {
pub fn remote(
remote_id: u64,
replica_id: ReplicaId,
capability: Capability,
base_text: String,
) -> Self {
Self::build(
TextBuffer::new(replica_id, remote_id, base_text),
None,
None,
capability,
)
}
pub fn from_proto(
replica_id: ReplicaId,
capability: Capability,
message: proto::BufferState,
file: Option<Arc<dyn File>>,
) -> Result<Self> {
@@ -426,6 +441,7 @@ impl Buffer {
buffer,
message.diff_base.map(|text| text.into_boxed_str().into()),
file,
capability,
);
this.text.set_line_ending(proto::deserialize_line_ending(
rpc::proto::LineEnding::from_i32(message.line_ending)
@@ -504,10 +520,19 @@ impl Buffer {
self
}
pub fn capability(&self) -> Capability {
self.capability
}
pub fn read_only(&self) -> bool {
self.capability == Capability::ReadOnly
}
pub fn build(
buffer: TextBuffer,
diff_base: Option<String>,
file: Option<Arc<dyn File>>,
capability: Capability,
) -> Self {
let saved_mtime = if let Some(file) = file.as_ref() {
file.mtime()
@@ -526,6 +551,7 @@ impl Buffer {
diff_base,
git_diff: git::diff::BufferDiff::new(),
file,
capability,
syntax_map: Mutex::new(SyntaxMap::new()),
parsing_in_background: false,
parse_count: 0,
+10 -4
View File
@@ -1926,7 +1926,7 @@ fn test_serialization(cx: &mut gpui::AppContext) {
.background_executor()
.block(buffer1.read(cx).serialize_ops(None, cx));
let buffer2 = cx.new_model(|cx| {
let mut buffer = Buffer::from_proto(1, state, None).unwrap();
let mut buffer = Buffer::from_proto(1, Capability::ReadWrite, state, None).unwrap();
buffer
.apply_ops(
ops.into_iter()
@@ -1967,7 +1967,8 @@ fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
let ops = cx
.background_executor()
.block(base_buffer.read(cx).serialize_ops(None, cx));
let mut buffer = Buffer::from_proto(i as ReplicaId, state, None).unwrap();
let mut buffer =
Buffer::from_proto(i as ReplicaId, Capability::ReadWrite, state, None).unwrap();
buffer
.apply_ops(
ops.into_iter()
@@ -2083,8 +2084,13 @@ fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
replica_id
);
new_buffer = Some(cx.new_model(|cx| {
let mut new_buffer =
Buffer::from_proto(new_replica_id, old_buffer_state, None).unwrap();
let mut new_buffer = Buffer::from_proto(
new_replica_id,
Capability::ReadWrite,
old_buffer_state,
None,
)
.unwrap();
new_buffer
.apply_ops(
old_buffer_ops
+4 -2
View File
@@ -772,9 +772,10 @@ impl Render for LspLogToolbarItemView {
}),
);
if server_selected && row.logs_selected {
let selected_ix = menu.select_last();
debug_assert_eq!(
Some(ix * 3 + 1),
menu.select_last(),
selected_ix,
"Could not scroll to a just added LSP menu item"
);
}
@@ -822,9 +823,10 @@ impl Render for LspLogToolbarItemView {
}),
);
if server_selected && row.rpc_trace_selected {
let selected_ix = menu.select_last();
debug_assert_eq!(
Some(ix * 3 + 2),
menu.select_last(),
selected_ix,
"Could not scroll to a just added LSP menu item"
);
}
+1
View File
@@ -62,6 +62,7 @@ impl<'a> VideoGrant<'a> {
Self {
room: Some(Cow::Borrowed(room)),
room_join: Some(true),
can_publish: Some(false),
can_subscribe: Some(true),
..Default::default()
}
+23 -16
View File
@@ -11,7 +11,7 @@ pub use language::Completion;
use language::{
char_kind,
language_settings::{language_settings, LanguageSettings},
AutoindentMode, Buffer, BufferChunks, BufferSnapshot, CharKind, Chunk, CursorShape,
AutoindentMode, Buffer, BufferChunks, BufferSnapshot, Capability, CharKind, Chunk, CursorShape,
DiagnosticEntry, File, IndentSize, Language, LanguageScope, OffsetRangeExt, OffsetUtf16,
Outline, OutlineItem, Point, PointUtf16, Selection, TextDimension, ToOffset as _,
ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _, TransactionId, Unclipped,
@@ -55,6 +55,7 @@ pub struct MultiBuffer {
replica_id: ReplicaId,
history: History,
title: Option<String>,
capability: Capability,
}
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -225,13 +226,14 @@ struct ExcerptBytes<'a> {
}
impl MultiBuffer {
pub fn new(replica_id: ReplicaId) -> Self {
pub fn new(replica_id: ReplicaId, capability: Capability) -> Self {
Self {
snapshot: Default::default(),
buffers: Default::default(),
next_excerpt_id: 1,
subscriptions: Default::default(),
singleton: false,
capability,
replica_id,
history: History {
next_transaction_id: Default::default(),
@@ -271,6 +273,7 @@ impl MultiBuffer {
next_excerpt_id: 1,
subscriptions: Default::default(),
singleton: self.singleton,
capability: self.capability,
replica_id: self.replica_id,
history: self.history.clone(),
title: self.title.clone(),
@@ -282,8 +285,12 @@ impl MultiBuffer {
self
}
pub fn read_only(&self) -> bool {
self.capability == Capability::ReadOnly
}
pub fn singleton(buffer: Model<Buffer>, cx: &mut ModelContext<Self>) -> Self {
let mut this = Self::new(buffer.read(cx).replica_id());
let mut this = Self::new(buffer.read(cx).replica_id(), buffer.read(cx).capability());
this.singleton = true;
this.push_excerpts(
buffer,
@@ -1657,7 +1664,7 @@ impl MultiBuffer {
excerpts: [(&str, Vec<Range<Point>>); COUNT],
cx: &mut gpui::AppContext,
) -> Model<Self> {
let multi = cx.new_model(|_| Self::new(0));
let multi = cx.new_model(|_| Self::new(0, Capability::ReadWrite));
for (text, ranges) in excerpts {
let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text));
let excerpt_ranges = ranges.into_iter().map(|range| ExcerptRange {
@@ -1678,7 +1685,7 @@ impl MultiBuffer {
pub fn build_random(rng: &mut impl rand::Rng, cx: &mut gpui::AppContext) -> Model<Self> {
cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0);
let mut multibuffer = MultiBuffer::new(0, Capability::ReadWrite);
let mutation_count = rng.gen_range(1..=5);
multibuffer.randomly_edit_excerpts(rng, mutation_count, cx);
multibuffer
@@ -4176,7 +4183,7 @@ mod tests {
let ops = cx
.background_executor()
.block(host_buffer.read(cx).serialize_ops(None, cx));
let mut buffer = Buffer::from_proto(1, state, None).unwrap();
let mut buffer = Buffer::from_proto(1, Capability::ReadWrite, state, None).unwrap();
buffer
.apply_ops(
ops.into_iter()
@@ -4205,7 +4212,7 @@ mod tests {
cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(6, 6, 'a')));
let buffer_2 =
cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(6, 6, 'g')));
let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
let events = Arc::new(RwLock::new(Vec::<Event>::new()));
multibuffer.update(cx, |_, cx| {
@@ -4442,8 +4449,8 @@ mod tests {
let buffer_2 =
cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(10, 3, 'm')));
let leader_multibuffer = cx.new_model(|_| MultiBuffer::new(0));
let follower_multibuffer = cx.new_model(|_| MultiBuffer::new(0));
let leader_multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
let follower_multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
let follower_edit_event_count = Arc::new(RwLock::new(0));
follower_multibuffer.update(cx, |_, cx| {
@@ -4547,7 +4554,7 @@ mod tests {
fn test_push_excerpts_with_context_lines(cx: &mut AppContext) {
let buffer =
cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(20, 3, 'a')));
let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
let anchor_ranges = multibuffer.update(cx, |multibuffer, cx| {
multibuffer.push_excerpts_with_context_lines(
buffer.clone(),
@@ -4584,7 +4591,7 @@ mod tests {
async fn test_stream_excerpts_with_context_lines(cx: &mut TestAppContext) {
let buffer =
cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(20, 3, 'a')));
let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
let anchor_ranges = multibuffer.update(cx, |multibuffer, cx| {
let snapshot = buffer.read(cx);
let ranges = vec![
@@ -4619,7 +4626,7 @@ mod tests {
#[gpui::test]
fn test_empty_multibuffer(cx: &mut AppContext) {
let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
let snapshot = multibuffer.read(cx).snapshot(cx);
assert_eq!(snapshot.text(), "");
@@ -4652,7 +4659,7 @@ mod tests {
let buffer_1 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abcd"));
let buffer_2 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "efghi"));
let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0);
let mut multibuffer = MultiBuffer::new(0, Capability::ReadWrite);
multibuffer.push_excerpts(
buffer_1.clone(),
[ExcerptRange {
@@ -4710,7 +4717,7 @@ mod tests {
let buffer_1 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abcd"));
let buffer_2 =
cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "ABCDEFGHIJKLMNOP"));
let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
// Create an insertion id in buffer 1 that doesn't exist in buffer 2.
// Add an excerpt from buffer 1 that spans this new insertion.
@@ -4844,7 +4851,7 @@ mod tests {
.unwrap_or(10);
let mut buffers: Vec<Model<Buffer>> = Vec::new();
let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
let mut excerpt_ids = Vec::<ExcerptId>::new();
let mut expected_excerpts = Vec::<(Model<Buffer>, Range<text::Anchor>)>::new();
let mut anchors = Vec::new();
@@ -5266,7 +5273,7 @@ mod tests {
let buffer_1 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "1234"));
let buffer_2 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "5678"));
let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
let group_interval = multibuffer.read(cx).history.group_interval;
multibuffer.update(cx, |multibuffer, cx| {
multibuffer.push_excerpts(
+36 -9
View File
@@ -39,11 +39,11 @@ use language::{
deserialize_anchor, deserialize_fingerprint, deserialize_line_ending, deserialize_version,
serialize_anchor, serialize_version, split_operations,
},
range_from_lsp, range_to_lsp, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CodeAction,
CodeLabel, Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Diff, Event as BufferEvent,
File as _, Language, LanguageRegistry, LanguageServerName, LocalFile, LspAdapterDelegate,
OffsetRangeExt, Operation, Patch, PendingLanguageServer, PointUtf16, TextBufferSnapshot,
ToOffset, ToPointUtf16, Transaction, Unclipped,
range_from_lsp, range_to_lsp, Bias, Buffer, BufferSnapshot, CachedLspAdapter, Capability,
CodeAction, CodeLabel, Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Diff,
Event as BufferEvent, File as _, Language, LanguageRegistry, LanguageServerName, LocalFile,
LspAdapterDelegate, OffsetRangeExt, Operation, Patch, PendingLanguageServer, PointUtf16,
TextBufferSnapshot, ToOffset, ToPointUtf16, Transaction, Unclipped,
};
use log::error;
use lsp::{
@@ -262,6 +262,7 @@ enum ProjectClientState {
},
Remote {
sharing_has_stopped: bool,
capability: Capability,
remote_id: u64,
replica_id: ReplicaId,
},
@@ -702,6 +703,7 @@ impl Project {
user_store: Model<UserStore>,
languages: Arc<LanguageRegistry>,
fs: Arc<dyn Fs>,
role: proto::ChannelRole,
mut cx: AsyncAppContext,
) -> Result<Model<Self>> {
client.authenticate_and_connect(true, &cx).await?;
@@ -756,6 +758,7 @@ impl Project {
client: client.clone(),
client_state: Some(ProjectClientState::Remote {
sharing_has_stopped: false,
capability: Capability::ReadWrite,
remote_id,
replica_id,
}),
@@ -796,6 +799,7 @@ impl Project {
prettiers_per_worktree: HashMap::default(),
prettier_instances: HashMap::default(),
};
this.set_role(role);
for worktree in worktrees {
let _ = this.add_worktree(&worktree, cx);
}
@@ -1618,6 +1622,17 @@ impl Project {
cx.notify();
}
pub fn set_role(&mut self, role: proto::ChannelRole) {
if let Some(ProjectClientState::Remote { capability, .. }) = &mut self.client_state {
*capability = if role == proto::ChannelRole::Member || role == proto::ChannelRole::Admin
{
Capability::ReadWrite
} else {
Capability::ReadOnly
};
}
}
fn disconnected_from_host_internal(&mut self, cx: &mut AppContext) {
if let Some(ProjectClientState::Remote {
sharing_has_stopped,
@@ -1659,7 +1674,7 @@ impl Project {
cx.emit(Event::Closed);
}
pub fn is_read_only(&self) -> bool {
pub fn is_disconnected(&self) -> bool {
match &self.client_state {
Some(ProjectClientState::Remote {
sharing_has_stopped,
@@ -1669,6 +1684,17 @@ impl Project {
}
}
pub fn capability(&self) -> Capability {
match &self.client_state {
Some(ProjectClientState::Remote { capability, .. }) => *capability,
Some(ProjectClientState::Local { .. }) | None => Capability::ReadWrite,
}
}
pub fn is_read_only(&self) -> bool {
self.is_disconnected() || self.capability() == Capability::ReadOnly
}
pub fn is_local(&self) -> bool {
match &self.client_state {
Some(ProjectClientState::Remote { .. }) => false,
@@ -6013,7 +6039,7 @@ impl Project {
this.upgrade().context("project dropped")?;
let response = rpc.request(message).await?;
let this = this.upgrade().context("project dropped")?;
if this.update(&mut cx, |this, _| this.is_read_only())? {
if this.update(&mut cx, |this, _| this.is_disconnected())? {
Err(anyhow!("disconnected before completing request"))
} else {
request
@@ -7192,7 +7218,8 @@ impl Project {
let buffer_id = state.id;
let buffer = cx.new_model(|_| {
Buffer::from_proto(this.replica_id(), state, buffer_file).unwrap()
Buffer::from_proto(this.replica_id(), this.capability(), state, buffer_file)
.unwrap()
});
this.incomplete_remote_buffers
.insert(buffer_id, Some(buffer));
@@ -7940,7 +7967,7 @@ impl Project {
if let Some(buffer) = buffer {
break buffer;
} else if this.update(&mut cx, |this, _| this.is_read_only())? {
} else if this.update(&mut cx, |this, _| this.is_disconnected())? {
return Err(anyhow!("disconnected before buffer {} could be opened", id));
}
+10 -2
View File
@@ -32,7 +32,8 @@ use language::{
deserialize_fingerprint, deserialize_version, serialize_fingerprint, serialize_line_ending,
serialize_version,
},
Buffer, DiagnosticEntry, File as _, LineEnding, PointUtf16, Rope, RopeFingerprint, Unclipped,
Buffer, Capability, DiagnosticEntry, File as _, LineEnding, PointUtf16, Rope, RopeFingerprint,
Unclipped,
};
use lsp::LanguageServerId;
use parking_lot::Mutex;
@@ -682,7 +683,14 @@ impl LocalWorktree {
.background_executor()
.spawn(async move { text::Buffer::new(0, id, contents) })
.await;
cx.new_model(|_| Buffer::build(text_buffer, diff_base, Some(Arc::new(file))))
cx.new_model(|_| {
Buffer::build(
text_buffer,
diff_base,
Some(Arc::new(file)),
Capability::ReadWrite,
)
})
})
}
+25 -10
View File
@@ -388,8 +388,18 @@ impl ProjectPanel {
let is_dir = entry.is_dir();
let worktree_id = worktree.id();
let is_local = project.is_local();
let is_read_only = project.is_read_only();
let context_menu = ContextMenu::build(cx, |mut menu, cx| {
if is_read_only {
menu = menu.action("Copy Relative Path", Box::new(CopyRelativePath));
if is_dir {
menu = menu.action("Search Inside", Box::new(NewSearchInDirectory))
}
return menu;
}
if is_local {
menu = menu.action(
"Add Folder to Project",
@@ -1473,6 +1483,7 @@ impl ProjectPanel {
impl Render for ProjectPanel {
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement {
let has_worktree = self.visible_entries.len() != 0;
let project = self.project.read(cx);
if has_worktree {
div()
@@ -1485,21 +1496,25 @@ impl Render for ProjectPanel {
.on_action(cx.listener(Self::expand_selected_entry))
.on_action(cx.listener(Self::collapse_selected_entry))
.on_action(cx.listener(Self::collapse_all_entries))
.on_action(cx.listener(Self::new_file))
.on_action(cx.listener(Self::new_directory))
.on_action(cx.listener(Self::rename))
.on_action(cx.listener(Self::delete))
.on_action(cx.listener(Self::confirm))
.on_action(cx.listener(Self::open_file))
.on_action(cx.listener(Self::confirm))
.on_action(cx.listener(Self::cancel))
.on_action(cx.listener(Self::cut))
.on_action(cx.listener(Self::copy))
.on_action(cx.listener(Self::copy_path))
.on_action(cx.listener(Self::copy_relative_path))
.on_action(cx.listener(Self::paste))
.on_action(cx.listener(Self::reveal_in_finder))
.on_action(cx.listener(Self::open_in_terminal))
.on_action(cx.listener(Self::new_search_in_directory))
.when(!project.is_read_only(), |el| {
el.on_action(cx.listener(Self::new_file))
.on_action(cx.listener(Self::new_directory))
.on_action(cx.listener(Self::rename))
.on_action(cx.listener(Self::delete))
.on_action(cx.listener(Self::cut))
.on_action(cx.listener(Self::copy))
.on_action(cx.listener(Self::paste))
})
.when(project.is_local(), |el| {
el.on_action(cx.listener(Self::reveal_in_finder))
.on_action(cx.listener(Self::open_in_terminal))
})
.track_focus(&self.focus_handle)
.child(
uniform_list(
@@ -395,6 +395,7 @@ mod tests {
language::init(cx);
Project::init_settings(cx);
workspace::init_settings(cx);
editor::init(cx);
});
}
+1
View File
@@ -269,6 +269,7 @@ message Participant {
repeated ParticipantProject projects = 3;
ParticipantLocation location = 4;
uint32 participant_index = 5;
ChannelRole role = 6;
}
message PendingParticipant {
+2 -1
View File
@@ -70,7 +70,7 @@ impl BufferSearchBar {
fn render_text_input(&self, editor: &View<Editor>, cx: &ViewContext<Self>) -> impl IntoElement {
let settings = ThemeSettings::get_global(cx);
let text_style = TextStyle {
color: if editor.read(cx).read_only() {
color: if editor.read(cx).read_only(cx) {
cx.theme().colors().text_disabled
} else {
cx.theme().colors().text
@@ -223,6 +223,7 @@ impl Render for BufferSearchBar {
.gap_2()
.border_1()
.border_color(editor_border)
.min_w(rems(384. / 16.))
.rounded_lg()
.child(IconElement::new(Icon::MagnifyingGlass))
.child(self.render_text_input(&self.query_editor, cx))
+315 -29
View File
@@ -38,8 +38,8 @@ use std::{
use theme::ThemeSettings;
use ui::{
h_stack, prelude::*, v_stack, Button, Icon, IconButton, IconElement, Label, LabelCommon,
LabelSize, Selectable, Tooltip,
h_stack, prelude::*, v_stack, Icon, IconButton, IconElement, Label, LabelCommon, LabelSize,
Selectable, ToggleButton, Tooltip,
};
use util::{paths::PathMatcher, ResultExt as _};
use workspace::{
@@ -61,12 +61,12 @@ struct ActiveSearches(HashMap<WeakModel<Project>, WeakView<ProjectSearchView>>);
struct ActiveSettings(HashMap<WeakModel<Project>, ProjectSearchSettings>);
pub fn init(cx: &mut AppContext) {
// todo!() po
cx.set_global(ActiveSearches::default());
cx.set_global(ActiveSettings::default());
cx.observe_new_views(|workspace: &mut Workspace, _cx| {
workspace
.register_action(ProjectSearchView::deploy)
.register_action(ProjectSearchView::new_search)
.register_action(ProjectSearchView::deploy_search)
.register_action(ProjectSearchBar::search_in_new);
})
.detach();
@@ -132,9 +132,11 @@ pub struct ProjectSearchBar {
impl ProjectSearch {
fn new(project: Model<Project>, cx: &mut ModelContext<Self>) -> Self {
let replica_id = project.read(cx).replica_id();
let capability = project.read(cx).capability();
Self {
project,
excerpts: cx.new_model(|_| MultiBuffer::new(replica_id)),
excerpts: cx.new_model(|_| MultiBuffer::new(replica_id, capability)),
pending_search: Default::default(),
match_ranges: Default::default(),
active_query: None,
@@ -286,7 +288,6 @@ impl Render for ProjectSearchView {
.size_full()
.track_focus(&self.focus_handle)
.child(self.results_editor.clone())
.into_any()
} else {
let model = self.model.read(cx);
let has_no_results = model.no_results.unwrap_or(false);
@@ -363,6 +364,7 @@ impl Render for ProjectSearchView {
.flex_1()
.size_full()
.justify_center()
.bg(cx.theme().colors().editor_background)
.track_focus(&self.focus_handle)
.child(
h_stack()
@@ -372,7 +374,6 @@ impl Render for ProjectSearchView {
.child(v_stack().child(major_text).children(minor_text))
.child(h_stack().flex_1()),
)
.into_any()
}
}
}
@@ -941,11 +942,41 @@ impl ProjectSearchView {
});
}
// Re-activate the most recently activated search or the most recent if it has been closed.
// If no search exists in the workspace, create a new one.
fn deploy_search(
workspace: &mut Workspace,
_: &workspace::DeploySearch,
cx: &mut ViewContext<Workspace>,
) {
let active_search = cx
.global::<ActiveSearches>()
.0
.get(&workspace.project().downgrade());
let existing = active_search
.and_then(|active_search| {
workspace
.items_of_type::<ProjectSearchView>(cx)
.filter(|search| &search.downgrade() == active_search)
.last()
})
.or_else(|| workspace.item_of_type::<ProjectSearchView>(cx));
Self::existing_or_new_search(workspace, existing, cx)
}
// Add another search tab to the workspace.
fn deploy(
fn new_search(
workspace: &mut Workspace,
_: &workspace::NewSearch,
cx: &mut ViewContext<Workspace>,
) {
Self::existing_or_new_search(workspace, None, cx)
}
fn existing_or_new_search(
workspace: &mut Workspace,
existing: Option<View<ProjectSearchView>>,
cx: &mut ViewContext<Workspace>,
) {
// Clean up entries for dropped projects
cx.update_global(|state: &mut ActiveSearches, _cx| {
@@ -962,19 +993,27 @@ impl ProjectSearchView {
}
});
let settings = cx
.global::<ActiveSettings>()
.0
.get(&workspace.project().downgrade());
let settings = if let Some(settings) = settings {
Some(settings.clone())
let search = if let Some(existing) = existing {
workspace.activate_item(&existing, cx);
existing
} else {
None
};
let settings = cx
.global::<ActiveSettings>()
.0
.get(&workspace.project().downgrade());
let model = cx.new_model(|cx| ProjectSearch::new(workspace.project().clone(), cx));
let search = cx.new_view(|cx| ProjectSearchView::new(model, cx, settings));
let settings = if let Some(settings) = settings {
Some(settings.clone())
} else {
None
};
let model = cx.new_model(|cx| ProjectSearch::new(workspace.project().clone(), cx));
let view = cx.new_view(|cx| ProjectSearchView::new(model, cx, settings));
workspace.add_item(Box::new(view.clone()), cx);
view
};
workspace.add_item(Box::new(search.clone()), cx);
@@ -1519,7 +1558,7 @@ impl ProjectSearchBar {
fn render_text_input(&self, editor: &View<Editor>, cx: &ViewContext<Self>) -> impl IntoElement {
let settings = ThemeSettings::get_global(cx);
let text_style = TextStyle {
color: if editor.read(cx).read_only() {
color: if editor.read(cx).read_only(cx) {
cx.theme().colors().text_disabled
} else {
cx.theme().colors().text
@@ -1639,20 +1678,26 @@ impl Render for ProjectSearchBar {
let mode_column = v_stack().items_start().justify_start().child(
h_stack()
.gap_2()
.child(
h_stack()
.child(
Button::new("project-search-text-button", "Text")
ToggleButton::new("project-search-text-button", "Text")
.style(ButtonStyle::Filled)
.size(ButtonSize::Large)
.selected(search.current_mode == SearchMode::Text)
.on_click(cx.listener(|this, _, cx| {
this.activate_search_mode(SearchMode::Text, cx)
}))
.tooltip(|cx| {
Tooltip::for_action("Toggle text search", &ActivateTextMode, cx)
}),
})
.first(),
)
.child(
Button::new("project-search-regex-button", "Regex")
ToggleButton::new("project-search-regex-button", "Regex")
.style(ButtonStyle::Filled)
.size(ButtonSize::Large)
.selected(search.current_mode == SearchMode::Regex)
.on_click(cx.listener(|this, _, cx| {
this.activate_search_mode(SearchMode::Regex, cx)
@@ -1663,11 +1708,20 @@ impl Render for ProjectSearchBar {
&ActivateRegexMode,
cx,
)
})
.map(|this| {
if semantic_is_available {
this.middle()
} else {
this.last()
}
}),
)
.when(semantic_is_available, |this| {
this.child(
Button::new("project-search-semantic-button", "Semantic")
ToggleButton::new("project-search-semantic-button", "Semantic")
.style(ButtonStyle::Filled)
.size(ButtonSize::Large)
.selected(search.current_mode == SearchMode::Semantic)
.on_click(cx.listener(|this, _, cx| {
this.activate_search_mode(SearchMode::Semantic, cx)
@@ -1678,7 +1732,8 @@ impl Render for ProjectSearchBar {
&ActivateSemanticMode,
cx,
)
}),
})
.last(),
)
}),
)
@@ -1829,6 +1884,7 @@ impl Render for ProjectSearchBar {
.child(
h_stack()
.justify_between()
.gap_2()
.child(query_column)
.child(mode_column)
.child(replace_column)
@@ -2060,7 +2116,7 @@ pub mod tests {
}
#[gpui::test]
async fn test_project_search_focus(cx: &mut TestAppContext) {
async fn test_deploy_project_search_focus(cx: &mut TestAppContext) {
init_test(cx);
let fs = FakeFs::new(cx.background_executor.clone());
@@ -2101,7 +2157,237 @@ pub mod tests {
.update(cx, |toolbar, cx| toolbar.add_item(search_bar, cx))
});
ProjectSearchView::deploy(workspace, &workspace::NewSearch, cx)
ProjectSearchView::deploy_search(workspace, &workspace::DeploySearch, cx)
})
.unwrap();
let Some(search_view) = cx.read(|cx| {
workspace
.read(cx)
.unwrap()
.active_pane()
.read(cx)
.active_item()
.and_then(|item| item.downcast::<ProjectSearchView>())
}) else {
panic!("Search view expected to appear after new search event trigger")
};
cx.spawn(|mut cx| async move {
window
.update(&mut cx, |_, cx| {
cx.dispatch_action(ToggleFocus.boxed_clone())
})
.unwrap();
})
.detach();
cx.background_executor.run_until_parked();
window
.update(cx, |_, cx| {
search_view.update(cx, |search_view, cx| {
assert!(
search_view.query_editor.focus_handle(cx).is_focused(cx),
"Empty search view should be focused after the toggle focus event: no results panel to focus on",
);
});
}).unwrap();
window
.update(cx, |_, cx| {
search_view.update(cx, |search_view, cx| {
let query_editor = &search_view.query_editor;
assert!(
query_editor.focus_handle(cx).is_focused(cx),
"Search view should be focused after the new search view is activated",
);
let query_text = query_editor.read(cx).text(cx);
assert!(
query_text.is_empty(),
"New search query should be empty but got '{query_text}'",
);
let results_text = search_view
.results_editor
.update(cx, |editor, cx| editor.display_text(cx));
assert!(
results_text.is_empty(),
"Empty search view should have no results but got '{results_text}'"
);
});
})
.unwrap();
window
.update(cx, |_, cx| {
search_view.update(cx, |search_view, cx| {
search_view.query_editor.update(cx, |query_editor, cx| {
query_editor.set_text("sOMETHINGtHATsURELYdOESnOTeXIST", cx)
});
search_view.search(cx);
});
})
.unwrap();
cx.background_executor.run_until_parked();
window
.update(cx, |_, cx| {
search_view.update(cx, |search_view, cx| {
let results_text = search_view
.results_editor
.update(cx, |editor, cx| editor.display_text(cx));
assert!(
results_text.is_empty(),
"Search view for mismatching query should have no results but got '{results_text}'"
);
assert!(
search_view.query_editor.focus_handle(cx).is_focused(cx),
"Search view should be focused after mismatching query had been used in search",
);
});
}).unwrap();
cx.spawn(|mut cx| async move {
window.update(&mut cx, |_, cx| {
cx.dispatch_action(ToggleFocus.boxed_clone())
})
})
.detach();
cx.background_executor.run_until_parked();
window.update(cx, |_, cx| {
search_view.update(cx, |search_view, cx| {
assert!(
search_view.query_editor.focus_handle(cx).is_focused(cx),
"Search view with mismatching query should be focused after the toggle focus event: still no results panel to focus on",
);
});
}).unwrap();
window
.update(cx, |_, cx| {
search_view.update(cx, |search_view, cx| {
search_view
.query_editor
.update(cx, |query_editor, cx| query_editor.set_text("TWO", cx));
search_view.search(cx);
});
})
.unwrap();
cx.background_executor.run_until_parked();
window.update(cx, |_, cx| {
search_view.update(cx, |search_view, cx| {
assert_eq!(
search_view
.results_editor
.update(cx, |editor, cx| editor.display_text(cx)),
"\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\nconst TWO: usize = one::ONE + one::ONE;",
"Search view results should match the query"
);
assert!(
search_view.results_editor.focus_handle(cx).is_focused(cx),
"Search view with mismatching query should be focused after search results are available",
);
});
}).unwrap();
cx.spawn(|mut cx| async move {
window
.update(&mut cx, |_, cx| {
cx.dispatch_action(ToggleFocus.boxed_clone())
})
.unwrap();
})
.detach();
cx.background_executor.run_until_parked();
window.update(cx, |_, cx| {
search_view.update(cx, |search_view, cx| {
assert!(
search_view.results_editor.focus_handle(cx).is_focused(cx),
"Search view with matching query should still have its results editor focused after the toggle focus event",
);
});
}).unwrap();
workspace
.update(cx, |workspace, cx| {
ProjectSearchView::deploy_search(workspace, &workspace::DeploySearch, cx)
})
.unwrap();
window.update(cx, |_, cx| {
search_view.update(cx, |search_view, cx| {
assert_eq!(search_view.query_editor.read(cx).text(cx), "two", "Query should be updated to first search result after search view 2nd open in a row");
assert_eq!(
search_view
.results_editor
.update(cx, |editor, cx| editor.display_text(cx)),
"\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\nconst TWO: usize = one::ONE + one::ONE;",
"Results should be unchanged after search view 2nd open in a row"
);
assert!(
search_view.query_editor.focus_handle(cx).is_focused(cx),
"Focus should be moved into query editor again after search view 2nd open in a row"
);
});
}).unwrap();
cx.spawn(|mut cx| async move {
window
.update(&mut cx, |_, cx| {
cx.dispatch_action(ToggleFocus.boxed_clone())
})
.unwrap();
})
.detach();
cx.background_executor.run_until_parked();
window.update(cx, |_, cx| {
search_view.update(cx, |search_view, cx| {
assert!(
search_view.results_editor.focus_handle(cx).is_focused(cx),
"Search view with matching query should switch focus to the results editor after the toggle focus event",
);
});
}).unwrap();
}
#[gpui::test]
async fn test_new_project_search_focus(cx: &mut TestAppContext) {
init_test(cx);
let fs = FakeFs::new(cx.background_executor.clone());
fs.insert_tree(
"/dir",
json!({
"one.rs": "const ONE: usize = 1;",
"two.rs": "const TWO: usize = one::ONE + one::ONE;",
"three.rs": "const THREE: usize = one::ONE + two::TWO;",
"four.rs": "const FOUR: usize = one::ONE + three::THREE;",
}),
)
.await;
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.clone();
let search_bar = window.build_view(cx, |_| ProjectSearchBar::new());
let active_item = cx.read(|cx| {
workspace
.read(cx)
.unwrap()
.active_pane()
.read(cx)
.active_item()
.and_then(|item| item.downcast::<ProjectSearchView>())
});
assert!(
active_item.is_none(),
"Expected no search panel to be active"
);
window
.update(cx, move |workspace, cx| {
assert_eq!(workspace.panes().len(), 1);
workspace.panes()[0].update(cx, move |pane, cx| {
pane.toolbar()
.update(cx, |toolbar, cx| toolbar.add_item(search_bar, cx))
});
ProjectSearchView::new_search(workspace, &workspace::NewSearch, cx)
})
.unwrap();
@@ -2250,7 +2536,7 @@ pub mod tests {
workspace
.update(cx, |workspace, cx| {
ProjectSearchView::deploy(workspace, &workspace::NewSearch, cx)
ProjectSearchView::new_search(workspace, &workspace::NewSearch, cx)
})
.unwrap();
cx.background_executor.run_until_parked();
@@ -2536,7 +2822,7 @@ pub mod tests {
.update(cx, |toolbar, cx| toolbar.add_item(search_bar, cx))
});
ProjectSearchView::deploy(workspace, &workspace::NewSearch, cx)
ProjectSearchView::new_search(workspace, &workspace::NewSearch, cx)
}
})
.unwrap();
-1
View File
@@ -14,7 +14,6 @@ test-support = ["gpui/test-support", "fs/test-support"]
[dependencies]
collections = { path = "../collections" }
gpui = { path = "../gpui" }
sqlez = { path = "../sqlez" }
fs = { path = "../fs" }
feature_flags = { path = "../feature_flags" }
util = { path = "../util" }
+7 -4
View File
@@ -53,7 +53,7 @@ pub struct TerminalPanel {
impl TerminalPanel {
fn new(workspace: &Workspace, cx: &mut ViewContext<Self>) -> Self {
let terminal_panel = cx.view().clone();
let terminal_panel = cx.view().downgrade();
let pane = cx.new_view(|cx| {
let mut pane = Pane::new(
workspace.weak_handle(),
@@ -77,14 +77,17 @@ impl TerminalPanel {
pane.set_can_navigate(false, cx);
pane.display_nav_history_buttons(false);
pane.set_render_tab_bar_buttons(cx, move |pane, cx| {
let terminal_panel = terminal_panel.clone();
h_stack()
.gap_2()
.child(
IconButton::new("plus", Icon::Plus)
.icon_size(IconSize::Small)
.on_click(cx.listener_for(&terminal_panel, |terminal_panel, _, cx| {
terminal_panel.add_terminal(None, cx);
}))
.on_click(move |_, cx| {
terminal_panel
.update(cx, |panel, cx| panel.add_terminal(None, cx))
.log_err();
})
.tooltip(|cx| Tooltip::text("New Terminal", cx)),
)
.child({
+9
View File
@@ -131,6 +131,15 @@ impl PlayerColors {
*self.0.last().unwrap()
}
pub fn read_only(&self) -> PlayerColor {
let local = self.local();
PlayerColor {
cursor: local.cursor.grayscale(),
background: local.background.grayscale(),
selection: local.selection.grayscale(),
}
}
pub fn color_for_participant(&self, participant_index: u32) -> PlayerColor {
let len = self.0.len() - 1;
self.0[(participant_index as usize % len) + 1]
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+451 -451
View File
@@ -15,457 +15,6 @@ pub fn one() -> UserThemeFamily {
name: "One".into(),
author: "Zed Industries".into(),
themes: vec![
UserTheme {
name: "One Light".into(),
appearance: Appearance::Light,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0xc9c9caff).into()),
border_variant: Some(rgba(0xdfdfe0ff).into()),
border_focused: Some(rgba(0xcbcdf6ff).into()),
border_selected: Some(rgba(0xcbcdf6ff).into()),
border_transparent: Some(rgba(0x00000000).into()),
border_disabled: Some(rgba(0xd3d3d4ff).into()),
elevated_surface_background: Some(rgba(0xebebecff).into()),
surface_background: Some(rgba(0xebebecff).into()),
background: Some(rgba(0xdcdcddff).into()),
panel_background: Some(rgba(0xebebecff).into()),
element_background: Some(rgba(0xebebecff).into()),
element_hover: Some(rgba(0xdfdfe0ff).into()),
element_active: Some(rgba(0xcacacaff).into()),
element_selected: Some(rgba(0xcacacaff).into()),
element_disabled: Some(rgba(0xebebecff).into()),
drop_target_background: Some(rgba(0x7f818880).into()),
ghost_element_background: Some(rgba(0x00000000).into()),
ghost_element_hover: Some(rgba(0xdfdfe0ff).into()),
ghost_element_active: Some(rgba(0xcacacaff).into()),
ghost_element_selected: Some(rgba(0xcacacaff).into()),
ghost_element_disabled: Some(rgba(0xebebecff).into()),
text: Some(rgba(0x383a41ff).into()),
text_muted: Some(rgba(0x7f8188ff).into()),
text_placeholder: Some(rgba(0xa1a1a3ff).into()),
text_disabled: Some(rgba(0xa1a1a3ff).into()),
text_accent: Some(rgba(0x5c79e2ff).into()),
icon: Some(rgba(0x383a41ff).into()),
icon_muted: Some(rgba(0x7f8188ff).into()),
icon_disabled: Some(rgba(0xa1a1a3ff).into()),
icon_placeholder: Some(rgba(0x7f8188ff).into()),
icon_accent: Some(rgba(0x5c79e2ff).into()),
status_bar_background: Some(rgba(0xdcdcddff).into()),
title_bar_background: Some(rgba(0xdcdcddff).into()),
toolbar_background: Some(rgba(0xfafafaff).into()),
tab_bar_background: Some(rgba(0xebebecff).into()),
tab_inactive_background: Some(rgba(0xebebecff).into()),
tab_active_background: Some(rgba(0xfafafaff).into()),
scrollbar_thumb_background: Some(rgba(0x383a414c).into()),
scrollbar_thumb_hover_background: Some(rgba(0xdfdfe0ff).into()),
scrollbar_thumb_border: Some(rgba(0xdfdfe0ff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0xeeeeeeff).into()),
editor_foreground: Some(rgba(0x383a41ff).into()),
editor_background: Some(rgba(0xfafafaff).into()),
editor_gutter_background: Some(rgba(0xfafafaff).into()),
editor_subheader_background: Some(rgba(0xebebecff).into()),
editor_active_line_background: Some(rgba(0xebebecbf).into()),
editor_highlighted_line_background: Some(rgba(0xebebecff).into()),
editor_line_number: Some(rgba(0x383a4159).into()),
editor_active_line_number: Some(rgba(0x383a41ff).into()),
editor_invisible: Some(rgba(0x7f8188ff).into()),
editor_wrap_guide: Some(rgba(0x383a410d).into()),
editor_active_wrap_guide: Some(rgba(0x383a411a).into()),
editor_document_highlight_read_background: Some(rgba(0x5c79e21a).into()),
editor_document_highlight_write_background: Some(rgba(0xa3a3a466).into()),
terminal_background: Some(rgba(0xfafafaff).into()),
terminal_ansi_bright_black: Some(rgba(0xaaaaaaff).into()),
terminal_ansi_bright_red: Some(rgba(0xf0b0a4ff).into()),
terminal_ansi_bright_green: Some(rgba(0xb2cfa9ff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xf1dfc1ff).into()),
terminal_ansi_bright_blue: Some(rgba(0xb5baf2ff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xcea6d3ff).into()),
terminal_ansi_bright_cyan: Some(rgba(0xa4bfdbff).into()),
terminal_ansi_bright_white: Some(rgba(0x383a41ff).into()),
terminal_ansi_black: Some(rgba(0xfafafaff).into()),
terminal_ansi_red: Some(rgba(0xd36151ff).into()),
terminal_ansi_green: Some(rgba(0x669f59ff).into()),
terminal_ansi_yellow: Some(rgba(0xdec184ff).into()),
terminal_ansi_blue: Some(rgba(0x5c79e2ff).into()),
terminal_ansi_magenta: Some(rgba(0x994fa6ff).into()),
terminal_ansi_cyan: Some(rgba(0x3b82b7ff).into()),
terminal_ansi_white: Some(rgba(0x383a41ff).into()),
link_text_hover: Some(rgba(0x5c79e2ff).into()),
..Default::default()
},
status: StatusColorsRefinement {
conflict: Some(rgba(0xdec184ff).into()),
conflict_background: Some(rgba(0xfaf2e6ff).into()),
conflict_border: Some(rgba(0xf5e8d2ff).into()),
created: Some(rgba(0x669f59ff).into()),
created_background: Some(rgba(0xe0ebdcff).into()),
created_border: Some(rgba(0xc8dcc1ff).into()),
deleted: Some(rgba(0xd36151ff).into()),
deleted_background: Some(rgba(0xfbdfd9ff).into()),
deleted_border: Some(rgba(0xf6c6bdff).into()),
error: Some(rgba(0xd36151ff).into()),
error_background: Some(rgba(0xfbdfd9ff).into()),
error_border: Some(rgba(0xf6c6bdff).into()),
hidden: Some(rgba(0xa1a1a3ff).into()),
hidden_background: Some(rgba(0xdcdcddff).into()),
hidden_border: Some(rgba(0xd3d3d4ff).into()),
hint: Some(rgba(0x9295beff).into()),
hint_background: Some(rgba(0xe2e2faff).into()),
hint_border: Some(rgba(0xcbcdf6ff).into()),
ignored: Some(rgba(0x7f8188ff).into()),
ignored_background: Some(rgba(0xdcdcddff).into()),
ignored_border: Some(rgba(0xc9c9caff).into()),
info: Some(rgba(0x5c79e2ff).into()),
info_background: Some(rgba(0xe2e2faff).into()),
info_border: Some(rgba(0xcbcdf6ff).into()),
modified: Some(rgba(0xdec184ff).into()),
modified_background: Some(rgba(0xfaf2e6ff).into()),
modified_border: Some(rgba(0xf5e8d2ff).into()),
predictive: Some(rgba(0x9c9fc7ff).into()),
predictive_background: Some(rgba(0xe0ebdcff).into()),
predictive_border: Some(rgba(0xc8dcc1ff).into()),
renamed: Some(rgba(0x5c79e2ff).into()),
renamed_background: Some(rgba(0xe2e2faff).into()),
renamed_border: Some(rgba(0xcbcdf6ff).into()),
success: Some(rgba(0x669f59ff).into()),
success_background: Some(rgba(0xe0ebdcff).into()),
success_border: Some(rgba(0xc8dcc1ff).into()),
unreachable: Some(rgba(0x7f8188ff).into()),
unreachable_background: Some(rgba(0xdcdcddff).into()),
unreachable_border: Some(rgba(0xc9c9caff).into()),
warning: Some(rgba(0xdec184ff).into()),
warning_background: Some(rgba(0xfaf2e6ff).into()),
warning_border: Some(rgba(0xf5e8d2ff).into()),
..Default::default()
},
player: Some(PlayerColors(vec![
PlayerColor {
cursor: rgba(0x5c79e2ff).into(),
background: rgba(0x5c79e2ff).into(),
selection: rgba(0x5c79e23d).into(),
},
PlayerColor {
cursor: rgba(0x994fa6ff).into(),
background: rgba(0x994fa6ff).into(),
selection: rgba(0x994fa63d).into(),
},
PlayerColor {
cursor: rgba(0xad6f27ff).into(),
background: rgba(0xad6f27ff).into(),
selection: rgba(0xad6f273d).into(),
},
PlayerColor {
cursor: rgba(0xa44aabff).into(),
background: rgba(0xa44aabff).into(),
selection: rgba(0xa44aab3d).into(),
},
PlayerColor {
cursor: rgba(0x3b82b7ff).into(),
background: rgba(0x3b82b7ff).into(),
selection: rgba(0x3b82b73d).into(),
},
PlayerColor {
cursor: rgba(0xd36151ff).into(),
background: rgba(0xd36151ff).into(),
selection: rgba(0xd361513d).into(),
},
PlayerColor {
cursor: rgba(0xdec184ff).into(),
background: rgba(0xdec184ff).into(),
selection: rgba(0xdec1843d).into(),
},
PlayerColor {
cursor: rgba(0x669f59ff).into(),
background: rgba(0x669f59ff).into(),
selection: rgba(0x669f593d).into(),
},
])),
syntax: Some(UserSyntaxTheme {
highlights: vec![
(
"attribute".into(),
UserHighlightStyle {
color: Some(rgba(0x5c79e2ff).into()),
..Default::default()
},
),
(
"boolean".into(),
UserHighlightStyle {
color: Some(rgba(0xad6f26ff).into()),
..Default::default()
},
),
(
"comment".into(),
UserHighlightStyle {
color: Some(rgba(0xa2a3a7ff).into()),
..Default::default()
},
),
(
"comment.doc".into(),
UserHighlightStyle {
color: Some(rgba(0x7c7e86ff).into()),
..Default::default()
},
),
(
"constant".into(),
UserHighlightStyle {
color: Some(rgba(0x669f59ff).into()),
..Default::default()
},
),
(
"constructor".into(),
UserHighlightStyle {
color: Some(rgba(0x5c79e2ff).into()),
..Default::default()
},
),
(
"embedded".into(),
UserHighlightStyle {
color: Some(rgba(0x383a41ff).into()),
..Default::default()
},
),
(
"emphasis".into(),
UserHighlightStyle {
color: Some(rgba(0x5c79e2ff).into()),
..Default::default()
},
),
(
"emphasis.strong".into(),
UserHighlightStyle {
color: Some(rgba(0xad6f26ff).into()),
font_weight: Some(UserFontWeight(700.0)),
..Default::default()
},
),
(
"enum".into(),
UserHighlightStyle {
color: Some(rgba(0xd36050ff).into()),
..Default::default()
},
),
(
"function".into(),
UserHighlightStyle {
color: Some(rgba(0x5b79e3ff).into()),
..Default::default()
},
),
(
"hint".into(),
UserHighlightStyle {
color: Some(rgba(0x9295beff).into()),
font_weight: Some(UserFontWeight(700.0)),
..Default::default()
},
),
(
"keyword".into(),
UserHighlightStyle {
color: Some(rgba(0xa449abff).into()),
..Default::default()
},
),
(
"label".into(),
UserHighlightStyle {
color: Some(rgba(0x5c79e2ff).into()),
..Default::default()
},
),
(
"link_text".into(),
UserHighlightStyle {
color: Some(rgba(0x5b79e3ff).into()),
font_style: Some(UserFontStyle::Italic),
..Default::default()
},
),
(
"link_uri".into(),
UserHighlightStyle {
color: Some(rgba(0x3982b7ff).into()),
..Default::default()
},
),
(
"number".into(),
UserHighlightStyle {
color: Some(rgba(0xad6f26ff).into()),
..Default::default()
},
),
(
"operator".into(),
UserHighlightStyle {
color: Some(rgba(0x3982b7ff).into()),
..Default::default()
},
),
(
"predictive".into(),
UserHighlightStyle {
color: Some(rgba(0x9c9fc7ff).into()),
font_style: Some(UserFontStyle::Italic),
..Default::default()
},
),
(
"preproc".into(),
UserHighlightStyle {
color: Some(rgba(0x383a41ff).into()),
..Default::default()
},
),
(
"primary".into(),
UserHighlightStyle {
color: Some(rgba(0x383a41ff).into()),
..Default::default()
},
),
(
"property".into(),
UserHighlightStyle {
color: Some(rgba(0xd36050ff).into()),
..Default::default()
},
),
(
"punctuation".into(),
UserHighlightStyle {
color: Some(rgba(0x383a41ff).into()),
..Default::default()
},
),
(
"punctuation.bracket".into(),
UserHighlightStyle {
color: Some(rgba(0x4d4f52ff).into()),
..Default::default()
},
),
(
"punctuation.delimiter".into(),
UserHighlightStyle {
color: Some(rgba(0x4d4f52ff).into()),
..Default::default()
},
),
(
"punctuation.list_marker".into(),
UserHighlightStyle {
color: Some(rgba(0xd36050ff).into()),
..Default::default()
},
),
(
"punctuation.special".into(),
UserHighlightStyle {
color: Some(rgba(0xb92c46ff).into()),
..Default::default()
},
),
(
"string".into(),
UserHighlightStyle {
color: Some(rgba(0x659f58ff).into()),
..Default::default()
},
),
(
"string.escape".into(),
UserHighlightStyle {
color: Some(rgba(0x7c7e86ff).into()),
..Default::default()
},
),
(
"string.regex".into(),
UserHighlightStyle {
color: Some(rgba(0xad6f27ff).into()),
..Default::default()
},
),
(
"string.special".into(),
UserHighlightStyle {
color: Some(rgba(0xad6f27ff).into()),
..Default::default()
},
),
(
"string.special.symbol".into(),
UserHighlightStyle {
color: Some(rgba(0xad6f27ff).into()),
..Default::default()
},
),
(
"tag".into(),
UserHighlightStyle {
color: Some(rgba(0x5c79e2ff).into()),
..Default::default()
},
),
(
"text.literal".into(),
UserHighlightStyle {
color: Some(rgba(0x659f58ff).into()),
..Default::default()
},
),
(
"title".into(),
UserHighlightStyle {
color: Some(rgba(0xd36050ff).into()),
font_weight: Some(UserFontWeight(400.0)),
..Default::default()
},
),
(
"type".into(),
UserHighlightStyle {
color: Some(rgba(0x3982b7ff).into()),
..Default::default()
},
),
(
"variable".into(),
UserHighlightStyle {
color: Some(rgba(0x383a41ff).into()),
..Default::default()
},
),
(
"variable.special".into(),
UserHighlightStyle {
color: Some(rgba(0xad6f26ff).into()),
..Default::default()
},
),
(
"variant".into(),
UserHighlightStyle {
color: Some(rgba(0x5b79e3ff).into()),
..Default::default()
},
),
],
}),
},
},
UserTheme {
name: "One Dark".into(),
appearance: Appearance::Dark,
@@ -917,6 +466,457 @@ pub fn one() -> UserThemeFamily {
}),
},
},
UserTheme {
name: "One Light".into(),
appearance: Appearance::Light,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0xc9c9caff).into()),
border_variant: Some(rgba(0xdfdfe0ff).into()),
border_focused: Some(rgba(0xcbcdf6ff).into()),
border_selected: Some(rgba(0xcbcdf6ff).into()),
border_transparent: Some(rgba(0x00000000).into()),
border_disabled: Some(rgba(0xd3d3d4ff).into()),
elevated_surface_background: Some(rgba(0xebebecff).into()),
surface_background: Some(rgba(0xebebecff).into()),
background: Some(rgba(0xdcdcddff).into()),
panel_background: Some(rgba(0xebebecff).into()),
element_background: Some(rgba(0xebebecff).into()),
element_hover: Some(rgba(0xdfdfe0ff).into()),
element_active: Some(rgba(0xcacacaff).into()),
element_selected: Some(rgba(0xcacacaff).into()),
element_disabled: Some(rgba(0xebebecff).into()),
drop_target_background: Some(rgba(0x7f818880).into()),
ghost_element_background: Some(rgba(0x00000000).into()),
ghost_element_hover: Some(rgba(0xdfdfe0ff).into()),
ghost_element_active: Some(rgba(0xcacacaff).into()),
ghost_element_selected: Some(rgba(0xcacacaff).into()),
ghost_element_disabled: Some(rgba(0xebebecff).into()),
text: Some(rgba(0x383a41ff).into()),
text_muted: Some(rgba(0x7f8188ff).into()),
text_placeholder: Some(rgba(0xa1a1a3ff).into()),
text_disabled: Some(rgba(0xa1a1a3ff).into()),
text_accent: Some(rgba(0x5c79e2ff).into()),
icon: Some(rgba(0x383a41ff).into()),
icon_muted: Some(rgba(0x7f8188ff).into()),
icon_disabled: Some(rgba(0xa1a1a3ff).into()),
icon_placeholder: Some(rgba(0x7f8188ff).into()),
icon_accent: Some(rgba(0x5c79e2ff).into()),
status_bar_background: Some(rgba(0xdcdcddff).into()),
title_bar_background: Some(rgba(0xdcdcddff).into()),
toolbar_background: Some(rgba(0xfafafaff).into()),
tab_bar_background: Some(rgba(0xebebecff).into()),
tab_inactive_background: Some(rgba(0xebebecff).into()),
tab_active_background: Some(rgba(0xfafafaff).into()),
scrollbar_thumb_background: Some(rgba(0x383a414c).into()),
scrollbar_thumb_hover_background: Some(rgba(0xdfdfe0ff).into()),
scrollbar_thumb_border: Some(rgba(0xdfdfe0ff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0xeeeeeeff).into()),
editor_foreground: Some(rgba(0x383a41ff).into()),
editor_background: Some(rgba(0xfafafaff).into()),
editor_gutter_background: Some(rgba(0xfafafaff).into()),
editor_subheader_background: Some(rgba(0xebebecff).into()),
editor_active_line_background: Some(rgba(0xebebecbf).into()),
editor_highlighted_line_background: Some(rgba(0xebebecff).into()),
editor_line_number: Some(rgba(0x383a4159).into()),
editor_active_line_number: Some(rgba(0x383a41ff).into()),
editor_invisible: Some(rgba(0x7f8188ff).into()),
editor_wrap_guide: Some(rgba(0x383a410d).into()),
editor_active_wrap_guide: Some(rgba(0x383a411a).into()),
editor_document_highlight_read_background: Some(rgba(0x5c79e21a).into()),
editor_document_highlight_write_background: Some(rgba(0xa3a3a466).into()),
terminal_background: Some(rgba(0xfafafaff).into()),
terminal_ansi_bright_black: Some(rgba(0xaaaaaaff).into()),
terminal_ansi_bright_red: Some(rgba(0xf0b0a4ff).into()),
terminal_ansi_bright_green: Some(rgba(0xb2cfa9ff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xf1dfc1ff).into()),
terminal_ansi_bright_blue: Some(rgba(0xb5baf2ff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xcea6d3ff).into()),
terminal_ansi_bright_cyan: Some(rgba(0xa4bfdbff).into()),
terminal_ansi_bright_white: Some(rgba(0x383a41ff).into()),
terminal_ansi_black: Some(rgba(0xfafafaff).into()),
terminal_ansi_red: Some(rgba(0xd36151ff).into()),
terminal_ansi_green: Some(rgba(0x669f59ff).into()),
terminal_ansi_yellow: Some(rgba(0xdec184ff).into()),
terminal_ansi_blue: Some(rgba(0x5c79e2ff).into()),
terminal_ansi_magenta: Some(rgba(0x994fa6ff).into()),
terminal_ansi_cyan: Some(rgba(0x3b82b7ff).into()),
terminal_ansi_white: Some(rgba(0x383a41ff).into()),
link_text_hover: Some(rgba(0x5c79e2ff).into()),
..Default::default()
},
status: StatusColorsRefinement {
conflict: Some(rgba(0xdec184ff).into()),
conflict_background: Some(rgba(0xfaf2e6ff).into()),
conflict_border: Some(rgba(0xf5e8d2ff).into()),
created: Some(rgba(0x669f59ff).into()),
created_background: Some(rgba(0xe0ebdcff).into()),
created_border: Some(rgba(0xc8dcc1ff).into()),
deleted: Some(rgba(0xd36151ff).into()),
deleted_background: Some(rgba(0xfbdfd9ff).into()),
deleted_border: Some(rgba(0xf6c6bdff).into()),
error: Some(rgba(0xd36151ff).into()),
error_background: Some(rgba(0xfbdfd9ff).into()),
error_border: Some(rgba(0xf6c6bdff).into()),
hidden: Some(rgba(0xa1a1a3ff).into()),
hidden_background: Some(rgba(0xdcdcddff).into()),
hidden_border: Some(rgba(0xd3d3d4ff).into()),
hint: Some(rgba(0x9295beff).into()),
hint_background: Some(rgba(0xe2e2faff).into()),
hint_border: Some(rgba(0xcbcdf6ff).into()),
ignored: Some(rgba(0x7f8188ff).into()),
ignored_background: Some(rgba(0xdcdcddff).into()),
ignored_border: Some(rgba(0xc9c9caff).into()),
info: Some(rgba(0x5c79e2ff).into()),
info_background: Some(rgba(0xe2e2faff).into()),
info_border: Some(rgba(0xcbcdf6ff).into()),
modified: Some(rgba(0xdec184ff).into()),
modified_background: Some(rgba(0xfaf2e6ff).into()),
modified_border: Some(rgba(0xf5e8d2ff).into()),
predictive: Some(rgba(0x9c9fc7ff).into()),
predictive_background: Some(rgba(0xe0ebdcff).into()),
predictive_border: Some(rgba(0xc8dcc1ff).into()),
renamed: Some(rgba(0x5c79e2ff).into()),
renamed_background: Some(rgba(0xe2e2faff).into()),
renamed_border: Some(rgba(0xcbcdf6ff).into()),
success: Some(rgba(0x669f59ff).into()),
success_background: Some(rgba(0xe0ebdcff).into()),
success_border: Some(rgba(0xc8dcc1ff).into()),
unreachable: Some(rgba(0x7f8188ff).into()),
unreachable_background: Some(rgba(0xdcdcddff).into()),
unreachable_border: Some(rgba(0xc9c9caff).into()),
warning: Some(rgba(0xdec184ff).into()),
warning_background: Some(rgba(0xfaf2e6ff).into()),
warning_border: Some(rgba(0xf5e8d2ff).into()),
..Default::default()
},
player: Some(PlayerColors(vec![
PlayerColor {
cursor: rgba(0x5c79e2ff).into(),
background: rgba(0x5c79e2ff).into(),
selection: rgba(0x5c79e23d).into(),
},
PlayerColor {
cursor: rgba(0x994fa6ff).into(),
background: rgba(0x994fa6ff).into(),
selection: rgba(0x994fa63d).into(),
},
PlayerColor {
cursor: rgba(0xad6f27ff).into(),
background: rgba(0xad6f27ff).into(),
selection: rgba(0xad6f273d).into(),
},
PlayerColor {
cursor: rgba(0xa44aabff).into(),
background: rgba(0xa44aabff).into(),
selection: rgba(0xa44aab3d).into(),
},
PlayerColor {
cursor: rgba(0x3b82b7ff).into(),
background: rgba(0x3b82b7ff).into(),
selection: rgba(0x3b82b73d).into(),
},
PlayerColor {
cursor: rgba(0xd36151ff).into(),
background: rgba(0xd36151ff).into(),
selection: rgba(0xd361513d).into(),
},
PlayerColor {
cursor: rgba(0xdec184ff).into(),
background: rgba(0xdec184ff).into(),
selection: rgba(0xdec1843d).into(),
},
PlayerColor {
cursor: rgba(0x669f59ff).into(),
background: rgba(0x669f59ff).into(),
selection: rgba(0x669f593d).into(),
},
])),
syntax: Some(UserSyntaxTheme {
highlights: vec![
(
"attribute".into(),
UserHighlightStyle {
color: Some(rgba(0x5c79e2ff).into()),
..Default::default()
},
),
(
"boolean".into(),
UserHighlightStyle {
color: Some(rgba(0xad6f26ff).into()),
..Default::default()
},
),
(
"comment".into(),
UserHighlightStyle {
color: Some(rgba(0xa2a3a7ff).into()),
..Default::default()
},
),
(
"comment.doc".into(),
UserHighlightStyle {
color: Some(rgba(0x7c7e86ff).into()),
..Default::default()
},
),
(
"constant".into(),
UserHighlightStyle {
color: Some(rgba(0x669f59ff).into()),
..Default::default()
},
),
(
"constructor".into(),
UserHighlightStyle {
color: Some(rgba(0x5c79e2ff).into()),
..Default::default()
},
),
(
"embedded".into(),
UserHighlightStyle {
color: Some(rgba(0x383a41ff).into()),
..Default::default()
},
),
(
"emphasis".into(),
UserHighlightStyle {
color: Some(rgba(0x5c79e2ff).into()),
..Default::default()
},
),
(
"emphasis.strong".into(),
UserHighlightStyle {
color: Some(rgba(0xad6f26ff).into()),
font_weight: Some(UserFontWeight(700.0)),
..Default::default()
},
),
(
"enum".into(),
UserHighlightStyle {
color: Some(rgba(0xd36050ff).into()),
..Default::default()
},
),
(
"function".into(),
UserHighlightStyle {
color: Some(rgba(0x5b79e3ff).into()),
..Default::default()
},
),
(
"hint".into(),
UserHighlightStyle {
color: Some(rgba(0x9295beff).into()),
font_weight: Some(UserFontWeight(700.0)),
..Default::default()
},
),
(
"keyword".into(),
UserHighlightStyle {
color: Some(rgba(0xa449abff).into()),
..Default::default()
},
),
(
"label".into(),
UserHighlightStyle {
color: Some(rgba(0x5c79e2ff).into()),
..Default::default()
},
),
(
"link_text".into(),
UserHighlightStyle {
color: Some(rgba(0x5b79e3ff).into()),
font_style: Some(UserFontStyle::Italic),
..Default::default()
},
),
(
"link_uri".into(),
UserHighlightStyle {
color: Some(rgba(0x3982b7ff).into()),
..Default::default()
},
),
(
"number".into(),
UserHighlightStyle {
color: Some(rgba(0xad6f26ff).into()),
..Default::default()
},
),
(
"operator".into(),
UserHighlightStyle {
color: Some(rgba(0x3982b7ff).into()),
..Default::default()
},
),
(
"predictive".into(),
UserHighlightStyle {
color: Some(rgba(0x9c9fc7ff).into()),
font_style: Some(UserFontStyle::Italic),
..Default::default()
},
),
(
"preproc".into(),
UserHighlightStyle {
color: Some(rgba(0x383a41ff).into()),
..Default::default()
},
),
(
"primary".into(),
UserHighlightStyle {
color: Some(rgba(0x383a41ff).into()),
..Default::default()
},
),
(
"property".into(),
UserHighlightStyle {
color: Some(rgba(0xd36050ff).into()),
..Default::default()
},
),
(
"punctuation".into(),
UserHighlightStyle {
color: Some(rgba(0x383a41ff).into()),
..Default::default()
},
),
(
"punctuation.bracket".into(),
UserHighlightStyle {
color: Some(rgba(0x4d4f52ff).into()),
..Default::default()
},
),
(
"punctuation.delimiter".into(),
UserHighlightStyle {
color: Some(rgba(0x4d4f52ff).into()),
..Default::default()
},
),
(
"punctuation.list_marker".into(),
UserHighlightStyle {
color: Some(rgba(0xd36050ff).into()),
..Default::default()
},
),
(
"punctuation.special".into(),
UserHighlightStyle {
color: Some(rgba(0xb92c46ff).into()),
..Default::default()
},
),
(
"string".into(),
UserHighlightStyle {
color: Some(rgba(0x659f58ff).into()),
..Default::default()
},
),
(
"string.escape".into(),
UserHighlightStyle {
color: Some(rgba(0x7c7e86ff).into()),
..Default::default()
},
),
(
"string.regex".into(),
UserHighlightStyle {
color: Some(rgba(0xad6f27ff).into()),
..Default::default()
},
),
(
"string.special".into(),
UserHighlightStyle {
color: Some(rgba(0xad6f27ff).into()),
..Default::default()
},
),
(
"string.special.symbol".into(),
UserHighlightStyle {
color: Some(rgba(0xad6f27ff).into()),
..Default::default()
},
),
(
"tag".into(),
UserHighlightStyle {
color: Some(rgba(0x5c79e2ff).into()),
..Default::default()
},
),
(
"text.literal".into(),
UserHighlightStyle {
color: Some(rgba(0x659f58ff).into()),
..Default::default()
},
),
(
"title".into(),
UserHighlightStyle {
color: Some(rgba(0xd36050ff).into()),
font_weight: Some(UserFontWeight(400.0)),
..Default::default()
},
),
(
"type".into(),
UserHighlightStyle {
color: Some(rgba(0x3982b7ff).into()),
..Default::default()
},
),
(
"variable".into(),
UserHighlightStyle {
color: Some(rgba(0x383a41ff).into()),
..Default::default()
},
),
(
"variable.special".into(),
UserHighlightStyle {
color: Some(rgba(0xad6f26ff).into()),
..Default::default()
},
),
(
"variant".into(),
UserHighlightStyle {
color: Some(rgba(0x5b79e3ff).into()),
..Default::default()
},
),
],
}),
},
},
],
}
}
+458 -458
View File
@@ -15,6 +15,464 @@ pub fn rose_pine() -> UserThemeFamily {
name: "Rosé Pine".into(),
author: "Zed Industries".into(),
themes: vec![
UserTheme {
name: "Rosé Pine".into(),
appearance: Appearance::Dark,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x423f55ff).into()),
border_variant: Some(rgba(0x232132ff).into()),
border_focused: Some(rgba(0x435255ff).into()),
border_selected: Some(rgba(0x435255ff).into()),
border_transparent: Some(rgba(0x00000000).into()),
border_disabled: Some(rgba(0x353347ff).into()),
elevated_surface_background: Some(rgba(0x1d1b2aff).into()),
surface_background: Some(rgba(0x1d1b2aff).into()),
background: Some(rgba(0x292739ff).into()),
panel_background: Some(rgba(0x1d1b2aff).into()),
element_background: Some(rgba(0x1d1b2aff).into()),
element_hover: Some(rgba(0x232132ff).into()),
element_active: Some(rgba(0x403e53ff).into()),
element_selected: Some(rgba(0x403e53ff).into()),
element_disabled: Some(rgba(0x1d1b2aff).into()),
drop_target_background: Some(rgba(0x75718e80).into()),
ghost_element_background: Some(rgba(0x00000000).into()),
ghost_element_hover: Some(rgba(0x232132ff).into()),
ghost_element_active: Some(rgba(0x403e53ff).into()),
ghost_element_selected: Some(rgba(0x403e53ff).into()),
ghost_element_disabled: Some(rgba(0x1d1b2aff).into()),
text: Some(rgba(0xe0def4ff).into()),
text_muted: Some(rgba(0x75718eff).into()),
text_placeholder: Some(rgba(0x2f2b43ff).into()),
text_disabled: Some(rgba(0x2f2b43ff).into()),
text_accent: Some(rgba(0x9cced7ff).into()),
icon: Some(rgba(0xe0def4ff).into()),
icon_muted: Some(rgba(0x75718eff).into()),
icon_disabled: Some(rgba(0x2f2b43ff).into()),
icon_placeholder: Some(rgba(0x75718eff).into()),
icon_accent: Some(rgba(0x9cced7ff).into()),
status_bar_background: Some(rgba(0x292739ff).into()),
title_bar_background: Some(rgba(0x292739ff).into()),
toolbar_background: Some(rgba(0x191724ff).into()),
tab_bar_background: Some(rgba(0x1d1b2aff).into()),
tab_inactive_background: Some(rgba(0x1d1b2aff).into()),
tab_active_background: Some(rgba(0x191724ff).into()),
scrollbar_thumb_background: Some(rgba(0xe0def44c).into()),
scrollbar_thumb_hover_background: Some(rgba(0x232132ff).into()),
scrollbar_thumb_border: Some(rgba(0x232132ff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0x1c1a29ff).into()),
editor_foreground: Some(rgba(0xe0def4ff).into()),
editor_background: Some(rgba(0x191724ff).into()),
editor_gutter_background: Some(rgba(0x191724ff).into()),
editor_subheader_background: Some(rgba(0x1d1b2aff).into()),
editor_active_line_background: Some(rgba(0x1d1b2abf).into()),
editor_highlighted_line_background: Some(rgba(0x1d1b2aff).into()),
editor_line_number: Some(rgba(0xe0def459).into()),
editor_active_line_number: Some(rgba(0xe0def4ff).into()),
editor_invisible: Some(rgba(0x75718eff).into()),
editor_wrap_guide: Some(rgba(0xe0def40d).into()),
editor_active_wrap_guide: Some(rgba(0xe0def41a).into()),
editor_document_highlight_read_background: Some(rgba(0x9cced71a).into()),
editor_document_highlight_write_background: Some(rgba(0x28253c66).into()),
terminal_background: Some(rgba(0x191724ff).into()),
terminal_ansi_bright_black: Some(rgba(0x403d55ff).into()),
terminal_ansi_bright_red: Some(rgba(0x7e3647ff).into()),
terminal_ansi_bright_green: Some(rgba(0x31614fff).into()),
terminal_ansi_bright_yellow: Some(rgba(0x8a653bff).into()),
terminal_ansi_bright_blue: Some(rgba(0x566c70ff).into()),
terminal_ansi_bright_magenta: Some(rgba(0x4c3b47ff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x203a46ff).into()),
terminal_ansi_bright_white: Some(rgba(0xe0def4ff).into()),
terminal_ansi_black: Some(rgba(0x191724ff).into()),
terminal_ansi_red: Some(rgba(0xea6f92ff).into()),
terminal_ansi_green: Some(rgba(0x5dc2a3ff).into()),
terminal_ansi_yellow: Some(rgba(0xf5c177ff).into()),
terminal_ansi_blue: Some(rgba(0x9cced7ff).into()),
terminal_ansi_magenta: Some(rgba(0x9d7691ff).into()),
terminal_ansi_cyan: Some(rgba(0x32748fff).into()),
terminal_ansi_white: Some(rgba(0xe0def4ff).into()),
link_text_hover: Some(rgba(0x9cced7ff).into()),
..Default::default()
},
status: StatusColorsRefinement {
conflict: Some(rgba(0xf5c177ff).into()),
conflict_background: Some(rgba(0x50341aff).into()),
conflict_border: Some(rgba(0x6d4d2bff).into()),
created: Some(rgba(0x5dc2a3ff).into()),
created_background: Some(rgba(0x182e23ff).into()),
created_border: Some(rgba(0x254839ff).into()),
deleted: Some(rgba(0xea6f92ff).into()),
deleted_background: Some(rgba(0x431820ff).into()),
deleted_border: Some(rgba(0x612834ff).into()),
error: Some(rgba(0xea6f92ff).into()),
error_background: Some(rgba(0x431820ff).into()),
error_border: Some(rgba(0x612834ff).into()),
hidden: Some(rgba(0x2f2b43ff).into()),
hidden_background: Some(rgba(0x292739ff).into()),
hidden_border: Some(rgba(0x353347ff).into()),
hint: Some(rgba(0x5e768cff).into()),
hint_background: Some(rgba(0x2f3739ff).into()),
hint_border: Some(rgba(0x435255ff).into()),
ignored: Some(rgba(0x75718eff).into()),
ignored_background: Some(rgba(0x292739ff).into()),
ignored_border: Some(rgba(0x423f55ff).into()),
info: Some(rgba(0x9cced7ff).into()),
info_background: Some(rgba(0x2f3739ff).into()),
info_border: Some(rgba(0x435255ff).into()),
modified: Some(rgba(0xf5c177ff).into()),
modified_background: Some(rgba(0x50341aff).into()),
modified_border: Some(rgba(0x6d4d2bff).into()),
predictive: Some(rgba(0x556b81ff).into()),
predictive_background: Some(rgba(0x182e23ff).into()),
predictive_border: Some(rgba(0x254839ff).into()),
renamed: Some(rgba(0x9cced7ff).into()),
renamed_background: Some(rgba(0x2f3739ff).into()),
renamed_border: Some(rgba(0x435255ff).into()),
success: Some(rgba(0x5dc2a3ff).into()),
success_background: Some(rgba(0x182e23ff).into()),
success_border: Some(rgba(0x254839ff).into()),
unreachable: Some(rgba(0x75718eff).into()),
unreachable_background: Some(rgba(0x292739ff).into()),
unreachable_border: Some(rgba(0x423f55ff).into()),
warning: Some(rgba(0xf5c177ff).into()),
warning_background: Some(rgba(0x50341aff).into()),
warning_border: Some(rgba(0x6d4d2bff).into()),
..Default::default()
},
player: Some(PlayerColors(vec![
PlayerColor {
cursor: rgba(0x9cced7ff).into(),
background: rgba(0x9cced7ff).into(),
selection: rgba(0x9cced73d).into(),
},
PlayerColor {
cursor: rgba(0x9d7691ff).into(),
background: rgba(0x9d7691ff).into(),
selection: rgba(0x9d76913d).into(),
},
PlayerColor {
cursor: rgba(0xc4a7e6ff).into(),
background: rgba(0xc4a7e6ff).into(),
selection: rgba(0xc4a7e63d).into(),
},
PlayerColor {
cursor: rgba(0xc4a7e6ff).into(),
background: rgba(0xc4a7e6ff).into(),
selection: rgba(0xc4a7e63d).into(),
},
PlayerColor {
cursor: rgba(0x32748fff).into(),
background: rgba(0x32748fff).into(),
selection: rgba(0x32748f3d).into(),
},
PlayerColor {
cursor: rgba(0xea6f92ff).into(),
background: rgba(0xea6f92ff).into(),
selection: rgba(0xea6f923d).into(),
},
PlayerColor {
cursor: rgba(0xf5c177ff).into(),
background: rgba(0xf5c177ff).into(),
selection: rgba(0xf5c1773d).into(),
},
PlayerColor {
cursor: rgba(0x5dc2a3ff).into(),
background: rgba(0x5dc2a3ff).into(),
selection: rgba(0x5dc2a33d).into(),
},
])),
syntax: Some(UserSyntaxTheme {
highlights: vec![
(
"attribute".into(),
UserHighlightStyle {
color: Some(rgba(0x9cced7ff).into()),
..Default::default()
},
),
(
"boolean".into(),
UserHighlightStyle {
color: Some(rgba(0xebbcbaff).into()),
..Default::default()
},
),
(
"comment".into(),
UserHighlightStyle {
color: Some(rgba(0x6e6a86ff).into()),
..Default::default()
},
),
(
"comment.doc".into(),
UserHighlightStyle {
color: Some(rgba(0x777390ff).into()),
..Default::default()
},
),
(
"constant".into(),
UserHighlightStyle {
color: Some(rgba(0x5dc2a3ff).into()),
..Default::default()
},
),
(
"constructor".into(),
UserHighlightStyle {
color: Some(rgba(0x9cced7ff).into()),
..Default::default()
},
),
(
"embedded".into(),
UserHighlightStyle {
color: Some(rgba(0xe0def4ff).into()),
..Default::default()
},
),
(
"emphasis".into(),
UserHighlightStyle {
color: Some(rgba(0x9cced7ff).into()),
..Default::default()
},
),
(
"emphasis.strong".into(),
UserHighlightStyle {
color: Some(rgba(0x9cced7ff).into()),
font_weight: Some(UserFontWeight(700.0)),
..Default::default()
},
),
(
"enum".into(),
UserHighlightStyle {
color: Some(rgba(0xc4a7e6ff).into()),
..Default::default()
},
),
(
"function".into(),
UserHighlightStyle {
color: Some(rgba(0xebbcbaff).into()),
..Default::default()
},
),
(
"function.method".into(),
UserHighlightStyle {
color: Some(rgba(0xebbcbaff).into()),
..Default::default()
},
),
(
"hint".into(),
UserHighlightStyle {
color: Some(rgba(0x5e768cff).into()),
font_weight: Some(UserFontWeight(700.0)),
..Default::default()
},
),
(
"keyword".into(),
UserHighlightStyle {
color: Some(rgba(0x31748fff).into()),
..Default::default()
},
),
(
"label".into(),
UserHighlightStyle {
color: Some(rgba(0x9cced7ff).into()),
..Default::default()
},
),
(
"link_text".into(),
UserHighlightStyle {
color: Some(rgba(0x9ccfd8ff).into()),
font_style: Some(UserFontStyle::Normal),
..Default::default()
},
),
(
"link_uri".into(),
UserHighlightStyle {
color: Some(rgba(0xebbcbaff).into()),
..Default::default()
},
),
(
"number".into(),
UserHighlightStyle {
color: Some(rgba(0x5dc2a3ff).into()),
..Default::default()
},
),
(
"operator".into(),
UserHighlightStyle {
color: Some(rgba(0x31748fff).into()),
..Default::default()
},
),
(
"predictive".into(),
UserHighlightStyle {
color: Some(rgba(0x556b81ff).into()),
font_style: Some(UserFontStyle::Italic),
..Default::default()
},
),
(
"preproc".into(),
UserHighlightStyle {
color: Some(rgba(0xe0def4ff).into()),
..Default::default()
},
),
(
"primary".into(),
UserHighlightStyle {
color: Some(rgba(0xe0def4ff).into()),
..Default::default()
},
),
(
"property".into(),
UserHighlightStyle {
color: Some(rgba(0x9cced7ff).into()),
..Default::default()
},
),
(
"punctuation".into(),
UserHighlightStyle {
color: Some(rgba(0x908caaff).into()),
..Default::default()
},
),
(
"punctuation.bracket".into(),
UserHighlightStyle {
color: Some(rgba(0x9d99b6ff).into()),
..Default::default()
},
),
(
"punctuation.delimiter".into(),
UserHighlightStyle {
color: Some(rgba(0x9d99b6ff).into()),
..Default::default()
},
),
(
"punctuation.list_marker".into(),
UserHighlightStyle {
color: Some(rgba(0x9d99b6ff).into()),
..Default::default()
},
),
(
"punctuation.special".into(),
UserHighlightStyle {
color: Some(rgba(0x9d99b6ff).into()),
..Default::default()
},
),
(
"string".into(),
UserHighlightStyle {
color: Some(rgba(0xf6c177ff).into()),
..Default::default()
},
),
(
"string.escape".into(),
UserHighlightStyle {
color: Some(rgba(0x777390ff).into()),
..Default::default()
},
),
(
"string.regex".into(),
UserHighlightStyle {
color: Some(rgba(0xc4a7e6ff).into()),
..Default::default()
},
),
(
"string.special".into(),
UserHighlightStyle {
color: Some(rgba(0xc4a7e6ff).into()),
..Default::default()
},
),
(
"string.special.symbol".into(),
UserHighlightStyle {
color: Some(rgba(0xc4a7e6ff).into()),
..Default::default()
},
),
(
"tag".into(),
UserHighlightStyle {
color: Some(rgba(0x9ccfd8ff).into()),
..Default::default()
},
),
(
"text.literal".into(),
UserHighlightStyle {
color: Some(rgba(0xc4a7e6ff).into()),
..Default::default()
},
),
(
"title".into(),
UserHighlightStyle {
color: Some(rgba(0xf6c177ff).into()),
font_weight: Some(UserFontWeight(700.0)),
..Default::default()
},
),
(
"type".into(),
UserHighlightStyle {
color: Some(rgba(0x9ccfd8ff).into()),
..Default::default()
},
),
(
"type.builtin".into(),
UserHighlightStyle {
color: Some(rgba(0x9ccfd8ff).into()),
..Default::default()
},
),
(
"variable".into(),
UserHighlightStyle {
color: Some(rgba(0xe0def4ff).into()),
..Default::default()
},
),
(
"variant".into(),
UserHighlightStyle {
color: Some(rgba(0x9cced7ff).into()),
..Default::default()
},
),
],
}),
},
},
UserTheme {
name: "Rosé Pine Dawn".into(),
appearance: Appearance::Light,
@@ -931,464 +1389,6 @@ pub fn rose_pine() -> UserThemeFamily {
}),
},
},
UserTheme {
name: "Rosé Pine".into(),
appearance: Appearance::Dark,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x423f55ff).into()),
border_variant: Some(rgba(0x232132ff).into()),
border_focused: Some(rgba(0x435255ff).into()),
border_selected: Some(rgba(0x435255ff).into()),
border_transparent: Some(rgba(0x00000000).into()),
border_disabled: Some(rgba(0x353347ff).into()),
elevated_surface_background: Some(rgba(0x1d1b2aff).into()),
surface_background: Some(rgba(0x1d1b2aff).into()),
background: Some(rgba(0x292739ff).into()),
panel_background: Some(rgba(0x1d1b2aff).into()),
element_background: Some(rgba(0x1d1b2aff).into()),
element_hover: Some(rgba(0x232132ff).into()),
element_active: Some(rgba(0x403e53ff).into()),
element_selected: Some(rgba(0x403e53ff).into()),
element_disabled: Some(rgba(0x1d1b2aff).into()),
drop_target_background: Some(rgba(0x75718e80).into()),
ghost_element_background: Some(rgba(0x00000000).into()),
ghost_element_hover: Some(rgba(0x232132ff).into()),
ghost_element_active: Some(rgba(0x403e53ff).into()),
ghost_element_selected: Some(rgba(0x403e53ff).into()),
ghost_element_disabled: Some(rgba(0x1d1b2aff).into()),
text: Some(rgba(0xe0def4ff).into()),
text_muted: Some(rgba(0x75718eff).into()),
text_placeholder: Some(rgba(0x2f2b43ff).into()),
text_disabled: Some(rgba(0x2f2b43ff).into()),
text_accent: Some(rgba(0x9cced7ff).into()),
icon: Some(rgba(0xe0def4ff).into()),
icon_muted: Some(rgba(0x75718eff).into()),
icon_disabled: Some(rgba(0x2f2b43ff).into()),
icon_placeholder: Some(rgba(0x75718eff).into()),
icon_accent: Some(rgba(0x9cced7ff).into()),
status_bar_background: Some(rgba(0x292739ff).into()),
title_bar_background: Some(rgba(0x292739ff).into()),
toolbar_background: Some(rgba(0x191724ff).into()),
tab_bar_background: Some(rgba(0x1d1b2aff).into()),
tab_inactive_background: Some(rgba(0x1d1b2aff).into()),
tab_active_background: Some(rgba(0x191724ff).into()),
scrollbar_thumb_background: Some(rgba(0xe0def44c).into()),
scrollbar_thumb_hover_background: Some(rgba(0x232132ff).into()),
scrollbar_thumb_border: Some(rgba(0x232132ff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0x1c1a29ff).into()),
editor_foreground: Some(rgba(0xe0def4ff).into()),
editor_background: Some(rgba(0x191724ff).into()),
editor_gutter_background: Some(rgba(0x191724ff).into()),
editor_subheader_background: Some(rgba(0x1d1b2aff).into()),
editor_active_line_background: Some(rgba(0x1d1b2abf).into()),
editor_highlighted_line_background: Some(rgba(0x1d1b2aff).into()),
editor_line_number: Some(rgba(0xe0def459).into()),
editor_active_line_number: Some(rgba(0xe0def4ff).into()),
editor_invisible: Some(rgba(0x75718eff).into()),
editor_wrap_guide: Some(rgba(0xe0def40d).into()),
editor_active_wrap_guide: Some(rgba(0xe0def41a).into()),
editor_document_highlight_read_background: Some(rgba(0x9cced71a).into()),
editor_document_highlight_write_background: Some(rgba(0x28253c66).into()),
terminal_background: Some(rgba(0x191724ff).into()),
terminal_ansi_bright_black: Some(rgba(0x403d55ff).into()),
terminal_ansi_bright_red: Some(rgba(0x7e3647ff).into()),
terminal_ansi_bright_green: Some(rgba(0x31614fff).into()),
terminal_ansi_bright_yellow: Some(rgba(0x8a653bff).into()),
terminal_ansi_bright_blue: Some(rgba(0x566c70ff).into()),
terminal_ansi_bright_magenta: Some(rgba(0x4c3b47ff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x203a46ff).into()),
terminal_ansi_bright_white: Some(rgba(0xe0def4ff).into()),
terminal_ansi_black: Some(rgba(0x191724ff).into()),
terminal_ansi_red: Some(rgba(0xea6f92ff).into()),
terminal_ansi_green: Some(rgba(0x5dc2a3ff).into()),
terminal_ansi_yellow: Some(rgba(0xf5c177ff).into()),
terminal_ansi_blue: Some(rgba(0x9cced7ff).into()),
terminal_ansi_magenta: Some(rgba(0x9d7691ff).into()),
terminal_ansi_cyan: Some(rgba(0x32748fff).into()),
terminal_ansi_white: Some(rgba(0xe0def4ff).into()),
link_text_hover: Some(rgba(0x9cced7ff).into()),
..Default::default()
},
status: StatusColorsRefinement {
conflict: Some(rgba(0xf5c177ff).into()),
conflict_background: Some(rgba(0x50341aff).into()),
conflict_border: Some(rgba(0x6d4d2bff).into()),
created: Some(rgba(0x5dc2a3ff).into()),
created_background: Some(rgba(0x182e23ff).into()),
created_border: Some(rgba(0x254839ff).into()),
deleted: Some(rgba(0xea6f92ff).into()),
deleted_background: Some(rgba(0x431820ff).into()),
deleted_border: Some(rgba(0x612834ff).into()),
error: Some(rgba(0xea6f92ff).into()),
error_background: Some(rgba(0x431820ff).into()),
error_border: Some(rgba(0x612834ff).into()),
hidden: Some(rgba(0x2f2b43ff).into()),
hidden_background: Some(rgba(0x292739ff).into()),
hidden_border: Some(rgba(0x353347ff).into()),
hint: Some(rgba(0x5e768cff).into()),
hint_background: Some(rgba(0x2f3739ff).into()),
hint_border: Some(rgba(0x435255ff).into()),
ignored: Some(rgba(0x75718eff).into()),
ignored_background: Some(rgba(0x292739ff).into()),
ignored_border: Some(rgba(0x423f55ff).into()),
info: Some(rgba(0x9cced7ff).into()),
info_background: Some(rgba(0x2f3739ff).into()),
info_border: Some(rgba(0x435255ff).into()),
modified: Some(rgba(0xf5c177ff).into()),
modified_background: Some(rgba(0x50341aff).into()),
modified_border: Some(rgba(0x6d4d2bff).into()),
predictive: Some(rgba(0x556b81ff).into()),
predictive_background: Some(rgba(0x182e23ff).into()),
predictive_border: Some(rgba(0x254839ff).into()),
renamed: Some(rgba(0x9cced7ff).into()),
renamed_background: Some(rgba(0x2f3739ff).into()),
renamed_border: Some(rgba(0x435255ff).into()),
success: Some(rgba(0x5dc2a3ff).into()),
success_background: Some(rgba(0x182e23ff).into()),
success_border: Some(rgba(0x254839ff).into()),
unreachable: Some(rgba(0x75718eff).into()),
unreachable_background: Some(rgba(0x292739ff).into()),
unreachable_border: Some(rgba(0x423f55ff).into()),
warning: Some(rgba(0xf5c177ff).into()),
warning_background: Some(rgba(0x50341aff).into()),
warning_border: Some(rgba(0x6d4d2bff).into()),
..Default::default()
},
player: Some(PlayerColors(vec![
PlayerColor {
cursor: rgba(0x9cced7ff).into(),
background: rgba(0x9cced7ff).into(),
selection: rgba(0x9cced73d).into(),
},
PlayerColor {
cursor: rgba(0x9d7691ff).into(),
background: rgba(0x9d7691ff).into(),
selection: rgba(0x9d76913d).into(),
},
PlayerColor {
cursor: rgba(0xc4a7e6ff).into(),
background: rgba(0xc4a7e6ff).into(),
selection: rgba(0xc4a7e63d).into(),
},
PlayerColor {
cursor: rgba(0xc4a7e6ff).into(),
background: rgba(0xc4a7e6ff).into(),
selection: rgba(0xc4a7e63d).into(),
},
PlayerColor {
cursor: rgba(0x32748fff).into(),
background: rgba(0x32748fff).into(),
selection: rgba(0x32748f3d).into(),
},
PlayerColor {
cursor: rgba(0xea6f92ff).into(),
background: rgba(0xea6f92ff).into(),
selection: rgba(0xea6f923d).into(),
},
PlayerColor {
cursor: rgba(0xf5c177ff).into(),
background: rgba(0xf5c177ff).into(),
selection: rgba(0xf5c1773d).into(),
},
PlayerColor {
cursor: rgba(0x5dc2a3ff).into(),
background: rgba(0x5dc2a3ff).into(),
selection: rgba(0x5dc2a33d).into(),
},
])),
syntax: Some(UserSyntaxTheme {
highlights: vec![
(
"attribute".into(),
UserHighlightStyle {
color: Some(rgba(0x9cced7ff).into()),
..Default::default()
},
),
(
"boolean".into(),
UserHighlightStyle {
color: Some(rgba(0xebbcbaff).into()),
..Default::default()
},
),
(
"comment".into(),
UserHighlightStyle {
color: Some(rgba(0x6e6a86ff).into()),
..Default::default()
},
),
(
"comment.doc".into(),
UserHighlightStyle {
color: Some(rgba(0x777390ff).into()),
..Default::default()
},
),
(
"constant".into(),
UserHighlightStyle {
color: Some(rgba(0x5dc2a3ff).into()),
..Default::default()
},
),
(
"constructor".into(),
UserHighlightStyle {
color: Some(rgba(0x9cced7ff).into()),
..Default::default()
},
),
(
"embedded".into(),
UserHighlightStyle {
color: Some(rgba(0xe0def4ff).into()),
..Default::default()
},
),
(
"emphasis".into(),
UserHighlightStyle {
color: Some(rgba(0x9cced7ff).into()),
..Default::default()
},
),
(
"emphasis.strong".into(),
UserHighlightStyle {
color: Some(rgba(0x9cced7ff).into()),
font_weight: Some(UserFontWeight(700.0)),
..Default::default()
},
),
(
"enum".into(),
UserHighlightStyle {
color: Some(rgba(0xc4a7e6ff).into()),
..Default::default()
},
),
(
"function".into(),
UserHighlightStyle {
color: Some(rgba(0xebbcbaff).into()),
..Default::default()
},
),
(
"function.method".into(),
UserHighlightStyle {
color: Some(rgba(0xebbcbaff).into()),
..Default::default()
},
),
(
"hint".into(),
UserHighlightStyle {
color: Some(rgba(0x5e768cff).into()),
font_weight: Some(UserFontWeight(700.0)),
..Default::default()
},
),
(
"keyword".into(),
UserHighlightStyle {
color: Some(rgba(0x31748fff).into()),
..Default::default()
},
),
(
"label".into(),
UserHighlightStyle {
color: Some(rgba(0x9cced7ff).into()),
..Default::default()
},
),
(
"link_text".into(),
UserHighlightStyle {
color: Some(rgba(0x9ccfd8ff).into()),
font_style: Some(UserFontStyle::Normal),
..Default::default()
},
),
(
"link_uri".into(),
UserHighlightStyle {
color: Some(rgba(0xebbcbaff).into()),
..Default::default()
},
),
(
"number".into(),
UserHighlightStyle {
color: Some(rgba(0x5dc2a3ff).into()),
..Default::default()
},
),
(
"operator".into(),
UserHighlightStyle {
color: Some(rgba(0x31748fff).into()),
..Default::default()
},
),
(
"predictive".into(),
UserHighlightStyle {
color: Some(rgba(0x556b81ff).into()),
font_style: Some(UserFontStyle::Italic),
..Default::default()
},
),
(
"preproc".into(),
UserHighlightStyle {
color: Some(rgba(0xe0def4ff).into()),
..Default::default()
},
),
(
"primary".into(),
UserHighlightStyle {
color: Some(rgba(0xe0def4ff).into()),
..Default::default()
},
),
(
"property".into(),
UserHighlightStyle {
color: Some(rgba(0x9cced7ff).into()),
..Default::default()
},
),
(
"punctuation".into(),
UserHighlightStyle {
color: Some(rgba(0x908caaff).into()),
..Default::default()
},
),
(
"punctuation.bracket".into(),
UserHighlightStyle {
color: Some(rgba(0x9d99b6ff).into()),
..Default::default()
},
),
(
"punctuation.delimiter".into(),
UserHighlightStyle {
color: Some(rgba(0x9d99b6ff).into()),
..Default::default()
},
),
(
"punctuation.list_marker".into(),
UserHighlightStyle {
color: Some(rgba(0x9d99b6ff).into()),
..Default::default()
},
),
(
"punctuation.special".into(),
UserHighlightStyle {
color: Some(rgba(0x9d99b6ff).into()),
..Default::default()
},
),
(
"string".into(),
UserHighlightStyle {
color: Some(rgba(0xf6c177ff).into()),
..Default::default()
},
),
(
"string.escape".into(),
UserHighlightStyle {
color: Some(rgba(0x777390ff).into()),
..Default::default()
},
),
(
"string.regex".into(),
UserHighlightStyle {
color: Some(rgba(0xc4a7e6ff).into()),
..Default::default()
},
),
(
"string.special".into(),
UserHighlightStyle {
color: Some(rgba(0xc4a7e6ff).into()),
..Default::default()
},
),
(
"string.special.symbol".into(),
UserHighlightStyle {
color: Some(rgba(0xc4a7e6ff).into()),
..Default::default()
},
),
(
"tag".into(),
UserHighlightStyle {
color: Some(rgba(0x9ccfd8ff).into()),
..Default::default()
},
),
(
"text.literal".into(),
UserHighlightStyle {
color: Some(rgba(0xc4a7e6ff).into()),
..Default::default()
},
),
(
"title".into(),
UserHighlightStyle {
color: Some(rgba(0xf6c177ff).into()),
font_weight: Some(UserFontWeight(700.0)),
..Default::default()
},
),
(
"type".into(),
UserHighlightStyle {
color: Some(rgba(0x9ccfd8ff).into()),
..Default::default()
},
),
(
"type.builtin".into(),
UserHighlightStyle {
color: Some(rgba(0x9ccfd8ff).into()),
..Default::default()
},
),
(
"variable".into(),
UserHighlightStyle {
color: Some(rgba(0xe0def4ff).into()),
..Default::default()
},
),
(
"variant".into(),
UserHighlightStyle {
color: Some(rgba(0x9cced7ff).into()),
..Default::default()
},
),
],
}),
},
},
],
}
}
+444 -444
View File
@@ -15,450 +15,6 @@ pub fn solarized() -> UserThemeFamily {
name: "Solarized".into(),
author: "Zed Industries".into(),
themes: vec![
UserTheme {
name: "Solarized Light".into(),
appearance: Appearance::Light,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x9faaa8ff).into()),
border_variant: Some(rgba(0xdcdacbff).into()),
border_focused: Some(rgba(0xbfd3efff).into()),
border_selected: Some(rgba(0xbfd3efff).into()),
border_transparent: Some(rgba(0x00000000).into()),
border_disabled: Some(rgba(0xb7bdb6ff).into()),
elevated_surface_background: Some(rgba(0xf3eddaff).into()),
surface_background: Some(rgba(0xf3eddaff).into()),
background: Some(rgba(0xcfd0c4ff).into()),
panel_background: Some(rgba(0xf3eddaff).into()),
element_background: Some(rgba(0xf3eddaff).into()),
element_hover: Some(rgba(0xdcdacbff).into()),
element_active: Some(rgba(0xa2aca9ff).into()),
element_selected: Some(rgba(0xa2aca9ff).into()),
element_disabled: Some(rgba(0xf3eddaff).into()),
drop_target_background: Some(rgba(0x34555e80).into()),
ghost_element_background: Some(rgba(0x00000000).into()),
ghost_element_hover: Some(rgba(0xdcdacbff).into()),
ghost_element_active: Some(rgba(0xa2aca9ff).into()),
ghost_element_selected: Some(rgba(0xa2aca9ff).into()),
ghost_element_disabled: Some(rgba(0xf3eddaff).into()),
text: Some(rgba(0x002b36ff).into()),
text_muted: Some(rgba(0x34555eff).into()),
text_placeholder: Some(rgba(0x6a7f86ff).into()),
text_disabled: Some(rgba(0x6a7f86ff).into()),
text_accent: Some(rgba(0x298bd1ff).into()),
icon: Some(rgba(0x002b36ff).into()),
icon_muted: Some(rgba(0x34555eff).into()),
icon_disabled: Some(rgba(0x6a7f86ff).into()),
icon_placeholder: Some(rgba(0x34555eff).into()),
icon_accent: Some(rgba(0x298bd1ff).into()),
status_bar_background: Some(rgba(0xcfd0c4ff).into()),
title_bar_background: Some(rgba(0xcfd0c4ff).into()),
toolbar_background: Some(rgba(0xfdf6e3ff).into()),
tab_bar_background: Some(rgba(0xf3eddaff).into()),
tab_inactive_background: Some(rgba(0xf3eddaff).into()),
tab_active_background: Some(rgba(0xfdf6e3ff).into()),
scrollbar_thumb_background: Some(rgba(0x002b364c).into()),
scrollbar_thumb_hover_background: Some(rgba(0xdcdacbff).into()),
scrollbar_thumb_border: Some(rgba(0xdcdacbff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0xf5eedbff).into()),
editor_foreground: Some(rgba(0x002b36ff).into()),
editor_background: Some(rgba(0xfdf6e3ff).into()),
editor_gutter_background: Some(rgba(0xfdf6e3ff).into()),
editor_subheader_background: Some(rgba(0xf3eddaff).into()),
editor_active_line_background: Some(rgba(0xf3eddabf).into()),
editor_highlighted_line_background: Some(rgba(0xf3eddaff).into()),
editor_line_number: Some(rgba(0x002b3659).into()),
editor_active_line_number: Some(rgba(0x002b36ff).into()),
editor_invisible: Some(rgba(0x34555eff).into()),
editor_wrap_guide: Some(rgba(0x002b360d).into()),
editor_active_wrap_guide: Some(rgba(0x002b361a).into()),
editor_document_highlight_read_background: Some(rgba(0x298bd11a).into()),
editor_document_highlight_write_background: Some(rgba(0x6d828866).into()),
terminal_background: Some(rgba(0xfdf6e3ff).into()),
terminal_ansi_bright_black: Some(rgba(0x7b8e91ff).into()),
terminal_ansi_bright_red: Some(rgba(0xfaa091ff).into()),
terminal_ansi_bright_green: Some(rgba(0xc6cb8bff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xe1c28aff).into()),
terminal_ansi_bright_blue: Some(rgba(0xa5c3e9ff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xf0a2bfff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x9fd0cbff).into()),
terminal_ansi_bright_white: Some(rgba(0x002b36ff).into()),
terminal_ansi_black: Some(rgba(0xfdf6e3ff).into()),
terminal_ansi_red: Some(rgba(0xdc3330ff).into()),
terminal_ansi_green: Some(rgba(0x859904ff).into()),
terminal_ansi_yellow: Some(rgba(0xb58904ff).into()),
terminal_ansi_blue: Some(rgba(0x298bd1ff).into()),
terminal_ansi_magenta: Some(rgba(0xd33882ff).into()),
terminal_ansi_cyan: Some(rgba(0x2ca198ff).into()),
terminal_ansi_white: Some(rgba(0x002b36ff).into()),
link_text_hover: Some(rgba(0x298bd1ff).into()),
..Default::default()
},
status: StatusColorsRefinement {
conflict: Some(rgba(0xb58904ff).into()),
conflict_background: Some(rgba(0xf5e6d0ff).into()),
conflict_border: Some(rgba(0xebd3aaff).into()),
created: Some(rgba(0x859904ff).into()),
created_background: Some(rgba(0xe9ead0ff).into()),
created_border: Some(rgba(0xd6d9abff).into()),
deleted: Some(rgba(0xdc3330ff).into()),
deleted_background: Some(rgba(0xffd9d2ff).into()),
deleted_border: Some(rgba(0xffbbafff).into()),
error: Some(rgba(0xdc3330ff).into()),
error_background: Some(rgba(0xffd9d2ff).into()),
error_border: Some(rgba(0xffbbafff).into()),
hidden: Some(rgba(0x6a7f86ff).into()),
hidden_background: Some(rgba(0xcfd0c4ff).into()),
hidden_border: Some(rgba(0xb7bdb6ff).into()),
hint: Some(rgba(0x5889a3ff).into()),
hint_background: Some(rgba(0xdbe6f6ff).into()),
hint_border: Some(rgba(0xbfd3efff).into()),
ignored: Some(rgba(0x34555eff).into()),
ignored_background: Some(rgba(0xcfd0c4ff).into()),
ignored_border: Some(rgba(0x9faaa8ff).into()),
info: Some(rgba(0x298bd1ff).into()),
info_background: Some(rgba(0xdbe6f6ff).into()),
info_border: Some(rgba(0xbfd3efff).into()),
modified: Some(rgba(0xb58904ff).into()),
modified_background: Some(rgba(0xf5e6d0ff).into()),
modified_border: Some(rgba(0xebd3aaff).into()),
predictive: Some(rgba(0x679aafff).into()),
predictive_background: Some(rgba(0xe9ead0ff).into()),
predictive_border: Some(rgba(0xd6d9abff).into()),
renamed: Some(rgba(0x298bd1ff).into()),
renamed_background: Some(rgba(0xdbe6f6ff).into()),
renamed_border: Some(rgba(0xbfd3efff).into()),
success: Some(rgba(0x859904ff).into()),
success_background: Some(rgba(0xe9ead0ff).into()),
success_border: Some(rgba(0xd6d9abff).into()),
unreachable: Some(rgba(0x34555eff).into()),
unreachable_background: Some(rgba(0xcfd0c4ff).into()),
unreachable_border: Some(rgba(0x9faaa8ff).into()),
warning: Some(rgba(0xb58904ff).into()),
warning_background: Some(rgba(0xf5e6d0ff).into()),
warning_border: Some(rgba(0xebd3aaff).into()),
..Default::default()
},
player: Some(PlayerColors(vec![
PlayerColor {
cursor: rgba(0x298bd1ff).into(),
background: rgba(0x298bd1ff).into(),
selection: rgba(0x298bd13d).into(),
},
PlayerColor {
cursor: rgba(0xd33882ff).into(),
background: rgba(0xd33882ff).into(),
selection: rgba(0xd338823d).into(),
},
PlayerColor {
cursor: rgba(0xcb4c18ff).into(),
background: rgba(0xcb4c18ff).into(),
selection: rgba(0xcb4c183d).into(),
},
PlayerColor {
cursor: rgba(0x6d71c4ff).into(),
background: rgba(0x6d71c4ff).into(),
selection: rgba(0x6d71c43d).into(),
},
PlayerColor {
cursor: rgba(0x2ca198ff).into(),
background: rgba(0x2ca198ff).into(),
selection: rgba(0x2ca1983d).into(),
},
PlayerColor {
cursor: rgba(0xdc3330ff).into(),
background: rgba(0xdc3330ff).into(),
selection: rgba(0xdc33303d).into(),
},
PlayerColor {
cursor: rgba(0xb58904ff).into(),
background: rgba(0xb58904ff).into(),
selection: rgba(0xb589043d).into(),
},
PlayerColor {
cursor: rgba(0x859904ff).into(),
background: rgba(0x859904ff).into(),
selection: rgba(0x8599043d).into(),
},
])),
syntax: Some(UserSyntaxTheme {
highlights: vec![
(
"attribute".into(),
UserHighlightStyle {
color: Some(rgba(0x298bd1ff).into()),
..Default::default()
},
),
(
"boolean".into(),
UserHighlightStyle {
color: Some(rgba(0x859904ff).into()),
..Default::default()
},
),
(
"comment".into(),
UserHighlightStyle {
color: Some(rgba(0x30525bff).into()),
..Default::default()
},
),
(
"comment.doc".into(),
UserHighlightStyle {
color: Some(rgba(0x30525bff).into()),
..Default::default()
},
),
(
"constant".into(),
UserHighlightStyle {
color: Some(rgba(0x859904ff).into()),
..Default::default()
},
),
(
"constructor".into(),
UserHighlightStyle {
color: Some(rgba(0x298bd1ff).into()),
..Default::default()
},
),
(
"embedded".into(),
UserHighlightStyle {
color: Some(rgba(0x002b36ff).into()),
..Default::default()
},
),
(
"emphasis".into(),
UserHighlightStyle {
color: Some(rgba(0x298bd1ff).into()),
..Default::default()
},
),
(
"emphasis.strong".into(),
UserHighlightStyle {
color: Some(rgba(0x298bd1ff).into()),
font_weight: Some(UserFontWeight(700.0)),
..Default::default()
},
),
(
"enum".into(),
UserHighlightStyle {
color: Some(rgba(0xcb4c18ff).into()),
..Default::default()
},
),
(
"function".into(),
UserHighlightStyle {
color: Some(rgba(0xb58904ff).into()),
..Default::default()
},
),
(
"hint".into(),
UserHighlightStyle {
color: Some(rgba(0x5889a3ff).into()),
font_weight: Some(UserFontWeight(700.0)),
..Default::default()
},
),
(
"keyword".into(),
UserHighlightStyle {
color: Some(rgba(0x298bd1ff).into()),
..Default::default()
},
),
(
"label".into(),
UserHighlightStyle {
color: Some(rgba(0x298bd1ff).into()),
..Default::default()
},
),
(
"link_text".into(),
UserHighlightStyle {
color: Some(rgba(0xcb4c18ff).into()),
font_style: Some(UserFontStyle::Italic),
..Default::default()
},
),
(
"link_uri".into(),
UserHighlightStyle {
color: Some(rgba(0x859904ff).into()),
..Default::default()
},
),
(
"number".into(),
UserHighlightStyle {
color: Some(rgba(0x859904ff).into()),
..Default::default()
},
),
(
"operator".into(),
UserHighlightStyle {
color: Some(rgba(0xcb4c18ff).into()),
..Default::default()
},
),
(
"predictive".into(),
UserHighlightStyle {
color: Some(rgba(0x679aafff).into()),
font_style: Some(UserFontStyle::Italic),
..Default::default()
},
),
(
"preproc".into(),
UserHighlightStyle {
color: Some(rgba(0x002b36ff).into()),
..Default::default()
},
),
(
"primary".into(),
UserHighlightStyle {
color: Some(rgba(0x002b36ff).into()),
..Default::default()
},
),
(
"property".into(),
UserHighlightStyle {
color: Some(rgba(0x298bd1ff).into()),
..Default::default()
},
),
(
"punctuation".into(),
UserHighlightStyle {
color: Some(rgba(0x05333eff).into()),
..Default::default()
},
),
(
"punctuation.bracket".into(),
UserHighlightStyle {
color: Some(rgba(0x05333eff).into()),
..Default::default()
},
),
(
"punctuation.delimiter".into(),
UserHighlightStyle {
color: Some(rgba(0x05333eff).into()),
..Default::default()
},
),
(
"punctuation.list_marker".into(),
UserHighlightStyle {
color: Some(rgba(0x05333eff).into()),
..Default::default()
},
),
(
"punctuation.special".into(),
UserHighlightStyle {
color: Some(rgba(0x05333eff).into()),
..Default::default()
},
),
(
"string".into(),
UserHighlightStyle {
color: Some(rgba(0xcb4c18ff).into()),
..Default::default()
},
),
(
"string.escape".into(),
UserHighlightStyle {
color: Some(rgba(0x30525bff).into()),
..Default::default()
},
),
(
"string.regex".into(),
UserHighlightStyle {
color: Some(rgba(0xcb4c18ff).into()),
..Default::default()
},
),
(
"string.special".into(),
UserHighlightStyle {
color: Some(rgba(0xcb4c18ff).into()),
..Default::default()
},
),
(
"string.special.symbol".into(),
UserHighlightStyle {
color: Some(rgba(0xcb4c18ff).into()),
..Default::default()
},
),
(
"tag".into(),
UserHighlightStyle {
color: Some(rgba(0x298bd1ff).into()),
..Default::default()
},
),
(
"text.literal".into(),
UserHighlightStyle {
color: Some(rgba(0xcb4c18ff).into()),
..Default::default()
},
),
(
"title".into(),
UserHighlightStyle {
color: Some(rgba(0x002b36ff).into()),
font_weight: Some(UserFontWeight(700.0)),
..Default::default()
},
),
(
"type".into(),
UserHighlightStyle {
color: Some(rgba(0x2ca198ff).into()),
..Default::default()
},
),
(
"variable".into(),
UserHighlightStyle {
color: Some(rgba(0x002b36ff).into()),
..Default::default()
},
),
(
"variant".into(),
UserHighlightStyle {
color: Some(rgba(0x298bd1ff).into()),
..Default::default()
},
),
],
}),
},
},
UserTheme {
name: "Solarized Dark".into(),
appearance: Appearance::Dark,
@@ -903,6 +459,450 @@ pub fn solarized() -> UserThemeFamily {
}),
},
},
UserTheme {
name: "Solarized Light".into(),
appearance: Appearance::Light,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x9faaa8ff).into()),
border_variant: Some(rgba(0xdcdacbff).into()),
border_focused: Some(rgba(0xbfd3efff).into()),
border_selected: Some(rgba(0xbfd3efff).into()),
border_transparent: Some(rgba(0x00000000).into()),
border_disabled: Some(rgba(0xb7bdb6ff).into()),
elevated_surface_background: Some(rgba(0xf3eddaff).into()),
surface_background: Some(rgba(0xf3eddaff).into()),
background: Some(rgba(0xcfd0c4ff).into()),
panel_background: Some(rgba(0xf3eddaff).into()),
element_background: Some(rgba(0xf3eddaff).into()),
element_hover: Some(rgba(0xdcdacbff).into()),
element_active: Some(rgba(0xa2aca9ff).into()),
element_selected: Some(rgba(0xa2aca9ff).into()),
element_disabled: Some(rgba(0xf3eddaff).into()),
drop_target_background: Some(rgba(0x34555e80).into()),
ghost_element_background: Some(rgba(0x00000000).into()),
ghost_element_hover: Some(rgba(0xdcdacbff).into()),
ghost_element_active: Some(rgba(0xa2aca9ff).into()),
ghost_element_selected: Some(rgba(0xa2aca9ff).into()),
ghost_element_disabled: Some(rgba(0xf3eddaff).into()),
text: Some(rgba(0x002b36ff).into()),
text_muted: Some(rgba(0x34555eff).into()),
text_placeholder: Some(rgba(0x6a7f86ff).into()),
text_disabled: Some(rgba(0x6a7f86ff).into()),
text_accent: Some(rgba(0x298bd1ff).into()),
icon: Some(rgba(0x002b36ff).into()),
icon_muted: Some(rgba(0x34555eff).into()),
icon_disabled: Some(rgba(0x6a7f86ff).into()),
icon_placeholder: Some(rgba(0x34555eff).into()),
icon_accent: Some(rgba(0x298bd1ff).into()),
status_bar_background: Some(rgba(0xcfd0c4ff).into()),
title_bar_background: Some(rgba(0xcfd0c4ff).into()),
toolbar_background: Some(rgba(0xfdf6e3ff).into()),
tab_bar_background: Some(rgba(0xf3eddaff).into()),
tab_inactive_background: Some(rgba(0xf3eddaff).into()),
tab_active_background: Some(rgba(0xfdf6e3ff).into()),
scrollbar_thumb_background: Some(rgba(0x002b364c).into()),
scrollbar_thumb_hover_background: Some(rgba(0xdcdacbff).into()),
scrollbar_thumb_border: Some(rgba(0xdcdacbff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0xf5eedbff).into()),
editor_foreground: Some(rgba(0x002b36ff).into()),
editor_background: Some(rgba(0xfdf6e3ff).into()),
editor_gutter_background: Some(rgba(0xfdf6e3ff).into()),
editor_subheader_background: Some(rgba(0xf3eddaff).into()),
editor_active_line_background: Some(rgba(0xf3eddabf).into()),
editor_highlighted_line_background: Some(rgba(0xf3eddaff).into()),
editor_line_number: Some(rgba(0x002b3659).into()),
editor_active_line_number: Some(rgba(0x002b36ff).into()),
editor_invisible: Some(rgba(0x34555eff).into()),
editor_wrap_guide: Some(rgba(0x002b360d).into()),
editor_active_wrap_guide: Some(rgba(0x002b361a).into()),
editor_document_highlight_read_background: Some(rgba(0x298bd11a).into()),
editor_document_highlight_write_background: Some(rgba(0x6d828866).into()),
terminal_background: Some(rgba(0xfdf6e3ff).into()),
terminal_ansi_bright_black: Some(rgba(0x7b8e91ff).into()),
terminal_ansi_bright_red: Some(rgba(0xfaa091ff).into()),
terminal_ansi_bright_green: Some(rgba(0xc6cb8bff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xe1c28aff).into()),
terminal_ansi_bright_blue: Some(rgba(0xa5c3e9ff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xf0a2bfff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x9fd0cbff).into()),
terminal_ansi_bright_white: Some(rgba(0x002b36ff).into()),
terminal_ansi_black: Some(rgba(0xfdf6e3ff).into()),
terminal_ansi_red: Some(rgba(0xdc3330ff).into()),
terminal_ansi_green: Some(rgba(0x859904ff).into()),
terminal_ansi_yellow: Some(rgba(0xb58904ff).into()),
terminal_ansi_blue: Some(rgba(0x298bd1ff).into()),
terminal_ansi_magenta: Some(rgba(0xd33882ff).into()),
terminal_ansi_cyan: Some(rgba(0x2ca198ff).into()),
terminal_ansi_white: Some(rgba(0x002b36ff).into()),
link_text_hover: Some(rgba(0x298bd1ff).into()),
..Default::default()
},
status: StatusColorsRefinement {
conflict: Some(rgba(0xb58904ff).into()),
conflict_background: Some(rgba(0xf5e6d0ff).into()),
conflict_border: Some(rgba(0xebd3aaff).into()),
created: Some(rgba(0x859904ff).into()),
created_background: Some(rgba(0xe9ead0ff).into()),
created_border: Some(rgba(0xd6d9abff).into()),
deleted: Some(rgba(0xdc3330ff).into()),
deleted_background: Some(rgba(0xffd9d2ff).into()),
deleted_border: Some(rgba(0xffbbafff).into()),
error: Some(rgba(0xdc3330ff).into()),
error_background: Some(rgba(0xffd9d2ff).into()),
error_border: Some(rgba(0xffbbafff).into()),
hidden: Some(rgba(0x6a7f86ff).into()),
hidden_background: Some(rgba(0xcfd0c4ff).into()),
hidden_border: Some(rgba(0xb7bdb6ff).into()),
hint: Some(rgba(0x5889a3ff).into()),
hint_background: Some(rgba(0xdbe6f6ff).into()),
hint_border: Some(rgba(0xbfd3efff).into()),
ignored: Some(rgba(0x34555eff).into()),
ignored_background: Some(rgba(0xcfd0c4ff).into()),
ignored_border: Some(rgba(0x9faaa8ff).into()),
info: Some(rgba(0x298bd1ff).into()),
info_background: Some(rgba(0xdbe6f6ff).into()),
info_border: Some(rgba(0xbfd3efff).into()),
modified: Some(rgba(0xb58904ff).into()),
modified_background: Some(rgba(0xf5e6d0ff).into()),
modified_border: Some(rgba(0xebd3aaff).into()),
predictive: Some(rgba(0x679aafff).into()),
predictive_background: Some(rgba(0xe9ead0ff).into()),
predictive_border: Some(rgba(0xd6d9abff).into()),
renamed: Some(rgba(0x298bd1ff).into()),
renamed_background: Some(rgba(0xdbe6f6ff).into()),
renamed_border: Some(rgba(0xbfd3efff).into()),
success: Some(rgba(0x859904ff).into()),
success_background: Some(rgba(0xe9ead0ff).into()),
success_border: Some(rgba(0xd6d9abff).into()),
unreachable: Some(rgba(0x34555eff).into()),
unreachable_background: Some(rgba(0xcfd0c4ff).into()),
unreachable_border: Some(rgba(0x9faaa8ff).into()),
warning: Some(rgba(0xb58904ff).into()),
warning_background: Some(rgba(0xf5e6d0ff).into()),
warning_border: Some(rgba(0xebd3aaff).into()),
..Default::default()
},
player: Some(PlayerColors(vec![
PlayerColor {
cursor: rgba(0x298bd1ff).into(),
background: rgba(0x298bd1ff).into(),
selection: rgba(0x298bd13d).into(),
},
PlayerColor {
cursor: rgba(0xd33882ff).into(),
background: rgba(0xd33882ff).into(),
selection: rgba(0xd338823d).into(),
},
PlayerColor {
cursor: rgba(0xcb4c18ff).into(),
background: rgba(0xcb4c18ff).into(),
selection: rgba(0xcb4c183d).into(),
},
PlayerColor {
cursor: rgba(0x6d71c4ff).into(),
background: rgba(0x6d71c4ff).into(),
selection: rgba(0x6d71c43d).into(),
},
PlayerColor {
cursor: rgba(0x2ca198ff).into(),
background: rgba(0x2ca198ff).into(),
selection: rgba(0x2ca1983d).into(),
},
PlayerColor {
cursor: rgba(0xdc3330ff).into(),
background: rgba(0xdc3330ff).into(),
selection: rgba(0xdc33303d).into(),
},
PlayerColor {
cursor: rgba(0xb58904ff).into(),
background: rgba(0xb58904ff).into(),
selection: rgba(0xb589043d).into(),
},
PlayerColor {
cursor: rgba(0x859904ff).into(),
background: rgba(0x859904ff).into(),
selection: rgba(0x8599043d).into(),
},
])),
syntax: Some(UserSyntaxTheme {
highlights: vec![
(
"attribute".into(),
UserHighlightStyle {
color: Some(rgba(0x298bd1ff).into()),
..Default::default()
},
),
(
"boolean".into(),
UserHighlightStyle {
color: Some(rgba(0x859904ff).into()),
..Default::default()
},
),
(
"comment".into(),
UserHighlightStyle {
color: Some(rgba(0x30525bff).into()),
..Default::default()
},
),
(
"comment.doc".into(),
UserHighlightStyle {
color: Some(rgba(0x30525bff).into()),
..Default::default()
},
),
(
"constant".into(),
UserHighlightStyle {
color: Some(rgba(0x859904ff).into()),
..Default::default()
},
),
(
"constructor".into(),
UserHighlightStyle {
color: Some(rgba(0x298bd1ff).into()),
..Default::default()
},
),
(
"embedded".into(),
UserHighlightStyle {
color: Some(rgba(0x002b36ff).into()),
..Default::default()
},
),
(
"emphasis".into(),
UserHighlightStyle {
color: Some(rgba(0x298bd1ff).into()),
..Default::default()
},
),
(
"emphasis.strong".into(),
UserHighlightStyle {
color: Some(rgba(0x298bd1ff).into()),
font_weight: Some(UserFontWeight(700.0)),
..Default::default()
},
),
(
"enum".into(),
UserHighlightStyle {
color: Some(rgba(0xcb4c18ff).into()),
..Default::default()
},
),
(
"function".into(),
UserHighlightStyle {
color: Some(rgba(0xb58904ff).into()),
..Default::default()
},
),
(
"hint".into(),
UserHighlightStyle {
color: Some(rgba(0x5889a3ff).into()),
font_weight: Some(UserFontWeight(700.0)),
..Default::default()
},
),
(
"keyword".into(),
UserHighlightStyle {
color: Some(rgba(0x298bd1ff).into()),
..Default::default()
},
),
(
"label".into(),
UserHighlightStyle {
color: Some(rgba(0x298bd1ff).into()),
..Default::default()
},
),
(
"link_text".into(),
UserHighlightStyle {
color: Some(rgba(0xcb4c18ff).into()),
font_style: Some(UserFontStyle::Italic),
..Default::default()
},
),
(
"link_uri".into(),
UserHighlightStyle {
color: Some(rgba(0x859904ff).into()),
..Default::default()
},
),
(
"number".into(),
UserHighlightStyle {
color: Some(rgba(0x859904ff).into()),
..Default::default()
},
),
(
"operator".into(),
UserHighlightStyle {
color: Some(rgba(0xcb4c18ff).into()),
..Default::default()
},
),
(
"predictive".into(),
UserHighlightStyle {
color: Some(rgba(0x679aafff).into()),
font_style: Some(UserFontStyle::Italic),
..Default::default()
},
),
(
"preproc".into(),
UserHighlightStyle {
color: Some(rgba(0x002b36ff).into()),
..Default::default()
},
),
(
"primary".into(),
UserHighlightStyle {
color: Some(rgba(0x002b36ff).into()),
..Default::default()
},
),
(
"property".into(),
UserHighlightStyle {
color: Some(rgba(0x298bd1ff).into()),
..Default::default()
},
),
(
"punctuation".into(),
UserHighlightStyle {
color: Some(rgba(0x05333eff).into()),
..Default::default()
},
),
(
"punctuation.bracket".into(),
UserHighlightStyle {
color: Some(rgba(0x05333eff).into()),
..Default::default()
},
),
(
"punctuation.delimiter".into(),
UserHighlightStyle {
color: Some(rgba(0x05333eff).into()),
..Default::default()
},
),
(
"punctuation.list_marker".into(),
UserHighlightStyle {
color: Some(rgba(0x05333eff).into()),
..Default::default()
},
),
(
"punctuation.special".into(),
UserHighlightStyle {
color: Some(rgba(0x05333eff).into()),
..Default::default()
},
),
(
"string".into(),
UserHighlightStyle {
color: Some(rgba(0xcb4c18ff).into()),
..Default::default()
},
),
(
"string.escape".into(),
UserHighlightStyle {
color: Some(rgba(0x30525bff).into()),
..Default::default()
},
),
(
"string.regex".into(),
UserHighlightStyle {
color: Some(rgba(0xcb4c18ff).into()),
..Default::default()
},
),
(
"string.special".into(),
UserHighlightStyle {
color: Some(rgba(0xcb4c18ff).into()),
..Default::default()
},
),
(
"string.special.symbol".into(),
UserHighlightStyle {
color: Some(rgba(0xcb4c18ff).into()),
..Default::default()
},
),
(
"tag".into(),
UserHighlightStyle {
color: Some(rgba(0x298bd1ff).into()),
..Default::default()
},
),
(
"text.literal".into(),
UserHighlightStyle {
color: Some(rgba(0xcb4c18ff).into()),
..Default::default()
},
),
(
"title".into(),
UserHighlightStyle {
color: Some(rgba(0x002b36ff).into()),
font_weight: Some(UserFontWeight(700.0)),
..Default::default()
},
),
(
"type".into(),
UserHighlightStyle {
color: Some(rgba(0x2ca198ff).into()),
..Default::default()
},
),
(
"variable".into(),
UserHighlightStyle {
color: Some(rgba(0x002b36ff).into()),
..Default::default()
},
),
(
"variant".into(),
UserHighlightStyle {
color: Some(rgba(0x298bd1ff).into()),
..Default::default()
},
),
],
}),
},
},
],
}
}
+1
View File
@@ -11,6 +11,7 @@ clap = { version = "4.4", features = ["derive"] }
convert_case = "0.6.0"
gpui = { path = "../gpui" }
indexmap = { version = "1.6.2", features = ["serde"] }
indoc.workspace = true
json_comments = "0.2.2"
log.workspace = true
palette = { version = "0.7.3", default-features = false, features = ["std"] }
+47 -3
View File
@@ -18,6 +18,7 @@ use clap::Parser;
use convert_case::{Case, Casing};
use gpui::serde_json;
use indexmap::IndexMap;
use indoc::formatdoc;
use json_comments::StripComments;
use log::LevelFilter;
use serde::Deserialize;
@@ -28,7 +29,7 @@ use crate::theme_printer::UserThemeFamilyPrinter;
use crate::vscode::VsCodeTheme;
use crate::vscode::VsCodeThemeConverter;
use crate::zed1::theme::Theme as Zed1Theme;
use crate::zed1::Zed1ThemeConverter;
use crate::zed1::{zed1_theme_licenses, Zed1ThemeConverter};
#[derive(Debug, Deserialize)]
struct FamilyMetadata {
@@ -200,7 +201,13 @@ fn main() -> Result<()> {
"Summercamp",
];
let mut zed1_themes_by_family: HashMap<String, Vec<UserTheme>> = HashMap::from_iter(
let zed1_licenses_by_theme: HashMap<String, zed1::Zed1ThemeLicense> = HashMap::from_iter(
zed1_theme_licenses()
.into_iter()
.map(|theme_license| (theme_license.theme.clone(), theme_license)),
);
let mut zed1_themes_by_family: IndexMap<String, Vec<UserTheme>> = IndexMap::from_iter(
zed1_theme_familes
.into_iter()
.map(|family| (family.to_string(), Vec::new())),
@@ -254,13 +261,44 @@ fn main() -> Result<()> {
themes_for_family.push(theme);
}
zed1_themes_by_family.sort_keys();
let mut licenses = Vec::new();
for (family, themes) in zed1_themes_by_family {
let theme_family = UserThemeFamily {
let mut theme_family = UserThemeFamily {
name: family,
author: "Zed Industries".to_string(),
themes,
};
theme_family
.themes
.sort_unstable_by_key(|theme| theme.name.clone());
for theme in &theme_family.themes {
let license = zed1_licenses_by_theme
.get(&theme.name)
.ok_or_else(|| anyhow!("missing license for theme: '{}'", theme.name))?;
let license_header = match license.license_url.as_ref() {
Some(license_url) => {
format!("[{theme_name}]({license_url})", theme_name = theme.name)
}
None => theme.name.clone(),
};
licenses.push(formatdoc!(
"
## {license_header}
{license_text}
********************************************************************************
",
license_text = license.license_text
));
}
theme_families.push(theme_family);
}
@@ -357,6 +395,12 @@ fn main() -> Result<()> {
mod_rs_file.write_all(mod_rs_contents.as_bytes())?;
log::info!("Writing LICENSES file...");
let mut licenses_file = File::create(themes_output_path.join(format!("LICENSES")))?;
licenses_file.write_all(licenses.join("\n").as_bytes())?;
log::info!("Formatting themes...");
let format_result = format_themes_crate()
+2
View File
@@ -1,4 +1,6 @@
mod converter;
mod licenses;
pub mod theme;
pub use converter::*;
pub use licenses::*;
File diff suppressed because it is too large Load Diff
@@ -92,6 +92,13 @@ impl Selectable for Button {
}
}
impl SelectableButton for Button {
fn selected_style(mut self, style: ButtonStyle) -> Self {
self.base = self.base.selected_style(style);
self
}
}
impl Disableable for Button {
fn disabled(mut self, disabled: bool) -> Self {
self.base = self.base.disabled(disabled);
@@ -12,6 +12,7 @@ pub(super) struct ButtonIcon {
disabled: bool,
selected: bool,
selected_icon: Option<Icon>,
selected_style: Option<ButtonStyle>,
}
impl ButtonIcon {
@@ -23,6 +24,7 @@ impl ButtonIcon {
disabled: false,
selected: false,
selected_icon: None,
selected_style: None,
}
}
@@ -62,6 +64,13 @@ impl Selectable for ButtonIcon {
}
}
impl SelectableButton for ButtonIcon {
fn selected_style(mut self, style: ButtonStyle) -> Self {
self.selected_style = Some(style);
self
}
}
impl RenderOnce for ButtonIcon {
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
let icon = self
@@ -71,6 +80,8 @@ impl RenderOnce for ButtonIcon {
let icon_color = if self.disabled {
Color::Disabled
} else if self.selected_style.is_some() && self.selected {
self.selected_style.unwrap().into()
} else if self.selected {
Color::Selected
} else {
+80 -36
View File
@@ -4,6 +4,10 @@ use smallvec::SmallVec;
use crate::prelude::*;
pub trait SelectableButton: Selectable {
fn selected_style(self, style: ButtonStyle) -> Self;
}
pub trait ButtonCommon: Clickable + Disableable {
/// A unique element ID to identify the button.
fn id(&self) -> &ElementId;
@@ -36,17 +40,68 @@ pub enum IconPosition {
End,
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
pub enum TintColor {
#[default]
Accent,
Negative,
Warning,
}
impl TintColor {
fn button_like_style(self, cx: &mut WindowContext) -> ButtonLikeStyles {
match self {
TintColor::Accent => ButtonLikeStyles {
background: cx.theme().status().info_background,
border_color: cx.theme().status().info_border,
label_color: cx.theme().colors().text,
icon_color: cx.theme().colors().text,
},
TintColor::Negative => ButtonLikeStyles {
background: cx.theme().status().error_background,
border_color: cx.theme().status().error_border,
label_color: cx.theme().colors().text,
icon_color: cx.theme().colors().text,
},
TintColor::Warning => ButtonLikeStyles {
background: cx.theme().status().warning_background,
border_color: cx.theme().status().warning_border,
label_color: cx.theme().colors().text,
icon_color: cx.theme().colors().text,
},
}
}
}
impl From<TintColor> for Color {
fn from(tint: TintColor) -> Self {
match tint {
TintColor::Accent => Color::Accent,
TintColor::Negative => Color::Error,
TintColor::Warning => Color::Warning,
}
}
}
// Used to go from ButtonStyle -> Color through tint colors.
impl From<ButtonStyle> for Color {
fn from(style: ButtonStyle) -> Self {
match style {
ButtonStyle::Tinted(tint) => tint.into(),
_ => Color::Default,
}
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
pub enum ButtonStyle {
/// A filled button with a solid background color. Provides emphasis versus
/// the more common subtle button.
Filled,
/// 🚧 Under construction 🚧
///
/// Used to emphasize a button in some way, like a selected state, or a semantic
/// coloring like an error or success button.
Tinted,
Tinted(TintColor),
/// The default button style, used for most buttons. Has a transparent background,
/// but has a background color to indicate states like hover and active.
@@ -86,12 +141,7 @@ impl ButtonStyle {
label_color: Color::Default.color(cx),
icon_color: Color::Default.color(cx),
},
ButtonStyle::Tinted => ButtonLikeStyles {
background: gpui::red(),
border_color: gpui::red(),
label_color: gpui::red(),
icon_color: gpui::red(),
},
ButtonStyle::Tinted(tint) => tint.button_like_style(cx),
ButtonStyle::Subtle => ButtonLikeStyles {
background: cx.theme().colors().ghost_element_background,
border_color: transparent_black(),
@@ -115,12 +165,7 @@ impl ButtonStyle {
label_color: Color::Default.color(cx),
icon_color: Color::Default.color(cx),
},
ButtonStyle::Tinted => ButtonLikeStyles {
background: gpui::red(),
border_color: gpui::red(),
label_color: gpui::red(),
icon_color: gpui::red(),
},
ButtonStyle::Tinted(tint) => tint.button_like_style(cx),
ButtonStyle::Subtle => ButtonLikeStyles {
background: cx.theme().colors().ghost_element_hover,
border_color: transparent_black(),
@@ -146,12 +191,7 @@ impl ButtonStyle {
label_color: Color::Default.color(cx),
icon_color: Color::Default.color(cx),
},
ButtonStyle::Tinted => ButtonLikeStyles {
background: gpui::red(),
border_color: gpui::red(),
label_color: gpui::red(),
icon_color: gpui::red(),
},
ButtonStyle::Tinted(tint) => tint.button_like_style(cx),
ButtonStyle::Subtle => ButtonLikeStyles {
background: cx.theme().colors().ghost_element_active,
border_color: transparent_black(),
@@ -178,12 +218,7 @@ impl ButtonStyle {
label_color: Color::Default.color(cx),
icon_color: Color::Default.color(cx),
},
ButtonStyle::Tinted => ButtonLikeStyles {
background: gpui::red(),
border_color: gpui::red(),
label_color: gpui::red(),
icon_color: gpui::red(),
},
ButtonStyle::Tinted(tint) => tint.button_like_style(cx),
ButtonStyle::Subtle => ButtonLikeStyles {
background: cx.theme().colors().ghost_element_background,
border_color: cx.theme().colors().border_focused,
@@ -208,12 +243,7 @@ impl ButtonStyle {
label_color: Color::Disabled.color(cx),
icon_color: Color::Disabled.color(cx),
},
ButtonStyle::Tinted => ButtonLikeStyles {
background: gpui::red(),
border_color: gpui::red(),
label_color: gpui::red(),
icon_color: gpui::red(),
},
ButtonStyle::Tinted(tint) => tint.button_like_style(cx),
ButtonStyle::Subtle => ButtonLikeStyles {
background: cx.theme().colors().ghost_element_disabled,
border_color: cx.theme().colors().border_disabled,
@@ -264,6 +294,7 @@ pub struct ButtonLike {
pub(super) style: ButtonStyle,
pub(super) disabled: bool,
pub(super) selected: bool,
pub(super) selected_style: Option<ButtonStyle>,
pub(super) width: Option<DefiniteLength>,
size: ButtonSize,
rounding: Option<ButtonLikeRounding>,
@@ -280,6 +311,7 @@ impl ButtonLike {
style: ButtonStyle::default(),
disabled: false,
selected: false,
selected_style: None,
width: None,
size: ButtonSize::Default,
rounding: Some(ButtonLikeRounding::All),
@@ -309,6 +341,13 @@ impl Selectable for ButtonLike {
}
}
impl SelectableButton for ButtonLike {
fn selected_style(mut self, style: ButtonStyle) -> Self {
self.selected_style = Some(style);
self
}
}
impl Clickable for ButtonLike {
fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
self.on_click = Some(Box::new(handler));
@@ -364,6 +403,11 @@ impl ParentElement for ButtonLike {
impl RenderOnce for ButtonLike {
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
let style = self
.selected_style
.filter(|_| self.selected)
.unwrap_or(self.style);
self.base
.h_flex()
.id(self.id.clone())
@@ -382,12 +426,12 @@ impl RenderOnce for ButtonLike {
ButtonSize::Default | ButtonSize::Compact => this.px_1(),
ButtonSize::None => this,
})
.bg(self.style.enabled(cx).background)
.bg(style.enabled(cx).background)
.when(self.disabled, |this| this.cursor_not_allowed())
.when(!self.disabled, |this| {
this.cursor_pointer()
.hover(|hover| hover.bg(self.style.hovered(cx).background))
.active(|active| active.bg(self.style.active(cx).background))
.hover(|hover| hover.bg(style.hovered(cx).background))
.active(|active| active.bg(style.active(cx).background))
})
.when_some(
self.on_click.filter(|_| !self.disabled),
+10 -1
View File
@@ -1,6 +1,6 @@
use gpui::{AnyView, DefiniteLength};
use crate::prelude::*;
use crate::{prelude::*, SelectableButton};
use crate::{ButtonCommon, ButtonLike, ButtonSize, ButtonStyle, Icon, IconSize};
use super::button_icon::ButtonIcon;
@@ -55,6 +55,13 @@ impl Selectable for IconButton {
}
}
impl SelectableButton for IconButton {
fn selected_style(mut self, style: ButtonStyle) -> Self {
self.base = self.base.selected_style(style);
self
}
}
impl Clickable for IconButton {
fn on_click(
mut self,
@@ -109,12 +116,14 @@ impl RenderOnce for IconButton {
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
let is_disabled = self.base.disabled;
let is_selected = self.base.selected;
let selected_style = self.base.selected_style;
self.base.child(
ButtonIcon::new(self.icon)
.disabled(is_disabled)
.selected(is_selected)
.selected_icon(self.selected_icon)
.when_some(selected_style, |this, style| this.selected_style(style))
.size(self.icon_size)
.color(self.icon_color),
)
@@ -63,6 +63,13 @@ impl Selectable for ToggleButton {
}
}
impl SelectableButton for ToggleButton {
fn selected_style(mut self, style: ButtonStyle) -> Self {
self.base.selected_style = Some(style);
self
}
}
impl Disableable for ToggleButton {
fn disabled(mut self, disabled: bool) -> Self {
self.base = self.base.disabled(disabled);
+23 -156
View File
@@ -1,4 +1,4 @@
use gpui::{div, prelude::*, Element, ElementId, IntoElement, Styled, WindowContext};
use gpui::{div, prelude::*, ElementId, IntoElement, Styled, WindowContext};
use crate::prelude::*;
use crate::{Color, Icon, IconElement, Selection};
@@ -18,126 +18,6 @@ pub struct Checkbox {
on_click: Option<CheckHandler>,
}
impl RenderOnce for Checkbox {
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
let group_id = format!("checkbox_group_{:?}", self.id);
let icon = match self.checked {
// When selected, we show a checkmark.
Selection::Selected => {
Some(
IconElement::new(Icon::Check)
.size(crate::IconSize::Small)
.color(
// If the checkbox is disabled we change the color of the icon.
if self.disabled {
Color::Disabled
} else {
Color::Selected
},
),
)
}
// In an indeterminate state, we show a dash.
Selection::Indeterminate => {
Some(
IconElement::new(Icon::Dash)
.size(crate::IconSize::Small)
.color(
// If the checkbox is disabled we change the color of the icon.
if self.disabled {
Color::Disabled
} else {
Color::Selected
},
),
)
}
// When unselected, we show nothing.
Selection::Unselected => None,
};
// A checkbox could be in an indeterminate state,
// for example the indeterminate state could represent:
// - a group of options of which only some are selected
// - an enabled option that is no longer available
// - a previously agreed to license that has been updated
//
// For the sake of styles we treat the indeterminate state as selected,
// but it's icon will be different.
let selected =
self.checked == Selection::Selected || self.checked == Selection::Indeterminate;
// We could use something like this to make the checkbox background when selected:
//
// ~~~rust
// ...
// .when(selected, |this| {
// this.bg(cx.theme().colors().element_selected)
// })
// ~~~
//
// But we use a match instead here because the checkbox might be disabled,
// and it could be disabled _while_ it is selected, as well as while it is not selected.
let (bg_color, border_color) = match (self.disabled, selected) {
(true, _) => (
cx.theme().colors().ghost_element_disabled,
cx.theme().colors().border_disabled,
),
(false, true) => (
cx.theme().colors().element_selected,
cx.theme().colors().border,
),
(false, false) => (
cx.theme().colors().element_background,
cx.theme().colors().border,
),
};
div()
.id(self.id)
// Rather than adding `px_1()` to add some space around the checkbox,
// we use a larger parent element to create a slightly larger
// click area for the checkbox.
.size_5()
// Because we've enlarged the click area, we need to create a
// `group` to pass down interactivity events to the checkbox.
.group(group_id.clone())
.child(
div()
.flex()
// This prevent the flex element from growing
// or shrinking in response to any size changes
.flex_none()
// The combo of `justify_center()` and `items_center()`
// is used frequently to center elements in a flex container.
//
// We use this to center the icon in the checkbox.
.justify_center()
.items_center()
.m_1()
.size_4()
.rounded_sm()
.bg(bg_color)
.border()
.border_color(border_color)
// We only want the interactivity states to fire when we
// are in a checkbox that isn't disabled.
.when(!self.disabled, |this| {
// Here instead of `hover()` we use `group_hover()`
// to pass it the group id.
this.group_hover(group_id.clone(), |el| {
el.bg(cx.theme().colors().element_hover)
})
})
.children(icon),
)
.when_some(
self.on_click.filter(|_| !self.disabled),
|this, on_click| this.on_click(move |_, cx| on_click(&self.checked.inverse(), cx)),
)
}
}
impl Checkbox {
pub fn new(id: impl Into<ElementId>, checked: Selection) -> Self {
Self {
@@ -160,42 +40,29 @@ impl Checkbox {
self.on_click = Some(Box::new(handler));
self
}
}
pub fn render(self, cx: &mut WindowContext) -> impl Element {
impl RenderOnce for Checkbox {
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
let group_id = format!("checkbox_group_{:?}", self.id);
let icon = match self.checked {
// When selected, we show a checkmark.
Selection::Selected => {
Some(
IconElement::new(Icon::Check)
.size(crate::IconSize::Small)
.color(
// If the checkbox is disabled we change the color of the icon.
if self.disabled {
Color::Disabled
} else {
Color::Selected
},
),
)
}
// In an indeterminate state, we show a dash.
Selection::Indeterminate => {
Some(
IconElement::new(Icon::Dash)
.size(crate::IconSize::Small)
.color(
// If the checkbox is disabled we change the color of the icon.
if self.disabled {
Color::Disabled
} else {
Color::Selected
},
),
)
}
// When unselected, we show nothing.
Selection::Selected => Some(IconElement::new(Icon::Check).size(IconSize::Small).color(
if self.disabled {
Color::Disabled
} else {
Color::Selected
},
)),
Selection::Indeterminate => Some(
IconElement::new(Icon::Dash)
.size(IconSize::Small)
.color(if self.disabled {
Color::Disabled
} else {
Color::Selected
}),
),
Selection::Unselected => None,
};
@@ -212,12 +79,12 @@ impl Checkbox {
// We could use something like this to make the checkbox background when selected:
//
// ~~~rust
// ```rs
// ...
// .when(selected, |this| {
// this.bg(cx.theme().colors().element_selected)
// })
// ~~~
// ```
//
// But we use a match instead here because the checkbox might be disabled,
// and it could be disabled _while_ it is selected, as well as while it is not selected.
@@ -236,7 +103,7 @@ impl Checkbox {
),
};
div()
h_stack()
.id(self.id)
// Rather than adding `px_1()` to add some space around the checkbox,
// we use a larger parent element to create a slightly larger
+1 -1
View File
@@ -12,7 +12,7 @@ pub use crate::selectable::*;
pub use crate::styles::{vh, vw};
pub use crate::visible_on_hover::*;
pub use crate::{h_stack, v_stack};
pub use crate::{Button, ButtonSize, ButtonStyle, IconButton};
pub use crate::{Button, ButtonSize, ButtonStyle, IconButton, SelectableButton};
pub use crate::{ButtonCommon, Color, StyledExt};
pub use crate::{Icon, IconElement, IconPosition, IconSize};
pub use crate::{Label, LabelCommon, LabelSize, LineHeightStyle};
+149 -143
View File
@@ -59,153 +59,159 @@ pub struct WelcomePage {
impl Render for WelcomePage {
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement {
h_stack().full().track_focus(&self.focus_handle).child(
v_stack()
.w_96()
.gap_4()
.mx_auto()
.child(
svg()
.path("icons/logo_96.svg")
.text_color(gpui::white())
.w(px(96.))
.h(px(96.))
.mx_auto(),
)
.child(
h_stack()
.justify_center()
.child(Label::new("Code at the speed of thought")),
)
.child(
v_stack()
.gap_2()
.child(
Button::new("choose-theme", "Choose a theme")
.full_width()
.on_click(cx.listener(|this, _, cx| {
this.workspace
.update(cx, |workspace, cx| {
theme_selector::toggle(
workspace,
&Default::default(),
cx,
)
})
.ok();
})),
)
.child(
Button::new("choose-keymap", "Choose a keymap")
.full_width()
.on_click(cx.listener(|this, _, cx| {
this.workspace
.update(cx, |workspace, cx| {
base_keymap_picker::toggle(
workspace,
&Default::default(),
cx,
)
})
.ok();
})),
)
.child(
Button::new("install-cli", "Install the CLI")
.full_width()
.on_click(cx.listener(|_, _, cx| {
cx.app_mut()
.spawn(
|cx| async move { install_cli::install_cli(&cx).await },
h_stack()
.full()
.bg(cx.theme().colors().editor_background)
.track_focus(&self.focus_handle)
.child(
v_stack()
.w_96()
.gap_4()
.mx_auto()
.child(
svg()
.path("icons/logo_96.svg")
.text_color(gpui::white())
.w(px(96.))
.h(px(96.))
.mx_auto(),
)
.child(
h_stack()
.justify_center()
.child(Label::new("Code at the speed of thought")),
)
.child(
v_stack()
.gap_2()
.child(
Button::new("choose-theme", "Choose a theme")
.full_width()
.on_click(cx.listener(|this, _, cx| {
this.workspace
.update(cx, |workspace, cx| {
theme_selector::toggle(
workspace,
&Default::default(),
cx,
)
})
.ok();
})),
)
.child(
Button::new("choose-keymap", "Choose a keymap")
.full_width()
.on_click(cx.listener(|this, _, cx| {
this.workspace
.update(cx, |workspace, cx| {
base_keymap_picker::toggle(
workspace,
&Default::default(),
cx,
)
})
.ok();
})),
)
.child(
Button::new("install-cli", "Install the CLI")
.full_width()
.on_click(cx.listener(|_, _, cx| {
cx.app_mut()
.spawn(|cx| async move {
install_cli::install_cli(&cx).await
})
.detach_and_log_err(cx);
})),
),
)
.child(
v_stack()
.p_3()
.gap_2()
.bg(cx.theme().colors().elevated_surface_background)
.border_1()
.border_color(cx.theme().colors().border)
.rounded_md()
.child(
h_stack()
.gap_2()
.child(
Checkbox::new(
"enable-vim",
if VimModeSetting::get_global(cx).0 {
ui::Selection::Selected
} else {
ui::Selection::Unselected
},
)
.detach_and_log_err(cx);
})),
),
)
.child(
v_stack()
.p_3()
.gap_2()
.bg(cx.theme().colors().elevated_surface_background)
.border_1()
.border_color(cx.theme().colors().border)
.rounded_md()
.child(
h_stack()
.gap_2()
.child(
Checkbox::new(
"enable-vim",
if VimModeSetting::get_global(cx).0 {
ui::Selection::Selected
} else {
ui::Selection::Unselected
},
.on_click(
cx.listener(move |this, selection, cx| {
this.update_settings::<VimModeSetting>(
selection,
cx,
|setting, value| *setting = Some(value),
);
}),
),
)
.on_click(cx.listener(
move |this, selection, cx| {
this.update_settings::<VimModeSetting>(
selection,
cx,
|setting, value| *setting = Some(value),
);
},
)),
)
.child(Label::new("Enable vim mode")),
)
.child(
h_stack()
.gap_2()
.child(
Checkbox::new(
"enable-telemetry",
if TelemetrySettings::get_global(cx).metrics {
ui::Selection::Selected
} else {
ui::Selection::Unselected
},
.child(Label::new("Enable vim mode")),
)
.child(
h_stack()
.gap_2()
.child(
Checkbox::new(
"enable-telemetry",
if TelemetrySettings::get_global(cx).metrics {
ui::Selection::Selected
} else {
ui::Selection::Unselected
},
)
.on_click(
cx.listener(move |this, selection, cx| {
this.update_settings::<TelemetrySettings>(
selection,
cx,
|settings, value| {
settings.metrics = Some(value)
},
);
}),
),
)
.on_click(cx.listener(
move |this, selection, cx| {
this.update_settings::<TelemetrySettings>(
selection,
cx,
|settings, value| settings.metrics = Some(value),
);
},
)),
)
.child(Label::new("Send anonymous usage data")),
)
.child(
h_stack()
.gap_2()
.child(
Checkbox::new(
"enable-crash",
if TelemetrySettings::get_global(cx).diagnostics {
ui::Selection::Selected
} else {
ui::Selection::Unselected
},
.child(Label::new("Send anonymous usage data")),
)
.child(
h_stack()
.gap_2()
.child(
Checkbox::new(
"enable-crash",
if TelemetrySettings::get_global(cx).diagnostics {
ui::Selection::Selected
} else {
ui::Selection::Unselected
},
)
.on_click(
cx.listener(move |this, selection, cx| {
this.update_settings::<TelemetrySettings>(
selection,
cx,
|settings, value| {
settings.diagnostics = Some(value)
},
);
}),
),
)
.on_click(cx.listener(
move |this, selection, cx| {
this.update_settings::<TelemetrySettings>(
selection,
cx,
|settings, value| {
settings.diagnostics = Some(value)
},
);
},
)),
)
.child(Label::new("Send crash reports")),
),
),
)
.child(Label::new("Send crash reports")),
),
),
)
}
}
+1
View File
@@ -32,6 +32,7 @@ language = { path = "../language" }
node_runtime = { path = "../node_runtime" }
project = { path = "../project" }
settings = { path = "../settings" }
sqlez = { path = "../sqlez" }
terminal = { path = "../terminal" }
theme = { path = "../theme" }
util = { path = "../util" }
+7 -3
View File
@@ -672,9 +672,13 @@ impl Render for PanelButtons {
&& panel.position_is_valid(position, cx)
{
let panel = panel.clone();
menu = menu.entry(position.to_label(), None, move |cx| {
panel.set_position(position, cx);
})
menu = menu.entry(
format!("Dock {}", position.to_label()),
None,
move |cx| {
panel.set_position(position, cx);
},
)
}
}
menu
+9 -1
View File
@@ -246,7 +246,15 @@ impl Member {
.size_full()
.child(pane.clone())
.when_some(leader_border, |this, color| {
this.border_2().border_color(color)
this.child(
div()
.absolute()
.size_full()
.left_0()
.top_0()
.border_2()
.border_color(color),
)
})
.when_some(leader_status_box, |this, status_box| {
this.child(
+128 -8
View File
@@ -6,7 +6,12 @@ use std::path::Path;
use anyhow::{anyhow, bail, Context, Result};
use db::{define_connection, query, sqlez::connection::Connection, sqlez_macros::sql};
use gpui::{Axis, WindowBounds};
use gpui::{point, size, Axis, Bounds, WindowBounds};
use sqlez::{
bindable::{Bind, Column, StaticColumnCount},
statement::Statement,
};
use util::{unzip_option, ResultExt};
use uuid::Uuid;
@@ -20,6 +25,121 @@ use model::{
use self::model::DockStructure;
#[derive(Copy, Clone, Debug, PartialEq)]
pub(crate) struct SerializedAxis(pub(crate) gpui::Axis);
impl sqlez::bindable::StaticColumnCount for SerializedAxis {}
impl sqlez::bindable::Bind for SerializedAxis {
fn bind(
&self,
statement: &sqlez::statement::Statement,
start_index: i32,
) -> anyhow::Result<i32> {
match self.0 {
gpui::Axis::Horizontal => "Horizontal",
gpui::Axis::Vertical => "Vertical",
}
.bind(statement, start_index)
}
}
impl sqlez::bindable::Column for SerializedAxis {
fn column(
statement: &mut sqlez::statement::Statement,
start_index: i32,
) -> anyhow::Result<(Self, i32)> {
String::column(statement, start_index).and_then(|(axis_text, next_index)| {
Ok((
match axis_text.as_str() {
"Horizontal" => Self(Axis::Horizontal),
"Vertical" => Self(Axis::Vertical),
_ => anyhow::bail!("Stored serialized item kind is incorrect"),
},
next_index,
))
})
}
}
#[derive(Clone, Debug, PartialEq)]
pub(crate) struct SerializedWindowsBounds(pub(crate) WindowBounds);
impl StaticColumnCount for SerializedWindowsBounds {
fn column_count() -> usize {
5
}
}
impl Bind for SerializedWindowsBounds {
fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
let (region, next_index) = match self.0 {
WindowBounds::Fullscreen => {
let next_index = statement.bind(&"Fullscreen", start_index)?;
(None, next_index)
}
WindowBounds::Maximized => {
let next_index = statement.bind(&"Maximized", start_index)?;
(None, next_index)
}
WindowBounds::Fixed(region) => {
let next_index = statement.bind(&"Fixed", start_index)?;
(Some(region), next_index)
}
};
statement.bind(
&region.map(|region| {
(
SerializedGlobalPixels(region.origin.x),
SerializedGlobalPixels(region.origin.y),
SerializedGlobalPixels(region.size.width),
SerializedGlobalPixels(region.size.height),
)
}),
next_index,
)
}
}
impl Column for SerializedWindowsBounds {
fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
let (window_state, next_index) = String::column(statement, start_index)?;
let bounds = match window_state.as_str() {
"Fullscreen" => SerializedWindowsBounds(WindowBounds::Fullscreen),
"Maximized" => SerializedWindowsBounds(WindowBounds::Maximized),
"Fixed" => {
let ((x, y, width, height), _) = Column::column(statement, next_index)?;
let x: f64 = x;
let y: f64 = y;
let width: f64 = width;
let height: f64 = height;
SerializedWindowsBounds(WindowBounds::Fixed(Bounds {
origin: point(x.into(), y.into()),
size: size(width.into(), height.into()),
}))
}
_ => bail!("Window State did not have a valid string"),
};
Ok((bounds, next_index + 4))
}
}
#[derive(Clone, Debug, PartialEq)]
struct SerializedGlobalPixels(gpui::GlobalPixels);
impl sqlez::bindable::StaticColumnCount for SerializedGlobalPixels {}
impl sqlez::bindable::Bind for SerializedGlobalPixels {
fn bind(
&self,
statement: &sqlez::statement::Statement,
start_index: i32,
) -> anyhow::Result<i32> {
let this: f64 = self.0.into();
let this: f32 = this as _;
this.bind(statement, start_index)
}
}
define_connection! {
// Current schema shape using pseudo-rust syntax:
//
@@ -181,7 +301,7 @@ impl WorkspaceDb {
/// Returns a serialized workspace for the given worktree_roots. If the passed array
/// is empty, the most recent workspace is returned instead. If no workspace for the
/// passed roots is stored, returns none.
pub fn workspace_for_roots<P: AsRef<Path>>(
pub(crate) fn workspace_for_roots<P: AsRef<Path>>(
&self,
worktree_roots: &[P],
) -> Option<SerializedWorkspace> {
@@ -192,7 +312,7 @@ impl WorkspaceDb {
let (workspace_id, workspace_location, bounds, display, docks): (
WorkspaceId,
WorkspaceLocation,
Option<WindowBounds>,
Option<SerializedWindowsBounds>,
Option<Uuid>,
DockStructure,
) = self
@@ -230,7 +350,7 @@ impl WorkspaceDb {
.get_center_pane_group(workspace_id)
.context("Getting center group")
.log_err()?,
bounds,
bounds: bounds.map(|bounds| bounds.0),
display,
docks,
})
@@ -238,7 +358,7 @@ impl WorkspaceDb {
/// Saves a workspace using the worktree roots. Will garbage collect any workspaces
/// that used this workspace previously
pub async fn save_workspace(&self, workspace: SerializedWorkspace) {
pub(crate) async fn save_workspace(&self, workspace: SerializedWorkspace) {
self.write(move |conn| {
conn.with_savepoint("update_worktrees", || {
// Clear out panes and pane_groups
@@ -367,7 +487,7 @@ impl WorkspaceDb {
type GroupKey = (Option<GroupId>, WorkspaceId);
type GroupOrPane = (
Option<GroupId>,
Option<Axis>,
Option<SerializedAxis>,
Option<PaneId>,
Option<bool>,
Option<String>,
@@ -536,7 +656,7 @@ impl WorkspaceDb {
}
query! {
pub async fn set_window_bounds(workspace_id: WorkspaceId, bounds: WindowBounds, display: Uuid) -> Result<()> {
pub(crate) async fn set_window_bounds(workspace_id: WorkspaceId, bounds: SerializedWindowsBounds, display: Uuid) -> Result<()> {
UPDATE workspaces
SET window_state = ?2,
window_x = ?3,
@@ -683,7 +803,7 @@ mod tests {
fn group(axis: Axis, children: Vec<SerializedPaneGroup>) -> SerializedPaneGroup {
SerializedPaneGroup::Group {
axis,
axis: SerializedAxis(axis),
flexes: None,
children,
}
+12 -11
View File
@@ -1,3 +1,4 @@
use super::SerializedAxis;
use crate::{item::ItemHandle, ItemDeserializers, Member, Pane, PaneAxis, Workspace, WorkspaceId};
use anyhow::{Context, Result};
use async_recursion::async_recursion;
@@ -5,7 +6,7 @@ use db::sqlez::{
bindable::{Bind, Column, StaticColumnCount},
statement::Statement,
};
use gpui::{AsyncWindowContext, Axis, Model, Task, View, WeakView, WindowBounds};
use gpui::{AsyncWindowContext, Model, Task, View, WeakView, WindowBounds};
use project::Project;
use std::{
path::{Path, PathBuf},
@@ -54,13 +55,13 @@ impl Column for WorkspaceLocation {
}
#[derive(Debug, PartialEq, Clone)]
pub struct SerializedWorkspace {
pub id: WorkspaceId,
pub location: WorkspaceLocation,
pub center_group: SerializedPaneGroup,
pub bounds: Option<WindowBounds>,
pub display: Option<Uuid>,
pub docks: DockStructure,
pub(crate) struct SerializedWorkspace {
pub(crate) id: WorkspaceId,
pub(crate) location: WorkspaceLocation,
pub(crate) center_group: SerializedPaneGroup,
pub(crate) bounds: Option<WindowBounds>,
pub(crate) display: Option<Uuid>,
pub(crate) docks: DockStructure,
}
#[derive(Debug, PartialEq, Clone, Default)]
@@ -126,9 +127,9 @@ impl Bind for DockData {
}
#[derive(Debug, PartialEq, Clone)]
pub enum SerializedPaneGroup {
pub(crate) enum SerializedPaneGroup {
Group {
axis: Axis,
axis: SerializedAxis,
flexes: Option<Vec<f32>>,
children: Vec<SerializedPaneGroup>,
},
@@ -183,7 +184,7 @@ impl SerializedPaneGroup {
}
Some((
Member::Axis(PaneAxis::load(axis, members, flexes)),
Member::Axis(PaneAxis::load(axis.0, members, flexes)),
current_active_pane,
items,
))
+19 -14
View File
@@ -42,9 +42,9 @@ use node_runtime::NodeRuntime;
use notifications::{simple_message_notification::MessageNotification, NotificationHandle};
pub use pane::*;
pub use pane_group::*;
use persistence::DB;
use persistence::{model::SerializedWorkspace, SerializedWindowsBounds, DB};
pub use persistence::{
model::{ItemId, SerializedWorkspace, WorkspaceLocation},
model::{ItemId, WorkspaceLocation},
WorkspaceDb, DB as WORKSPACE_DB,
};
use postage::stream::Stream;
@@ -70,8 +70,9 @@ use util::ResultExt;
use uuid::Uuid;
pub use workspace_settings::{AutosaveSetting, WorkspaceSettings};
use crate::persistence::model::{
DockData, DockStructure, SerializedItem, SerializedPane, SerializedPaneGroup,
use crate::persistence::{
model::{DockData, DockStructure, SerializedItem, SerializedPane, SerializedPaneGroup},
SerializedAxis,
};
lazy_static! {
@@ -107,6 +108,7 @@ actions!(
NewCenterTerminal,
ToggleTerminalFocus,
NewSearch,
DeploySearch,
Feedback,
Restart,
Welcome,
@@ -624,7 +626,11 @@ impl Workspace {
if let Some(display_uuid) = display.uuid().log_err() {
cx.background_executor()
.spawn(DB.set_window_bounds(workspace_id, bounds, display_uuid))
.spawn(DB.set_window_bounds(
workspace_id,
SerializedWindowsBounds(bounds),
display_uuid,
))
.detach_and_log_err(cx);
}
}
@@ -1186,7 +1192,7 @@ impl Workspace {
mut save_intent: SaveIntent,
cx: &mut ViewContext<Self>,
) -> Task<Result<bool>> {
if self.project.read(cx).is_read_only() {
if self.project.read(cx).is_disconnected() {
return Task::ready(Ok(true));
}
let dirty_items = self
@@ -2509,7 +2515,7 @@ impl Workspace {
}
fn update_window_edited(&mut self, cx: &mut ViewContext<Self>) {
let is_edited = !self.project.read(cx).is_read_only()
let is_edited = !self.project.read(cx).is_disconnected()
&& self
.items(cx)
.any(|item| item.has_conflict(cx) || item.is_dirty(cx));
@@ -2988,7 +2994,7 @@ impl Workspace {
flexes,
bounding_boxes: _,
}) => SerializedPaneGroup::Group {
axis: *axis,
axis: SerializedAxis(*axis),
children: members
.iter()
.map(|member| build_serialized_pane_group(member, cx))
@@ -3265,6 +3271,7 @@ impl Workspace {
let user_store = project.read(cx).user_store();
let workspace_store = cx.new_model(|cx| WorkspaceStore::new(client.clone(), cx));
cx.activate_window();
let app_state = Arc::new(AppState {
languages: project.read(cx).languages().clone(),
workspace_store,
@@ -3633,7 +3640,7 @@ impl Render for Workspace {
})),
)
.child(self.status_bar.clone())
.children(if self.project.read(cx).is_read_only() {
.children(if self.project.read(cx).is_disconnected() {
Some(DisconnectedOverlay)
} else {
None
@@ -4763,8 +4770,7 @@ mod tests {
});
// Deactivating the window saves the file.
cx.simulate_deactivation();
cx.executor().run_until_parked();
cx.deactivate_window();
item.update(cx, |item, _| assert_eq!(item.save_count, 1));
// Autosave on focus change.
@@ -4784,14 +4790,13 @@ mod tests {
item.update(cx, |item, _| assert_eq!(item.save_count, 2));
// Deactivating the window still saves the file.
cx.simulate_activation();
cx.update(|cx| cx.activate_window());
item.update(cx, |item, cx| {
cx.focus_self();
item.is_dirty = true;
});
cx.simulate_deactivation();
cx.deactivate_window();
cx.executor().run_until_parked();
item.update(cx, |item, _| assert_eq!(item.save_count, 3));
// Autosave after delay.
-29
View File
@@ -1,29 +0,0 @@
[⬅ Back to Index](../index.md)
# Generating Theme Types
## How to generate theme types:
Run a script
```bash
./script/build-theme-types
```
Types are generated in `styles/src/types/zed.ts`
## How it works:
1. Rust types
The `crates/theme` contains theme types.
Crate `schemars` used to generate a JSON schema from the theme structs.
Every struct that represent theme type has a `#[derive(JsonSchema)]` attribute.
Task lotaked at `crates/xtask/src/main.rs` generates a JSON schema from the theme structs.
2. TypeScript types
Script `npm run build-types` from `styles` package generates TypeScript types from the JSON schema and saves them to `styles/src/types/zed.ts`.
+1 -6
View File
@@ -4,17 +4,12 @@ set -eu
source script/lib/deploy-helpers.sh
if [[ $# < 2 ]]; then
echo "Usage: $0 <production|staging|preview> <tag-name> (nightly is not yet supported)"
echo "Usage: $0 <production|staging|preview|nightly> <tag-name>"
exit 1
fi
environment=$1
version=$2
if [[ ${environment} == "nightly" ]]; then
echo "nightly is not yet supported"
exit 1
fi
export_vars_for_environment ${environment}
image_id=$(image_id_for_version ${version})
+1 -4
View File
@@ -9,10 +9,7 @@ OUTPUT_FILE=$(pwd)/assets/licenses.md
echo -e "# ###### THEME LICENSES ######\n" >> $OUTPUT_FILE
echo "Generating theme licenses"
cd styles
npm --silent ci
npm run --silent build-licenses >> $OUTPUT_FILE
cd ..
cat crates/theme/src/themes/LICENSES >> $OUTPUT_FILE
echo -e "# ###### CODE LICENSES ######\n" >> $OUTPUT_FILE
-34
View File
@@ -1,34 +0,0 @@
module.exports = {
env: {
node: true,
},
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:import/typescript",
],
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
},
plugins: ["@typescript-eslint", "import"],
globals: {
module: true,
},
settings: {
"import/parsers": {
"@typescript-eslint/parser": [".ts"],
},
"import/resolver": {
typescript: true,
node: true,
},
"import/extensions": [".ts"],
},
rules: {
"linebreak-style": ["error", "unix"],
"@typescript-eslint/no-explicit-any": "off",
semi: ["error", "never"],
},
}
-2
View File
@@ -1,2 +0,0 @@
node_modules/
coverage/
-3
View File
@@ -1,3 +0,0 @@
package-lock.json
package.json
target
-6
View File
@@ -1,6 +0,0 @@
{
"semi": false,
"printWidth": 80,
"htmlWhitespaceSensitivity": "strict",
"tabWidth": 4
}

Some files were not shown because too many files have changed in this diff Show More