Merge branch 'main' into copilot-ui

This commit is contained in:
Nate Butler 2024-01-05 15:56:33 -05:00
commit dcb9c0b9d8
213 changed files with 12127 additions and 23303 deletions

15
Cargo.lock generated
View File

@ -1441,7 +1441,7 @@ dependencies = [
[[package]] [[package]]
name = "collab" name = "collab"
version = "0.33.0" version = "0.34.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -1926,11 +1926,12 @@ dependencies = [
[[package]] [[package]]
name = "ctor" name = "ctor"
version = "0.1.20" version = "0.2.6"
source = "git+https://github.com/zed-industries/rust-ctor?rev=7f824cf6a7943885a649b579f33f9ac53f0d1db6#7f824cf6a7943885a649b579f33f9ac53f0d1db6" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30d2b3721e861707777e3195b0158f950ae6dc4a27e4d02ff9f67e3eb3de199e"
dependencies = [ dependencies = [
"quote", "quote",
"syn 1.0.109", "syn 2.0.37",
] ]
[[package]] [[package]]
@ -3065,7 +3066,6 @@ dependencies = [
"slotmap", "slotmap",
"smallvec", "smallvec",
"smol", "smol",
"sqlez",
"sum_tree", "sum_tree",
"taffy", "taffy",
"thiserror", "thiserror",
@ -6846,7 +6846,6 @@ dependencies = [
"serde_json", "serde_json",
"serde_json_lenient", "serde_json_lenient",
"smallvec", "smallvec",
"sqlez",
"toml 0.5.11", "toml 0.5.11",
"tree-sitter", "tree-sitter",
"tree-sitter-json 0.19.0", "tree-sitter-json 0.19.0",
@ -7838,6 +7837,7 @@ dependencies = [
"convert_case 0.6.0", "convert_case 0.6.0",
"gpui", "gpui",
"indexmap 1.9.3", "indexmap 1.9.3",
"indoc",
"json_comments", "json_comments",
"log", "log",
"palette", "palette",
@ -8489,7 +8489,7 @@ dependencies = [
[[package]] [[package]]
name = "tree-sitter-nu" name = "tree-sitter-nu"
version = "0.0.1" 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 = [ dependencies = [
"cc", "cc",
"tree-sitter", "tree-sitter",
@ -9439,6 +9439,7 @@ dependencies = [
"serde_json", "serde_json",
"settings", "settings",
"smallvec", "smallvec",
"sqlez",
"terminal", "terminal",
"theme", "theme",
"ui", "ui",

View File

@ -92,10 +92,7 @@ resolver = "2"
anyhow = { version = "1.0.57" } anyhow = { version = "1.0.57" }
async-trait = { version = "0.1" } async-trait = { version = "0.1" }
async-compression = { version = "0.4", features = ["gzip", "futures-io"] } async-compression = { version = "0.4", features = ["gzip", "futures-io"] }
# TODO: Switch back to the published version of `ctor` once: ctor = "0.2.6"
# 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" }
derive_more = { version = "0.99.17" } derive_more = { version = "0.99.17" }
env_logger = { version = "0.9" } env_logger = { version = "0.9" }
futures = { version = "0.3" } futures = { version = "0.3" }
@ -158,7 +155,7 @@ 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-yaml = { git = "https://github.com/zed-industries/tree-sitter-yaml", rev = "f545a41f57502e1b5ddf2a6668896c1b0620f930"}
tree-sitter-lua = "0.0.14" tree-sitter-lua = "0.0.14"
tree-sitter-nix = { git = "https://github.com/nix-community/tree-sitter-nix", rev = "66e3e9ce9180ae08fc57372061006ef83f0abde7" } 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-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"} tree-sitter-uiua = {git = "https://github.com/shnarazk/tree-sitter-uiua", rev = "9260f11be5900beda4ee6d1a24ab8ddfaf5a19b2"}

View File

@ -1286,8 +1286,8 @@ impl Panel for AssistantPanel {
} }
} }
fn icon(&self, _cx: &WindowContext) -> Option<Icon> { fn icon(&self, cx: &WindowContext) -> Option<Icon> {
Some(Icon::Ai) Some(Icon::Ai).filter(|_| AssistantSettings::get_global(cx).button)
} }
fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> { 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>) { fn handle_codegen_changed(&mut self, _: Model<Codegen>, cx: &mut ViewContext<Self>) {
let is_read_only = !self.codegen.read(cx).idle(); let is_read_only = !self.codegen.read(cx).idle();
self.prompt_editor.update(cx, |editor, _cx| { self.prompt_editor.update(cx, |editor, cx| {
let was_read_only = editor.read_only(); let was_read_only = editor.read_only(cx);
if was_read_only != is_read_only { if was_read_only != is_read_only {
if is_read_only { if is_read_only {
editor.set_read_only(true); editor.set_read_only(true);
@ -3063,7 +3063,7 @@ impl InlineAssistant {
fn render_prompt_editor(&self, cx: &mut ViewContext<Self>) -> impl IntoElement { fn render_prompt_editor(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let settings = ThemeSettings::get_global(cx); let settings = ThemeSettings::get_global(cx);
let text_style = TextStyle { 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 cx.theme().colors().text_disabled
} else { } else {
cx.theme().colors().text cx.theme().colors().text

View File

@ -36,12 +36,14 @@ impl ParticipantLocation {
pub struct LocalParticipant { pub struct LocalParticipant {
pub projects: Vec<proto::ParticipantProject>, pub projects: Vec<proto::ParticipantProject>,
pub active_project: Option<WeakModel<Project>>, pub active_project: Option<WeakModel<Project>>,
pub role: proto::ChannelRole,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct RemoteParticipant { pub struct RemoteParticipant {
pub user: Arc<User>, pub user: Arc<User>,
pub peer_id: proto::PeerId, pub peer_id: proto::PeerId,
pub role: proto::ChannelRole,
pub projects: Vec<proto::ParticipantProject>, pub projects: Vec<proto::ParticipantProject>,
pub location: ParticipantLocation, pub location: ParticipantLocation,
pub participant_index: ParticipantIndex, pub participant_index: ParticipantIndex,

View File

@ -247,14 +247,18 @@ impl Room {
let response = client.request(proto::CreateRoom {}).await?; let response = client.request(proto::CreateRoom {}).await?;
let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?; let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?;
let room = cx.new_model(|cx| { let room = cx.new_model(|cx| {
Self::new( let mut room = Self::new(
room_proto.id, room_proto.id,
None, None,
response.live_kit_connection_info, response.live_kit_connection_info,
client, client,
user_store, user_store,
cx, 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 { let initial_project_id = if let Some(initial_project) = initial_project {
@ -606,6 +610,16 @@ impl Room {
.find(|p| p.peer_id == peer_id) .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>] { pub fn pending_participants(&self) -> &[Arc<User>] {
&self.pending_participants &self.pending_participants
} }
@ -710,7 +724,20 @@ impl Room {
this.participant_user_ids.clear(); this.participant_user_ids.clear();
if let Some(participant) = local_participant { if let Some(participant) = local_participant {
let role = participant.role();
this.local_participant.projects = participant.projects; 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 { } else {
this.local_participant.projects.clear(); this.local_participant.projects.clear();
} }
@ -766,6 +793,7 @@ impl Room {
}); });
} }
let role = participant.role();
let location = ParticipantLocation::from_proto(participant.location) let location = ParticipantLocation::from_proto(participant.location)
.unwrap_or(ParticipantLocation::External); .unwrap_or(ParticipantLocation::External);
if let Some(remote_participant) = if let Some(remote_participant) =
@ -774,8 +802,11 @@ impl Room {
remote_participant.peer_id = peer_id; remote_participant.peer_id = peer_id;
remote_participant.projects = participant.projects; remote_participant.projects = participant.projects;
remote_participant.participant_index = participant_index; 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.location = location;
remote_participant.role = role;
cx.emit(Event::ParticipantLocationChanged { cx.emit(Event::ParticipantLocationChanged {
participant_id: peer_id, participant_id: peer_id,
}); });
@ -789,6 +820,7 @@ impl Room {
peer_id, peer_id,
projects: participant.projects, projects: participant.projects,
location, location,
role,
muted: true, muted: true,
speaking: false, speaking: false,
video_tracks: Default::default(), video_tracks: Default::default(),
@ -1091,15 +1123,24 @@ impl Room {
) -> Task<Result<Model<Project>>> { ) -> Task<Result<Model<Project>>> {
let client = self.client.clone(); let client = self.client.clone();
let user_store = self.user_store.clone(); let user_store = self.user_store.clone();
let role = self.local_participant.role;
cx.emit(Event::RemoteProjectJoined { project_id: id }); cx.emit(Event::RemoteProjectJoined { project_id: id });
cx.spawn(move |this, mut cx| async move { cx.spawn(move |this, mut cx| async move {
let project = let project = Project::remote(
Project::remote(id, client, user_store, language_registry, fs, cx.clone()).await?; id,
client,
user_store,
language_registry,
fs,
role,
cx.clone(),
)
.await?;
this.update(&mut cx, |this, cx| { this.update(&mut cx, |this, cx| {
this.joined_projects.retain(|project| { this.joined_projects.retain(|project| {
if let Some(project) = project.upgrade() { if let Some(project) = project.upgrade() {
!project.read(cx).is_read_only() !project.read(cx).is_disconnected()
} else { } else {
false false
} }
@ -1224,6 +1265,11 @@ impl Room {
.unwrap_or(false) .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 { pub fn is_speaking(&self) -> bool {
self.live_kit self.live_kit
.as_ref() .as_ref()

View File

@ -62,7 +62,12 @@ impl ChannelBuffer {
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
let buffer = cx.new_model(|_| { 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))??; buffer.update(&mut cx, |buffer, cx| buffer.apply_ops(operations, cx))??;

View File

@ -11,6 +11,7 @@ use gpui::{
AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, SharedString, Task, AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, SharedString, Task,
WeakModel, WeakModel,
}; };
use language::Capability;
use rpc::{ use rpc::{
proto::{self, ChannelVisibility}, proto::{self, ChannelVisibility},
TypedEnvelope, TypedEnvelope,
@ -74,8 +75,12 @@ impl Channel {
slug.trim_matches(|c| c == '-').to_string() slug.trim_matches(|c| c == '-').to_string()
} }
pub fn can_edit_notes(&self) -> bool { pub fn channel_buffer_capability(&self) -> Capability {
self.role == proto::ChannelRole::Member || self.role == proto::ChannelRole::Admin if self.role == proto::ChannelRole::Member || self.role == proto::ChannelRole::Admin {
Capability::ReadWrite
} else {
Capability::ReadOnly
}
} }
} }

View File

@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathan@zed.dev>"]
default-run = "collab" default-run = "collab"
edition = "2021" edition = "2021"
name = "collab" name = "collab"
version = "0.33.0" version = "0.34.0"
publish = false publish = false
[[bin]] [[bin]]

View File

@ -13,6 +13,7 @@ metadata:
annotations: annotations:
service.beta.kubernetes.io/do-loadbalancer-tls-ports: "443" 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-certificate-id: ${ZED_DO_CERTIFICATE_ID}
service.beta.kubernetes.io/do-loadbalancer-disable-lets-encrypt-dns-records: "true"
spec: spec:
type: LoadBalancer type: LoadBalancer
selector: selector:

View File

@ -7,6 +7,7 @@ metadata:
annotations: annotations:
service.beta.kubernetes.io/do-loadbalancer-tls-ports: "443" 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-certificate-id: ${ZED_DO_CERTIFICATE_ID}
service.beta.kubernetes.io/do-loadbalancer-disable-lets-encrypt-dns-records: "true"
spec: spec:
type: LoadBalancer type: LoadBalancer
selector: selector:

View File

@ -161,7 +161,8 @@ CREATE TABLE "room_participants" (
"calling_user_id" INTEGER NOT NULL REFERENCES users (id), "calling_user_id" INTEGER NOT NULL REFERENCES users (id),
"calling_connection_id" INTEGER NOT NULL, "calling_connection_id" INTEGER NOT NULL,
"calling_connection_server_id" INTEGER REFERENCES servers (id) ON DELETE SET 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 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"); CREATE INDEX "index_room_participants_on_room_id" ON "room_participants" ("room_id");

View File

@ -0,0 +1 @@
ALTER TABLE room_participants ADD COLUMN role TEXT;

View File

@ -132,6 +132,14 @@ impl ChannelRole {
Admin | Member | Banned => false, 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 { impl From<proto::ChannelRole> for ChannelRole {

View File

@ -132,10 +132,7 @@ impl Database {
debug_assert!( debug_assert!(
self.channel_role_for_user(&channel, user_id, &*tx).await? == role self.channel_role_for_user(&channel, user_id, &*tx).await? == role
); );
} } else if channel.visibility == ChannelVisibility::Public {
}
if channel.visibility == ChannelVisibility::Public {
role = Some(ChannelRole::Guest); role = Some(ChannelRole::Guest);
let channel_to_join = self let channel_to_join = self
.public_ancestors_including_self(&channel, &*tx) .public_ancestors_including_self(&channel, &*tx)
@ -159,21 +156,25 @@ impl Database {
.await?, .await?,
); );
debug_assert!(self.channel_role_for_user(&channel, user_id, &*tx).await? == role); debug_assert!(
self.channel_role_for_user(&channel, user_id, &*tx).await? == role
);
}
} }
if role.is_none() || role == Some(ChannelRole::Banned) { if role.is_none() || role == Some(ChannelRole::Banned) {
Err(anyhow!("not allowed"))? Err(anyhow!("not allowed"))?
} }
let role = role.unwrap();
let live_kit_room = format!("channel-{}", nanoid::nanoid!(30)); let live_kit_room = format!("channel-{}", nanoid::nanoid!(30));
let room_id = self let room_id = self
.get_or_create_channel_room(channel_id, &live_kit_room, environment, &*tx) .get_or_create_channel_room(channel_id, &live_kit_room, environment, &*tx)
.await?; .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 .await
.map(|jr| (jr, accept_invite_result, role.unwrap())) .map(|jr| (jr, accept_invite_result, role))
}) })
.await .await
} }

View File

@ -46,6 +46,13 @@ impl Database {
if participant.room_id != room_id { if participant.room_id != room_id {
return Err(anyhow!("shared project on unexpected room"))?; 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 { let project = project::ActiveModel {
room_id: ActiveValue::set(participant.room_id), room_id: ActiveValue::set(participant.room_id),

View File

@ -131,7 +131,12 @@ impl Database {
connection.owner_id as i32, connection.owner_id as i32,
))), ))),
participant_index: ActiveValue::set(Some(0)), 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) .insert(&*tx)
.await?; .await?;
@ -151,6 +156,22 @@ impl Database {
initial_project_id: Option<ProjectId>, initial_project_id: Option<ProjectId>,
) -> Result<RoomGuard<(proto::Room, proto::IncomingCall)>> { ) -> Result<RoomGuard<(proto::Room, proto::IncomingCall)>> {
self.room_transaction(room_id, |tx| async move { 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_participant::ActiveModel {
room_id: ActiveValue::set(room_id), room_id: ActiveValue::set(room_id),
user_id: ActiveValue::set(called_user_id), user_id: ActiveValue::set(called_user_id),
@ -162,7 +183,13 @@ impl Database {
calling_connection.owner_id as i32, calling_connection.owner_id as i32,
))), ))),
initial_project_id: ActiveValue::set(initial_project_id), 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) .insert(&*tx)
.await?; .await?;
@ -384,6 +411,7 @@ impl Database {
room_id: RoomId, room_id: RoomId,
user_id: UserId, user_id: UserId,
connection: ConnectionId, connection: ConnectionId,
role: ChannelRole,
tx: &DatabaseTransaction, tx: &DatabaseTransaction,
) -> Result<JoinRoom> { ) -> Result<JoinRoom> {
let participant_index = self let participant_index = self
@ -404,7 +432,11 @@ impl Database {
connection.owner_id as i32, connection.owner_id as i32,
))), ))),
participant_index: ActiveValue::Set(Some(participant_index)), 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( .on_conflict(
OnConflict::columns([room_participant::Column::UserId]) OnConflict::columns([room_participant::Column::UserId])
@ -413,6 +445,7 @@ impl Database {
room_participant::Column::AnsweringConnectionServerId, room_participant::Column::AnsweringConnectionServerId,
room_participant::Column::AnsweringConnectionLost, room_participant::Column::AnsweringConnectionLost,
room_participant::Column::ParticipantIndex, room_participant::Column::ParticipantIndex,
room_participant::Column::Role,
]) ])
.to_owned(), .to_owned(),
) )
@ -1126,6 +1159,7 @@ impl Database {
projects: Default::default(), projects: Default::default(),
location: Some(proto::ParticipantLocation { variant: location }), location: Some(proto::ParticipantLocation { variant: location }),
participant_index: participant_index as u32, participant_index: participant_index as u32,
role: db_participant.role.unwrap_or(ChannelRole::Member).into(),
}, },
); );
} else { } else {

View File

@ -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 rpc::ConnectionId;
use sea_orm::entity::prelude::*; use sea_orm::entity::prelude::*;
@ -19,6 +19,7 @@ pub struct Model {
pub calling_connection_id: i32, pub calling_connection_id: i32,
pub calling_connection_server_id: Option<ServerId>, pub calling_connection_server_id: Option<ServerId>,
pub participant_index: Option<i32>, pub participant_index: Option<i32>,
pub role: Option<ChannelRole>,
} }
impl Model { impl Model {

View File

@ -2,6 +2,7 @@ use call::Room;
use gpui::{Model, TestAppContext}; use gpui::{Model, TestAppContext};
mod channel_buffer_tests; mod channel_buffer_tests;
mod channel_guest_tests;
mod channel_message_tests; mod channel_message_tests;
mod channel_tests; mod channel_tests;
mod editor_tests; mod editor_tests;

View File

@ -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()))
}

View File

@ -19,6 +19,7 @@ use project::{
search::SearchQuery, DiagnosticSummary, FormatTrigger, HoverBlockKind, Project, ProjectPath, search::SearchQuery, DiagnosticSummary, FormatTrigger, HoverBlockKind, Project, ProjectPath,
}; };
use rand::prelude::*; use rand::prelude::*;
use rpc::proto::ChannelRole;
use serde_json::json; use serde_json::json;
use settings::SettingsStore; use settings::SettingsStore;
use std::{ use std::{
@ -1380,7 +1381,7 @@ async fn test_unshare_project(
.unwrap(); .unwrap();
executor.run_until_parked(); 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. // Client C opens the project.
let project_c = client_c.build_remote_project(project_id, cx_c).await; 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!(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. // Client C can open the project again after client A re-shares.
let project_id = active_call_a 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_a.read_with(cx_a, |project, _| assert!(!project.is_shared()));
project_c2.read_with(cx_c, |project, _| { project_c2.read_with(cx_c, |project, _| {
assert!(project.is_read_only()); assert!(project.is_disconnected());
assert!(project.collaborators().is_empty()); assert!(project.collaborators().is_empty());
}); });
} }
@ -1551,7 +1552,7 @@ async fn test_project_reconnect(
}); });
project_b1.read_with(cx_b, |project, _| { project_b1.read_with(cx_b, |project, _| {
assert!(!project.is_read_only()); assert!(!project.is_disconnected());
assert_eq!(project.collaborators().len(), 1); assert_eq!(project.collaborators().len(), 1);
}); });
@ -1653,7 +1654,7 @@ async fn test_project_reconnect(
}); });
project_b1.read_with(cx_b, |project, cx| { project_b1.read_with(cx_b, |project, cx| {
assert!(!project.is_read_only()); assert!(!project.is_disconnected());
assert_eq!( assert_eq!(
project project
.worktree_for_id(worktree1_id, cx) .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")); 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(); executor.run_until_parked();
project_b1.read_with(cx_b, |project, cx| { project_b1.read_with(cx_b, |project, cx| {
assert!(!project.is_read_only()); assert!(!project.is_disconnected());
assert_eq!( assert_eq!(
project project
.worktree_for_id(worktree1_id, cx) .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")); 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, _| { project_b2.read_with(cx_b, |project, _| {
assert!(project.is_read_only()); assert!(project.is_disconnected());
}); });
project_c.read_with(cx_c, |project, _| { project_c.read_with(cx_c, |project, _| {
@ -3550,6 +3551,7 @@ async fn test_leaving_project(
client_b.user_store().clone(), client_b.user_store().clone(),
client_b.language_registry().clone(), client_b.language_registry().clone(),
FakeFs::new(cx.background_executor().clone()), FakeFs::new(cx.background_executor().clone()),
ChannelRole::Member,
cx, cx,
) )
}) })
@ -3568,11 +3570,11 @@ async fn test_leaving_project(
}); });
project_b2.read_with(cx_b, |project, _| { project_b2.read_with(cx_b, |project, _| {
assert!(project.is_read_only()); assert!(project.is_disconnected());
}); });
project_c.read_with(cx_c, |project, _| { project_c.read_with(cx_c, |project, _| {
assert!(project.is_read_only()); assert!(project.is_disconnected());
}); });
} }

View File

@ -1149,7 +1149,7 @@ impl RandomizedTest for ProjectCollaborationTest {
Some((project, cx)) Some((project, cx))
}); });
if !guest_project.is_read_only() { if !guest_project.is_disconnected() {
if let Some((host_project, host_cx)) = host_project { if let Some((host_project, host_cx)) = host_project {
let host_worktree_snapshots = let host_worktree_snapshots =
host_project.read_with(host_cx, |host_project, cx| { host_project.read_with(host_cx, |host_project, cx| {
@ -1236,7 +1236,7 @@ impl RandomizedTest for ProjectCollaborationTest {
let buffers = client.buffers().clone(); let buffers = client.buffers().clone();
for (guest_project, guest_buffers) in &buffers { for (guest_project, guest_buffers) in &buffers {
let project_id = if guest_project.read_with(client_cx, |project, _| { 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; continue;
} else { } else {

View File

@ -518,7 +518,7 @@ impl<T: RandomizedTest> TestPlan<T> {
for project in client.remote_projects().iter() { for project in client.remote_projects().iter() {
project.read_with(&client_cx, |project, _| { project.read_with(&client_cx, |project, _| {
assert!( assert!(
project.is_read_only(), project.is_disconnected(),
"project {:?} should be read only", "project {:?} should be read only",
project.remote_id() project.remote_id()
) )

View File

@ -138,12 +138,6 @@ impl ChannelView {
editor.set_collaboration_hub(Box::new(ChannelBufferCollaborationHub( editor.set_collaboration_hub(Box::new(ChannelBufferCollaborationHub(
channel_buffer.clone(), channel_buffer.clone(),
))); )));
editor.set_read_only(
!channel_buffer
.read(cx)
.channel(cx)
.is_some_and(|c| c.can_edit_notes()),
);
editor editor
}); });
let _editor_event_subscription = let _editor_event_subscription =
@ -178,8 +172,7 @@ impl ChannelView {
cx.notify(); cx.notify();
}), }),
ChannelBufferEvent::ChannelChanged => { ChannelBufferEvent::ChannelChanged => {
self.editor.update(cx, |editor, cx| { self.editor.update(cx, |_, cx| {
editor.set_read_only(!self.channel(cx).is_some_and(|c| c.can_edit_notes()));
cx.emit(editor::EditorEvent::TitleChanged); cx.emit(editor::EditorEvent::TitleChanged);
cx.notify() cx.notify()
}); });
@ -254,11 +247,11 @@ impl Item for ChannelView {
fn tab_content(&self, _: Option<usize>, selected: bool, cx: &WindowContext) -> AnyElement { fn tab_content(&self, _: Option<usize>, selected: bool, cx: &WindowContext) -> AnyElement {
let label = if let Some(channel) = self.channel(cx) { let label = if let Some(channel) = self.channel(cx) {
match ( match (
channel.can_edit_notes(), self.channel_buffer.read(cx).buffer().read(cx).read_only(),
self.channel_buffer.read(cx).is_connected(), self.channel_buffer.read(cx).is_connected(),
) { ) {
(true, true) => format!("#{}", channel.name), (false, true) => format!("#{}", channel.name),
(false, true) => format!("#{} (read-only)", channel.name), (true, true) => format!("#{} (read-only)", channel.name),
(_, false) => format!("#{} (disconnected)", channel.name), (_, false) => format!("#{} (disconnected)", channel.name),
} }
} else { } else {

View File

@ -607,8 +607,12 @@ impl Panel for ChatPanel {
"ChatPanel" "ChatPanel"
} }
fn icon(&self, _cx: &WindowContext) -> Option<ui::Icon> { fn icon(&self, cx: &WindowContext) -> Option<ui::Icon> {
Some(ui::Icon::MessageBubbles) 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> { fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> {

View File

@ -151,6 +151,10 @@ enum ListEntry {
peer_id: Option<PeerId>, peer_id: Option<PeerId>,
is_last: bool, is_last: bool,
}, },
GuestCount {
count: usize,
has_visible_participants: bool,
},
IncomingRequest(Arc<User>), IncomingRequest(Arc<User>),
OutgoingRequest(Arc<User>), OutgoingRequest(Arc<User>),
ChannelInvite(Arc<Channel>), ChannelInvite(Arc<Channel>),
@ -380,10 +384,14 @@ impl CollabPanel {
if !self.collapsed_sections.contains(&Section::ActiveCall) { if !self.collapsed_sections.contains(&Section::ActiveCall) {
let room = room.read(cx); 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() { if let Some(channel_id) = room.channel_id() {
self.entries.push(ListEntry::ChannelNotes { 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. // Populate the active user.
@ -402,7 +410,7 @@ impl CollabPanel {
&Default::default(), &Default::default(),
executor.clone(), executor.clone(),
)); ));
if !matches.is_empty() { if !matches.is_empty() && !room.read_only() {
let user_id = user.id; let user_id = user.id;
self.entries.push(ListEntry::CallParticipant { self.entries.push(ListEntry::CallParticipant {
user, user,
@ -430,13 +438,23 @@ impl CollabPanel {
// Populate remote participants. // Populate remote participants.
self.match_candidates.clear(); self.match_candidates.clear();
self.match_candidates self.match_candidates
.extend(room.remote_participants().iter().map(|(_, participant)| { .extend(
StringMatchCandidate { 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, id: participant.user.id as usize,
string: participant.user.github_login.clone(), string: participant.user.github_login.clone(),
char_bag: participant.user.github_login.chars().collect(), char_bag: participant.user.github_login.chars().collect(),
} })
})); }),
);
let matches = executor.block(match_strings( let matches = executor.block(match_strings(
&self.match_candidates, &self.match_candidates,
&query, &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. // Populate pending participants.
self.match_candidates.clear(); self.match_candidates.clear();
@ -959,6 +986,41 @@ impl CollabPanel {
.tooltip(move |cx| Tooltip::text("Open Chat", cx)) .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 { fn has_subchannels(&self, ix: usize) -> bool {
self.entries.get(ix).map_or(false, |entry| { self.entries.get(ix).map_or(false, |entry| {
if let ListEntry::Channel { has_children, .. } = 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, .. } => { ListEntry::Channel { channel, .. } => {
let is_active = maybe!({ let is_active = maybe!({
let call_channel = ActiveCall::global(cx) let call_channel = ActiveCall::global(cx)
@ -1735,6 +1809,12 @@ impl CollabPanel {
ListEntry::ParticipantScreen { peer_id, is_last } => self ListEntry::ParticipantScreen { peer_id, is_last } => self
.render_participant_screen(*peer_id, *is_last, is_selected, cx) .render_participant_screen(*peer_id, *is_last, is_selected, cx)
.into_any_element(), .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 ListEntry::ChannelNotes { channel_id } => self
.render_channel_notes(*channel_id, is_selected, cx) .render_channel_notes(*channel_id, is_selected, cx)
.into_any_element(), .into_any_element(),
@ -1766,7 +1846,7 @@ impl CollabPanel {
) -> impl IntoElement { ) -> impl IntoElement {
let settings = ThemeSettings::get_global(cx); let settings = ThemeSettings::get_global(cx);
let text_style = TextStyle { let text_style = TextStyle {
color: if editor.read(cx).read_only() { color: if editor.read(cx).read_only(cx) {
cx.theme().colors().text_disabled cx.theme().colors().text_disabled
} else { } else {
cx.theme().colors().text cx.theme().colors().text
@ -2538,6 +2618,11 @@ impl PartialEq for ListEntry {
return true; return true;
} }
} }
ListEntry::GuestCount { .. } => {
if let ListEntry::GuestCount { .. } = other {
return true;
}
}
} }
false false
} }

View File

@ -10,11 +10,12 @@ use gpui::{
}; };
use project::{Project, RepositoryEntry}; use project::{Project, RepositoryEntry};
use recent_projects::RecentProjects; use recent_projects::RecentProjects;
use rpc::proto;
use std::sync::Arc; use std::sync::Arc;
use theme::{ActiveTheme, PlayerColors}; use theme::{ActiveTheme, PlayerColors};
use ui::{ use ui::{
h_stack, popover_menu, prelude::*, Avatar, Button, ButtonLike, ButtonStyle, ContextMenu, Icon, h_stack, popover_menu, prelude::*, Avatar, Button, ButtonLike, ButtonStyle, ContextMenu, Icon,
IconButton, IconElement, Tooltip, IconButton, IconElement, TintColor, Tooltip,
}; };
use util::ResultExt; use util::ResultExt;
use vcs_menu::{build_branch_list, BranchList, OpenRecent as ToggleVcsMenu}; 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_muted = room.is_muted(cx);
let is_deafened = room.is_deafened().unwrap_or(false); let is_deafened = room.is_deafened().unwrap_or(false);
let is_screen_sharing = room.is_screen_sharing(); 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( this.child(
Button::new( Button::new(
"toggle_sharing", "toggle_sharing",
if is_shared { "Unshare" } else { "Share" }, if is_shared { "Unshare" } else { "Share" },
) )
.style(ButtonStyle::Subtle) .style(ButtonStyle::Subtle)
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
.selected(is_shared)
.label_size(LabelSize::Small) .label_size(LabelSize::Small)
.on_click(cx.listener( .on_click(cx.listener(
move |this, _, cx| { move |this, _, cx| {
@ -205,7 +209,8 @@ impl Render for CollabTitlebarItem {
.detach_and_log_err(cx); .detach_and_log_err(cx);
}), }),
) )
.child( .when(!read_only, |this| {
this.child(
IconButton::new( IconButton::new(
"mute-microphone", "mute-microphone",
if is_muted { if is_muted {
@ -217,8 +222,10 @@ impl Render for CollabTitlebarItem {
.style(ButtonStyle::Subtle) .style(ButtonStyle::Subtle)
.icon_size(IconSize::Small) .icon_size(IconSize::Small)
.selected(is_muted) .selected(is_muted)
.selected_style(ButtonStyle::Tinted(TintColor::Negative))
.on_click(move |_, cx| crate::toggle_mute(&Default::default(), cx)), .on_click(move |_, cx| crate::toggle_mute(&Default::default(), cx)),
) )
})
.child( .child(
IconButton::new( IconButton::new(
"mute-sound", "mute-sound",
@ -229,23 +236,36 @@ impl Render for CollabTitlebarItem {
}, },
) )
.style(ButtonStyle::Subtle) .style(ButtonStyle::Subtle)
.selected_style(ButtonStyle::Tinted(TintColor::Negative))
.icon_size(IconSize::Small) .icon_size(IconSize::Small)
.selected(is_deafened) .selected(is_deafened)
.tooltip(move |cx| { .tooltip(move |cx| {
Tooltip::with_meta("Deafen Audio", None, "Mic will be muted", cx) if !read_only {
}) Tooltip::with_meta(
.on_click(move |_, cx| crate::toggle_mute(&Default::default(), cx)), "Deafen Audio",
None,
"Mic will be muted",
cx,
) )
.child( } else {
Tooltip::text("Deafen Audio", cx)
}
})
.on_click(move |_, cx| crate::toggle_deafen(&Default::default(), cx)),
)
.when(!read_only, |this| {
this.child(
IconButton::new("screen-share", ui::Icon::Screen) IconButton::new("screen-share", ui::Icon::Screen)
.style(ButtonStyle::Subtle) .style(ButtonStyle::Subtle)
.icon_size(IconSize::Small) .icon_size(IconSize::Small)
.selected(is_screen_sharing) .selected(is_screen_sharing)
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
.on_click(move |_, cx| { .on_click(move |_, cx| {
crate::toggle_screen_sharing(&Default::default(), cx) crate::toggle_screen_sharing(&Default::default(), cx)
}), }),
) )
}) })
})
.map(|el| { .map(|el| {
let status = self.client.status(); let status = self.client.status();
let status = &*status.borrow(); let status = &*status.borrow();
@ -409,6 +429,10 @@ impl CollabTitlebarItem {
current_user: &Arc<User>, current_user: &Arc<User>,
cx: &ViewContext<Self>, cx: &ViewContext<Self>,
) -> Option<FacePile> { ) -> 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 followers = project_id.map_or(&[] as &[_], |id| room.followers_for(peer_id, id));
let pile = FacePile::default() let pile = FacePile::default()
@ -504,8 +528,7 @@ impl CollabTitlebarItem {
| client::Status::ReconnectionError { .. } => Some( | client::Status::ReconnectionError { .. } => Some(
div() div()
.id("disconnected") .id("disconnected")
.bg(gpui::red()) // todo!() @nate .child(IconElement::new(Icon::Disconnected).size(IconSize::Small))
.child(IconElement::new(Icon::Disconnected))
.tooltip(|cx| Tooltip::text("Disconnected", cx)) .tooltip(|cx| Tooltip::text("Disconnected", cx))
.into_any_element(), .into_any_element(),
), ),
@ -522,9 +545,9 @@ impl CollabTitlebarItem {
}; };
Some( Some(
div() Button::new("connection-status", label)
.bg(gpui::red()) // todo!() @nate .label_size(LabelSize::Small)
.child(Button::new("connection-status", label).on_click(|_, cx| { .on_click(|_, cx| {
if let Some(auto_updater) = auto_update::AutoUpdater::get(cx) { if let Some(auto_updater) = auto_update::AutoUpdater::get(cx) {
if auto_updater.read(cx).status() == AutoUpdateStatus::Updated { if auto_updater.read(cx).status() == AutoUpdateStatus::Updated {
workspace::restart(&Default::default(), cx); workspace::restart(&Default::default(), cx);
@ -532,7 +555,7 @@ impl CollabTitlebarItem {
} }
} }
auto_update::check(&Default::default(), cx); auto_update::check(&Default::default(), cx);
})) })
.into_any_element(), .into_any_element(),
) )
} }
@ -542,7 +565,9 @@ impl CollabTitlebarItem {
pub fn render_sign_in_button(&mut self, _: &mut ViewContext<Self>) -> Button { pub fn render_sign_in_button(&mut self, _: &mut ViewContext<Self>) -> Button {
let client = self.client.clone(); let client = self.client.clone();
Button::new("sign_in", "Sign in").on_click(move |_, cx| { Button::new("sign_in", "Sign in")
.label_size(LabelSize::Small)
.on_click(move |_, cx| {
let client = client.clone(); let client = client.clone();
cx.spawn(move |mut cx| async move { cx.spawn(move |mut cx| async move {
client client

View File

@ -151,7 +151,12 @@ impl ProjectDiagnosticsEditor {
let focus_in_subscription = let focus_in_subscription =
cx.on_focus_in(&focus_handle, |diagnostics, cx| diagnostics.focus_in(cx)); 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 editor = cx.new_view(|cx| {
let mut editor = let mut editor =
Editor::for_multibuffer(excerpts.clone(), Some(project_handle.clone()), cx); Editor::for_multibuffer(excerpts.clone(), Some(project_handle.clone()), cx);
@ -1572,6 +1577,7 @@ mod tests {
workspace::init_settings(cx); workspace::init_settings(cx);
Project::init_settings(cx); Project::init_settings(cx);
crate::init(cx); crate::init(cx);
editor::init(cx);
}); });
} }

View File

@ -54,10 +54,10 @@ use itertools::Itertools;
pub use language::{char_kind, CharKind}; pub use language::{char_kind, CharKind};
use language::{ use language::{
language_settings::{self, all_language_settings, InlayHintSettings}, language_settings::{self, all_language_settings, InlayHintSettings},
markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CodeAction,
Completion, CursorShape, Diagnostic, Documentation, IndentKind, IndentSize, Language, CodeLabel, Completion, CursorShape, Diagnostic, Documentation, IndentKind, IndentSize,
LanguageRegistry, LanguageServerName, OffsetRangeExt, Point, Selection, SelectionGoal, Language, LanguageRegistry, LanguageServerName, OffsetRangeExt, Point, Selection,
TransactionId, SelectionGoal, TransactionId,
}; };
use link_go_to_definition::{GoToDefinitionLink, InlayHighlight, LinkGoToDefinitionState}; use link_go_to_definition::{GoToDefinitionLink, InlayHighlight, LinkGoToDefinitionState};
@ -2049,8 +2049,8 @@ impl Editor {
} }
} }
pub fn read_only(&self) -> bool { pub fn read_only(&self, cx: &AppContext) -> bool {
self.read_only self.read_only || self.buffer.read(cx).read_only()
} }
pub fn set_read_only(&mut self, read_only: bool) { pub fn set_read_only(&mut self, read_only: bool) {
@ -2199,7 +2199,7 @@ impl Editor {
S: ToOffset, S: ToOffset,
T: Into<Arc<str>>, T: Into<Arc<str>>,
{ {
if self.read_only { if self.read_only(cx) {
return; return;
} }
@ -2213,7 +2213,7 @@ impl Editor {
S: ToOffset, S: ToOffset,
T: Into<Arc<str>>, T: Into<Arc<str>>,
{ {
if self.read_only { if self.read_only(cx) {
return; return;
} }
@ -2232,7 +2232,7 @@ impl Editor {
S: ToOffset, S: ToOffset,
T: Into<Arc<str>>, T: Into<Arc<str>>,
{ {
if self.read_only { if self.read_only(cx) {
return; return;
} }
@ -2596,7 +2596,7 @@ impl Editor {
pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) { pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
let text: Arc<str> = text.into(); let text: Arc<str> = text.into();
if self.read_only { if self.read_only(cx) {
return; return;
} }
@ -3049,7 +3049,7 @@ impl Editor {
autoindent_mode: Option<AutoindentMode>, autoindent_mode: Option<AutoindentMode>,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) { ) {
if self.read_only { if self.read_only(cx) {
return; return;
} }
@ -3786,7 +3786,8 @@ impl Editor {
let mut ranges_to_highlight = Vec::new(); let mut ranges_to_highlight = Vec::new();
let excerpt_buffer = cx.new_model(|cx| { 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 { for (buffer_handle, transaction) in &entries {
let buffer = buffer_handle.read(cx); let buffer = buffer_handle.read(cx);
ranges_to_highlight.extend( ranges_to_highlight.extend(
@ -7491,9 +7492,10 @@ impl Editor {
locations.sort_by_key(|location| location.buffer.read(cx).remote_id()); locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
let mut locations = locations.into_iter().peekable(); let mut locations = locations.into_iter().peekable();
let mut ranges_to_highlight = Vec::new(); let mut ranges_to_highlight = Vec::new();
let capability = workspace.project().read(cx).capability();
let excerpt_buffer = cx.new_model(|cx| { 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() { while let Some(location) = locations.next() {
let buffer = location.buffer.read(cx); let buffer = location.buffer.read(cx);
let mut ranges_for_buffer = Vec::new(); let mut ranges_for_buffer = Vec::new();
@ -8608,7 +8610,8 @@ impl Editor {
} }
pub fn show_local_cursors(&self, cx: &WindowContext) -> bool { 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>) { fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {

View File

@ -17,8 +17,9 @@ use gpui::{
use indoc::indoc; use indoc::indoc;
use language::{ use language::{
language_settings::{AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent}, language_settings::{AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent},
BracketPairConfig, FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageRegistry, BracketPairConfig,
Override, Point, Capability::ReadWrite,
FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageRegistry, Override, Point,
}; };
use parking_lot::Mutex; use parking_lot::Mutex;
use project::project_settings::{LspSettings, ProjectSettings}; use project::project_settings::{LspSettings, ProjectSettings};
@ -2355,7 +2356,7 @@ fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
.with_language(rust_language, cx) .with_language(rust_language, cx)
}); });
let multibuffer = cx.new_model(|cx| { let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0); let mut multibuffer = MultiBuffer::new(0, ReadWrite);
multibuffer.push_excerpts( multibuffer.push_excerpts(
toml_buffer.clone(), toml_buffer.clone(),
[ExcerptRange { [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 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 multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0); let mut multibuffer = MultiBuffer::new(0, ReadWrite);
multibuffer.push_excerpts( multibuffer.push_excerpts(
buffer.clone(), 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 buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), initial_text));
let multibuffer = cx.new_model(|cx| { 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.push_excerpts(buffer, excerpt_ranges, cx);
multibuffer 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 buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(3, 4, 'a')));
let mut excerpt1_id = None; let mut excerpt1_id = None;
let multibuffer = cx.new_model(|cx| { let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0); let mut multibuffer = MultiBuffer::new(0, ReadWrite);
excerpt1_id = multibuffer excerpt1_id = multibuffer
.push_excerpts( .push_excerpts(
buffer.clone(), 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 buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(3, 4, 'a')));
let mut excerpt1_id = None; let mut excerpt1_id = None;
let multibuffer = cx.new_model(|cx| { let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0); let mut multibuffer = MultiBuffer::new(0, ReadWrite);
excerpt1_id = multibuffer excerpt1_id = multibuffer
.push_excerpts( .push_excerpts(
buffer.clone(), 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 cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
let leader = pane.update(cx, |_, 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)) 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_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 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 multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0); let mut multibuffer = MultiBuffer::new(0, ReadWrite);
multibuffer.push_excerpts( multibuffer.push_excerpts(
buffer_1.clone(), buffer_1.clone(),
[ExcerptRange { [ExcerptRange {
@ -7552,7 +7553,7 @@ async fn test_copilot_disabled_globs(executor: BackgroundExecutor, cx: &mut gpui
.unwrap(); .unwrap();
let multibuffer = cx.new_model(|cx| { let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0); let mut multibuffer = MultiBuffer::new(0, ReadWrite);
multibuffer.push_excerpts( multibuffer.push_excerpts(
private_buffer.clone(), private_buffer.clone(),
[ExcerptRange { [ExcerptRange {

View File

@ -1910,7 +1910,13 @@ impl EditorElement {
layouts.push(layout); 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 { if let Some(collaboration_hub) = &editor.collaboration_hub {

View File

@ -93,6 +93,7 @@ mod tests {
use crate::editor_tests::init_test; use crate::editor_tests::init_test;
use crate::Point; use crate::Point;
use gpui::{Context, TestAppContext}; use gpui::{Context, TestAppContext};
use language::Capability::ReadWrite;
use multi_buffer::{ExcerptRange, MultiBuffer}; use multi_buffer::{ExcerptRange, MultiBuffer};
use project::{FakeFs, Project}; use project::{FakeFs, Project};
use unindent::Unindent; use unindent::Unindent;
@ -183,7 +184,7 @@ mod tests {
cx.background_executor.run_until_parked(); cx.background_executor.run_until_parked();
let multibuffer = cx.new_model(|cx| { let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0); let mut multibuffer = MultiBuffer::new(0, ReadWrite);
multibuffer.push_excerpts( multibuffer.push_excerpts(
buffer_1.clone(), buffer_1.clone(),
[ [

View File

@ -515,34 +515,28 @@ impl DiagnosticPopover {
}; };
struct DiagnosticColors { struct DiagnosticColors {
pub text: Hsla,
pub background: Hsla, pub background: Hsla,
pub border: Hsla, pub border: Hsla,
} }
let diagnostic_colors = match self.local_diagnostic.diagnostic.severity { let diagnostic_colors = match self.local_diagnostic.diagnostic.severity {
DiagnosticSeverity::ERROR => DiagnosticColors { DiagnosticSeverity::ERROR => DiagnosticColors {
text: style.status.error,
background: style.status.error_background, background: style.status.error_background,
border: style.status.error_border, border: style.status.error_border,
}, },
DiagnosticSeverity::WARNING => DiagnosticColors { DiagnosticSeverity::WARNING => DiagnosticColors {
text: style.status.warning,
background: style.status.warning_background, background: style.status.warning_background,
border: style.status.warning_border, border: style.status.warning_border,
}, },
DiagnosticSeverity::INFORMATION => DiagnosticColors { DiagnosticSeverity::INFORMATION => DiagnosticColors {
text: style.status.info,
background: style.status.info_background, background: style.status.info_background,
border: style.status.info_border, border: style.status.info_border,
}, },
DiagnosticSeverity::HINT => DiagnosticColors { DiagnosticSeverity::HINT => DiagnosticColors {
text: style.status.hint,
background: style.status.hint_background, background: style.status.hint_background,
border: style.status.hint_border, border: style.status.hint_border,
}, },
_ => DiagnosticColors { _ => DiagnosticColors {
text: style.status.ignored,
background: style.status.ignored_background, background: style.status.ignored_background,
border: style.status.ignored_border, border: style.status.ignored_border,
}, },
@ -554,7 +548,7 @@ impl DiagnosticPopover {
.px_2() .px_2()
.py_1() .py_1()
.bg(diagnostic_colors.background) .bg(diagnostic_colors.background)
.text_color(diagnostic_colors.text) .text_color(style.text.color)
.border_1() .border_1()
.border_color(diagnostic_colors.border) .border_color(diagnostic_colors.border)
.rounded_md() .rounded_md()

View File

@ -1206,7 +1206,8 @@ pub mod tests {
use gpui::{Context, TestAppContext, WindowHandle}; use gpui::{Context, TestAppContext, WindowHandle};
use itertools::Itertools; use itertools::Itertools;
use language::{ use language::{
language_settings::AllLanguageSettingsContent, FakeLspAdapter, Language, LanguageConfig, language_settings::AllLanguageSettingsContent, Capability, FakeLspAdapter, Language,
LanguageConfig,
}; };
use lsp::FakeLanguageServer; use lsp::FakeLanguageServer;
use parking_lot::Mutex; use parking_lot::Mutex;
@ -2459,7 +2460,7 @@ pub mod tests {
.await .await
.unwrap(); .unwrap();
let multibuffer = cx.new_model(|cx| { let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0); let mut multibuffer = MultiBuffer::new(0, Capability::ReadWrite);
multibuffer.push_excerpts( multibuffer.push_excerpts(
buffer_1.clone(), buffer_1.clone(),
[ [
@ -2798,7 +2799,7 @@ pub mod tests {
}) })
.await .await
.unwrap(); .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, buffer_2_excerpts) = multibuffer.update(cx, |multibuffer, cx| {
let buffer_1_excerpts = multibuffer.push_excerpts( let buffer_1_excerpts = multibuffer.push_excerpts(
buffer_1.clone(), buffer_1.clone(),

View File

@ -103,7 +103,8 @@ impl FollowableItem for Editor {
if state.singleton && buffers.len() == 1 { if state.singleton && buffers.len() == 1 {
multibuffer = MultiBuffer::singleton(buffers.pop().unwrap(), cx) multibuffer = MultiBuffer::singleton(buffers.pop().unwrap(), cx)
} else { } else {
multibuffer = MultiBuffer::new(replica_id); multibuffer =
MultiBuffer::new(replica_id, project.read(cx).capability());
let mut excerpts = state.excerpts.into_iter().peekable(); let mut excerpts = state.excerpts.into_iter().peekable();
while let Some(excerpt) = excerpts.peek() { while let Some(excerpt) = excerpts.peek() {
let buffer_id = excerpt.buffer_id; let buffer_id = excerpt.buffer_id;

View File

@ -930,10 +930,7 @@ mod tests {
fn do_work() { «test»(); } fn do_work() { «test»(); }
"}); "});
// Deactivating the window dismisses the highlight cx.cx.cx.deactivate_window();
cx.update_workspace(|workspace, cx| {
workspace.on_window_activation_changed(cx);
});
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {" cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
fn test() { do_work(); } fn test() { do_work(); }
fn do_work() { test(); } fn do_work() { test(); }

View File

@ -461,6 +461,7 @@ mod tests {
Buffer, DisplayMap, ExcerptRange, InlayId, MultiBuffer, Buffer, DisplayMap, ExcerptRange, InlayId, MultiBuffer,
}; };
use gpui::{font, Context as _}; use gpui::{font, Context as _};
use language::Capability;
use project::Project; use project::Project;
use settings::SettingsStore; use settings::SettingsStore;
use util::post_inc; use util::post_inc;
@ -766,7 +767,7 @@ mod tests {
let buffer = let buffer =
cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abc\ndefg\nhijkl\nmn")); cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abc\ndefg\nhijkl\nmn"));
let multibuffer = cx.new_model(|cx| { let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0); let mut multibuffer = MultiBuffer::new(0, Capability::ReadWrite);
multibuffer.push_excerpts( multibuffer.push_excerpts(
buffer.clone(), buffer.clone(),
[ [

View File

@ -17,7 +17,7 @@ use regex::Regex;
use serde_derive::Serialize; use serde_derive::Serialize;
use ui::{prelude::*, Button, ButtonStyle, IconPosition, Tooltip}; use ui::{prelude::*, Button, ButtonStyle, IconPosition, Tooltip};
use util::ResultExt; use util::ResultExt;
use workspace::{ModalView, Workspace}; use workspace::{ModalView, Toast, Workspace};
use crate::{system_specs::SystemSpecs, GiveFeedback, OpenZedCommunityRepo}; use crate::{system_specs::SystemSpecs, GiveFeedback, OpenZedCommunityRepo};
@ -125,6 +125,20 @@ impl FeedbackModal {
.language_for_name("Markdown"); .language_for_name("Markdown");
let project = workspace.project().clone(); 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 { cx.spawn(|workspace, mut cx| async move {
let markdown = markdown.await.log_err(); let markdown = markdown.await.log_err();

View File

@ -18,7 +18,6 @@ collections = { path = "../collections" }
gpui_macros = { path = "../gpui_macros" } gpui_macros = { path = "../gpui_macros" }
util = { path = "../util" } util = { path = "../util" }
sum_tree = { path = "../sum_tree" } sum_tree = { path = "../sum_tree" }
sqlez = { path = "../sqlez" }
async-task = "4.7" async-task = "4.7"
backtrace = { version = "0.3", optional = true } backtrace = { version = "0.3", optional = true }
ctor.workspace = true ctor.workspace = true

View File

@ -1,14 +1,13 @@
use crate::{ use crate::{
div, Action, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext, div, Action, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext,
BackgroundExecutor, Bounds, ClipboardItem, Context, Entity, EventEmitter, ForegroundExecutor, BackgroundExecutor, ClipboardItem, Context, Entity, EventEmitter, ForegroundExecutor,
InputEvent, IntoElement, KeyDownEvent, Keystroke, Model, ModelContext, Pixels, Platform, IntoElement, Keystroke, Model, ModelContext, Pixels, Platform, Render, Result, Size, Task,
PlatformWindow, Point, Render, Result, Size, Task, TestDispatcher, TestPlatform, TestWindow, TestDispatcher, TestPlatform, TestWindow, TextSystem, View, ViewContext, VisualContext,
TestWindowHandlers, TextSystem, View, ViewContext, VisualContext, WindowBounds, WindowContext, WindowContext, WindowHandle, WindowOptions,
WindowHandle, WindowOptions,
}; };
use anyhow::{anyhow, bail}; use anyhow::{anyhow, bail};
use futures::{Stream, StreamExt}; 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)] #[derive(Clone)]
pub struct TestAppContext { pub struct TestAppContext {
@ -185,42 +184,7 @@ impl TestAppContext {
} }
pub fn simulate_window_resize(&self, window_handle: AnyWindowHandle, size: Size<Pixels>) { pub fn simulate_window_resize(&self, window_handle: AnyWindowHandle, size: Size<Pixels>) {
let (mut handlers, scale_factor) = self self.test_window(window_handle).simulate_resize(size);
.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();
} }
pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut) -> Task<R> pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut) -> Task<R>
@ -313,41 +277,22 @@ impl TestAppContext {
keystroke: Keystroke, keystroke: Keystroke,
is_held: bool, is_held: bool,
) { ) {
let keystroke2 = keystroke.clone(); self.test_window(window)
let handled = window .simulate_keystroke(keystroke, is_held)
.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()); pub fn test_window(&self, window: AnyWindowHandle) -> TestWindow {
let Some(input_handler) = input_handler else { self.app
panic!( .borrow_mut()
"dispatch_keystroke {:?} failed to dispatch action or input", .windows
&keystroke2 .get_mut(window.id)
);
};
let text = keystroke2.ime_key.unwrap_or(keystroke2.key);
input_handler.lock().replace_text_in_range(None, &text);
}
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())
})
.unwrap() .unwrap()
.as_mut()
.unwrap()
.platform_window
.as_test()
.unwrap()
.clone()
} }
pub fn notifications<T: 'static>(&mut self, entity: &impl Entity<T>) -> impl Stream<Item = ()> { pub fn notifications<T: 'static>(&mut self, entity: &impl Entity<T>) -> impl Stream<Item = ()> {
@ -563,11 +508,7 @@ impl<'a> VisualTestContext<'a> {
} }
pub fn window_title(&mut self) -> Option<String> { pub fn window_title(&mut self) -> Option<String> {
self.cx self.cx.test_window(self.window).0.lock().title.clone()
.update_window(self.window, |_, cx| {
cx.window.platform_window.as_test().unwrap().title.clone()
})
.unwrap()
} }
pub fn simulate_keystrokes(&mut self, keystrokes: &str) { pub fn simulate_keystrokes(&mut self, keystrokes: &str) {
@ -578,37 +519,11 @@ impl<'a> VisualTestContext<'a> {
self.cx.simulate_input(self.window, input) self.cx.simulate_input(self.window, input)
} }
pub fn simulate_activation(&mut self) { pub fn deactivate_window(&mut self) {
self.simulate_window_events(&mut |handlers| { if Some(self.window) == self.test_platform.active_window() {
handlers self.test_platform.set_active_window(None)
.active_status_change
.iter_mut()
.for_each(|f| f(true));
})
} }
self.background_executor.run_until_parked();
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());
} }
} }

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. /// 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. /// 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) { pub fn fade_out(&mut self, factor: f32) {

View File

@ -44,8 +44,9 @@ pub trait IntoElement: Sized {
} }
/// Convert into an element, then draw in the current window at the given origin. /// 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. /// The available space argument is provided to the layout engine to determine the size of the
/// Once the element is drawn, its associated element staet is yielded to the given callback. // root element. Once the element is drawn, its associated element state is yielded to the
// given callback.
fn draw_and_update_state<T, R>( fn draw_and_update_state<T, R>(
self, self,
origin: Point<Pixels>, origin: Point<Pixels>,

View File

@ -31,39 +31,6 @@ pub trait Along {
fn apply_along(&self, axis: Axis, f: impl FnOnce(Self::Unit) -> Self::Unit) -> Self; 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. /// Describes a location in a 2D cartesian coordinate space.
/// ///
/// It holds two public fields, `x` and `y`, which represent the coordinates in the 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]. /// 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. /// Rems are used for defining lengths that are scalable and consistent across different UI elements.

View File

@ -6,19 +6,17 @@ mod mac;
mod test; mod test;
use crate::{ use crate::{
point, size, Action, AnyWindowHandle, BackgroundExecutor, Bounds, DevicePixels, Font, FontId, Action, AnyWindowHandle, BackgroundExecutor, Bounds, DevicePixels, Font, FontId, FontMetrics,
FontMetrics, FontRun, ForegroundExecutor, GlobalPixels, GlyphId, InputEvent, Keymap, FontRun, ForegroundExecutor, GlobalPixels, GlyphId, InputEvent, Keymap, LineLayout, Pixels,
LineLayout, Pixels, Point, RenderGlyphParams, RenderImageParams, RenderSvgParams, Result, Point, RenderGlyphParams, RenderImageParams, RenderSvgParams, Result, Scene, SharedString,
Scene, SharedString, Size, TaskLabel, Size, TaskLabel,
}; };
use anyhow::{anyhow, bail}; use anyhow::anyhow;
use async_task::Runnable; use async_task::Runnable;
use futures::channel::oneshot; use futures::channel::oneshot;
use parking::Unparker; use parking::Unparker;
use seahash::SeaHasher; use seahash::SeaHasher;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlez::bindable::{Bind, Column, StaticColumnCount};
use sqlez::statement::Statement;
use std::borrow::Cow; use std::borrow::Cow;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::time::Duration; use std::time::Duration;
@ -396,67 +394,6 @@ pub enum WindowBounds {
Fixed(Bounds<GlobalPixels>), 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)] #[derive(Copy, Clone, Debug)]
pub enum WindowAppearance { pub enum WindowAppearance {
Light, Light,

View File

@ -19,7 +19,7 @@ pub struct TestPlatform {
background_executor: BackgroundExecutor, background_executor: BackgroundExecutor,
foreground_executor: ForegroundExecutor, foreground_executor: ForegroundExecutor,
active_window: Arc<Mutex<Option<AnyWindowHandle>>>, pub(crate) active_window: RefCell<Option<TestWindow>>,
active_display: Rc<dyn PlatformDisplay>, active_display: Rc<dyn PlatformDisplay>,
active_cursor: Mutex<CursorStyle>, active_cursor: Mutex<CursorStyle>,
current_clipboard_item: Mutex<Option<ClipboardItem>>, current_clipboard_item: Mutex<Option<ClipboardItem>>,
@ -79,6 +79,28 @@ impl TestPlatform {
self.prompts.borrow_mut().multiple_choice.push_back(tx); self.prompts.borrow_mut().multiple_choice.push_back(tx);
rx 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") // todo!("implement out what our tests needed in GPUI 1")
@ -106,7 +128,7 @@ impl Platform for TestPlatform {
} }
fn activate(&self, _ignoring_other_apps: bool) { fn activate(&self, _ignoring_other_apps: bool) {
unimplemented!() //
} }
fn hide(&self) { fn hide(&self) {
@ -130,7 +152,10 @@ impl Platform for TestPlatform {
} }
fn active_window(&self) -> Option<crate::AnyWindowHandle> { 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( fn open_window(
@ -139,12 +164,13 @@ impl Platform for TestPlatform {
options: WindowOptions, options: WindowOptions,
_draw: Box<dyn FnMut() -> Result<Scene>>, _draw: Box<dyn FnMut() -> Result<Scene>>,
) -> Box<dyn crate::PlatformWindow> { ) -> Box<dyn crate::PlatformWindow> {
*self.active_window.lock() = Some(handle); let window = TestWindow::new(
Box::new(TestWindow::new(
options, options,
handle,
self.weak.clone(), self.weak.clone(),
self.active_display.clone(), self.active_display.clone(),
)) );
Box::new(window)
} }
fn set_display_link_output_callback( fn set_display_link_output_callback(

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
px, AtlasKey, AtlasTextureId, AtlasTile, Pixels, PlatformAtlas, PlatformDisplay, px, AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, Bounds, InputEvent, KeyDownEvent,
PlatformInputHandler, PlatformWindow, Point, Size, TestPlatform, TileId, WindowAppearance, Keystroke, Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point,
WindowBounds, WindowOptions, Size, TestPlatform, TileId, WindowAppearance, WindowBounds, WindowOptions,
}; };
use collections::HashMap; use collections::HashMap;
use parking_lot::Mutex; use parking_lot::Mutex;
@ -10,51 +10,122 @@ use std::{
sync::{self, Arc}, sync::{self, Arc},
}; };
#[derive(Default)] pub struct TestWindowState {
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(crate) bounds: WindowBounds, pub(crate) bounds: WindowBounds,
pub(crate) handle: AnyWindowHandle,
display: Rc<dyn PlatformDisplay>, display: Rc<dyn PlatformDisplay>,
pub(crate) title: Option<String>, pub(crate) title: Option<String>,
pub(crate) edited: bool, pub(crate) edited: bool,
pub(crate) input_handler: Option<Arc<Mutex<Box<dyn PlatformInputHandler>>>>,
pub(crate) handlers: Arc<Mutex<TestWindowHandlers>>,
platform: Weak<TestPlatform>, platform: Weak<TestPlatform>,
sprite_atlas: Arc<dyn PlatformAtlas>, 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 { impl TestWindow {
pub fn new( pub fn new(
options: WindowOptions, options: WindowOptions,
handle: AnyWindowHandle,
platform: Weak<TestPlatform>, platform: Weak<TestPlatform>,
display: Rc<dyn PlatformDisplay>, display: Rc<dyn PlatformDisplay>,
) -> Self { ) -> Self {
Self { Self(Arc::new(Mutex::new(TestWindowState {
bounds: options.bounds, bounds: options.bounds,
display, display,
platform, platform,
input_handler: None, handle,
sprite_atlas: Arc::new(TestAtlas::new()), sprite_atlas: Arc::new(TestAtlas::new()),
handlers: Default::default(),
title: Default::default(), title: Default::default(),
edited: false, 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 { impl PlatformWindow for TestWindow {
fn bounds(&self) -> WindowBounds { fn bounds(&self) -> WindowBounds {
self.bounds self.0.lock().bounds
} }
fn content_size(&self) -> Size<Pixels> { fn content_size(&self) -> Size<Pixels> {
let bounds = match self.bounds { let bounds = match self.bounds() {
WindowBounds::Fixed(bounds) => bounds, WindowBounds::Fixed(bounds) => bounds,
WindowBounds::Maximized | WindowBounds::Fullscreen => self.display().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> { fn display(&self) -> std::rc::Rc<dyn crate::PlatformDisplay> {
self.display.clone() self.0.lock().display.clone()
} }
fn mouse_position(&self) -> Point<Pixels> { 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>) { 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) { fn clear_input_handler(&mut self) {
self.input_handler = None; self.0.lock().input_handler = None;
} }
fn prompt( fn prompt(
@ -103,19 +174,29 @@ impl PlatformWindow for TestWindow {
_msg: &str, _msg: &str,
_answers: &[&str], _answers: &[&str],
) -> futures::channel::oneshot::Receiver<usize> { ) -> futures::channel::oneshot::Receiver<usize> {
self.platform.upgrade().expect("platform dropped").prompt() self.0
.lock()
.platform
.upgrade()
.expect("platform dropped")
.prompt()
} }
fn activate(&self) { fn activate(&self) {
unimplemented!() self.0
.lock()
.platform
.upgrade()
.unwrap()
.set_active_window(Some(self.clone()))
} }
fn set_title(&mut self, title: &str) { 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) { fn set_edited(&mut self, edited: bool) {
self.edited = edited; self.0.lock().edited = edited;
} }
fn show_character_palette(&self) { fn show_character_palette(&self) {
@ -135,15 +216,15 @@ impl PlatformWindow for TestWindow {
} }
fn on_input(&self, callback: Box<dyn FnMut(crate::InputEvent) -> bool>) { 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)>) { 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)>) { 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)>) { 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()>) { 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>) { 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> { 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> { fn as_test(&mut self) -> Option<&mut TestWindow> {

View File

@ -57,6 +57,12 @@ lazy_static! {
pub static ref BUFFER_DIFF_TASK: TaskLabel = TaskLabel::new(); pub static ref BUFFER_DIFF_TASK: TaskLabel = TaskLabel::new();
} }
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum Capability {
ReadWrite,
ReadOnly,
}
pub struct Buffer { pub struct Buffer {
text: TextBuffer, text: TextBuffer,
diff_base: Option<String>, diff_base: Option<String>,
@ -90,6 +96,7 @@ pub struct Buffer {
completion_triggers: Vec<String>, completion_triggers: Vec<String>,
completion_triggers_timestamp: clock::Lamport, completion_triggers_timestamp: clock::Lamport,
deferred_ops: OperationQueue<Operation>, deferred_ops: OperationQueue<Operation>,
capability: Capability,
} }
pub struct BufferSnapshot { pub struct BufferSnapshot {
@ -405,19 +412,27 @@ impl Buffer {
TextBuffer::new(replica_id, id, base_text.into()), TextBuffer::new(replica_id, id, base_text.into()),
None, None,
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( Self::build(
TextBuffer::new(replica_id, remote_id, base_text), TextBuffer::new(replica_id, remote_id, base_text),
None, None,
None, None,
capability,
) )
} }
pub fn from_proto( pub fn from_proto(
replica_id: ReplicaId, replica_id: ReplicaId,
capability: Capability,
message: proto::BufferState, message: proto::BufferState,
file: Option<Arc<dyn File>>, file: Option<Arc<dyn File>>,
) -> Result<Self> { ) -> Result<Self> {
@ -426,6 +441,7 @@ impl Buffer {
buffer, buffer,
message.diff_base.map(|text| text.into_boxed_str().into()), message.diff_base.map(|text| text.into_boxed_str().into()),
file, file,
capability,
); );
this.text.set_line_ending(proto::deserialize_line_ending( this.text.set_line_ending(proto::deserialize_line_ending(
rpc::proto::LineEnding::from_i32(message.line_ending) rpc::proto::LineEnding::from_i32(message.line_ending)
@ -504,10 +520,19 @@ impl Buffer {
self self
} }
pub fn capability(&self) -> Capability {
self.capability
}
pub fn read_only(&self) -> bool {
self.capability == Capability::ReadOnly
}
pub fn build( pub fn build(
buffer: TextBuffer, buffer: TextBuffer,
diff_base: Option<String>, diff_base: Option<String>,
file: Option<Arc<dyn File>>, file: Option<Arc<dyn File>>,
capability: Capability,
) -> Self { ) -> Self {
let saved_mtime = if let Some(file) = file.as_ref() { let saved_mtime = if let Some(file) = file.as_ref() {
file.mtime() file.mtime()
@ -526,6 +551,7 @@ impl Buffer {
diff_base, diff_base,
git_diff: git::diff::BufferDiff::new(), git_diff: git::diff::BufferDiff::new(),
file, file,
capability,
syntax_map: Mutex::new(SyntaxMap::new()), syntax_map: Mutex::new(SyntaxMap::new()),
parsing_in_background: false, parsing_in_background: false,
parse_count: 0, parse_count: 0,

View File

@ -1926,7 +1926,7 @@ fn test_serialization(cx: &mut gpui::AppContext) {
.background_executor() .background_executor()
.block(buffer1.read(cx).serialize_ops(None, cx)); .block(buffer1.read(cx).serialize_ops(None, cx));
let buffer2 = cx.new_model(|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 buffer
.apply_ops( .apply_ops(
ops.into_iter() ops.into_iter()
@ -1967,7 +1967,8 @@ fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
let ops = cx let ops = cx
.background_executor() .background_executor()
.block(base_buffer.read(cx).serialize_ops(None, cx)); .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 buffer
.apply_ops( .apply_ops(
ops.into_iter() ops.into_iter()
@ -2083,8 +2084,13 @@ fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
replica_id replica_id
); );
new_buffer = Some(cx.new_model(|cx| { new_buffer = Some(cx.new_model(|cx| {
let mut new_buffer = let mut new_buffer = Buffer::from_proto(
Buffer::from_proto(new_replica_id, old_buffer_state, None).unwrap(); new_replica_id,
Capability::ReadWrite,
old_buffer_state,
None,
)
.unwrap();
new_buffer new_buffer
.apply_ops( .apply_ops(
old_buffer_ops old_buffer_ops

View File

@ -772,9 +772,10 @@ impl Render for LspLogToolbarItemView {
}), }),
); );
if server_selected && row.logs_selected { if server_selected && row.logs_selected {
let selected_ix = menu.select_last();
debug_assert_eq!( debug_assert_eq!(
Some(ix * 3 + 1), Some(ix * 3 + 1),
menu.select_last(), selected_ix,
"Could not scroll to a just added LSP menu item" "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 { if server_selected && row.rpc_trace_selected {
let selected_ix = menu.select_last();
debug_assert_eq!( debug_assert_eq!(
Some(ix * 3 + 2), Some(ix * 3 + 2),
menu.select_last(), selected_ix,
"Could not scroll to a just added LSP menu item" "Could not scroll to a just added LSP menu item"
); );
} }

View File

@ -62,6 +62,7 @@ impl<'a> VideoGrant<'a> {
Self { Self {
room: Some(Cow::Borrowed(room)), room: Some(Cow::Borrowed(room)),
room_join: Some(true), room_join: Some(true),
can_publish: Some(false),
can_subscribe: Some(true), can_subscribe: Some(true),
..Default::default() ..Default::default()
} }

View File

@ -11,7 +11,7 @@ pub use language::Completion;
use language::{ use language::{
char_kind, char_kind,
language_settings::{language_settings, LanguageSettings}, 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, DiagnosticEntry, File, IndentSize, Language, LanguageScope, OffsetRangeExt, OffsetUtf16,
Outline, OutlineItem, Point, PointUtf16, Selection, TextDimension, ToOffset as _, Outline, OutlineItem, Point, PointUtf16, Selection, TextDimension, ToOffset as _,
ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _, TransactionId, Unclipped, ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _, TransactionId, Unclipped,
@ -55,6 +55,7 @@ pub struct MultiBuffer {
replica_id: ReplicaId, replica_id: ReplicaId,
history: History, history: History,
title: Option<String>, title: Option<String>,
capability: Capability,
} }
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
@ -225,13 +226,14 @@ struct ExcerptBytes<'a> {
} }
impl MultiBuffer { impl MultiBuffer {
pub fn new(replica_id: ReplicaId) -> Self { pub fn new(replica_id: ReplicaId, capability: Capability) -> Self {
Self { Self {
snapshot: Default::default(), snapshot: Default::default(),
buffers: Default::default(), buffers: Default::default(),
next_excerpt_id: 1, next_excerpt_id: 1,
subscriptions: Default::default(), subscriptions: Default::default(),
singleton: false, singleton: false,
capability,
replica_id, replica_id,
history: History { history: History {
next_transaction_id: Default::default(), next_transaction_id: Default::default(),
@ -271,6 +273,7 @@ impl MultiBuffer {
next_excerpt_id: 1, next_excerpt_id: 1,
subscriptions: Default::default(), subscriptions: Default::default(),
singleton: self.singleton, singleton: self.singleton,
capability: self.capability,
replica_id: self.replica_id, replica_id: self.replica_id,
history: self.history.clone(), history: self.history.clone(),
title: self.title.clone(), title: self.title.clone(),
@ -282,8 +285,12 @@ impl MultiBuffer {
self self
} }
pub fn read_only(&self) -> bool {
self.capability == Capability::ReadOnly
}
pub fn singleton(buffer: Model<Buffer>, cx: &mut ModelContext<Self>) -> Self { 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.singleton = true;
this.push_excerpts( this.push_excerpts(
buffer, buffer,
@ -1657,7 +1664,7 @@ impl MultiBuffer {
excerpts: [(&str, Vec<Range<Point>>); COUNT], excerpts: [(&str, Vec<Range<Point>>); COUNT],
cx: &mut gpui::AppContext, cx: &mut gpui::AppContext,
) -> Model<Self> { ) -> 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 { for (text, ranges) in excerpts {
let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text)); let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text));
let excerpt_ranges = ranges.into_iter().map(|range| ExcerptRange { 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> { pub fn build_random(rng: &mut impl rand::Rng, cx: &mut gpui::AppContext) -> Model<Self> {
cx.new_model(|cx| { 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); let mutation_count = rng.gen_range(1..=5);
multibuffer.randomly_edit_excerpts(rng, mutation_count, cx); multibuffer.randomly_edit_excerpts(rng, mutation_count, cx);
multibuffer multibuffer
@ -4176,7 +4183,7 @@ mod tests {
let ops = cx let ops = cx
.background_executor() .background_executor()
.block(host_buffer.read(cx).serialize_ops(None, cx)); .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 buffer
.apply_ops( .apply_ops(
ops.into_iter() 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'))); cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(6, 6, 'a')));
let buffer_2 = let buffer_2 =
cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(6, 6, 'g'))); 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())); let events = Arc::new(RwLock::new(Vec::<Event>::new()));
multibuffer.update(cx, |_, cx| { multibuffer.update(cx, |_, cx| {
@ -4442,8 +4449,8 @@ mod tests {
let buffer_2 = let buffer_2 =
cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(10, 3, 'm'))); 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 leader_multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
let follower_multibuffer = cx.new_model(|_| MultiBuffer::new(0)); let follower_multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
let follower_edit_event_count = Arc::new(RwLock::new(0)); let follower_edit_event_count = Arc::new(RwLock::new(0));
follower_multibuffer.update(cx, |_, cx| { follower_multibuffer.update(cx, |_, cx| {
@ -4547,7 +4554,7 @@ mod tests {
fn test_push_excerpts_with_context_lines(cx: &mut AppContext) { fn test_push_excerpts_with_context_lines(cx: &mut AppContext) {
let buffer = let buffer =
cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(20, 3, 'a'))); 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 anchor_ranges = multibuffer.update(cx, |multibuffer, cx| {
multibuffer.push_excerpts_with_context_lines( multibuffer.push_excerpts_with_context_lines(
buffer.clone(), buffer.clone(),
@ -4584,7 +4591,7 @@ mod tests {
async fn test_stream_excerpts_with_context_lines(cx: &mut TestAppContext) { async fn test_stream_excerpts_with_context_lines(cx: &mut TestAppContext) {
let buffer = let buffer =
cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(20, 3, 'a'))); 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 anchor_ranges = multibuffer.update(cx, |multibuffer, cx| {
let snapshot = buffer.read(cx); let snapshot = buffer.read(cx);
let ranges = vec![ let ranges = vec![
@ -4619,7 +4626,7 @@ mod tests {
#[gpui::test] #[gpui::test]
fn test_empty_multibuffer(cx: &mut AppContext) { 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); let snapshot = multibuffer.read(cx).snapshot(cx);
assert_eq!(snapshot.text(), ""); 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_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 buffer_2 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "efghi"));
let multibuffer = cx.new_model(|cx| { let multibuffer = cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new(0); let mut multibuffer = MultiBuffer::new(0, Capability::ReadWrite);
multibuffer.push_excerpts( multibuffer.push_excerpts(
buffer_1.clone(), buffer_1.clone(),
[ExcerptRange { [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_1 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abcd"));
let buffer_2 = let buffer_2 =
cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "ABCDEFGHIJKLMNOP")); 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. // 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. // Add an excerpt from buffer 1 that spans this new insertion.
@ -4844,7 +4851,7 @@ mod tests {
.unwrap_or(10); .unwrap_or(10);
let mut buffers: Vec<Model<Buffer>> = Vec::new(); 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 excerpt_ids = Vec::<ExcerptId>::new();
let mut expected_excerpts = Vec::<(Model<Buffer>, Range<text::Anchor>)>::new(); let mut expected_excerpts = Vec::<(Model<Buffer>, Range<text::Anchor>)>::new();
let mut anchors = Vec::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_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 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; let group_interval = multibuffer.read(cx).history.group_interval;
multibuffer.update(cx, |multibuffer, cx| { multibuffer.update(cx, |multibuffer, cx| {
multibuffer.push_excerpts( multibuffer.push_excerpts(

View File

@ -39,11 +39,11 @@ use language::{
deserialize_anchor, deserialize_fingerprint, deserialize_line_ending, deserialize_version, deserialize_anchor, deserialize_fingerprint, deserialize_line_ending, deserialize_version,
serialize_anchor, serialize_version, split_operations, serialize_anchor, serialize_version, split_operations,
}, },
range_from_lsp, range_to_lsp, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CodeAction, range_from_lsp, range_to_lsp, Bias, Buffer, BufferSnapshot, CachedLspAdapter, Capability,
CodeLabel, Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Diff, Event as BufferEvent, CodeAction, CodeLabel, Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Diff,
File as _, Language, LanguageRegistry, LanguageServerName, LocalFile, LspAdapterDelegate, Event as BufferEvent, File as _, Language, LanguageRegistry, LanguageServerName, LocalFile,
OffsetRangeExt, Operation, Patch, PendingLanguageServer, PointUtf16, TextBufferSnapshot, LspAdapterDelegate, OffsetRangeExt, Operation, Patch, PendingLanguageServer, PointUtf16,
ToOffset, ToPointUtf16, Transaction, Unclipped, TextBufferSnapshot, ToOffset, ToPointUtf16, Transaction, Unclipped,
}; };
use log::error; use log::error;
use lsp::{ use lsp::{
@ -262,6 +262,7 @@ enum ProjectClientState {
}, },
Remote { Remote {
sharing_has_stopped: bool, sharing_has_stopped: bool,
capability: Capability,
remote_id: u64, remote_id: u64,
replica_id: ReplicaId, replica_id: ReplicaId,
}, },
@ -702,6 +703,7 @@ impl Project {
user_store: Model<UserStore>, user_store: Model<UserStore>,
languages: Arc<LanguageRegistry>, languages: Arc<LanguageRegistry>,
fs: Arc<dyn Fs>, fs: Arc<dyn Fs>,
role: proto::ChannelRole,
mut cx: AsyncAppContext, mut cx: AsyncAppContext,
) -> Result<Model<Self>> { ) -> Result<Model<Self>> {
client.authenticate_and_connect(true, &cx).await?; client.authenticate_and_connect(true, &cx).await?;
@ -756,6 +758,7 @@ impl Project {
client: client.clone(), client: client.clone(),
client_state: Some(ProjectClientState::Remote { client_state: Some(ProjectClientState::Remote {
sharing_has_stopped: false, sharing_has_stopped: false,
capability: Capability::ReadWrite,
remote_id, remote_id,
replica_id, replica_id,
}), }),
@ -796,6 +799,7 @@ impl Project {
prettiers_per_worktree: HashMap::default(), prettiers_per_worktree: HashMap::default(),
prettier_instances: HashMap::default(), prettier_instances: HashMap::default(),
}; };
this.set_role(role);
for worktree in worktrees { for worktree in worktrees {
let _ = this.add_worktree(&worktree, cx); let _ = this.add_worktree(&worktree, cx);
} }
@ -1618,6 +1622,17 @@ impl Project {
cx.notify(); 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) { fn disconnected_from_host_internal(&mut self, cx: &mut AppContext) {
if let Some(ProjectClientState::Remote { if let Some(ProjectClientState::Remote {
sharing_has_stopped, sharing_has_stopped,
@ -1659,7 +1674,7 @@ impl Project {
cx.emit(Event::Closed); cx.emit(Event::Closed);
} }
pub fn is_read_only(&self) -> bool { pub fn is_disconnected(&self) -> bool {
match &self.client_state { match &self.client_state {
Some(ProjectClientState::Remote { Some(ProjectClientState::Remote {
sharing_has_stopped, 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 { pub fn is_local(&self) -> bool {
match &self.client_state { match &self.client_state {
Some(ProjectClientState::Remote { .. }) => false, Some(ProjectClientState::Remote { .. }) => false,
@ -6013,7 +6039,7 @@ impl Project {
this.upgrade().context("project dropped")?; this.upgrade().context("project dropped")?;
let response = rpc.request(message).await?; let response = rpc.request(message).await?;
let this = this.upgrade().context("project dropped")?; 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")) Err(anyhow!("disconnected before completing request"))
} else { } else {
request request
@ -7192,7 +7218,8 @@ impl Project {
let buffer_id = state.id; let buffer_id = state.id;
let buffer = cx.new_model(|_| { 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 this.incomplete_remote_buffers
.insert(buffer_id, Some(buffer)); .insert(buffer_id, Some(buffer));
@ -7940,7 +7967,7 @@ impl Project {
if let Some(buffer) = buffer { if let Some(buffer) = buffer {
break 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)); return Err(anyhow!("disconnected before buffer {} could be opened", id));
} }

View File

@ -32,7 +32,8 @@ use language::{
deserialize_fingerprint, deserialize_version, serialize_fingerprint, serialize_line_ending, deserialize_fingerprint, deserialize_version, serialize_fingerprint, serialize_line_ending,
serialize_version, 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 lsp::LanguageServerId;
use parking_lot::Mutex; use parking_lot::Mutex;
@ -682,7 +683,14 @@ impl LocalWorktree {
.background_executor() .background_executor()
.spawn(async move { text::Buffer::new(0, id, contents) }) .spawn(async move { text::Buffer::new(0, id, contents) })
.await; .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,
)
})
}) })
} }

View File

@ -388,8 +388,18 @@ impl ProjectPanel {
let is_dir = entry.is_dir(); let is_dir = entry.is_dir();
let worktree_id = worktree.id(); let worktree_id = worktree.id();
let is_local = project.is_local(); let is_local = project.is_local();
let is_read_only = project.is_read_only();
let context_menu = ContextMenu::build(cx, |mut menu, cx| { 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 { if is_local {
menu = menu.action( menu = menu.action(
"Add Folder to Project", "Add Folder to Project",
@ -1473,6 +1483,7 @@ impl ProjectPanel {
impl Render for ProjectPanel { impl Render for ProjectPanel {
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement { fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement {
let has_worktree = self.visible_entries.len() != 0; let has_worktree = self.visible_entries.len() != 0;
let project = self.project.read(cx);
if has_worktree { if has_worktree {
div() div()
@ -1485,21 +1496,25 @@ impl Render for ProjectPanel {
.on_action(cx.listener(Self::expand_selected_entry)) .on_action(cx.listener(Self::expand_selected_entry))
.on_action(cx.listener(Self::collapse_selected_entry)) .on_action(cx.listener(Self::collapse_selected_entry))
.on_action(cx.listener(Self::collapse_all_entries)) .on_action(cx.listener(Self::collapse_all_entries))
.on_action(cx.listener(Self::new_file)) .on_action(cx.listener(Self::open_file))
.on_action(cx.listener(Self::confirm))
.on_action(cx.listener(Self::cancel))
.on_action(cx.listener(Self::copy_path))
.on_action(cx.listener(Self::copy_relative_path))
.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::new_directory))
.on_action(cx.listener(Self::rename)) .on_action(cx.listener(Self::rename))
.on_action(cx.listener(Self::delete)) .on_action(cx.listener(Self::delete))
.on_action(cx.listener(Self::confirm))
.on_action(cx.listener(Self::open_file))
.on_action(cx.listener(Self::cancel))
.on_action(cx.listener(Self::cut)) .on_action(cx.listener(Self::cut))
.on_action(cx.listener(Self::copy)) .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::paste))
.on_action(cx.listener(Self::reveal_in_finder)) })
.when(project.is_local(), |el| {
el.on_action(cx.listener(Self::reveal_in_finder))
.on_action(cx.listener(Self::open_in_terminal)) .on_action(cx.listener(Self::open_in_terminal))
.on_action(cx.listener(Self::new_search_in_directory)) })
.track_focus(&self.focus_handle) .track_focus(&self.focus_handle)
.child( .child(
uniform_list( uniform_list(

View File

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

View File

@ -269,6 +269,7 @@ message Participant {
repeated ParticipantProject projects = 3; repeated ParticipantProject projects = 3;
ParticipantLocation location = 4; ParticipantLocation location = 4;
uint32 participant_index = 5; uint32 participant_index = 5;
ChannelRole role = 6;
} }
message PendingParticipant { message PendingParticipant {

View File

@ -70,7 +70,7 @@ impl BufferSearchBar {
fn render_text_input(&self, editor: &View<Editor>, cx: &ViewContext<Self>) -> impl IntoElement { fn render_text_input(&self, editor: &View<Editor>, cx: &ViewContext<Self>) -> impl IntoElement {
let settings = ThemeSettings::get_global(cx); let settings = ThemeSettings::get_global(cx);
let text_style = TextStyle { let text_style = TextStyle {
color: if editor.read(cx).read_only() { color: if editor.read(cx).read_only(cx) {
cx.theme().colors().text_disabled cx.theme().colors().text_disabled
} else { } else {
cx.theme().colors().text cx.theme().colors().text
@ -223,6 +223,7 @@ impl Render for BufferSearchBar {
.gap_2() .gap_2()
.border_1() .border_1()
.border_color(editor_border) .border_color(editor_border)
.min_w(rems(384. / 16.))
.rounded_lg() .rounded_lg()
.child(IconElement::new(Icon::MagnifyingGlass)) .child(IconElement::new(Icon::MagnifyingGlass))
.child(self.render_text_input(&self.query_editor, cx)) .child(self.render_text_input(&self.query_editor, cx))

View File

@ -38,8 +38,8 @@ use std::{
use theme::ThemeSettings; use theme::ThemeSettings;
use ui::{ use ui::{
h_stack, prelude::*, v_stack, Button, Icon, IconButton, IconElement, Label, LabelCommon, h_stack, prelude::*, v_stack, Icon, IconButton, IconElement, Label, LabelCommon, LabelSize,
LabelSize, Selectable, Tooltip, Selectable, ToggleButton, Tooltip,
}; };
use util::{paths::PathMatcher, ResultExt as _}; use util::{paths::PathMatcher, ResultExt as _};
use workspace::{ use workspace::{
@ -132,9 +132,11 @@ pub struct ProjectSearchBar {
impl ProjectSearch { impl ProjectSearch {
fn new(project: Model<Project>, cx: &mut ModelContext<Self>) -> Self { fn new(project: Model<Project>, cx: &mut ModelContext<Self>) -> Self {
let replica_id = project.read(cx).replica_id(); let replica_id = project.read(cx).replica_id();
let capability = project.read(cx).capability();
Self { Self {
project, project,
excerpts: cx.new_model(|_| MultiBuffer::new(replica_id)), excerpts: cx.new_model(|_| MultiBuffer::new(replica_id, capability)),
pending_search: Default::default(), pending_search: Default::default(),
match_ranges: Default::default(), match_ranges: Default::default(),
active_query: None, active_query: None,
@ -286,7 +288,6 @@ impl Render for ProjectSearchView {
.size_full() .size_full()
.track_focus(&self.focus_handle) .track_focus(&self.focus_handle)
.child(self.results_editor.clone()) .child(self.results_editor.clone())
.into_any()
} else { } else {
let model = self.model.read(cx); let model = self.model.read(cx);
let has_no_results = model.no_results.unwrap_or(false); let has_no_results = model.no_results.unwrap_or(false);
@ -363,6 +364,7 @@ impl Render for ProjectSearchView {
.flex_1() .flex_1()
.size_full() .size_full()
.justify_center() .justify_center()
.bg(cx.theme().colors().editor_background)
.track_focus(&self.focus_handle) .track_focus(&self.focus_handle)
.child( .child(
h_stack() h_stack()
@ -372,7 +374,6 @@ impl Render for ProjectSearchView {
.child(v_stack().child(major_text).children(minor_text)) .child(v_stack().child(major_text).children(minor_text))
.child(h_stack().flex_1()), .child(h_stack().flex_1()),
) )
.into_any()
} }
} }
} }
@ -1557,7 +1558,7 @@ impl ProjectSearchBar {
fn render_text_input(&self, editor: &View<Editor>, cx: &ViewContext<Self>) -> impl IntoElement { fn render_text_input(&self, editor: &View<Editor>, cx: &ViewContext<Self>) -> impl IntoElement {
let settings = ThemeSettings::get_global(cx); let settings = ThemeSettings::get_global(cx);
let text_style = TextStyle { let text_style = TextStyle {
color: if editor.read(cx).read_only() { color: if editor.read(cx).read_only(cx) {
cx.theme().colors().text_disabled cx.theme().colors().text_disabled
} else { } else {
cx.theme().colors().text cx.theme().colors().text
@ -1677,20 +1678,26 @@ impl Render for ProjectSearchBar {
let mode_column = v_stack().items_start().justify_start().child( let mode_column = v_stack().items_start().justify_start().child(
h_stack() h_stack()
.gap_2()
.child( .child(
h_stack() h_stack()
.child( .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) .selected(search.current_mode == SearchMode::Text)
.on_click(cx.listener(|this, _, cx| { .on_click(cx.listener(|this, _, cx| {
this.activate_search_mode(SearchMode::Text, cx) this.activate_search_mode(SearchMode::Text, cx)
})) }))
.tooltip(|cx| { .tooltip(|cx| {
Tooltip::for_action("Toggle text search", &ActivateTextMode, cx) Tooltip::for_action("Toggle text search", &ActivateTextMode, cx)
}), })
.first(),
) )
.child( .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) .selected(search.current_mode == SearchMode::Regex)
.on_click(cx.listener(|this, _, cx| { .on_click(cx.listener(|this, _, cx| {
this.activate_search_mode(SearchMode::Regex, cx) this.activate_search_mode(SearchMode::Regex, cx)
@ -1701,11 +1708,20 @@ impl Render for ProjectSearchBar {
&ActivateRegexMode, &ActivateRegexMode,
cx, cx,
) )
})
.map(|this| {
if semantic_is_available {
this.middle()
} else {
this.last()
}
}), }),
) )
.when(semantic_is_available, |this| { .when(semantic_is_available, |this| {
this.child( 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) .selected(search.current_mode == SearchMode::Semantic)
.on_click(cx.listener(|this, _, cx| { .on_click(cx.listener(|this, _, cx| {
this.activate_search_mode(SearchMode::Semantic, cx) this.activate_search_mode(SearchMode::Semantic, cx)
@ -1716,7 +1732,8 @@ impl Render for ProjectSearchBar {
&ActivateSemanticMode, &ActivateSemanticMode,
cx, cx,
) )
}), })
.last(),
) )
}), }),
) )
@ -1867,6 +1884,7 @@ impl Render for ProjectSearchBar {
.child( .child(
h_stack() h_stack()
.justify_between() .justify_between()
.gap_2()
.child(query_column) .child(query_column)
.child(mode_column) .child(mode_column)
.child(replace_column) .child(replace_column)

View File

@ -14,7 +14,6 @@ test-support = ["gpui/test-support", "fs/test-support"]
[dependencies] [dependencies]
collections = { path = "../collections" } collections = { path = "../collections" }
gpui = { path = "../gpui" } gpui = { path = "../gpui" }
sqlez = { path = "../sqlez" }
fs = { path = "../fs" } fs = { path = "../fs" }
feature_flags = { path = "../feature_flags" } feature_flags = { path = "../feature_flags" }
util = { path = "../util" } util = { path = "../util" }

View File

@ -131,6 +131,15 @@ impl PlayerColors {
*self.0.last().unwrap() *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 { pub fn color_for_participant(&self, participant_index: u32) -> PlayerColor {
let len = self.0.len() - 1; let len = self.0.len() - 1;
self.0[(participant_index as usize % 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

View File

@ -15,457 +15,6 @@ pub fn one() -> UserThemeFamily {
name: "One".into(), name: "One".into(),
author: "Zed Industries".into(), author: "Zed Industries".into(),
themes: vec![ 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 { UserTheme {
name: "One Dark".into(), name: "One Dark".into(),
appearance: Appearance::Dark, 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()
},
),
],
}),
},
},
], ],
} }
} }

View File

@ -15,6 +15,464 @@ pub fn rose_pine() -> UserThemeFamily {
name: "Rosé Pine".into(), name: "Rosé Pine".into(),
author: "Zed Industries".into(), author: "Zed Industries".into(),
themes: vec![ 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 { UserTheme {
name: "Rosé Pine Dawn".into(), name: "Rosé Pine Dawn".into(),
appearance: Appearance::Light, 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()
},
),
],
}),
},
},
], ],
} }
} }

View File

@ -15,450 +15,6 @@ pub fn solarized() -> UserThemeFamily {
name: "Solarized".into(), name: "Solarized".into(),
author: "Zed Industries".into(), author: "Zed Industries".into(),
themes: vec![ 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 { UserTheme {
name: "Solarized Dark".into(), name: "Solarized Dark".into(),
appearance: Appearance::Dark, 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()
},
),
],
}),
},
},
], ],
} }
} }

View File

@ -11,6 +11,7 @@ clap = { version = "4.4", features = ["derive"] }
convert_case = "0.6.0" convert_case = "0.6.0"
gpui = { path = "../gpui" } gpui = { path = "../gpui" }
indexmap = { version = "1.6.2", features = ["serde"] } indexmap = { version = "1.6.2", features = ["serde"] }
indoc.workspace = true
json_comments = "0.2.2" json_comments = "0.2.2"
log.workspace = true log.workspace = true
palette = { version = "0.7.3", default-features = false, features = ["std"] } palette = { version = "0.7.3", default-features = false, features = ["std"] }

View File

@ -18,6 +18,7 @@ use clap::Parser;
use convert_case::{Case, Casing}; use convert_case::{Case, Casing};
use gpui::serde_json; use gpui::serde_json;
use indexmap::IndexMap; use indexmap::IndexMap;
use indoc::formatdoc;
use json_comments::StripComments; use json_comments::StripComments;
use log::LevelFilter; use log::LevelFilter;
use serde::Deserialize; use serde::Deserialize;
@ -28,7 +29,7 @@ use crate::theme_printer::UserThemeFamilyPrinter;
use crate::vscode::VsCodeTheme; use crate::vscode::VsCodeTheme;
use crate::vscode::VsCodeThemeConverter; use crate::vscode::VsCodeThemeConverter;
use crate::zed1::theme::Theme as Zed1Theme; use crate::zed1::theme::Theme as Zed1Theme;
use crate::zed1::Zed1ThemeConverter; use crate::zed1::{zed1_theme_licenses, Zed1ThemeConverter};
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
struct FamilyMetadata { struct FamilyMetadata {
@ -200,7 +201,13 @@ fn main() -> Result<()> {
"Summercamp", "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 zed1_theme_familes
.into_iter() .into_iter()
.map(|family| (family.to_string(), Vec::new())), .map(|family| (family.to_string(), Vec::new())),
@ -254,13 +261,44 @@ fn main() -> Result<()> {
themes_for_family.push(theme); themes_for_family.push(theme);
} }
zed1_themes_by_family.sort_keys();
let mut licenses = Vec::new();
for (family, themes) in zed1_themes_by_family { for (family, themes) in zed1_themes_by_family {
let theme_family = UserThemeFamily { let mut theme_family = UserThemeFamily {
name: family, name: family,
author: "Zed Industries".to_string(), author: "Zed Industries".to_string(),
themes, 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); theme_families.push(theme_family);
} }
@ -357,6 +395,12 @@ fn main() -> Result<()> {
mod_rs_file.write_all(mod_rs_contents.as_bytes())?; 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..."); log::info!("Formatting themes...");
let format_result = format_themes_crate() let format_result = format_themes_crate()

View File

@ -1,4 +1,6 @@
mod converter; mod converter;
mod licenses;
pub mod theme; pub mod theme;
pub use converter::*; pub use converter::*;
pub use licenses::*;

File diff suppressed because it is too large Load Diff

View File

@ -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 { impl Disableable for Button {
fn disabled(mut self, disabled: bool) -> Self { fn disabled(mut self, disabled: bool) -> Self {
self.base = self.base.disabled(disabled); self.base = self.base.disabled(disabled);

View File

@ -12,6 +12,7 @@ pub(super) struct ButtonIcon {
disabled: bool, disabled: bool,
selected: bool, selected: bool,
selected_icon: Option<Icon>, selected_icon: Option<Icon>,
selected_style: Option<ButtonStyle>,
} }
impl ButtonIcon { impl ButtonIcon {
@ -23,6 +24,7 @@ impl ButtonIcon {
disabled: false, disabled: false,
selected: false, selected: false,
selected_icon: None, 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 { impl RenderOnce for ButtonIcon {
fn render(self, _cx: &mut WindowContext) -> impl IntoElement { fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
let icon = self let icon = self
@ -71,6 +80,8 @@ impl RenderOnce for ButtonIcon {
let icon_color = if self.disabled { let icon_color = if self.disabled {
Color::Disabled Color::Disabled
} else if self.selected_style.is_some() && self.selected {
self.selected_style.unwrap().into()
} else if self.selected { } else if self.selected {
Color::Selected Color::Selected
} else { } else {

View File

@ -4,6 +4,10 @@ use smallvec::SmallVec;
use crate::prelude::*; use crate::prelude::*;
pub trait SelectableButton: Selectable {
fn selected_style(self, style: ButtonStyle) -> Self;
}
pub trait ButtonCommon: Clickable + Disableable { pub trait ButtonCommon: Clickable + Disableable {
/// A unique element ID to identify the button. /// A unique element ID to identify the button.
fn id(&self) -> &ElementId; fn id(&self) -> &ElementId;
@ -36,17 +40,68 @@ pub enum IconPosition {
End, 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)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
pub enum ButtonStyle { pub enum ButtonStyle {
/// A filled button with a solid background color. Provides emphasis versus /// A filled button with a solid background color. Provides emphasis versus
/// the more common subtle button. /// the more common subtle button.
Filled, Filled,
/// 🚧 Under construction 🚧
///
/// Used to emphasize a button in some way, like a selected state, or a semantic /// Used to emphasize a button in some way, like a selected state, or a semantic
/// coloring like an error or success button. /// coloring like an error or success button.
Tinted, Tinted(TintColor),
/// The default button style, used for most buttons. Has a transparent background, /// The default button style, used for most buttons. Has a transparent background,
/// but has a background color to indicate states like hover and active. /// but has a background color to indicate states like hover and active.
@ -86,12 +141,7 @@ impl ButtonStyle {
label_color: Color::Default.color(cx), label_color: Color::Default.color(cx),
icon_color: Color::Default.color(cx), icon_color: Color::Default.color(cx),
}, },
ButtonStyle::Tinted => ButtonLikeStyles { ButtonStyle::Tinted(tint) => tint.button_like_style(cx),
background: gpui::red(),
border_color: gpui::red(),
label_color: gpui::red(),
icon_color: gpui::red(),
},
ButtonStyle::Subtle => ButtonLikeStyles { ButtonStyle::Subtle => ButtonLikeStyles {
background: cx.theme().colors().ghost_element_background, background: cx.theme().colors().ghost_element_background,
border_color: transparent_black(), border_color: transparent_black(),
@ -115,12 +165,7 @@ impl ButtonStyle {
label_color: Color::Default.color(cx), label_color: Color::Default.color(cx),
icon_color: Color::Default.color(cx), icon_color: Color::Default.color(cx),
}, },
ButtonStyle::Tinted => ButtonLikeStyles { ButtonStyle::Tinted(tint) => tint.button_like_style(cx),
background: gpui::red(),
border_color: gpui::red(),
label_color: gpui::red(),
icon_color: gpui::red(),
},
ButtonStyle::Subtle => ButtonLikeStyles { ButtonStyle::Subtle => ButtonLikeStyles {
background: cx.theme().colors().ghost_element_hover, background: cx.theme().colors().ghost_element_hover,
border_color: transparent_black(), border_color: transparent_black(),
@ -146,12 +191,7 @@ impl ButtonStyle {
label_color: Color::Default.color(cx), label_color: Color::Default.color(cx),
icon_color: Color::Default.color(cx), icon_color: Color::Default.color(cx),
}, },
ButtonStyle::Tinted => ButtonLikeStyles { ButtonStyle::Tinted(tint) => tint.button_like_style(cx),
background: gpui::red(),
border_color: gpui::red(),
label_color: gpui::red(),
icon_color: gpui::red(),
},
ButtonStyle::Subtle => ButtonLikeStyles { ButtonStyle::Subtle => ButtonLikeStyles {
background: cx.theme().colors().ghost_element_active, background: cx.theme().colors().ghost_element_active,
border_color: transparent_black(), border_color: transparent_black(),
@ -178,12 +218,7 @@ impl ButtonStyle {
label_color: Color::Default.color(cx), label_color: Color::Default.color(cx),
icon_color: Color::Default.color(cx), icon_color: Color::Default.color(cx),
}, },
ButtonStyle::Tinted => ButtonLikeStyles { ButtonStyle::Tinted(tint) => tint.button_like_style(cx),
background: gpui::red(),
border_color: gpui::red(),
label_color: gpui::red(),
icon_color: gpui::red(),
},
ButtonStyle::Subtle => ButtonLikeStyles { ButtonStyle::Subtle => ButtonLikeStyles {
background: cx.theme().colors().ghost_element_background, background: cx.theme().colors().ghost_element_background,
border_color: cx.theme().colors().border_focused, border_color: cx.theme().colors().border_focused,
@ -208,12 +243,7 @@ impl ButtonStyle {
label_color: Color::Disabled.color(cx), label_color: Color::Disabled.color(cx),
icon_color: Color::Disabled.color(cx), icon_color: Color::Disabled.color(cx),
}, },
ButtonStyle::Tinted => ButtonLikeStyles { ButtonStyle::Tinted(tint) => tint.button_like_style(cx),
background: gpui::red(),
border_color: gpui::red(),
label_color: gpui::red(),
icon_color: gpui::red(),
},
ButtonStyle::Subtle => ButtonLikeStyles { ButtonStyle::Subtle => ButtonLikeStyles {
background: cx.theme().colors().ghost_element_disabled, background: cx.theme().colors().ghost_element_disabled,
border_color: cx.theme().colors().border_disabled, border_color: cx.theme().colors().border_disabled,
@ -264,6 +294,7 @@ pub struct ButtonLike {
pub(super) style: ButtonStyle, pub(super) style: ButtonStyle,
pub(super) disabled: bool, pub(super) disabled: bool,
pub(super) selected: bool, pub(super) selected: bool,
pub(super) selected_style: Option<ButtonStyle>,
pub(super) width: Option<DefiniteLength>, pub(super) width: Option<DefiniteLength>,
size: ButtonSize, size: ButtonSize,
rounding: Option<ButtonLikeRounding>, rounding: Option<ButtonLikeRounding>,
@ -280,6 +311,7 @@ impl ButtonLike {
style: ButtonStyle::default(), style: ButtonStyle::default(),
disabled: false, disabled: false,
selected: false, selected: false,
selected_style: None,
width: None, width: None,
size: ButtonSize::Default, size: ButtonSize::Default,
rounding: Some(ButtonLikeRounding::All), 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 { impl Clickable for ButtonLike {
fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self { fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
self.on_click = Some(Box::new(handler)); self.on_click = Some(Box::new(handler));
@ -364,6 +403,11 @@ impl ParentElement for ButtonLike {
impl RenderOnce for ButtonLike { impl RenderOnce for ButtonLike {
fn render(self, cx: &mut WindowContext) -> impl IntoElement { fn render(self, cx: &mut WindowContext) -> impl IntoElement {
let style = self
.selected_style
.filter(|_| self.selected)
.unwrap_or(self.style);
self.base self.base
.h_flex() .h_flex()
.id(self.id.clone()) .id(self.id.clone())
@ -382,12 +426,12 @@ impl RenderOnce for ButtonLike {
ButtonSize::Default | ButtonSize::Compact => this.px_1(), ButtonSize::Default | ButtonSize::Compact => this.px_1(),
ButtonSize::None => this, 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_not_allowed())
.when(!self.disabled, |this| { .when(!self.disabled, |this| {
this.cursor_pointer() this.cursor_pointer()
.hover(|hover| hover.bg(self.style.hovered(cx).background)) .hover(|hover| hover.bg(style.hovered(cx).background))
.active(|active| active.bg(self.style.active(cx).background)) .active(|active| active.bg(style.active(cx).background))
}) })
.when_some( .when_some(
self.on_click.filter(|_| !self.disabled), self.on_click.filter(|_| !self.disabled),

View File

@ -1,6 +1,6 @@
use gpui::{AnyView, DefiniteLength}; use gpui::{AnyView, DefiniteLength};
use crate::prelude::*; use crate::{prelude::*, SelectableButton};
use crate::{ButtonCommon, ButtonLike, ButtonSize, ButtonStyle, Icon, IconSize}; use crate::{ButtonCommon, ButtonLike, ButtonSize, ButtonStyle, Icon, IconSize};
use super::button_icon::ButtonIcon; 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 { impl Clickable for IconButton {
fn on_click( fn on_click(
mut self, mut self,
@ -109,12 +116,14 @@ impl RenderOnce for IconButton {
fn render(self, _cx: &mut WindowContext) -> impl IntoElement { fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
let is_disabled = self.base.disabled; let is_disabled = self.base.disabled;
let is_selected = self.base.selected; let is_selected = self.base.selected;
let selected_style = self.base.selected_style;
self.base.child( self.base.child(
ButtonIcon::new(self.icon) ButtonIcon::new(self.icon)
.disabled(is_disabled) .disabled(is_disabled)
.selected(is_selected) .selected(is_selected)
.selected_icon(self.selected_icon) .selected_icon(self.selected_icon)
.when_some(selected_style, |this, style| this.selected_style(style))
.size(self.icon_size) .size(self.icon_size)
.color(self.icon_color), .color(self.icon_color),
) )

View File

@ -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 { impl Disableable for ToggleButton {
fn disabled(mut self, disabled: bool) -> Self { fn disabled(mut self, disabled: bool) -> Self {
self.base = self.base.disabled(disabled); self.base = self.base.disabled(disabled);

View File

@ -12,7 +12,7 @@ pub use crate::selectable::*;
pub use crate::styles::{vh, vw}; pub use crate::styles::{vh, vw};
pub use crate::visible_on_hover::*; pub use crate::visible_on_hover::*;
pub use crate::{h_stack, v_stack}; 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::{ButtonCommon, Color, StyledExt};
pub use crate::{Headline, HeadlineSize}; pub use crate::{Headline, HeadlineSize};
pub use crate::{Icon, IconElement, IconPosition, IconSize}; pub use crate::{Icon, IconElement, IconPosition, IconSize};

View File

@ -59,7 +59,11 @@ pub struct WelcomePage {
impl Render for WelcomePage { impl Render for WelcomePage {
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement { fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement {
h_stack().full().track_focus(&self.focus_handle).child( h_stack()
.full()
.bg(cx.theme().colors().editor_background)
.track_focus(&self.focus_handle)
.child(
v_stack() v_stack()
.w_96() .w_96()
.gap_4() .gap_4()
@ -115,9 +119,9 @@ impl Render for WelcomePage {
.full_width() .full_width()
.on_click(cx.listener(|_, _, cx| { .on_click(cx.listener(|_, _, cx| {
cx.app_mut() cx.app_mut()
.spawn( .spawn(|cx| async move {
|cx| async move { install_cli::install_cli(&cx).await }, install_cli::install_cli(&cx).await
) })
.detach_and_log_err(cx); .detach_and_log_err(cx);
})), })),
), ),
@ -142,15 +146,15 @@ impl Render for WelcomePage {
ui::Selection::Unselected ui::Selection::Unselected
}, },
) )
.on_click(cx.listener( .on_click(
move |this, selection, cx| { cx.listener(move |this, selection, cx| {
this.update_settings::<VimModeSetting>( this.update_settings::<VimModeSetting>(
selection, selection,
cx, cx,
|setting, value| *setting = Some(value), |setting, value| *setting = Some(value),
); );
}, }),
)), ),
) )
.child(Label::new("Enable vim mode")), .child(Label::new("Enable vim mode")),
) )
@ -166,15 +170,17 @@ impl Render for WelcomePage {
ui::Selection::Unselected ui::Selection::Unselected
}, },
) )
.on_click(cx.listener( .on_click(
move |this, selection, cx| { cx.listener(move |this, selection, cx| {
this.update_settings::<TelemetrySettings>( this.update_settings::<TelemetrySettings>(
selection, selection,
cx, cx,
|settings, value| settings.metrics = Some(value), |settings, value| {
); settings.metrics = Some(value)
}, },
)), );
}),
),
) )
.child(Label::new("Send anonymous usage data")), .child(Label::new("Send anonymous usage data")),
) )
@ -190,8 +196,8 @@ impl Render for WelcomePage {
ui::Selection::Unselected ui::Selection::Unselected
}, },
) )
.on_click(cx.listener( .on_click(
move |this, selection, cx| { cx.listener(move |this, selection, cx| {
this.update_settings::<TelemetrySettings>( this.update_settings::<TelemetrySettings>(
selection, selection,
cx, cx,
@ -199,8 +205,8 @@ impl Render for WelcomePage {
settings.diagnostics = Some(value) settings.diagnostics = Some(value)
}, },
); );
}, }),
)), ),
) )
.child(Label::new("Send crash reports")), .child(Label::new("Send crash reports")),
), ),

View File

@ -32,6 +32,7 @@ language = { path = "../language" }
node_runtime = { path = "../node_runtime" } node_runtime = { path = "../node_runtime" }
project = { path = "../project" } project = { path = "../project" }
settings = { path = "../settings" } settings = { path = "../settings" }
sqlez = { path = "../sqlez" }
terminal = { path = "../terminal" } terminal = { path = "../terminal" }
theme = { path = "../theme" } theme = { path = "../theme" }
util = { path = "../util" } util = { path = "../util" }

View File

@ -651,9 +651,13 @@ impl Render for PanelButtons {
&& panel.position_is_valid(position, cx) && panel.position_is_valid(position, cx)
{ {
let panel = panel.clone(); let panel = panel.clone();
menu = menu.entry(position.to_label(), None, move |cx| { menu = menu.entry(
format!("Dock {}", position.to_label()),
None,
move |cx| {
panel.set_position(position, cx); panel.set_position(position, cx);
}) },
)
} }
} }
menu menu

View File

@ -1128,12 +1128,7 @@ impl Pane {
if self.items.len() == 1 && should_activate { if self.items.len() == 1 && should_activate {
self.focus_handle.focus(cx); self.focus_handle.focus(cx);
} else { } else {
self.activate_item( self.activate_item(index_to_activate, should_activate, should_activate, cx);
dbg!(index_to_activate),
dbg!(should_activate),
should_activate,
cx,
);
} }
} }

View File

@ -6,7 +6,12 @@ use std::path::Path;
use anyhow::{anyhow, bail, Context, Result}; use anyhow::{anyhow, bail, Context, Result};
use db::{define_connection, query, sqlez::connection::Connection, sqlez_macros::sql}; 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 util::{unzip_option, ResultExt};
use uuid::Uuid; use uuid::Uuid;
@ -20,6 +25,121 @@ use model::{
use self::model::DockStructure; 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! { define_connection! {
// Current schema shape using pseudo-rust syntax: // 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 /// 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 /// is empty, the most recent workspace is returned instead. If no workspace for the
/// passed roots is stored, returns none. /// passed roots is stored, returns none.
pub fn workspace_for_roots<P: AsRef<Path>>( pub(crate) fn workspace_for_roots<P: AsRef<Path>>(
&self, &self,
worktree_roots: &[P], worktree_roots: &[P],
) -> Option<SerializedWorkspace> { ) -> Option<SerializedWorkspace> {
@ -192,7 +312,7 @@ impl WorkspaceDb {
let (workspace_id, workspace_location, bounds, display, docks): ( let (workspace_id, workspace_location, bounds, display, docks): (
WorkspaceId, WorkspaceId,
WorkspaceLocation, WorkspaceLocation,
Option<WindowBounds>, Option<SerializedWindowsBounds>,
Option<Uuid>, Option<Uuid>,
DockStructure, DockStructure,
) = self ) = self
@ -230,7 +350,7 @@ impl WorkspaceDb {
.get_center_pane_group(workspace_id) .get_center_pane_group(workspace_id)
.context("Getting center group") .context("Getting center group")
.log_err()?, .log_err()?,
bounds, bounds: bounds.map(|bounds| bounds.0),
display, display,
docks, docks,
}) })
@ -238,7 +358,7 @@ impl WorkspaceDb {
/// Saves a workspace using the worktree roots. Will garbage collect any workspaces /// Saves a workspace using the worktree roots. Will garbage collect any workspaces
/// that used this workspace previously /// 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| { self.write(move |conn| {
conn.with_savepoint("update_worktrees", || { conn.with_savepoint("update_worktrees", || {
// Clear out panes and pane_groups // Clear out panes and pane_groups
@ -367,7 +487,7 @@ impl WorkspaceDb {
type GroupKey = (Option<GroupId>, WorkspaceId); type GroupKey = (Option<GroupId>, WorkspaceId);
type GroupOrPane = ( type GroupOrPane = (
Option<GroupId>, Option<GroupId>,
Option<Axis>, Option<SerializedAxis>,
Option<PaneId>, Option<PaneId>,
Option<bool>, Option<bool>,
Option<String>, Option<String>,
@ -536,7 +656,7 @@ impl WorkspaceDb {
} }
query! { 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 UPDATE workspaces
SET window_state = ?2, SET window_state = ?2,
window_x = ?3, window_x = ?3,
@ -683,7 +803,7 @@ mod tests {
fn group(axis: Axis, children: Vec<SerializedPaneGroup>) -> SerializedPaneGroup { fn group(axis: Axis, children: Vec<SerializedPaneGroup>) -> SerializedPaneGroup {
SerializedPaneGroup::Group { SerializedPaneGroup::Group {
axis, axis: SerializedAxis(axis),
flexes: None, flexes: None,
children, children,
} }

View File

@ -1,3 +1,4 @@
use super::SerializedAxis;
use crate::{item::ItemHandle, ItemDeserializers, Member, Pane, PaneAxis, Workspace, WorkspaceId}; use crate::{item::ItemHandle, ItemDeserializers, Member, Pane, PaneAxis, Workspace, WorkspaceId};
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use async_recursion::async_recursion; use async_recursion::async_recursion;
@ -5,7 +6,7 @@ use db::sqlez::{
bindable::{Bind, Column, StaticColumnCount}, bindable::{Bind, Column, StaticColumnCount},
statement::Statement, statement::Statement,
}; };
use gpui::{AsyncWindowContext, Axis, Model, Task, View, WeakView, WindowBounds}; use gpui::{AsyncWindowContext, Model, Task, View, WeakView, WindowBounds};
use project::Project; use project::Project;
use std::{ use std::{
path::{Path, PathBuf}, path::{Path, PathBuf},
@ -54,13 +55,13 @@ impl Column for WorkspaceLocation {
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct SerializedWorkspace { pub(crate) struct SerializedWorkspace {
pub id: WorkspaceId, pub(crate) id: WorkspaceId,
pub location: WorkspaceLocation, pub(crate) location: WorkspaceLocation,
pub center_group: SerializedPaneGroup, pub(crate) center_group: SerializedPaneGroup,
pub bounds: Option<WindowBounds>, pub(crate) bounds: Option<WindowBounds>,
pub display: Option<Uuid>, pub(crate) display: Option<Uuid>,
pub docks: DockStructure, pub(crate) docks: DockStructure,
} }
#[derive(Debug, PartialEq, Clone, Default)] #[derive(Debug, PartialEq, Clone, Default)]
@ -126,9 +127,9 @@ impl Bind for DockData {
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum SerializedPaneGroup { pub(crate) enum SerializedPaneGroup {
Group { Group {
axis: Axis, axis: SerializedAxis,
flexes: Option<Vec<f32>>, flexes: Option<Vec<f32>>,
children: Vec<SerializedPaneGroup>, children: Vec<SerializedPaneGroup>,
}, },
@ -183,7 +184,7 @@ impl SerializedPaneGroup {
} }
Some(( Some((
Member::Axis(PaneAxis::load(axis, members, flexes)), Member::Axis(PaneAxis::load(axis.0, members, flexes)),
current_active_pane, current_active_pane,
items, items,
)) ))

View File

@ -42,9 +42,9 @@ use node_runtime::NodeRuntime;
use notifications::{simple_message_notification::MessageNotification, NotificationHandle}; use notifications::{simple_message_notification::MessageNotification, NotificationHandle};
pub use pane::*; pub use pane::*;
pub use pane_group::*; pub use pane_group::*;
use persistence::DB; use persistence::{model::SerializedWorkspace, SerializedWindowsBounds, DB};
pub use persistence::{ pub use persistence::{
model::{ItemId, SerializedWorkspace, WorkspaceLocation}, model::{ItemId, WorkspaceLocation},
WorkspaceDb, DB as WORKSPACE_DB, WorkspaceDb, DB as WORKSPACE_DB,
}; };
use postage::stream::Stream; use postage::stream::Stream;
@ -70,8 +70,9 @@ use util::ResultExt;
use uuid::Uuid; use uuid::Uuid;
pub use workspace_settings::{AutosaveSetting, WorkspaceSettings}; pub use workspace_settings::{AutosaveSetting, WorkspaceSettings};
use crate::persistence::model::{ use crate::persistence::{
DockData, DockStructure, SerializedItem, SerializedPane, SerializedPaneGroup, model::{DockData, DockStructure, SerializedItem, SerializedPane, SerializedPaneGroup},
SerializedAxis,
}; };
lazy_static! { lazy_static! {
@ -625,7 +626,11 @@ impl Workspace {
if let Some(display_uuid) = display.uuid().log_err() { if let Some(display_uuid) = display.uuid().log_err() {
cx.background_executor() 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); .detach_and_log_err(cx);
} }
} }
@ -1187,7 +1192,7 @@ impl Workspace {
mut save_intent: SaveIntent, mut save_intent: SaveIntent,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> Task<Result<bool>> { ) -> Task<Result<bool>> {
if self.project.read(cx).is_read_only() { if self.project.read(cx).is_disconnected() {
return Task::ready(Ok(true)); return Task::ready(Ok(true));
} }
let dirty_items = self let dirty_items = self
@ -2510,7 +2515,7 @@ impl Workspace {
} }
fn update_window_edited(&mut self, cx: &mut ViewContext<Self>) { 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 && self
.items(cx) .items(cx)
.any(|item| item.has_conflict(cx) || item.is_dirty(cx)); .any(|item| item.has_conflict(cx) || item.is_dirty(cx));
@ -2989,7 +2994,7 @@ impl Workspace {
flexes, flexes,
bounding_boxes: _, bounding_boxes: _,
}) => SerializedPaneGroup::Group { }) => SerializedPaneGroup::Group {
axis: *axis, axis: SerializedAxis(*axis),
children: members children: members
.iter() .iter()
.map(|member| build_serialized_pane_group(member, cx)) .map(|member| build_serialized_pane_group(member, cx))
@ -3266,6 +3271,7 @@ impl Workspace {
let user_store = project.read(cx).user_store(); let user_store = project.read(cx).user_store();
let workspace_store = cx.new_model(|cx| WorkspaceStore::new(client.clone(), cx)); let workspace_store = cx.new_model(|cx| WorkspaceStore::new(client.clone(), cx));
cx.activate_window();
let app_state = Arc::new(AppState { let app_state = Arc::new(AppState {
languages: project.read(cx).languages().clone(), languages: project.read(cx).languages().clone(),
workspace_store, workspace_store,
@ -3634,7 +3640,7 @@ impl Render for Workspace {
})), })),
) )
.child(self.status_bar.clone()) .child(self.status_bar.clone())
.children(if self.project.read(cx).is_read_only() { .children(if self.project.read(cx).is_disconnected() {
Some(DisconnectedOverlay) Some(DisconnectedOverlay)
} else { } else {
None None
@ -4764,8 +4770,7 @@ mod tests {
}); });
// Deactivating the window saves the file. // Deactivating the window saves the file.
cx.simulate_deactivation(); cx.deactivate_window();
cx.executor().run_until_parked();
item.update(cx, |item, _| assert_eq!(item.save_count, 1)); item.update(cx, |item, _| assert_eq!(item.save_count, 1));
// Autosave on focus change. // Autosave on focus change.
@ -4785,14 +4790,13 @@ mod tests {
item.update(cx, |item, _| assert_eq!(item.save_count, 2)); item.update(cx, |item, _| assert_eq!(item.save_count, 2));
// Deactivating the window still saves the file. // Deactivating the window still saves the file.
cx.simulate_activation(); cx.update(|cx| cx.activate_window());
item.update(cx, |item, cx| { item.update(cx, |item, cx| {
cx.focus_self(); cx.focus_self();
item.is_dirty = true; 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)); item.update(cx, |item, _| assert_eq!(item.save_count, 3));
// Autosave after delay. // Autosave after delay.

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`.

View File

@ -4,17 +4,12 @@ set -eu
source script/lib/deploy-helpers.sh source script/lib/deploy-helpers.sh
if [[ $# < 2 ]]; then 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 exit 1
fi fi
environment=$1 environment=$1
version=$2 version=$2
if [[ ${environment} == "nightly" ]]; then
echo "nightly is not yet supported"
exit 1
fi
export_vars_for_environment ${environment} export_vars_for_environment ${environment}
image_id=$(image_id_for_version ${version}) image_id=$(image_id_for_version ${version})

View File

@ -9,10 +9,7 @@ OUTPUT_FILE=$(pwd)/assets/licenses.md
echo -e "# ###### THEME LICENSES ######\n" >> $OUTPUT_FILE echo -e "# ###### THEME LICENSES ######\n" >> $OUTPUT_FILE
echo "Generating theme licenses" echo "Generating theme licenses"
cd styles cat crates/theme/src/themes/LICENSES >> $OUTPUT_FILE
npm --silent ci
npm run --silent build-licenses >> $OUTPUT_FILE
cd ..
echo -e "# ###### CODE LICENSES ######\n" >> $OUTPUT_FILE echo -e "# ###### CODE LICENSES ######\n" >> $OUTPUT_FILE

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
styles/.gitignore vendored
View File

@ -1,2 +0,0 @@
node_modules/
coverage/

View File

@ -1,3 +0,0 @@
package-lock.json
package.json
target

View File

@ -1,6 +0,0 @@
{
"semi": false,
"printWidth": 80,
"htmlWhitespaceSensitivity": "strict",
"tabWidth": 4
}

View File

@ -1,20 +0,0 @@
// Folder-specific settings
//
// For a full list of overridable settings, and general information on folder-specific settings,
// see the documentation: https://docs.zed.dev/configuration/configuring-zed#folder-specific-settings
{
"languages": {
"TypeScript": {
"tab_size": 4
},
"TSX": {
"tab_size": 4
},
"JavaScript": {
"tab_size": 4
},
"JSON": {
"tab_size": 4
}
}
}

4372
styles/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,37 +0,0 @@
{
"name": "styles",
"version": "1.0.0",
"description": "Typescript app that builds Zed's themes",
"main": "./src/build_themes.ts",
"scripts": {
"build": "ts-node ./src/build_themes.ts",
"build-licenses": "ts-node ./src/build_licenses.ts",
"build-tokens": "ts-node ./src/build_tokens.ts",
"build-types": "ts-node ./src/build_types.ts",
"test": "vitest"
},
"author": "Zed Industries (https://github.com/zed-industries/)",
"license": "ISC",
"dependencies": {
"@tokens-studio/types": "^0.2.3",
"@types/chroma-js": "^2.4.0",
"@types/node": "^18.14.1",
"@typescript-eslint/eslint-plugin": "^5.60.1",
"@typescript-eslint/parser": "^5.60.1",
"@vitest/coverage-v8": "^0.32.0",
"ayu": "^8.0.1",
"chroma-js": "^2.4.2",
"deepmerge": "^4.3.0",
"eslint": "^8.43.0",
"eslint-import-resolver-typescript": "^3.5.5",
"eslint-plugin-import": "^2.27.5",
"json-schema-to-typescript": "^13.0.2",
"toml": "^3.0.0",
"ts-deepmerge": "^6.0.3",
"ts-node": "^10.9.1",
"typescript": "^5.1.5",
"utility-types": "^3.10.0",
"vitest": "^0.32.0",
"zustand": "^4.3.8"
}
}

View File

@ -1,50 +0,0 @@
import * as fs from "fs"
import toml from "toml"
import { themes } from "./themes"
import { ThemeConfig } from "./common"
const ACCEPTED_LICENSES_FILE = `${__dirname}/../../script/licenses/zed-licenses.toml`
// Use the cargo-about configuration file as the source of truth for supported licenses.
function parse_accepted_toml(file: string): string[] {
const buffer = fs.readFileSync(file).toString()
const obj = toml.parse(buffer)
if (!Array.isArray(obj.accepted)) {
throw Error("Accepted license source is malformed")
}
return obj.accepted
}
function check_licenses(themes: ThemeConfig[]) {
for (const theme of themes) {
if (!theme.license_file) {
throw Error(`Theme ${theme.name} should have a LICENSE file`)
}
}
}
function generate_license_file(themes: ThemeConfig[]) {
check_licenses(themes)
for (const theme of themes) {
const license_text = fs.readFileSync(theme.license_file).toString()
write_license(theme.name, license_text, theme.license_url)
}
}
function write_license(
theme_name: string,
license_text: string,
license_url?: string
) {
process.stdout.write(
license_url
? `## [${theme_name}](${license_url})\n\n${license_text}\n********************************************************************************\n\n`
: `## ${theme_name}\n\n${license_text}\n********************************************************************************\n\n`
)
}
const accepted_licenses = parse_accepted_toml(ACCEPTED_LICENSES_FILE)
generate_license_file(themes)

View File

@ -1,45 +0,0 @@
import * as fs from "fs"
import { tmpdir } from "os"
import * as path from "path"
import app from "./style_tree/app"
import { Theme, create_theme } from "./theme/create_theme"
import { themes } from "./themes"
import { useThemeStore } from "./theme"
const assets_directory = `${__dirname}/../../assets`
const temp_directory = fs.mkdtempSync(path.join(tmpdir(), "build-themes"))
function clear_themes(theme_directory: string) {
if (!fs.existsSync(theme_directory)) {
fs.mkdirSync(theme_directory, { recursive: true })
} else {
for (const file of fs.readdirSync(theme_directory)) {
if (file.endsWith(".json")) {
fs.unlinkSync(path.join(theme_directory, file))
}
}
}
}
const all_themes: Theme[] = themes.map((theme) => create_theme(theme))
function write_themes(themes: Theme[], output_directory: string) {
clear_themes(output_directory)
for (const theme of themes) {
const { setTheme } = useThemeStore.getState()
setTheme(theme)
const style_tree = app()
// Nathan: New elements will read directly from the theme colors.
// Adding this during the transition. Afterwards, we can port all themes to Rust.
style_tree.base_theme = theme
const style_tree_json = JSON.stringify(style_tree, null, 2)
const temp_path = path.join(temp_directory, `${theme.name}.json`)
const out_path = path.join(output_directory, `${theme.name}.json`)
fs.writeFileSync(temp_path, style_tree_json)
fs.renameSync(temp_path, out_path)
console.log(`- ${out_path} created`)
}
}
write_themes(all_themes, `${assets_directory}/themes`)

View File

@ -1,88 +0,0 @@
import * as fs from "fs"
import * as path from "path"
import { Theme, create_theme, useThemeStore } from "./common"
import { themes } from "./themes"
import { slugify } from "./utils/slugify"
import { theme_tokens } from "./theme/tokens/theme"
const TOKENS_DIRECTORY = path.join(__dirname, "..", "target", "tokens")
const TOKENS_FILE = path.join(TOKENS_DIRECTORY, "$themes.json")
const METADATA_FILE = path.join(TOKENS_DIRECTORY, "$metadata.json")
function clear_tokens(tokens_directory: string) {
if (!fs.existsSync(tokens_directory)) {
fs.mkdirSync(tokens_directory, { recursive: true })
} else {
for (const file of fs.readdirSync(tokens_directory)) {
if (file.endsWith(".json")) {
fs.unlinkSync(path.join(tokens_directory, file))
}
}
}
}
type TokenSet = {
id: string
name: string
selected_token_sets: { [key: string]: "enabled" }
}
function build_token_set_order(theme: Theme[]): {
token_set_order: string[]
} {
const token_set_order: string[] = theme.map((scheme) =>
scheme.name.toLowerCase().replace(/\s+/g, "_")
)
return { token_set_order }
}
function build_themes_index(theme: Theme[]): TokenSet[] {
const themes_index: TokenSet[] = theme.map((scheme, index) => {
const id = `${scheme.is_light ? "light" : "dark"}_${scheme.name
.toLowerCase()
.replace(/\s+/g, "_")}_${index}`
const selected_token_sets: { [key: string]: "enabled" } = {}
const token_set = scheme.name.toLowerCase().replace(/\s+/g, "_")
selected_token_sets[token_set] = "enabled"
return {
id,
name: `${scheme.name} - ${scheme.is_light ? "Light" : "Dark"}`,
selected_token_sets,
}
})
return themes_index
}
function write_tokens(themes: Theme[], tokens_directory: string) {
clear_tokens(tokens_directory)
for (const theme of themes) {
const { setTheme } = useThemeStore.getState()
setTheme(theme)
const file_name = slugify(theme.name) + ".json"
const tokens = theme_tokens()
const tokens_json = JSON.stringify(tokens, null, 2)
const out_path = path.join(tokens_directory, file_name)
fs.writeFileSync(out_path, tokens_json, { mode: 0o644 })
console.log(`- ${out_path} created`)
}
const theme_index_data = build_themes_index(themes)
const themes_json = JSON.stringify(theme_index_data, null, 2)
fs.writeFileSync(TOKENS_FILE, themes_json, { mode: 0o644 })
console.log(`- ${TOKENS_FILE} created`)
const token_set_order_data = build_token_set_order(themes)
const metadata_json = JSON.stringify(token_set_order_data, null, 2)
fs.writeFileSync(METADATA_FILE, metadata_json, { mode: 0o644 })
console.log(`- ${METADATA_FILE} created`)
}
const all_themes: Theme[] = themes.map((theme) => create_theme(theme))
write_tokens(all_themes, TOKENS_DIRECTORY)

View File

@ -1,62 +0,0 @@
import * as fs from "fs/promises"
import * as fsSync from "fs"
import * as path from "path"
import { compile } from "json-schema-to-typescript"
const BANNER = `/*
* This file is autogenerated
*/\n\n`
const dirname = __dirname
async function main() {
const schemas_path = path.join(dirname, "../../", "crates/theme/schemas")
const schema_files = (await fs.readdir(schemas_path)).filter((x) =>
x.endsWith(".json")
)
const compiled_types = new Set()
for (const filename of schema_files) {
const file_path = path.join(schemas_path, filename)
const file_contents = await fs.readFile(file_path)
const schema = JSON.parse(file_contents.toString())
const compiled = await compile(schema, schema.title, {
bannerComment: "",
})
const each_type = compiled.split("export")
for (const type of each_type) {
if (!type) {
continue
}
compiled_types.add("export " + type.trim())
}
}
const output = BANNER + Array.from(compiled_types).join("\n\n")
const output_path = path.join(dirname, "../../styles/src/types/zed.ts")
try {
const existing = await fs.readFile(output_path)
if (existing.toString() == output) {
// Skip writing if it hasn't changed
console.log("Schemas are up to date")
return
}
} catch (e) {
if (e.code !== "ENOENT") {
throw e
}
}
const types_dic = path.dirname(output_path)
if (!fsSync.existsSync(types_dic)) {
await fs.mkdir(types_dic)
}
await fs.writeFile(output_path, output)
console.log(`Wrote Typescript types to ${output_path}`)
}
main().catch((e) => {
console.error(e)
process.exit(1)
})

View File

@ -1,34 +0,0 @@
import chroma from "chroma-js"
export * from "./theme"
export * from "./theme/theme_config"
export { chroma }
export const font_families = {
ui_sans: "IBM Plex Sans",
sans: "Zed Sans",
mono: "Zed Mono",
}
export const font_sizes = {
"2xs": 10,
xs: 12,
sm: 14,
md: 16,
lg: 18,
}
export type FontWeight = "normal" | "bold"
export const font_weights: { [key: string]: FontWeight } = {
normal: "normal",
bold: "bold",
}
export const sizes = {
px: 1,
xs: 2,
sm: 4,
md: 6,
lg: 8,
xl: 12,
}

View File

@ -1,127 +0,0 @@
import { font_sizes, useTheme } from "../common"
import { Layer, Theme } from "../theme"
import { TextStyle, background } from "../style_tree/components"
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Button {
export type Options = {
layer: Layer
background: keyof Theme["lowest"]
color: keyof Theme["lowest"]
variant: Button.Variant
size: Button.Size
shape: Button.Shape
margin: {
top?: number
bottom?: number
left?: number
right?: number
}
states: {
enabled?: boolean
hovered?: boolean
pressed?: boolean
focused?: boolean
disabled?: boolean
}
}
export type ToggleableOptions = Options & {
active_background: keyof Theme["lowest"]
active_color: keyof Theme["lowest"]
}
/** Padding added to each side of a Shape.Rectangle button */
export const RECTANGLE_PADDING = 2
export const FONT_SIZE = font_sizes.sm
export const ICON_SIZE = 14
export const CORNER_RADIUS = 6
export const variant = {
Default: "filled",
Outline: "outline",
Ghost: "ghost",
} as const
export type Variant = (typeof variant)[keyof typeof variant]
export const shape = {
Rectangle: "rectangle",
Square: "square",
} as const
export type Shape = (typeof shape)[keyof typeof shape]
export const size = {
Small: "sm",
Medium: "md",
} as const
export type Size = (typeof size)[keyof typeof size]
export type BaseStyle = {
corder_radius: number
background: string | null
padding: {
top: number
bottom: number
left: number
right: number
}
margin: Button.Options["margin"]
button_height: number
}
export type LabelButtonStyle = BaseStyle & TextStyle
// export type IconButtonStyle = ButtonStyle
export const button_base = (
options: Partial<Button.Options> = {
variant: Button.variant.Default,
shape: Button.shape.Rectangle,
states: {
hovered: true,
pressed: true,
},
}
): BaseStyle => {
const theme = useTheme()
const layer = options.layer ?? theme.middle
const color = options.color ?? "base"
const background_color =
options.variant === Button.variant.Ghost
? null
: background(layer, options.background ?? color)
const m = {
top: options.margin?.top ?? 0,
bottom: options.margin?.bottom ?? 0,
left: options.margin?.left ?? 0,
right: options.margin?.right ?? 0,
}
const size = options.size || Button.size.Medium
const padding = 2
const base: BaseStyle = {
background: background_color,
corder_radius: Button.CORNER_RADIUS,
padding: {
top: padding,
bottom: padding,
left:
options.shape === Button.shape.Rectangle
? padding + Button.RECTANGLE_PADDING
: padding,
right:
options.shape === Button.shape.Rectangle
? padding + Button.RECTANGLE_PADDING
: padding,
},
margin: m,
button_height: 16,
}
return base
}
}

View File

@ -1,111 +0,0 @@
import { interactive, toggleable } from "../element"
import { background, foreground } from "../style_tree/components"
import { useTheme, Theme, Layer } from "../theme"
import { Button } from "./button"
export type Margin = {
top: number
bottom: number
left: number
right: number
}
interface IconButtonOptions {
layer?: Theme["lowest"] | Theme["middle"] | Theme["highest"]
color?: keyof Theme["lowest"]
background_color?: keyof Theme["lowest"]
margin?: Partial<Margin>
variant?: Button.Variant
size?: Button.Size
}
type ToggleableIconButtonOptions = IconButtonOptions & {
active_color?: keyof Theme["lowest"]
active_background_color?: keyof Theme["lowest"]
active_layer?: Layer
active_variant?: Button.Variant
}
export function icon_button(
{ color, background_color, margin, layer, variant, size }: IconButtonOptions = {
variant: Button.variant.Default,
size: Button.size.Medium,
}
) {
const theme = useTheme()
if (!color) color = "base"
const default_background =
variant === Button.variant.Ghost
? null
: background(layer ?? theme.lowest, background_color ?? color)
const m = {
top: margin?.top ?? 0,
bottom: margin?.bottom ?? 0,
left: margin?.left ?? 0,
right: margin?.right ?? 0,
}
const padding = {
top: size === Button.size.Small ? 2 : 2,
bottom: size === Button.size.Small ? 2 : 2,
left: size === Button.size.Small ? 2 : 4,
right: size === Button.size.Small ? 2 : 4,
}
return interactive({
base: {
corner_radius: 6,
padding: padding,
margin: m,
icon_width: 14,
icon_height: 14,
button_width: size === Button.size.Small ? 16 : 20,
button_height: 14,
},
state: {
default: {
background: default_background,
color: foreground(layer ?? theme.lowest, color),
},
hovered: {
background: background(layer ?? theme.lowest, background_color ?? color, "hovered"),
color: foreground(layer ?? theme.lowest, color, "hovered"),
},
clicked: {
background: background(layer ?? theme.lowest, background_color ?? color, "pressed"),
color: foreground(layer ?? theme.lowest, color, "pressed"),
},
},
})
}
export function toggleable_icon_button({
color,
background_color,
active_color,
active_background_color,
active_variant,
margin,
variant,
size,
active_layer,
}: ToggleableIconButtonOptions) {
if (!color) color = "base"
return toggleable({
state: {
inactive: icon_button({ color, background_color, margin, variant, size }),
active: icon_button({
color: active_color ? active_color : color,
background_color: active_background_color ? active_background_color : background_color,
margin,
layer: active_layer,
variant: active_variant || variant,
size,
}),
},
})
}

View File

@ -1,6 +0,0 @@
export * from "./icon_button"
export * from "./indicator"
export * from "./input"
export * from "./tab"
export * from "./tab_bar_button"
export * from "./text_button"

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