Merge branch 'main' into remove-old-theme
This commit is contained in:
Generated
+12
-10
@@ -576,8 +576,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-task"
|
||||
version = "4.0.3"
|
||||
source = "git+https://github.com/zed-industries/async-task?rev=341b57d6de98cdfd7b418567b8de2022ca993a6e#341b57d6de98cdfd7b418567b8de2022ca993a6e"
|
||||
version = "4.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799"
|
||||
|
||||
[[package]]
|
||||
name = "async-tls"
|
||||
@@ -1440,7 +1441,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "collab"
|
||||
version = "0.33.0"
|
||||
version = "0.34.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -1925,11 +1926,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ctor"
|
||||
version = "0.1.20"
|
||||
source = "git+https://github.com/zed-industries/rust-ctor?rev=7f824cf6a7943885a649b579f33f9ac53f0d1db6#7f824cf6a7943885a649b579f33f9ac53f0d1db6"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30d2b3721e861707777e3195b0158f950ae6dc4a27e4d02ff9f67e3eb3de199e"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.37",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2486,7 +2488,6 @@ dependencies = [
|
||||
"postage",
|
||||
"project",
|
||||
"regex",
|
||||
"search",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"settings",
|
||||
@@ -3065,7 +3066,6 @@ dependencies = [
|
||||
"slotmap",
|
||||
"smallvec",
|
||||
"smol",
|
||||
"sqlez",
|
||||
"sum_tree",
|
||||
"taffy",
|
||||
"thiserror",
|
||||
@@ -6846,7 +6846,6 @@ dependencies = [
|
||||
"serde_json",
|
||||
"serde_json_lenient",
|
||||
"smallvec",
|
||||
"sqlez",
|
||||
"toml 0.5.11",
|
||||
"tree-sitter",
|
||||
"tree-sitter-json 0.19.0",
|
||||
@@ -7762,6 +7761,7 @@ dependencies = [
|
||||
"procinfo",
|
||||
"project",
|
||||
"rand 0.8.5",
|
||||
"search",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"settings",
|
||||
@@ -7837,6 +7837,7 @@ dependencies = [
|
||||
"convert_case 0.6.0",
|
||||
"gpui",
|
||||
"indexmap 1.9.3",
|
||||
"indoc",
|
||||
"json_comments",
|
||||
"log",
|
||||
"palette",
|
||||
@@ -8488,7 +8489,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "tree-sitter-nu"
|
||||
version = "0.0.1"
|
||||
source = "git+https://github.com/nushell/tree-sitter-nu?rev=a0b80b2e21e5e39571252dc799e19eb89f1fc912#a0b80b2e21e5e39571252dc799e19eb89f1fc912"
|
||||
source = "git+https://github.com/nushell/tree-sitter-nu?rev=26bbaecda0039df4067861ab38ea8ea169f7f5aa#26bbaecda0039df4067861ab38ea8ea169f7f5aa"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"tree-sitter",
|
||||
@@ -9438,6 +9439,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"settings",
|
||||
"smallvec",
|
||||
"sqlez",
|
||||
"terminal",
|
||||
"theme",
|
||||
"ui",
|
||||
|
||||
+2
-6
@@ -92,10 +92,7 @@ resolver = "2"
|
||||
anyhow = { version = "1.0.57" }
|
||||
async-trait = { version = "0.1" }
|
||||
async-compression = { version = "0.4", features = ["gzip", "futures-io"] }
|
||||
# TODO: Switch back to the published version of `ctor` once:
|
||||
# 1. A new version of `ctor` is published with this change: https://github.com/mmastrac/rust-ctor/pull/295
|
||||
# 2. We've confirmed it's fine to update to the latest version of `ctor` (we're currently on v0.1.20).
|
||||
ctor = { git = "https://github.com/zed-industries/rust-ctor", rev = "7f824cf6a7943885a649b579f33f9ac53f0d1db6" }
|
||||
ctor = "0.2.6"
|
||||
derive_more = { version = "0.99.17" }
|
||||
env_logger = { version = "0.9" }
|
||||
futures = { version = "0.3" }
|
||||
@@ -158,13 +155,12 @@ tree-sitter-racket = { git = "https://github.com/zed-industries/tree-sitter-rack
|
||||
tree-sitter-yaml = { git = "https://github.com/zed-industries/tree-sitter-yaml", rev = "f545a41f57502e1b5ddf2a6668896c1b0620f930"}
|
||||
tree-sitter-lua = "0.0.14"
|
||||
tree-sitter-nix = { git = "https://github.com/nix-community/tree-sitter-nix", rev = "66e3e9ce9180ae08fc57372061006ef83f0abde7" }
|
||||
tree-sitter-nu = { git = "https://github.com/nushell/tree-sitter-nu", rev = "a0b80b2e21e5e39571252dc799e19eb89f1fc912"}
|
||||
tree-sitter-nu = { git = "https://github.com/nushell/tree-sitter-nu", rev = "26bbaecda0039df4067861ab38ea8ea169f7f5aa"}
|
||||
tree-sitter-vue = {git = "https://github.com/zed-industries/tree-sitter-vue", rev = "6608d9d60c386f19d80af7d8132322fa11199c42"}
|
||||
tree-sitter-uiua = {git = "https://github.com/shnarazk/tree-sitter-uiua", rev = "9260f11be5900beda4ee6d1a24ab8ddfaf5a19b2"}
|
||||
|
||||
[patch.crates-io]
|
||||
tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "31c40449749c4263a91a43593831b82229049a4c" }
|
||||
async-task = { git = "https://github.com/zed-industries/async-task", rev = "341b57d6de98cdfd7b418567b8de2022ca993a6e" }
|
||||
# wasmtime = { git = "https://github.com/bytecodealliance/wasmtime", rev = "v16.0.0" }
|
||||
|
||||
# TODO - Remove when a version is released with this PR: https://github.com/servo/core-foundation-rs/pull/457
|
||||
|
||||
@@ -402,7 +402,7 @@
|
||||
"cmd-r": "workspace::ToggleRightDock",
|
||||
"cmd-j": "workspace::ToggleBottomDock",
|
||||
"alt-cmd-y": "workspace::CloseAllDocks",
|
||||
"cmd-shift-f": "workspace::NewSearch",
|
||||
"cmd-shift-f": "workspace::DeploySearch",
|
||||
"cmd-k cmd-t": "theme_selector::Toggle",
|
||||
"cmd-k cmd-s": "zed::OpenKeymap",
|
||||
"cmd-t": "project_symbols::Toggle",
|
||||
|
||||
@@ -38,7 +38,7 @@ use gpui::{
|
||||
};
|
||||
use language::{language_settings::SoftWrap, Buffer, LanguageRegistry, ToOffset as _};
|
||||
use project::Project;
|
||||
use search::BufferSearchBar;
|
||||
use search::{buffer_search::DivRegistrar, BufferSearchBar};
|
||||
use semantic_index::{SemanticIndex, SemanticIndexStatus};
|
||||
use settings::{Settings, SettingsStore};
|
||||
use std::{
|
||||
@@ -1156,7 +1156,18 @@ impl Render for AssistantPanel {
|
||||
div()
|
||||
});
|
||||
|
||||
let contents = if self.active_editor().is_some() {
|
||||
let mut registrar = DivRegistrar::new(
|
||||
|panel, cx| panel.toolbar.read(cx).item_of_type::<BufferSearchBar>(),
|
||||
cx,
|
||||
);
|
||||
BufferSearchBar::register_inner(&mut registrar);
|
||||
registrar.into_div()
|
||||
} else {
|
||||
div()
|
||||
};
|
||||
v_stack()
|
||||
.key_context("AssistantPanel")
|
||||
.size_full()
|
||||
.on_action(cx.listener(|this, _: &workspace::NewFile, cx| {
|
||||
this.new_conversation(cx);
|
||||
@@ -1175,7 +1186,7 @@ impl Render for AssistantPanel {
|
||||
Some(self.toolbar.clone())
|
||||
})
|
||||
.child(
|
||||
div()
|
||||
contents
|
||||
.flex_1()
|
||||
.child(if let Some(editor) = self.active_editor() {
|
||||
editor.clone().into_any_element()
|
||||
@@ -1275,8 +1286,8 @@ impl Panel for AssistantPanel {
|
||||
}
|
||||
}
|
||||
|
||||
fn icon(&self, _cx: &WindowContext) -> Option<Icon> {
|
||||
Some(Icon::Ai)
|
||||
fn icon(&self, cx: &WindowContext) -> Option<Icon> {
|
||||
Some(Icon::Ai).filter(|_| AssistantSettings::get_global(cx).button)
|
||||
}
|
||||
|
||||
fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> {
|
||||
@@ -2816,8 +2827,8 @@ impl InlineAssistant {
|
||||
|
||||
fn handle_codegen_changed(&mut self, _: Model<Codegen>, cx: &mut ViewContext<Self>) {
|
||||
let is_read_only = !self.codegen.read(cx).idle();
|
||||
self.prompt_editor.update(cx, |editor, _cx| {
|
||||
let was_read_only = editor.read_only();
|
||||
self.prompt_editor.update(cx, |editor, cx| {
|
||||
let was_read_only = editor.read_only(cx);
|
||||
if was_read_only != is_read_only {
|
||||
if is_read_only {
|
||||
editor.set_read_only(true);
|
||||
@@ -3052,7 +3063,7 @@ impl InlineAssistant {
|
||||
fn render_prompt_editor(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
let settings = ThemeSettings::get_global(cx);
|
||||
let text_style = TextStyle {
|
||||
color: if self.prompt_editor.read(cx).read_only() {
|
||||
color: if self.prompt_editor.read(cx).read_only(cx) {
|
||||
cx.theme().colors().text_disabled
|
||||
} else {
|
||||
cx.theme().colors().text
|
||||
|
||||
@@ -270,7 +270,7 @@ impl AutoUpdater {
|
||||
ReleaseChannel::Nightly => cx
|
||||
.try_read_global::<AppCommitSha, _>(|sha, _| release.version != sha.0)
|
||||
.unwrap_or(true),
|
||||
_ => release.version.parse::<SemanticVersion>()? <= current_version,
|
||||
_ => release.version.parse::<SemanticVersion>()? > current_version,
|
||||
};
|
||||
|
||||
if !should_download {
|
||||
|
||||
@@ -36,12 +36,14 @@ impl ParticipantLocation {
|
||||
pub struct LocalParticipant {
|
||||
pub projects: Vec<proto::ParticipantProject>,
|
||||
pub active_project: Option<WeakModel<Project>>,
|
||||
pub role: proto::ChannelRole,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RemoteParticipant {
|
||||
pub user: Arc<User>,
|
||||
pub peer_id: proto::PeerId,
|
||||
pub role: proto::ChannelRole,
|
||||
pub projects: Vec<proto::ParticipantProject>,
|
||||
pub location: ParticipantLocation,
|
||||
pub participant_index: ParticipantIndex,
|
||||
|
||||
+52
-6
@@ -247,14 +247,18 @@ impl Room {
|
||||
let response = client.request(proto::CreateRoom {}).await?;
|
||||
let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?;
|
||||
let room = cx.new_model(|cx| {
|
||||
Self::new(
|
||||
let mut room = Self::new(
|
||||
room_proto.id,
|
||||
None,
|
||||
response.live_kit_connection_info,
|
||||
client,
|
||||
user_store,
|
||||
cx,
|
||||
)
|
||||
);
|
||||
if let Some(participant) = room_proto.participants.first() {
|
||||
room.local_participant.role = participant.role()
|
||||
}
|
||||
room
|
||||
})?;
|
||||
|
||||
let initial_project_id = if let Some(initial_project) = initial_project {
|
||||
@@ -606,6 +610,16 @@ impl Room {
|
||||
.find(|p| p.peer_id == peer_id)
|
||||
}
|
||||
|
||||
pub fn role_for_user(&self, user_id: u64) -> Option<proto::ChannelRole> {
|
||||
self.remote_participants
|
||||
.get(&user_id)
|
||||
.map(|participant| participant.role)
|
||||
}
|
||||
|
||||
pub fn local_participant_is_admin(&self) -> bool {
|
||||
self.local_participant.role == proto::ChannelRole::Admin
|
||||
}
|
||||
|
||||
pub fn pending_participants(&self) -> &[Arc<User>] {
|
||||
&self.pending_participants
|
||||
}
|
||||
@@ -710,7 +724,20 @@ impl Room {
|
||||
this.participant_user_ids.clear();
|
||||
|
||||
if let Some(participant) = local_participant {
|
||||
let role = participant.role();
|
||||
this.local_participant.projects = participant.projects;
|
||||
if this.local_participant.role != role {
|
||||
this.local_participant.role = role;
|
||||
|
||||
this.joined_projects.retain(|project| {
|
||||
if let Some(project) = project.upgrade() {
|
||||
project.update(cx, |project, _| project.set_role(role));
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.local_participant.projects.clear();
|
||||
}
|
||||
@@ -766,6 +793,7 @@ impl Room {
|
||||
});
|
||||
}
|
||||
|
||||
let role = participant.role();
|
||||
let location = ParticipantLocation::from_proto(participant.location)
|
||||
.unwrap_or(ParticipantLocation::External);
|
||||
if let Some(remote_participant) =
|
||||
@@ -774,8 +802,11 @@ impl Room {
|
||||
remote_participant.peer_id = peer_id;
|
||||
remote_participant.projects = participant.projects;
|
||||
remote_participant.participant_index = participant_index;
|
||||
if location != remote_participant.location {
|
||||
if location != remote_participant.location
|
||||
|| role != remote_participant.role
|
||||
{
|
||||
remote_participant.location = location;
|
||||
remote_participant.role = role;
|
||||
cx.emit(Event::ParticipantLocationChanged {
|
||||
participant_id: peer_id,
|
||||
});
|
||||
@@ -789,6 +820,7 @@ impl Room {
|
||||
peer_id,
|
||||
projects: participant.projects,
|
||||
location,
|
||||
role,
|
||||
muted: true,
|
||||
speaking: false,
|
||||
video_tracks: Default::default(),
|
||||
@@ -1091,15 +1123,24 @@ impl Room {
|
||||
) -> Task<Result<Model<Project>>> {
|
||||
let client = self.client.clone();
|
||||
let user_store = self.user_store.clone();
|
||||
let role = self.local_participant.role;
|
||||
cx.emit(Event::RemoteProjectJoined { project_id: id });
|
||||
cx.spawn(move |this, mut cx| async move {
|
||||
let project =
|
||||
Project::remote(id, client, user_store, language_registry, fs, cx.clone()).await?;
|
||||
let project = Project::remote(
|
||||
id,
|
||||
client,
|
||||
user_store,
|
||||
language_registry,
|
||||
fs,
|
||||
role,
|
||||
cx.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.joined_projects.retain(|project| {
|
||||
if let Some(project) = project.upgrade() {
|
||||
!project.read(cx).is_read_only()
|
||||
!project.read(cx).is_disconnected()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
@@ -1224,6 +1265,11 @@ impl Room {
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn read_only(&self) -> bool {
|
||||
!(self.local_participant().role == proto::ChannelRole::Member
|
||||
|| self.local_participant().role == proto::ChannelRole::Admin)
|
||||
}
|
||||
|
||||
pub fn is_speaking(&self) -> bool {
|
||||
self.live_kit
|
||||
.as_ref()
|
||||
|
||||
@@ -62,7 +62,12 @@ impl ChannelBuffer {
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let buffer = cx.new_model(|_| {
|
||||
language::Buffer::remote(response.buffer_id, response.replica_id as u16, base_text)
|
||||
language::Buffer::remote(
|
||||
response.buffer_id,
|
||||
response.replica_id as u16,
|
||||
channel.channel_buffer_capability(),
|
||||
base_text,
|
||||
)
|
||||
})?;
|
||||
buffer.update(&mut cx, |buffer, cx| buffer.apply_ops(operations, cx))??;
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ use gpui::{
|
||||
AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, SharedString, Task,
|
||||
WeakModel,
|
||||
};
|
||||
use language::Capability;
|
||||
use rpc::{
|
||||
proto::{self, ChannelVisibility},
|
||||
TypedEnvelope,
|
||||
@@ -74,8 +75,12 @@ impl Channel {
|
||||
slug.trim_matches(|c| c == '-').to_string()
|
||||
}
|
||||
|
||||
pub fn can_edit_notes(&self) -> bool {
|
||||
self.role == proto::ChannelRole::Member || self.role == proto::ChannelRole::Admin
|
||||
pub fn channel_buffer_capability(&self) -> Capability {
|
||||
if self.role == proto::ChannelRole::Member || self.role == proto::ChannelRole::Admin {
|
||||
Capability::ReadWrite
|
||||
} else {
|
||||
Capability::ReadOnly
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathan@zed.dev>"]
|
||||
default-run = "collab"
|
||||
edition = "2021"
|
||||
name = "collab"
|
||||
version = "0.33.0"
|
||||
version = "0.34.0"
|
||||
publish = false
|
||||
|
||||
[[bin]]
|
||||
|
||||
@@ -13,6 +13,7 @@ metadata:
|
||||
annotations:
|
||||
service.beta.kubernetes.io/do-loadbalancer-tls-ports: "443"
|
||||
service.beta.kubernetes.io/do-loadbalancer-certificate-id: ${ZED_DO_CERTIFICATE_ID}
|
||||
service.beta.kubernetes.io/do-loadbalancer-disable-lets-encrypt-dns-records: true
|
||||
spec:
|
||||
type: LoadBalancer
|
||||
selector:
|
||||
|
||||
@@ -7,6 +7,7 @@ metadata:
|
||||
annotations:
|
||||
service.beta.kubernetes.io/do-loadbalancer-tls-ports: "443"
|
||||
service.beta.kubernetes.io/do-loadbalancer-certificate-id: ${ZED_DO_CERTIFICATE_ID}
|
||||
service.beta.kubernetes.io/do-loadbalancer-disable-lets-encrypt-dns-records: true
|
||||
spec:
|
||||
type: LoadBalancer
|
||||
selector:
|
||||
|
||||
@@ -161,7 +161,8 @@ CREATE TABLE "room_participants" (
|
||||
"calling_user_id" INTEGER NOT NULL REFERENCES users (id),
|
||||
"calling_connection_id" INTEGER NOT NULL,
|
||||
"calling_connection_server_id" INTEGER REFERENCES servers (id) ON DELETE SET NULL,
|
||||
"participant_index" INTEGER
|
||||
"participant_index" INTEGER,
|
||||
"role" TEXT
|
||||
);
|
||||
CREATE UNIQUE INDEX "index_room_participants_on_user_id" ON "room_participants" ("user_id");
|
||||
CREATE INDEX "index_room_participants_on_room_id" ON "room_participants" ("room_id");
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE room_participants ADD COLUMN role TEXT;
|
||||
@@ -132,6 +132,14 @@ impl ChannelRole {
|
||||
Admin | Member | Banned => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_share_projects(&self) -> bool {
|
||||
use ChannelRole::*;
|
||||
match self {
|
||||
Admin | Member => true,
|
||||
Guest | Banned => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<proto::ChannelRole> for ChannelRole {
|
||||
|
||||
@@ -129,51 +129,52 @@ impl Database {
|
||||
.await?,
|
||||
);
|
||||
|
||||
debug_assert!(
|
||||
self.channel_role_for_user(&channel, user_id, &*tx).await? == role
|
||||
);
|
||||
} else if channel.visibility == ChannelVisibility::Public {
|
||||
role = Some(ChannelRole::Guest);
|
||||
let channel_to_join = self
|
||||
.public_ancestors_including_self(&channel, &*tx)
|
||||
.await?
|
||||
.first()
|
||||
.cloned()
|
||||
.unwrap_or(channel.clone());
|
||||
|
||||
channel_member::Entity::insert(channel_member::ActiveModel {
|
||||
id: ActiveValue::NotSet,
|
||||
channel_id: ActiveValue::Set(channel_to_join.id),
|
||||
user_id: ActiveValue::Set(user_id),
|
||||
accepted: ActiveValue::Set(true),
|
||||
role: ActiveValue::Set(ChannelRole::Guest),
|
||||
})
|
||||
.exec(&*tx)
|
||||
.await?;
|
||||
|
||||
accept_invite_result = Some(
|
||||
self.calculate_membership_updated(&channel_to_join, user_id, &*tx)
|
||||
.await?,
|
||||
);
|
||||
|
||||
debug_assert!(
|
||||
self.channel_role_for_user(&channel, user_id, &*tx).await? == role
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if channel.visibility == ChannelVisibility::Public {
|
||||
role = Some(ChannelRole::Guest);
|
||||
let channel_to_join = self
|
||||
.public_ancestors_including_self(&channel, &*tx)
|
||||
.await?
|
||||
.first()
|
||||
.cloned()
|
||||
.unwrap_or(channel.clone());
|
||||
|
||||
channel_member::Entity::insert(channel_member::ActiveModel {
|
||||
id: ActiveValue::NotSet,
|
||||
channel_id: ActiveValue::Set(channel_to_join.id),
|
||||
user_id: ActiveValue::Set(user_id),
|
||||
accepted: ActiveValue::Set(true),
|
||||
role: ActiveValue::Set(ChannelRole::Guest),
|
||||
})
|
||||
.exec(&*tx)
|
||||
.await?;
|
||||
|
||||
accept_invite_result = Some(
|
||||
self.calculate_membership_updated(&channel_to_join, user_id, &*tx)
|
||||
.await?,
|
||||
);
|
||||
|
||||
debug_assert!(self.channel_role_for_user(&channel, user_id, &*tx).await? == role);
|
||||
}
|
||||
|
||||
if role.is_none() || role == Some(ChannelRole::Banned) {
|
||||
Err(anyhow!("not allowed"))?
|
||||
}
|
||||
let role = role.unwrap();
|
||||
|
||||
let live_kit_room = format!("channel-{}", nanoid::nanoid!(30));
|
||||
let room_id = self
|
||||
.get_or_create_channel_room(channel_id, &live_kit_room, environment, &*tx)
|
||||
.await?;
|
||||
|
||||
self.join_channel_room_internal(room_id, user_id, connection, &*tx)
|
||||
self.join_channel_room_internal(room_id, user_id, connection, role, &*tx)
|
||||
.await
|
||||
.map(|jr| (jr, accept_invite_result, role.unwrap()))
|
||||
.map(|jr| (jr, accept_invite_result, role))
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -46,6 +46,13 @@ impl Database {
|
||||
if participant.room_id != room_id {
|
||||
return Err(anyhow!("shared project on unexpected room"))?;
|
||||
}
|
||||
if !participant
|
||||
.role
|
||||
.unwrap_or(ChannelRole::Member)
|
||||
.can_share_projects()
|
||||
{
|
||||
return Err(anyhow!("guests cannot share projects"))?;
|
||||
}
|
||||
|
||||
let project = project::ActiveModel {
|
||||
room_id: ActiveValue::set(participant.room_id),
|
||||
|
||||
@@ -131,7 +131,12 @@ impl Database {
|
||||
connection.owner_id as i32,
|
||||
))),
|
||||
participant_index: ActiveValue::set(Some(0)),
|
||||
..Default::default()
|
||||
role: ActiveValue::set(Some(ChannelRole::Admin)),
|
||||
|
||||
id: ActiveValue::NotSet,
|
||||
location_kind: ActiveValue::NotSet,
|
||||
location_project_id: ActiveValue::NotSet,
|
||||
initial_project_id: ActiveValue::NotSet,
|
||||
}
|
||||
.insert(&*tx)
|
||||
.await?;
|
||||
@@ -151,6 +156,22 @@ impl Database {
|
||||
initial_project_id: Option<ProjectId>,
|
||||
) -> Result<RoomGuard<(proto::Room, proto::IncomingCall)>> {
|
||||
self.room_transaction(room_id, |tx| async move {
|
||||
let caller = room_participant::Entity::find()
|
||||
.filter(
|
||||
room_participant::Column::UserId
|
||||
.eq(calling_user_id)
|
||||
.and(room_participant::Column::RoomId.eq(room_id)),
|
||||
)
|
||||
.one(&*tx)
|
||||
.await?
|
||||
.ok_or_else(|| anyhow!("user is not in the room"))?;
|
||||
|
||||
let called_user_role = match caller.role.unwrap_or(ChannelRole::Member) {
|
||||
ChannelRole::Admin | ChannelRole::Member => ChannelRole::Member,
|
||||
ChannelRole::Guest => ChannelRole::Guest,
|
||||
ChannelRole::Banned => return Err(anyhow!("banned users cannot invite").into()),
|
||||
};
|
||||
|
||||
room_participant::ActiveModel {
|
||||
room_id: ActiveValue::set(room_id),
|
||||
user_id: ActiveValue::set(called_user_id),
|
||||
@@ -162,7 +183,13 @@ impl Database {
|
||||
calling_connection.owner_id as i32,
|
||||
))),
|
||||
initial_project_id: ActiveValue::set(initial_project_id),
|
||||
..Default::default()
|
||||
role: ActiveValue::set(Some(called_user_role)),
|
||||
|
||||
id: ActiveValue::NotSet,
|
||||
answering_connection_id: ActiveValue::NotSet,
|
||||
answering_connection_server_id: ActiveValue::NotSet,
|
||||
location_kind: ActiveValue::NotSet,
|
||||
location_project_id: ActiveValue::NotSet,
|
||||
}
|
||||
.insert(&*tx)
|
||||
.await?;
|
||||
@@ -384,6 +411,7 @@ impl Database {
|
||||
room_id: RoomId,
|
||||
user_id: UserId,
|
||||
connection: ConnectionId,
|
||||
role: ChannelRole,
|
||||
tx: &DatabaseTransaction,
|
||||
) -> Result<JoinRoom> {
|
||||
let participant_index = self
|
||||
@@ -404,7 +432,11 @@ impl Database {
|
||||
connection.owner_id as i32,
|
||||
))),
|
||||
participant_index: ActiveValue::Set(Some(participant_index)),
|
||||
..Default::default()
|
||||
role: ActiveValue::set(Some(role)),
|
||||
id: ActiveValue::NotSet,
|
||||
location_kind: ActiveValue::NotSet,
|
||||
location_project_id: ActiveValue::NotSet,
|
||||
initial_project_id: ActiveValue::NotSet,
|
||||
}])
|
||||
.on_conflict(
|
||||
OnConflict::columns([room_participant::Column::UserId])
|
||||
@@ -413,6 +445,7 @@ impl Database {
|
||||
room_participant::Column::AnsweringConnectionServerId,
|
||||
room_participant::Column::AnsweringConnectionLost,
|
||||
room_participant::Column::ParticipantIndex,
|
||||
room_participant::Column::Role,
|
||||
])
|
||||
.to_owned(),
|
||||
)
|
||||
@@ -1126,6 +1159,7 @@ impl Database {
|
||||
projects: Default::default(),
|
||||
location: Some(proto::ParticipantLocation { variant: location }),
|
||||
participant_index: participant_index as u32,
|
||||
role: db_participant.role.unwrap_or(ChannelRole::Member).into(),
|
||||
},
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::db::{ProjectId, RoomId, RoomParticipantId, ServerId, UserId};
|
||||
use crate::db::{ChannelRole, ProjectId, RoomId, RoomParticipantId, ServerId, UserId};
|
||||
use rpc::ConnectionId;
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
@@ -19,6 +19,7 @@ pub struct Model {
|
||||
pub calling_connection_id: i32,
|
||||
pub calling_connection_server_id: Option<ServerId>,
|
||||
pub participant_index: Option<i32>,
|
||||
pub role: Option<ChannelRole>,
|
||||
}
|
||||
|
||||
impl Model {
|
||||
|
||||
@@ -2,6 +2,7 @@ use call::Room;
|
||||
use gpui::{Model, TestAppContext};
|
||||
|
||||
mod channel_buffer_tests;
|
||||
mod channel_guest_tests;
|
||||
mod channel_message_tests;
|
||||
mod channel_tests;
|
||||
mod editor_tests;
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
use crate::tests::TestServer;
|
||||
use call::ActiveCall;
|
||||
use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext};
|
||||
use rpc::proto;
|
||||
use workspace::Workspace;
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_channel_guests(
|
||||
executor: BackgroundExecutor,
|
||||
cx_a: &mut TestAppContext,
|
||||
cx_b: &mut TestAppContext,
|
||||
) {
|
||||
let mut server = TestServer::start(executor.clone()).await;
|
||||
let client_a = server.create_client(cx_a, "user_a").await;
|
||||
let client_b = server.create_client(cx_b, "user_b").await;
|
||||
|
||||
let channel_id = server
|
||||
.make_channel("the-channel", None, (&client_a, cx_a), &mut [])
|
||||
.await;
|
||||
|
||||
client_a
|
||||
.channel_store()
|
||||
.update(cx_a, |channel_store, cx| {
|
||||
channel_store.set_channel_visibility(channel_id, proto::ChannelVisibility::Public, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
client_a
|
||||
.fs()
|
||||
.insert_tree(
|
||||
"/a",
|
||||
serde_json::json!({
|
||||
"a.txt": "a-contents",
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
let active_call_a = cx_a.read(ActiveCall::global);
|
||||
|
||||
// Client A shares a project in the channel
|
||||
active_call_a
|
||||
.update(cx_a, |call, cx| call.join_channel(channel_id, cx))
|
||||
.await
|
||||
.unwrap();
|
||||
let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
|
||||
let project_id = active_call_a
|
||||
.update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
|
||||
.await
|
||||
.unwrap();
|
||||
cx_a.executor().run_until_parked();
|
||||
|
||||
// Client B joins channel A as a guest
|
||||
cx_b.update(|cx| workspace::join_channel(channel_id, client_b.app_state.clone(), None, cx))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// b should be following a in the shared project.
|
||||
// B is a guest,
|
||||
cx_a.executor().run_until_parked();
|
||||
|
||||
// todo!() the test window does not call activation handlers
|
||||
// correctly yet, so this API does not work.
|
||||
// let project_b = active_call_b.read_with(cx_b, |call, _| {
|
||||
// call.location()
|
||||
// .unwrap()
|
||||
// .upgrade()
|
||||
// .expect("should not be weak")
|
||||
// });
|
||||
|
||||
let window_b = cx_b.update(|cx| cx.active_window().unwrap());
|
||||
let cx_b = &mut VisualTestContext::from_window(window_b, cx_b);
|
||||
|
||||
let workspace_b = window_b
|
||||
.downcast::<Workspace>()
|
||||
.unwrap()
|
||||
.root_view(cx_b)
|
||||
.unwrap();
|
||||
let project_b = workspace_b.update(cx_b, |workspace, _| workspace.project().clone());
|
||||
|
||||
assert_eq!(
|
||||
project_b.read_with(cx_b, |project, _| project.remote_id()),
|
||||
Some(project_id),
|
||||
);
|
||||
assert!(project_b.read_with(cx_b, |project, _| project.is_read_only()))
|
||||
}
|
||||
+1884
-1889
File diff suppressed because it is too large
Load Diff
@@ -19,6 +19,7 @@ use project::{
|
||||
search::SearchQuery, DiagnosticSummary, FormatTrigger, HoverBlockKind, Project, ProjectPath,
|
||||
};
|
||||
use rand::prelude::*;
|
||||
use rpc::proto::ChannelRole;
|
||||
use serde_json::json;
|
||||
use settings::SettingsStore;
|
||||
use std::{
|
||||
@@ -1380,7 +1381,7 @@ async fn test_unshare_project(
|
||||
.unwrap();
|
||||
executor.run_until_parked();
|
||||
|
||||
assert!(project_b.read_with(cx_b, |project, _| project.is_read_only()));
|
||||
assert!(project_b.read_with(cx_b, |project, _| project.is_disconnected()));
|
||||
|
||||
// Client C opens the project.
|
||||
let project_c = client_c.build_remote_project(project_id, cx_c).await;
|
||||
@@ -1393,7 +1394,7 @@ async fn test_unshare_project(
|
||||
|
||||
assert!(worktree_a.read_with(cx_a, |tree, _| !tree.as_local().unwrap().is_shared()));
|
||||
|
||||
assert!(project_c.read_with(cx_c, |project, _| project.is_read_only()));
|
||||
assert!(project_c.read_with(cx_c, |project, _| project.is_disconnected()));
|
||||
|
||||
// Client C can open the project again after client A re-shares.
|
||||
let project_id = active_call_a
|
||||
@@ -1419,7 +1420,7 @@ async fn test_unshare_project(
|
||||
project_a.read_with(cx_a, |project, _| assert!(!project.is_shared()));
|
||||
|
||||
project_c2.read_with(cx_c, |project, _| {
|
||||
assert!(project.is_read_only());
|
||||
assert!(project.is_disconnected());
|
||||
assert!(project.collaborators().is_empty());
|
||||
});
|
||||
}
|
||||
@@ -1551,7 +1552,7 @@ async fn test_project_reconnect(
|
||||
});
|
||||
|
||||
project_b1.read_with(cx_b, |project, _| {
|
||||
assert!(!project.is_read_only());
|
||||
assert!(!project.is_disconnected());
|
||||
assert_eq!(project.collaborators().len(), 1);
|
||||
});
|
||||
|
||||
@@ -1653,7 +1654,7 @@ async fn test_project_reconnect(
|
||||
});
|
||||
|
||||
project_b1.read_with(cx_b, |project, cx| {
|
||||
assert!(!project.is_read_only());
|
||||
assert!(!project.is_disconnected());
|
||||
assert_eq!(
|
||||
project
|
||||
.worktree_for_id(worktree1_id, cx)
|
||||
@@ -1687,9 +1688,9 @@ async fn test_project_reconnect(
|
||||
);
|
||||
});
|
||||
|
||||
project_b2.read_with(cx_b, |project, _| assert!(project.is_read_only()));
|
||||
project_b2.read_with(cx_b, |project, _| assert!(project.is_disconnected()));
|
||||
|
||||
project_b3.read_with(cx_b, |project, _| assert!(!project.is_read_only()));
|
||||
project_b3.read_with(cx_b, |project, _| assert!(!project.is_disconnected()));
|
||||
|
||||
buffer_a1.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "WaZ"));
|
||||
|
||||
@@ -1746,7 +1747,7 @@ async fn test_project_reconnect(
|
||||
executor.run_until_parked();
|
||||
|
||||
project_b1.read_with(cx_b, |project, cx| {
|
||||
assert!(!project.is_read_only());
|
||||
assert!(!project.is_disconnected());
|
||||
assert_eq!(
|
||||
project
|
||||
.worktree_for_id(worktree1_id, cx)
|
||||
@@ -1780,7 +1781,7 @@ async fn test_project_reconnect(
|
||||
);
|
||||
});
|
||||
|
||||
project_b3.read_with(cx_b, |project, _| assert!(project.is_read_only()));
|
||||
project_b3.read_with(cx_b, |project, _| assert!(project.is_disconnected()));
|
||||
|
||||
buffer_a1.read_with(cx_a, |buffer, _| assert_eq!(buffer.text(), "WXaYZ"));
|
||||
|
||||
@@ -3535,7 +3536,7 @@ async fn test_leaving_project(
|
||||
});
|
||||
|
||||
project_b2.read_with(cx_b, |project, _| {
|
||||
assert!(project.is_read_only());
|
||||
assert!(project.is_disconnected());
|
||||
});
|
||||
|
||||
project_c.read_with(cx_c, |project, _| {
|
||||
@@ -3550,6 +3551,7 @@ async fn test_leaving_project(
|
||||
client_b.user_store().clone(),
|
||||
client_b.language_registry().clone(),
|
||||
FakeFs::new(cx.background_executor().clone()),
|
||||
ChannelRole::Member,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
@@ -3568,11 +3570,11 @@ async fn test_leaving_project(
|
||||
});
|
||||
|
||||
project_b2.read_with(cx_b, |project, _| {
|
||||
assert!(project.is_read_only());
|
||||
assert!(project.is_disconnected());
|
||||
});
|
||||
|
||||
project_c.read_with(cx_c, |project, _| {
|
||||
assert!(project.is_read_only());
|
||||
assert!(project.is_disconnected());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1149,7 +1149,7 @@ impl RandomizedTest for ProjectCollaborationTest {
|
||||
Some((project, cx))
|
||||
});
|
||||
|
||||
if !guest_project.is_read_only() {
|
||||
if !guest_project.is_disconnected() {
|
||||
if let Some((host_project, host_cx)) = host_project {
|
||||
let host_worktree_snapshots =
|
||||
host_project.read_with(host_cx, |host_project, cx| {
|
||||
@@ -1236,7 +1236,7 @@ impl RandomizedTest for ProjectCollaborationTest {
|
||||
let buffers = client.buffers().clone();
|
||||
for (guest_project, guest_buffers) in &buffers {
|
||||
let project_id = if guest_project.read_with(client_cx, |project, _| {
|
||||
project.is_local() || project.is_read_only()
|
||||
project.is_local() || project.is_disconnected()
|
||||
}) {
|
||||
continue;
|
||||
} else {
|
||||
|
||||
@@ -518,7 +518,7 @@ impl<T: RandomizedTest> TestPlan<T> {
|
||||
for project in client.remote_projects().iter() {
|
||||
project.read_with(&client_cx, |project, _| {
|
||||
assert!(
|
||||
project.is_read_only(),
|
||||
project.is_disconnected(),
|
||||
"project {:?} should be read only",
|
||||
project.remote_id()
|
||||
)
|
||||
|
||||
@@ -138,12 +138,6 @@ impl ChannelView {
|
||||
editor.set_collaboration_hub(Box::new(ChannelBufferCollaborationHub(
|
||||
channel_buffer.clone(),
|
||||
)));
|
||||
editor.set_read_only(
|
||||
!channel_buffer
|
||||
.read(cx)
|
||||
.channel(cx)
|
||||
.is_some_and(|c| c.can_edit_notes()),
|
||||
);
|
||||
editor
|
||||
});
|
||||
let _editor_event_subscription =
|
||||
@@ -178,8 +172,7 @@ impl ChannelView {
|
||||
cx.notify();
|
||||
}),
|
||||
ChannelBufferEvent::ChannelChanged => {
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
editor.set_read_only(!self.channel(cx).is_some_and(|c| c.can_edit_notes()));
|
||||
self.editor.update(cx, |_, cx| {
|
||||
cx.emit(editor::EditorEvent::TitleChanged);
|
||||
cx.notify()
|
||||
});
|
||||
@@ -254,11 +247,11 @@ impl Item for ChannelView {
|
||||
fn tab_content(&self, _: Option<usize>, selected: bool, cx: &WindowContext) -> AnyElement {
|
||||
let label = if let Some(channel) = self.channel(cx) {
|
||||
match (
|
||||
channel.can_edit_notes(),
|
||||
self.channel_buffer.read(cx).buffer().read(cx).read_only(),
|
||||
self.channel_buffer.read(cx).is_connected(),
|
||||
) {
|
||||
(true, true) => format!("#{}", channel.name),
|
||||
(false, true) => format!("#{} (read-only)", channel.name),
|
||||
(false, true) => format!("#{}", channel.name),
|
||||
(true, true) => format!("#{} (read-only)", channel.name),
|
||||
(_, false) => format!("#{} (disconnected)", channel.name),
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -607,8 +607,12 @@ impl Panel for ChatPanel {
|
||||
"ChatPanel"
|
||||
}
|
||||
|
||||
fn icon(&self, _cx: &WindowContext) -> Option<ui::Icon> {
|
||||
Some(ui::Icon::MessageBubbles)
|
||||
fn icon(&self, cx: &WindowContext) -> Option<ui::Icon> {
|
||||
if !is_channels_feature_enabled(cx) {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(ui::Icon::MessageBubbles).filter(|_| ChatPanelSettings::get_global(cx).button)
|
||||
}
|
||||
|
||||
fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> {
|
||||
|
||||
@@ -11,15 +11,16 @@ use channel::{Channel, ChannelEvent, ChannelId, ChannelStore};
|
||||
use client::{Client, Contact, User, UserStore};
|
||||
use contact_finder::ContactFinder;
|
||||
use db::kvp::KEY_VALUE_STORE;
|
||||
use editor::Editor;
|
||||
use editor::{Editor, EditorElement, EditorStyle};
|
||||
use feature_flags::{ChannelsAlpha, FeatureFlagAppExt, FeatureFlagViewExt};
|
||||
use fuzzy::{match_strings, StringMatchCandidate};
|
||||
use gpui::{
|
||||
actions, canvas, div, fill, list, overlay, point, prelude::*, px, serde_json, AnyElement,
|
||||
AppContext, AsyncWindowContext, Bounds, ClipboardItem, DismissEvent, Div, EventEmitter,
|
||||
FocusHandle, FocusableView, InteractiveElement, IntoElement, ListOffset, ListState, Model,
|
||||
MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Render, RenderOnce, SharedString,
|
||||
Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView,
|
||||
FocusHandle, FocusableView, FontStyle, FontWeight, InteractiveElement, IntoElement, ListOffset,
|
||||
ListState, Model, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Render,
|
||||
RenderOnce, SharedString, Styled, Subscription, Task, TextStyle, View, ViewContext,
|
||||
VisualContext, WeakView, WhiteSpace,
|
||||
};
|
||||
use menu::{Cancel, Confirm, SelectNext, SelectPrev};
|
||||
use project::{Fs, Project};
|
||||
@@ -29,10 +30,9 @@ use settings::{Settings, SettingsStore};
|
||||
use smallvec::SmallVec;
|
||||
use std::{mem, sync::Arc};
|
||||
use theme::{ActiveTheme, ThemeSettings};
|
||||
use ui::prelude::*;
|
||||
use ui::{
|
||||
h_stack, v_stack, Avatar, Button, Color, ContextMenu, Icon, IconButton, IconElement, IconSize,
|
||||
Label, ListHeader, ListItem, Tooltip,
|
||||
prelude::*, Avatar, Button, Color, ContextMenu, Icon, IconButton, IconElement, IconSize, Label,
|
||||
ListHeader, ListItem, Tooltip,
|
||||
};
|
||||
use util::{maybe, ResultExt, TryFutureExt};
|
||||
use workspace::{
|
||||
@@ -151,6 +151,10 @@ enum ListEntry {
|
||||
peer_id: Option<PeerId>,
|
||||
is_last: bool,
|
||||
},
|
||||
GuestCount {
|
||||
count: usize,
|
||||
has_visible_participants: bool,
|
||||
},
|
||||
IncomingRequest(Arc<User>),
|
||||
OutgoingRequest(Arc<User>),
|
||||
ChannelInvite(Arc<Channel>),
|
||||
@@ -380,10 +384,14 @@ impl CollabPanel {
|
||||
|
||||
if !self.collapsed_sections.contains(&Section::ActiveCall) {
|
||||
let room = room.read(cx);
|
||||
let mut guest_count_ix = 0;
|
||||
let mut guest_count = if room.read_only() { 1 } else { 0 };
|
||||
let mut non_guest_count = if room.read_only() { 0 } else { 1 };
|
||||
|
||||
if let Some(channel_id) = room.channel_id() {
|
||||
self.entries.push(ListEntry::ChannelNotes { channel_id });
|
||||
self.entries.push(ListEntry::ChannelChat { channel_id })
|
||||
self.entries.push(ListEntry::ChannelChat { channel_id });
|
||||
guest_count_ix = self.entries.len();
|
||||
}
|
||||
|
||||
// Populate the active user.
|
||||
@@ -402,7 +410,7 @@ impl CollabPanel {
|
||||
&Default::default(),
|
||||
executor.clone(),
|
||||
));
|
||||
if !matches.is_empty() {
|
||||
if !matches.is_empty() && !room.read_only() {
|
||||
let user_id = user.id;
|
||||
self.entries.push(ListEntry::CallParticipant {
|
||||
user,
|
||||
@@ -430,13 +438,23 @@ impl CollabPanel {
|
||||
// Populate remote participants.
|
||||
self.match_candidates.clear();
|
||||
self.match_candidates
|
||||
.extend(room.remote_participants().iter().map(|(_, participant)| {
|
||||
StringMatchCandidate {
|
||||
id: participant.user.id as usize,
|
||||
string: participant.user.github_login.clone(),
|
||||
char_bag: participant.user.github_login.chars().collect(),
|
||||
}
|
||||
}));
|
||||
.extend(
|
||||
room.remote_participants()
|
||||
.iter()
|
||||
.filter_map(|(_, participant)| {
|
||||
if participant.role == proto::ChannelRole::Guest {
|
||||
guest_count += 1;
|
||||
return None;
|
||||
} else {
|
||||
non_guest_count += 1;
|
||||
}
|
||||
Some(StringMatchCandidate {
|
||||
id: participant.user.id as usize,
|
||||
string: participant.user.github_login.clone(),
|
||||
char_bag: participant.user.github_login.chars().collect(),
|
||||
})
|
||||
}),
|
||||
);
|
||||
let matches = executor.block(match_strings(
|
||||
&self.match_candidates,
|
||||
&query,
|
||||
@@ -470,6 +488,15 @@ impl CollabPanel {
|
||||
});
|
||||
}
|
||||
}
|
||||
if guest_count > 0 {
|
||||
self.entries.insert(
|
||||
guest_count_ix,
|
||||
ListEntry::GuestCount {
|
||||
count: guest_count,
|
||||
has_visible_participants: non_guest_count > 0,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Populate pending participants.
|
||||
self.match_candidates.clear();
|
||||
@@ -959,6 +986,41 @@ impl CollabPanel {
|
||||
.tooltip(move |cx| Tooltip::text("Open Chat", cx))
|
||||
}
|
||||
|
||||
fn render_guest_count(
|
||||
&self,
|
||||
count: usize,
|
||||
has_visible_participants: bool,
|
||||
is_selected: bool,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> impl IntoElement {
|
||||
let manageable_channel_id = ActiveCall::global(cx).read(cx).room().and_then(|room| {
|
||||
let room = room.read(cx);
|
||||
if room.local_participant_is_admin() {
|
||||
room.channel_id()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
ListItem::new("guest_count")
|
||||
.selected(is_selected)
|
||||
.start_slot(
|
||||
h_stack()
|
||||
.gap_1()
|
||||
.child(render_tree_branch(!has_visible_participants, cx))
|
||||
.child(""),
|
||||
)
|
||||
.child(Label::new(if count == 1 {
|
||||
format!("{} guest", count)
|
||||
} else {
|
||||
format!("{} guests", count)
|
||||
}))
|
||||
.when_some(manageable_channel_id, |el, channel_id| {
|
||||
el.tooltip(move |cx| Tooltip::text("Manage Members", cx))
|
||||
.on_click(cx.listener(move |this, _, cx| this.manage_members(channel_id, cx)))
|
||||
})
|
||||
}
|
||||
|
||||
fn has_subchannels(&self, ix: usize) -> bool {
|
||||
self.entries.get(ix).map_or(false, |entry| {
|
||||
if let ListEntry::Channel { has_children, .. } = entry {
|
||||
@@ -1180,6 +1242,18 @@ impl CollabPanel {
|
||||
});
|
||||
}
|
||||
}
|
||||
ListEntry::GuestCount { .. } => {
|
||||
let Some(room) = ActiveCall::global(cx).read(cx).room() else {
|
||||
return;
|
||||
};
|
||||
let room = room.read(cx);
|
||||
let Some(channel_id) = room.channel_id() else {
|
||||
return;
|
||||
};
|
||||
if room.local_participant_is_admin() {
|
||||
self.manage_members(channel_id, cx)
|
||||
}
|
||||
}
|
||||
ListEntry::Channel { channel, .. } => {
|
||||
let is_active = maybe!({
|
||||
let call_channel = ActiveCall::global(cx)
|
||||
@@ -1735,6 +1809,12 @@ impl CollabPanel {
|
||||
ListEntry::ParticipantScreen { peer_id, is_last } => self
|
||||
.render_participant_screen(*peer_id, *is_last, is_selected, cx)
|
||||
.into_any_element(),
|
||||
ListEntry::GuestCount {
|
||||
count,
|
||||
has_visible_participants,
|
||||
} => self
|
||||
.render_guest_count(*count, *has_visible_participants, is_selected, cx)
|
||||
.into_any_element(),
|
||||
ListEntry::ChannelNotes { channel_id } => self
|
||||
.render_channel_notes(*channel_id, is_selected, cx)
|
||||
.into_any_element(),
|
||||
@@ -1749,15 +1829,49 @@ impl CollabPanel {
|
||||
.size_full()
|
||||
.child(list(self.list_state.clone()).full())
|
||||
.child(
|
||||
v_stack().p_2().child(
|
||||
v_stack()
|
||||
.border_primary(cx)
|
||||
.border_t()
|
||||
.child(self.filter_editor.clone()),
|
||||
),
|
||||
v_stack()
|
||||
.child(div().mx_2().border_primary(cx).border_t())
|
||||
.child(
|
||||
v_stack()
|
||||
.p_2()
|
||||
.child(self.render_filter_input(&self.filter_editor, cx)),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fn render_filter_input(
|
||||
&self,
|
||||
editor: &View<Editor>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> impl IntoElement {
|
||||
let settings = ThemeSettings::get_global(cx);
|
||||
let text_style = TextStyle {
|
||||
color: if editor.read(cx).read_only(cx) {
|
||||
cx.theme().colors().text_disabled
|
||||
} else {
|
||||
cx.theme().colors().text
|
||||
},
|
||||
font_family: settings.ui_font.family.clone(),
|
||||
font_features: settings.ui_font.features,
|
||||
font_size: rems(0.875).into(),
|
||||
font_weight: FontWeight::NORMAL,
|
||||
font_style: FontStyle::Normal,
|
||||
line_height: relative(1.3).into(),
|
||||
background_color: None,
|
||||
underline: None,
|
||||
white_space: WhiteSpace::Normal,
|
||||
};
|
||||
|
||||
EditorElement::new(
|
||||
editor,
|
||||
EditorStyle {
|
||||
local_player: cx.theme().players().local(),
|
||||
text: text_style,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn render_header(
|
||||
&self,
|
||||
section: Section,
|
||||
@@ -2504,6 +2618,11 @@ impl PartialEq for ListEntry {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
ListEntry::GuestCount { .. } => {
|
||||
if let ListEntry::GuestCount { .. } = other {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
@@ -10,11 +10,12 @@ use gpui::{
|
||||
};
|
||||
use project::{Project, RepositoryEntry};
|
||||
use recent_projects::RecentProjects;
|
||||
use rpc::proto;
|
||||
use std::sync::Arc;
|
||||
use theme::{ActiveTheme, PlayerColors};
|
||||
use ui::{
|
||||
h_stack, popover_menu, prelude::*, Avatar, Button, ButtonLike, ButtonStyle, ContextMenu, Icon,
|
||||
IconButton, IconElement, Tooltip,
|
||||
IconButton, IconElement, TintColor, Tooltip,
|
||||
};
|
||||
use util::ResultExt;
|
||||
use vcs_menu::{build_branch_list, BranchList, OpenRecent as ToggleVcsMenu};
|
||||
@@ -175,14 +176,17 @@ impl Render for CollabTitlebarItem {
|
||||
let is_muted = room.is_muted(cx);
|
||||
let is_deafened = room.is_deafened().unwrap_or(false);
|
||||
let is_screen_sharing = room.is_screen_sharing();
|
||||
let read_only = room.read_only();
|
||||
|
||||
this.when(is_local, |this| {
|
||||
this.when(is_local && !read_only, |this| {
|
||||
this.child(
|
||||
Button::new(
|
||||
"toggle_sharing",
|
||||
if is_shared { "Unshare" } else { "Share" },
|
||||
)
|
||||
.style(ButtonStyle::Subtle)
|
||||
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
|
||||
.selected(is_shared)
|
||||
.label_size(LabelSize::Small)
|
||||
.on_click(cx.listener(
|
||||
move |this, _, cx| {
|
||||
@@ -205,20 +209,23 @@ impl Render for CollabTitlebarItem {
|
||||
.detach_and_log_err(cx);
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
IconButton::new(
|
||||
"mute-microphone",
|
||||
if is_muted {
|
||||
ui::Icon::MicMute
|
||||
} else {
|
||||
ui::Icon::Mic
|
||||
},
|
||||
.when(!read_only, |this| {
|
||||
this.child(
|
||||
IconButton::new(
|
||||
"mute-microphone",
|
||||
if is_muted {
|
||||
ui::Icon::MicMute
|
||||
} else {
|
||||
ui::Icon::Mic
|
||||
},
|
||||
)
|
||||
.style(ButtonStyle::Subtle)
|
||||
.icon_size(IconSize::Small)
|
||||
.selected(is_muted)
|
||||
.selected_style(ButtonStyle::Tinted(TintColor::Negative))
|
||||
.on_click(move |_, cx| crate::toggle_mute(&Default::default(), cx)),
|
||||
)
|
||||
.style(ButtonStyle::Subtle)
|
||||
.icon_size(IconSize::Small)
|
||||
.selected(is_muted)
|
||||
.on_click(move |_, cx| crate::toggle_mute(&Default::default(), cx)),
|
||||
)
|
||||
})
|
||||
.child(
|
||||
IconButton::new(
|
||||
"mute-sound",
|
||||
@@ -229,22 +236,35 @@ impl Render for CollabTitlebarItem {
|
||||
},
|
||||
)
|
||||
.style(ButtonStyle::Subtle)
|
||||
.selected_style(ButtonStyle::Tinted(TintColor::Negative))
|
||||
.icon_size(IconSize::Small)
|
||||
.selected(is_deafened)
|
||||
.tooltip(move |cx| {
|
||||
Tooltip::with_meta("Deafen Audio", None, "Mic will be muted", cx)
|
||||
if !read_only {
|
||||
Tooltip::with_meta(
|
||||
"Deafen Audio",
|
||||
None,
|
||||
"Mic will be muted",
|
||||
cx,
|
||||
)
|
||||
} else {
|
||||
Tooltip::text("Deafen Audio", cx)
|
||||
}
|
||||
})
|
||||
.on_click(move |_, cx| crate::toggle_mute(&Default::default(), cx)),
|
||||
)
|
||||
.child(
|
||||
IconButton::new("screen-share", ui::Icon::Screen)
|
||||
.style(ButtonStyle::Subtle)
|
||||
.icon_size(IconSize::Small)
|
||||
.selected(is_screen_sharing)
|
||||
.on_click(move |_, cx| {
|
||||
crate::toggle_screen_sharing(&Default::default(), cx)
|
||||
}),
|
||||
.on_click(move |_, cx| crate::toggle_deafen(&Default::default(), cx)),
|
||||
)
|
||||
.when(!read_only, |this| {
|
||||
this.child(
|
||||
IconButton::new("screen-share", ui::Icon::Screen)
|
||||
.style(ButtonStyle::Subtle)
|
||||
.icon_size(IconSize::Small)
|
||||
.selected(is_screen_sharing)
|
||||
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
|
||||
.on_click(move |_, cx| {
|
||||
crate::toggle_screen_sharing(&Default::default(), cx)
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
.map(|el| {
|
||||
let status = self.client.status();
|
||||
@@ -409,6 +429,10 @@ impl CollabTitlebarItem {
|
||||
current_user: &Arc<User>,
|
||||
cx: &ViewContext<Self>,
|
||||
) -> Option<FacePile> {
|
||||
if room.role_for_user(user.id) == Some(proto::ChannelRole::Guest) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let followers = project_id.map_or(&[] as &[_], |id| room.followers_for(peer_id, id));
|
||||
|
||||
let pile = FacePile::default()
|
||||
|
||||
@@ -370,6 +370,7 @@ mod tests {
|
||||
use gpui::TestAppContext;
|
||||
use language::Point;
|
||||
use project::Project;
|
||||
use settings::KeymapFile;
|
||||
use workspace::{AppState, Workspace};
|
||||
|
||||
#[test]
|
||||
@@ -503,7 +504,20 @@ mod tests {
|
||||
workspace::init(app_state.clone(), cx);
|
||||
init(cx);
|
||||
Project::init_settings(cx);
|
||||
settings::load_default_keymap(cx);
|
||||
KeymapFile::parse(
|
||||
r#"[
|
||||
{
|
||||
"bindings": {
|
||||
"cmd-n": "workspace::NewFile",
|
||||
"enter": "menu::Confirm",
|
||||
"cmd-shift-p": "command_palette::Toggle"
|
||||
}
|
||||
}
|
||||
]"#,
|
||||
)
|
||||
.unwrap()
|
||||
.add_to_cx(cx)
|
||||
.unwrap();
|
||||
app_state
|
||||
})
|
||||
}
|
||||
|
||||
@@ -151,7 +151,12 @@ impl ProjectDiagnosticsEditor {
|
||||
let focus_in_subscription =
|
||||
cx.on_focus_in(&focus_handle, |diagnostics, cx| diagnostics.focus_in(cx));
|
||||
|
||||
let excerpts = cx.new_model(|cx| MultiBuffer::new(project_handle.read(cx).replica_id()));
|
||||
let excerpts = cx.new_model(|cx| {
|
||||
MultiBuffer::new(
|
||||
project_handle.read(cx).replica_id(),
|
||||
project_handle.read(cx).capability(),
|
||||
)
|
||||
});
|
||||
let editor = cx.new_view(|cx| {
|
||||
let mut editor =
|
||||
Editor::for_multibuffer(excerpts.clone(), Some(project_handle.clone()), cx);
|
||||
@@ -641,8 +646,13 @@ impl Item for ProjectDiagnosticsEditor {
|
||||
|
||||
fn tab_content(&self, _detail: Option<usize>, selected: bool, _: &WindowContext) -> AnyElement {
|
||||
if self.summary.error_count == 0 && self.summary.warning_count == 0 {
|
||||
let label = Label::new("No problems");
|
||||
label.into_any_element()
|
||||
Label::new("No problems")
|
||||
.color(if selected {
|
||||
Color::Default
|
||||
} else {
|
||||
Color::Muted
|
||||
})
|
||||
.into_any_element()
|
||||
} else {
|
||||
h_stack()
|
||||
.gap_1()
|
||||
@@ -1567,6 +1577,7 @@ mod tests {
|
||||
workspace::init_settings(cx);
|
||||
Project::init_settings(cx);
|
||||
crate::init(cx);
|
||||
editor::init(cx);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+18
-15
@@ -54,10 +54,10 @@ use itertools::Itertools;
|
||||
pub use language::{char_kind, CharKind};
|
||||
use language::{
|
||||
language_settings::{self, all_language_settings, InlayHintSettings},
|
||||
markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel,
|
||||
Completion, CursorShape, Diagnostic, Documentation, IndentKind, IndentSize, Language,
|
||||
LanguageRegistry, LanguageServerName, OffsetRangeExt, Point, Selection, SelectionGoal,
|
||||
TransactionId,
|
||||
markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CodeAction,
|
||||
CodeLabel, Completion, CursorShape, Diagnostic, Documentation, IndentKind, IndentSize,
|
||||
Language, LanguageRegistry, LanguageServerName, OffsetRangeExt, Point, Selection,
|
||||
SelectionGoal, TransactionId,
|
||||
};
|
||||
|
||||
use link_go_to_definition::{GoToDefinitionLink, InlayHighlight, LinkGoToDefinitionState};
|
||||
@@ -2049,8 +2049,8 @@ impl Editor {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_only(&self) -> bool {
|
||||
self.read_only
|
||||
pub fn read_only(&self, cx: &AppContext) -> bool {
|
||||
self.read_only || self.buffer.read(cx).read_only()
|
||||
}
|
||||
|
||||
pub fn set_read_only(&mut self, read_only: bool) {
|
||||
@@ -2199,7 +2199,7 @@ impl Editor {
|
||||
S: ToOffset,
|
||||
T: Into<Arc<str>>,
|
||||
{
|
||||
if self.read_only {
|
||||
if self.read_only(cx) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2213,7 +2213,7 @@ impl Editor {
|
||||
S: ToOffset,
|
||||
T: Into<Arc<str>>,
|
||||
{
|
||||
if self.read_only {
|
||||
if self.read_only(cx) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2232,7 +2232,7 @@ impl Editor {
|
||||
S: ToOffset,
|
||||
T: Into<Arc<str>>,
|
||||
{
|
||||
if self.read_only {
|
||||
if self.read_only(cx) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2596,7 +2596,7 @@ impl Editor {
|
||||
pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
|
||||
let text: Arc<str> = text.into();
|
||||
|
||||
if self.read_only {
|
||||
if self.read_only(cx) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3049,7 +3049,7 @@ impl Editor {
|
||||
autoindent_mode: Option<AutoindentMode>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
if self.read_only {
|
||||
if self.read_only(cx) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3786,7 +3786,8 @@ impl Editor {
|
||||
|
||||
let mut ranges_to_highlight = Vec::new();
|
||||
let excerpt_buffer = cx.new_model(|cx| {
|
||||
let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
|
||||
let mut multibuffer =
|
||||
MultiBuffer::new(replica_id, Capability::ReadWrite).with_title(title);
|
||||
for (buffer_handle, transaction) in &entries {
|
||||
let buffer = buffer_handle.read(cx);
|
||||
ranges_to_highlight.extend(
|
||||
@@ -7491,9 +7492,10 @@ impl Editor {
|
||||
locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
|
||||
let mut locations = locations.into_iter().peekable();
|
||||
let mut ranges_to_highlight = Vec::new();
|
||||
let capability = workspace.project().read(cx).capability();
|
||||
|
||||
let excerpt_buffer = cx.new_model(|cx| {
|
||||
let mut multibuffer = MultiBuffer::new(replica_id);
|
||||
let mut multibuffer = MultiBuffer::new(replica_id, capability);
|
||||
while let Some(location) = locations.next() {
|
||||
let buffer = location.buffer.read(cx);
|
||||
let mut ranges_for_buffer = Vec::new();
|
||||
@@ -8608,7 +8610,8 @@ impl Editor {
|
||||
}
|
||||
|
||||
pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
|
||||
self.blink_manager.read(cx).visible() && self.focus_handle.is_focused(cx)
|
||||
(self.read_only(cx) || self.blink_manager.read(cx).visible())
|
||||
&& self.focus_handle.is_focused(cx)
|
||||
}
|
||||
|
||||
fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
|
||||
@@ -9565,7 +9568,7 @@ impl InputHandler for Editor {
|
||||
) -> Option<gpui::Bounds<Pixels>> {
|
||||
let text_layout_details = self.text_layout_details(cx);
|
||||
let style = &text_layout_details.editor_style;
|
||||
let font_id = cx.text_system().font_id(&style.text.font()).unwrap();
|
||||
let font_id = cx.text_system().resolve_font(&style.text.font());
|
||||
let font_size = style.text.font_size.to_pixels(cx.rem_size());
|
||||
let line_height = style.text.line_height_in_pixels(cx.rem_size());
|
||||
let em_width = cx
|
||||
|
||||
@@ -17,8 +17,9 @@ use gpui::{
|
||||
use indoc::indoc;
|
||||
use language::{
|
||||
language_settings::{AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent},
|
||||
BracketPairConfig, FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageRegistry,
|
||||
Override, Point,
|
||||
BracketPairConfig,
|
||||
Capability::ReadWrite,
|
||||
FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageRegistry, Override, Point,
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
use project::project_settings::{LspSettings, ProjectSettings};
|
||||
@@ -2355,7 +2356,7 @@ fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
|
||||
.with_language(rust_language, cx)
|
||||
});
|
||||
let multibuffer = cx.new_model(|cx| {
|
||||
let mut multibuffer = MultiBuffer::new(0);
|
||||
let mut multibuffer = MultiBuffer::new(0, ReadWrite);
|
||||
multibuffer.push_excerpts(
|
||||
toml_buffer.clone(),
|
||||
[ExcerptRange {
|
||||
@@ -6019,7 +6020,7 @@ fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
|
||||
|
||||
let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(3, 4, 'a')));
|
||||
let multibuffer = cx.new_model(|cx| {
|
||||
let mut multibuffer = MultiBuffer::new(0);
|
||||
let mut multibuffer = MultiBuffer::new(0, ReadWrite);
|
||||
multibuffer.push_excerpts(
|
||||
buffer.clone(),
|
||||
[
|
||||
@@ -6103,7 +6104,7 @@ fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
|
||||
});
|
||||
let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), initial_text));
|
||||
let multibuffer = cx.new_model(|cx| {
|
||||
let mut multibuffer = MultiBuffer::new(0);
|
||||
let mut multibuffer = MultiBuffer::new(0, ReadWrite);
|
||||
multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
|
||||
multibuffer
|
||||
});
|
||||
@@ -6162,7 +6163,7 @@ fn test_refresh_selections(cx: &mut TestAppContext) {
|
||||
let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(3, 4, 'a')));
|
||||
let mut excerpt1_id = None;
|
||||
let multibuffer = cx.new_model(|cx| {
|
||||
let mut multibuffer = MultiBuffer::new(0);
|
||||
let mut multibuffer = MultiBuffer::new(0, ReadWrite);
|
||||
excerpt1_id = multibuffer
|
||||
.push_excerpts(
|
||||
buffer.clone(),
|
||||
@@ -6247,7 +6248,7 @@ fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
|
||||
let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(3, 4, 'a')));
|
||||
let mut excerpt1_id = None;
|
||||
let multibuffer = cx.new_model(|cx| {
|
||||
let mut multibuffer = MultiBuffer::new(0);
|
||||
let mut multibuffer = MultiBuffer::new(0, ReadWrite);
|
||||
excerpt1_id = multibuffer
|
||||
.push_excerpts(
|
||||
buffer.clone(),
|
||||
@@ -6636,7 +6637,7 @@ async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
|
||||
let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
|
||||
|
||||
let leader = pane.update(cx, |_, cx| {
|
||||
let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
|
||||
let multibuffer = cx.new_model(|_| MultiBuffer::new(0, ReadWrite));
|
||||
cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
|
||||
});
|
||||
|
||||
@@ -7425,7 +7426,7 @@ async fn test_copilot_multibuffer(executor: BackgroundExecutor, cx: &mut gpui::T
|
||||
let buffer_1 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "a = 1\nb = 2\n"));
|
||||
let buffer_2 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "c = 3\nd = 4\n"));
|
||||
let multibuffer = cx.new_model(|cx| {
|
||||
let mut multibuffer = MultiBuffer::new(0);
|
||||
let mut multibuffer = MultiBuffer::new(0, ReadWrite);
|
||||
multibuffer.push_excerpts(
|
||||
buffer_1.clone(),
|
||||
[ExcerptRange {
|
||||
@@ -7552,7 +7553,7 @@ async fn test_copilot_disabled_globs(executor: BackgroundExecutor, cx: &mut gpui
|
||||
.unwrap();
|
||||
|
||||
let multibuffer = cx.new_model(|cx| {
|
||||
let mut multibuffer = MultiBuffer::new(0);
|
||||
let mut multibuffer = MultiBuffer::new(0, ReadWrite);
|
||||
multibuffer.push_excerpts(
|
||||
private_buffer.clone(),
|
||||
[ExcerptRange {
|
||||
|
||||
@@ -1230,6 +1230,14 @@ impl EditorElement {
|
||||
return;
|
||||
}
|
||||
|
||||
// If a drag took place after we started dragging the scrollbar,
|
||||
// cancel the scrollbar drag.
|
||||
if cx.has_active_drag() {
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
editor.scroll_manager.set_is_dragging_scrollbar(false, cx);
|
||||
});
|
||||
}
|
||||
|
||||
let top = bounds.origin.y;
|
||||
let bottom = bounds.lower_left().y;
|
||||
let right = bounds.lower_right().x;
|
||||
@@ -1767,7 +1775,7 @@ impl EditorElement {
|
||||
let snapshot = editor.snapshot(cx);
|
||||
let style = self.style.clone();
|
||||
|
||||
let font_id = cx.text_system().font_id(&style.text.font()).unwrap();
|
||||
let font_id = cx.text_system().resolve_font(&style.text.font());
|
||||
let font_size = style.text.font_size.to_pixels(cx.rem_size());
|
||||
let line_height = style.text.line_height_in_pixels(cx.rem_size());
|
||||
let em_width = cx
|
||||
@@ -1902,7 +1910,13 @@ impl EditorElement {
|
||||
layouts.push(layout);
|
||||
}
|
||||
|
||||
selections.push((style.local_player, layouts));
|
||||
let player = if editor.read_only(cx) {
|
||||
cx.theme().players().read_only()
|
||||
} else {
|
||||
style.local_player
|
||||
};
|
||||
|
||||
selections.push((player, layouts));
|
||||
}
|
||||
|
||||
if let Some(collaboration_hub) = &editor.collaboration_hub {
|
||||
@@ -3774,7 +3788,7 @@ fn compute_auto_height_layout(
|
||||
}
|
||||
|
||||
let style = editor.style.as_ref().unwrap();
|
||||
let font_id = cx.text_system().font_id(&style.text.font()).unwrap();
|
||||
let font_id = cx.text_system().resolve_font(&style.text.font());
|
||||
let font_size = style.text.font_size.to_pixels(cx.rem_size());
|
||||
let line_height = style.text.line_height_in_pixels(cx.rem_size());
|
||||
let em_width = cx
|
||||
|
||||
@@ -93,6 +93,7 @@ mod tests {
|
||||
use crate::editor_tests::init_test;
|
||||
use crate::Point;
|
||||
use gpui::{Context, TestAppContext};
|
||||
use language::Capability::ReadWrite;
|
||||
use multi_buffer::{ExcerptRange, MultiBuffer};
|
||||
use project::{FakeFs, Project};
|
||||
use unindent::Unindent;
|
||||
@@ -183,7 +184,7 @@ mod tests {
|
||||
cx.background_executor.run_until_parked();
|
||||
|
||||
let multibuffer = cx.new_model(|cx| {
|
||||
let mut multibuffer = MultiBuffer::new(0);
|
||||
let mut multibuffer = MultiBuffer::new(0, ReadWrite);
|
||||
multibuffer.push_excerpts(
|
||||
buffer_1.clone(),
|
||||
[
|
||||
|
||||
@@ -1206,7 +1206,8 @@ pub mod tests {
|
||||
use gpui::{Context, TestAppContext, WindowHandle};
|
||||
use itertools::Itertools;
|
||||
use language::{
|
||||
language_settings::AllLanguageSettingsContent, FakeLspAdapter, Language, LanguageConfig,
|
||||
language_settings::AllLanguageSettingsContent, Capability, FakeLspAdapter, Language,
|
||||
LanguageConfig,
|
||||
};
|
||||
use lsp::FakeLanguageServer;
|
||||
use parking_lot::Mutex;
|
||||
@@ -2459,7 +2460,7 @@ pub mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
let multibuffer = cx.new_model(|cx| {
|
||||
let mut multibuffer = MultiBuffer::new(0);
|
||||
let mut multibuffer = MultiBuffer::new(0, Capability::ReadWrite);
|
||||
multibuffer.push_excerpts(
|
||||
buffer_1.clone(),
|
||||
[
|
||||
@@ -2798,7 +2799,7 @@ pub mod tests {
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
|
||||
let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
|
||||
let (buffer_1_excerpts, buffer_2_excerpts) = multibuffer.update(cx, |multibuffer, cx| {
|
||||
let buffer_1_excerpts = multibuffer.push_excerpts(
|
||||
buffer_1.clone(),
|
||||
|
||||
@@ -15,9 +15,11 @@ use language::{
|
||||
proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, CharKind, OffsetRangeExt,
|
||||
Point, SelectionGoal,
|
||||
};
|
||||
use project::repository::GitFileStatus;
|
||||
use project::{search::SearchQuery, FormatTrigger, Item as _, Project, ProjectPath};
|
||||
use rpc::proto::{self, update_view, PeerId};
|
||||
use settings::Settings;
|
||||
use workspace::item::ItemSettings;
|
||||
|
||||
use std::fmt::Write;
|
||||
use std::{
|
||||
@@ -29,7 +31,7 @@ use std::{
|
||||
sync::Arc,
|
||||
};
|
||||
use text::Selection;
|
||||
use theme::{ActiveTheme, Theme};
|
||||
use theme::Theme;
|
||||
use ui::{h_stack, prelude::*, Label};
|
||||
use util::{paths::PathExt, paths::FILE_ROW_COLUMN_DELIMITER, ResultExt, TryFutureExt};
|
||||
use workspace::{
|
||||
@@ -101,7 +103,8 @@ impl FollowableItem for Editor {
|
||||
if state.singleton && buffers.len() == 1 {
|
||||
multibuffer = MultiBuffer::singleton(buffers.pop().unwrap(), cx)
|
||||
} else {
|
||||
multibuffer = MultiBuffer::new(replica_id);
|
||||
multibuffer =
|
||||
MultiBuffer::new(replica_id, project.read(cx).capability());
|
||||
let mut excerpts = state.excerpts.into_iter().peekable();
|
||||
while let Some(excerpt) = excerpts.peek() {
|
||||
let buffer_id = excerpt.buffer_id;
|
||||
@@ -579,7 +582,28 @@ impl Item for Editor {
|
||||
}
|
||||
|
||||
fn tab_content(&self, detail: Option<usize>, selected: bool, cx: &WindowContext) -> AnyElement {
|
||||
let _theme = cx.theme();
|
||||
let git_status = if ItemSettings::get_global(cx).git_status {
|
||||
self.buffer()
|
||||
.read(cx)
|
||||
.as_singleton()
|
||||
.and_then(|buffer| buffer.read(cx).project_path(cx))
|
||||
.and_then(|path| self.project.as_ref()?.read(cx).entry_for_path(&path, cx))
|
||||
.and_then(|entry| entry.git_status())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let label_color = match git_status {
|
||||
Some(GitFileStatus::Added) => Color::Created,
|
||||
Some(GitFileStatus::Modified) => Color::Modified,
|
||||
Some(GitFileStatus::Conflict) => Color::Conflict,
|
||||
None => {
|
||||
if selected {
|
||||
Color::Default
|
||||
} else {
|
||||
Color::Muted
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let description = detail.and_then(|detail| {
|
||||
let path = path_for_buffer(&self.buffer, detail, false, cx)?;
|
||||
@@ -595,11 +619,7 @@ impl Item for Editor {
|
||||
|
||||
h_stack()
|
||||
.gap_2()
|
||||
.child(Label::new(self.title(cx).to_string()).color(if selected {
|
||||
Color::Default
|
||||
} else {
|
||||
Color::Muted
|
||||
}))
|
||||
.child(Label::new(self.title(cx).to_string()).color(label_color))
|
||||
.when_some(description, |this, description| {
|
||||
this.child(
|
||||
Label::new(description)
|
||||
|
||||
@@ -930,10 +930,7 @@ mod tests {
|
||||
fn do_work() { «test»(); }
|
||||
"});
|
||||
|
||||
// Deactivating the window dismisses the highlight
|
||||
cx.update_workspace(|workspace, cx| {
|
||||
workspace.on_window_activation_changed(cx);
|
||||
});
|
||||
cx.cx.cx.deactivate_window();
|
||||
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
|
||||
fn test() { do_work(); }
|
||||
fn do_work() { test(); }
|
||||
|
||||
@@ -461,6 +461,7 @@ mod tests {
|
||||
Buffer, DisplayMap, ExcerptRange, InlayId, MultiBuffer,
|
||||
};
|
||||
use gpui::{font, Context as _};
|
||||
use language::Capability;
|
||||
use project::Project;
|
||||
use settings::SettingsStore;
|
||||
use util::post_inc;
|
||||
@@ -766,7 +767,7 @@ mod tests {
|
||||
let buffer =
|
||||
cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abc\ndefg\nhijkl\nmn"));
|
||||
let multibuffer = cx.new_model(|cx| {
|
||||
let mut multibuffer = MultiBuffer::new(0);
|
||||
let mut multibuffer = MultiBuffer::new(0, Capability::ReadWrite);
|
||||
multibuffer.push_excerpts(
|
||||
buffer.clone(),
|
||||
[
|
||||
|
||||
@@ -18,7 +18,6 @@ gpui = { path = "../gpui" }
|
||||
language = { path = "../language" }
|
||||
menu = { path = "../menu" }
|
||||
project = { path = "../project" }
|
||||
search = { path = "../search" }
|
||||
settings = { path = "../settings" }
|
||||
theme = { path = "../theme" }
|
||||
ui = { path = "../ui" }
|
||||
|
||||
@@ -18,8 +18,7 @@ collections = { path = "../collections" }
|
||||
gpui_macros = { path = "../gpui_macros" }
|
||||
util = { path = "../util" }
|
||||
sum_tree = { path = "../sum_tree" }
|
||||
sqlez = { path = "../sqlez" }
|
||||
async-task = "4.0.3"
|
||||
async-task = "4.7"
|
||||
backtrace = { version = "0.3", optional = true }
|
||||
ctor.workspace = true
|
||||
linkme = "0.3"
|
||||
|
||||
+12
-4
@@ -327,6 +327,7 @@ impl AppContext {
|
||||
pub fn refresh(&mut self) {
|
||||
self.pending_effects.push_back(Effect::Refresh);
|
||||
}
|
||||
|
||||
pub(crate) fn update<R>(&mut self, update: impl FnOnce(&mut Self) -> R) -> R {
|
||||
self.pending_updates += 1;
|
||||
let result = update(self);
|
||||
@@ -840,10 +841,12 @@ impl AppContext {
|
||||
/// Update the global of the given type with a closure. Unlike `global_mut`, this method provides
|
||||
/// your closure with mutable access to the `AppContext` and the global simultaneously.
|
||||
pub fn update_global<G: 'static, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R {
|
||||
let mut global = self.lease_global::<G>();
|
||||
let result = f(&mut global, self);
|
||||
self.end_global_lease(global);
|
||||
result
|
||||
self.update(|cx| {
|
||||
let mut global = cx.lease_global::<G>();
|
||||
let result = f(&mut global, cx);
|
||||
cx.end_global_lease(global);
|
||||
result
|
||||
})
|
||||
}
|
||||
|
||||
/// Register a callback to be invoked when a global of the given type is updated.
|
||||
@@ -941,6 +944,11 @@ impl AppContext {
|
||||
self.pending_effects.push_back(Effect::Refresh);
|
||||
}
|
||||
|
||||
pub fn clear_key_bindings(&mut self) {
|
||||
self.keymap.lock().clear();
|
||||
self.pending_effects.push_back(Effect::Refresh);
|
||||
}
|
||||
|
||||
/// Register a global listener for actions invoked via the keyboard.
|
||||
pub fn on_action<A: Action>(&mut self, listener: impl Fn(&A, &mut Self) + 'static) {
|
||||
self.global_action_listeners
|
||||
|
||||
@@ -16,6 +16,9 @@ use std::{
|
||||
thread::panicking,
|
||||
};
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
use collections::HashMap;
|
||||
|
||||
slotmap::new_key_type! { pub struct EntityId; }
|
||||
|
||||
impl EntityId {
|
||||
@@ -38,6 +41,8 @@ pub(crate) struct EntityMap {
|
||||
struct EntityRefCounts {
|
||||
counts: SlotMap<EntityId, AtomicUsize>,
|
||||
dropped_entity_ids: Vec<EntityId>,
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
leak_detector: LeakDetector,
|
||||
}
|
||||
|
||||
impl EntityMap {
|
||||
@@ -47,6 +52,11 @@ impl EntityMap {
|
||||
ref_counts: Arc::new(RwLock::new(EntityRefCounts {
|
||||
counts: SlotMap::with_key(),
|
||||
dropped_entity_ids: Vec::new(),
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
leak_detector: LeakDetector {
|
||||
next_handle_id: 0,
|
||||
entity_handles: HashMap::default(),
|
||||
},
|
||||
})),
|
||||
}
|
||||
}
|
||||
@@ -156,6 +166,8 @@ pub struct AnyModel {
|
||||
pub(crate) entity_id: EntityId,
|
||||
pub(crate) entity_type: TypeId,
|
||||
entity_map: Weak<RwLock<EntityRefCounts>>,
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
handle_id: HandleId,
|
||||
}
|
||||
|
||||
impl AnyModel {
|
||||
@@ -163,7 +175,14 @@ impl AnyModel {
|
||||
Self {
|
||||
entity_id: id,
|
||||
entity_type,
|
||||
entity_map,
|
||||
entity_map: entity_map.clone(),
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
handle_id: entity_map
|
||||
.upgrade()
|
||||
.unwrap()
|
||||
.write()
|
||||
.leak_detector
|
||||
.handle_created(id),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,11 +226,20 @@ impl Clone for AnyModel {
|
||||
assert_ne!(prev_count, 0, "Detected over-release of a model.");
|
||||
}
|
||||
|
||||
Self {
|
||||
let this = Self {
|
||||
entity_id: self.entity_id,
|
||||
entity_type: self.entity_type,
|
||||
entity_map: self.entity_map.clone(),
|
||||
}
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
handle_id: self
|
||||
.entity_map
|
||||
.upgrade()
|
||||
.unwrap()
|
||||
.write()
|
||||
.leak_detector
|
||||
.handle_created(self.entity_id),
|
||||
};
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,6 +259,14 @@ impl Drop for AnyModel {
|
||||
entity_map.dropped_entity_ids.push(self.entity_id);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
if let Some(entity_map) = self.entity_map.upgrade() {
|
||||
entity_map
|
||||
.write()
|
||||
.leak_detector
|
||||
.handle_dropped(self.entity_id, self.handle_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,13 +459,43 @@ impl AnyWeakModel {
|
||||
return None;
|
||||
}
|
||||
ref_count.fetch_add(1, SeqCst);
|
||||
drop(ref_counts);
|
||||
|
||||
Some(AnyModel {
|
||||
entity_id: self.entity_id,
|
||||
entity_type: self.entity_type,
|
||||
entity_map: self.entity_ref_counts.clone(),
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
handle_id: self
|
||||
.entity_ref_counts
|
||||
.upgrade()
|
||||
.unwrap()
|
||||
.write()
|
||||
.leak_detector
|
||||
.handle_created(self.entity_id),
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn assert_dropped(&self) {
|
||||
self.entity_ref_counts
|
||||
.upgrade()
|
||||
.unwrap()
|
||||
.write()
|
||||
.leak_detector
|
||||
.assert_dropped(self.entity_id);
|
||||
|
||||
if self
|
||||
.entity_ref_counts
|
||||
.upgrade()
|
||||
.and_then(|ref_counts| Some(ref_counts.read().counts.get(self.entity_id)?.load(SeqCst)))
|
||||
.is_some()
|
||||
{
|
||||
panic!(
|
||||
"entity was recently dropped but resources are retained until the end of the effect cycle."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<WeakModel<T>> for AnyWeakModel {
|
||||
@@ -534,6 +600,59 @@ impl<T> PartialEq<Model<T>> for WeakModel<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
lazy_static::lazy_static! {
|
||||
static ref LEAK_BACKTRACE: bool =
|
||||
std::env::var("LEAK_BACKTRACE").map_or(false, |b| !b.is_empty());
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)]
|
||||
pub struct HandleId {
|
||||
id: u64, // id of the handle itself, not the pointed at object
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub struct LeakDetector {
|
||||
next_handle_id: u64,
|
||||
entity_handles: HashMap<EntityId, HashMap<HandleId, Option<backtrace::Backtrace>>>,
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
impl LeakDetector {
|
||||
#[track_caller]
|
||||
pub fn handle_created(&mut self, entity_id: EntityId) -> HandleId {
|
||||
let id = util::post_inc(&mut self.next_handle_id);
|
||||
let handle_id = HandleId { id };
|
||||
let handles = self.entity_handles.entry(entity_id).or_default();
|
||||
handles.insert(
|
||||
handle_id,
|
||||
LEAK_BACKTRACE.then(|| backtrace::Backtrace::new_unresolved()),
|
||||
);
|
||||
handle_id
|
||||
}
|
||||
|
||||
pub fn handle_dropped(&mut self, entity_id: EntityId, handle_id: HandleId) {
|
||||
let handles = self.entity_handles.entry(entity_id).or_default();
|
||||
handles.remove(&handle_id);
|
||||
}
|
||||
|
||||
pub fn assert_dropped(&mut self, entity_id: EntityId) {
|
||||
let handles = self.entity_handles.entry(entity_id).or_default();
|
||||
if !handles.is_empty() {
|
||||
for (_, backtrace) in handles {
|
||||
if let Some(mut backtrace) = backtrace.take() {
|
||||
backtrace.resolve();
|
||||
eprintln!("Leaked handle: {:#?}", backtrace);
|
||||
} else {
|
||||
eprintln!("Leaked handle: export LEAK_BACKTRACE to find allocation site");
|
||||
}
|
||||
}
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::EntityMap;
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
use crate::{
|
||||
div, Action, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext,
|
||||
BackgroundExecutor, Bounds, ClipboardItem, Context, Entity, EventEmitter, ForegroundExecutor,
|
||||
InputEvent, IntoElement, KeyDownEvent, Keystroke, Model, ModelContext, Pixels, Platform,
|
||||
PlatformWindow, Point, Render, Result, Size, Task, TestDispatcher, TestPlatform, TestWindow,
|
||||
TestWindowHandlers, TextSystem, View, ViewContext, VisualContext, WindowBounds, WindowContext,
|
||||
WindowHandle, WindowOptions,
|
||||
BackgroundExecutor, ClipboardItem, Context, Entity, EventEmitter, ForegroundExecutor,
|
||||
IntoElement, Keystroke, Model, ModelContext, Pixels, Platform, Render, Result, Size, Task,
|
||||
TestDispatcher, TestPlatform, TestWindow, TextSystem, View, ViewContext, VisualContext,
|
||||
WindowContext, WindowHandle, WindowOptions,
|
||||
};
|
||||
use anyhow::{anyhow, bail};
|
||||
use futures::{Stream, StreamExt};
|
||||
use std::{future::Future, mem, ops::Deref, rc::Rc, sync::Arc, time::Duration};
|
||||
use std::{future::Future, ops::Deref, rc::Rc, sync::Arc, time::Duration};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TestAppContext {
|
||||
@@ -185,42 +184,7 @@ impl TestAppContext {
|
||||
}
|
||||
|
||||
pub fn simulate_window_resize(&self, window_handle: AnyWindowHandle, size: Size<Pixels>) {
|
||||
let (mut handlers, scale_factor) = self
|
||||
.app
|
||||
.borrow_mut()
|
||||
.update_window(window_handle, |_, cx| {
|
||||
let platform_window = cx.window.platform_window.as_test().unwrap();
|
||||
let scale_factor = platform_window.scale_factor();
|
||||
match &mut platform_window.bounds {
|
||||
WindowBounds::Fullscreen | WindowBounds::Maximized => {
|
||||
platform_window.bounds = WindowBounds::Fixed(Bounds {
|
||||
origin: Point::default(),
|
||||
size: size.map(|pixels| f64::from(pixels).into()),
|
||||
});
|
||||
}
|
||||
WindowBounds::Fixed(bounds) => {
|
||||
bounds.size = size.map(|pixels| f64::from(pixels).into());
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
mem::take(&mut platform_window.handlers.lock().resize),
|
||||
scale_factor,
|
||||
)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
for handler in &mut handlers {
|
||||
handler(size, scale_factor);
|
||||
}
|
||||
|
||||
self.app
|
||||
.borrow_mut()
|
||||
.update_window(window_handle, |_, cx| {
|
||||
let platform_window = cx.window.platform_window.as_test().unwrap();
|
||||
platform_window.handlers.lock().resize = handlers;
|
||||
})
|
||||
.unwrap();
|
||||
self.test_window(window_handle).simulate_resize(size);
|
||||
}
|
||||
|
||||
pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut) -> Task<R>
|
||||
@@ -313,41 +277,22 @@ impl TestAppContext {
|
||||
keystroke: Keystroke,
|
||||
is_held: bool,
|
||||
) {
|
||||
let keystroke2 = keystroke.clone();
|
||||
let handled = window
|
||||
.update(self, |_, cx| {
|
||||
cx.dispatch_event(InputEvent::KeyDown(KeyDownEvent { keystroke, is_held }))
|
||||
})
|
||||
.is_ok_and(|handled| handled);
|
||||
if handled {
|
||||
return;
|
||||
}
|
||||
|
||||
let input_handler = self.update_test_window(window, |window| window.input_handler.clone());
|
||||
let Some(input_handler) = input_handler else {
|
||||
panic!(
|
||||
"dispatch_keystroke {:?} failed to dispatch action or input",
|
||||
&keystroke2
|
||||
);
|
||||
};
|
||||
let text = keystroke2.ime_key.unwrap_or(keystroke2.key);
|
||||
input_handler.lock().replace_text_in_range(None, &text);
|
||||
self.test_window(window)
|
||||
.simulate_keystroke(keystroke, is_held)
|
||||
}
|
||||
|
||||
pub fn update_test_window<R>(
|
||||
&mut self,
|
||||
window: AnyWindowHandle,
|
||||
f: impl FnOnce(&mut TestWindow) -> R,
|
||||
) -> R {
|
||||
window
|
||||
.update(self, |_, cx| {
|
||||
f(cx.window
|
||||
.platform_window
|
||||
.as_any_mut()
|
||||
.downcast_mut::<TestWindow>()
|
||||
.unwrap())
|
||||
})
|
||||
pub fn test_window(&self, window: AnyWindowHandle) -> TestWindow {
|
||||
self.app
|
||||
.borrow_mut()
|
||||
.windows
|
||||
.get_mut(window.id)
|
||||
.unwrap()
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.platform_window
|
||||
.as_test()
|
||||
.unwrap()
|
||||
.clone()
|
||||
}
|
||||
|
||||
pub fn notifications<T: 'static>(&mut self, entity: &impl Entity<T>) -> impl Stream<Item = ()> {
|
||||
@@ -563,11 +508,7 @@ impl<'a> VisualTestContext<'a> {
|
||||
}
|
||||
|
||||
pub fn window_title(&mut self) -> Option<String> {
|
||||
self.cx
|
||||
.update_window(self.window, |_, cx| {
|
||||
cx.window.platform_window.as_test().unwrap().title.clone()
|
||||
})
|
||||
.unwrap()
|
||||
self.cx.test_window(self.window).0.lock().title.clone()
|
||||
}
|
||||
|
||||
pub fn simulate_keystrokes(&mut self, keystrokes: &str) {
|
||||
@@ -578,37 +519,11 @@ impl<'a> VisualTestContext<'a> {
|
||||
self.cx.simulate_input(self.window, input)
|
||||
}
|
||||
|
||||
pub fn simulate_activation(&mut self) {
|
||||
self.simulate_window_events(&mut |handlers| {
|
||||
handlers
|
||||
.active_status_change
|
||||
.iter_mut()
|
||||
.for_each(|f| f(true));
|
||||
})
|
||||
}
|
||||
|
||||
pub fn simulate_deactivation(&mut self) {
|
||||
self.simulate_window_events(&mut |handlers| {
|
||||
handlers
|
||||
.active_status_change
|
||||
.iter_mut()
|
||||
.for_each(|f| f(false));
|
||||
})
|
||||
}
|
||||
|
||||
fn simulate_window_events(&mut self, f: &mut dyn FnMut(&mut TestWindowHandlers)) {
|
||||
let handlers = self
|
||||
.cx
|
||||
.update_window(self.window, |_, cx| {
|
||||
cx.window
|
||||
.platform_window
|
||||
.as_test()
|
||||
.unwrap()
|
||||
.handlers
|
||||
.clone()
|
||||
})
|
||||
.unwrap();
|
||||
f(&mut *handlers.lock());
|
||||
pub fn deactivate_window(&mut self) {
|
||||
if Some(self.window) == self.test_platform.active_window() {
|
||||
self.test_platform.set_active_window(None)
|
||||
}
|
||||
self.background_executor.run_until_parked();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -339,6 +339,15 @@ impl Hsla {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn grayscale(&self) -> Self {
|
||||
Hsla {
|
||||
h: self.h,
|
||||
s: 0.,
|
||||
l: self.l,
|
||||
a: self.a,
|
||||
}
|
||||
}
|
||||
|
||||
/// Fade out the color by a given factor. This factor should be between 0.0 and 1.0.
|
||||
/// Where 0.0 will leave the color unchanged, and 1.0 will completely fade out the color.
|
||||
pub fn fade_out(&mut self, factor: f32) {
|
||||
|
||||
@@ -44,8 +44,9 @@ pub trait IntoElement: Sized {
|
||||
}
|
||||
|
||||
/// Convert into an element, then draw in the current window at the given origin.
|
||||
/// The provided available space is provided to the layout engine to determine the size of the root element.
|
||||
/// Once the element is drawn, its associated element staet is yielded to the given callback.
|
||||
/// The available space argument is provided to the layout engine to determine the size of the
|
||||
// root element. Once the element is drawn, its associated element state is yielded to the
|
||||
// given callback.
|
||||
fn draw_and_update_state<T, R>(
|
||||
self,
|
||||
origin: Point<Pixels>,
|
||||
|
||||
@@ -31,39 +31,6 @@ pub trait Along {
|
||||
fn apply_along(&self, axis: Axis, f: impl FnOnce(Self::Unit) -> Self::Unit) -> Self;
|
||||
}
|
||||
|
||||
impl sqlez::bindable::StaticColumnCount for Axis {}
|
||||
impl sqlez::bindable::Bind for Axis {
|
||||
fn bind(
|
||||
&self,
|
||||
statement: &sqlez::statement::Statement,
|
||||
start_index: i32,
|
||||
) -> anyhow::Result<i32> {
|
||||
match self {
|
||||
Axis::Horizontal => "Horizontal",
|
||||
Axis::Vertical => "Vertical",
|
||||
}
|
||||
.bind(statement, start_index)
|
||||
}
|
||||
}
|
||||
|
||||
impl sqlez::bindable::Column for Axis {
|
||||
fn column(
|
||||
statement: &mut sqlez::statement::Statement,
|
||||
start_index: i32,
|
||||
) -> anyhow::Result<(Self, i32)> {
|
||||
String::column(statement, start_index).and_then(|(axis_text, next_index)| {
|
||||
Ok((
|
||||
match axis_text.as_str() {
|
||||
"Horizontal" => Axis::Horizontal,
|
||||
"Vertical" => Axis::Vertical,
|
||||
_ => anyhow::bail!("Stored serialized item kind is incorrect"),
|
||||
},
|
||||
next_index,
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes a location in a 2D cartesian coordinate space.
|
||||
///
|
||||
/// It holds two public fields, `x` and `y`, which represent the coordinates in the space.
|
||||
@@ -2296,18 +2263,6 @@ impl From<f64> for GlobalPixels {
|
||||
}
|
||||
}
|
||||
|
||||
impl sqlez::bindable::StaticColumnCount for GlobalPixels {}
|
||||
|
||||
impl sqlez::bindable::Bind for GlobalPixels {
|
||||
fn bind(
|
||||
&self,
|
||||
statement: &sqlez::statement::Statement,
|
||||
start_index: i32,
|
||||
) -> anyhow::Result<i32> {
|
||||
self.0.bind(statement, start_index)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a length in rems, a unit based on the font-size of the window, which can be assigned with [WindowContext::set_rem_size].
|
||||
///
|
||||
/// Rems are used for defining lengths that are scalable and consistent across different UI elements.
|
||||
|
||||
@@ -307,7 +307,10 @@ mod test {
|
||||
div().id("testview").child(
|
||||
div()
|
||||
.key_context("parent")
|
||||
.on_key_down(cx.listener(|this, _, _| this.saw_key_down = true))
|
||||
.on_key_down(cx.listener(|this, _, cx| {
|
||||
cx.stop_propagation();
|
||||
this.saw_key_down = true
|
||||
}))
|
||||
.on_action(
|
||||
cx.listener(|this: &mut TestView, _: &TestAction, _| {
|
||||
this.saw_action = true
|
||||
@@ -343,6 +346,7 @@ mod test {
|
||||
.update(cx, |test_view, cx| cx.focus(&test_view.focus_handle))
|
||||
.unwrap();
|
||||
|
||||
cx.dispatch_keystroke(*window, Keystroke::parse("a").unwrap(), false);
|
||||
cx.dispatch_keystroke(*window, Keystroke::parse("ctrl-g").unwrap(), false);
|
||||
|
||||
window
|
||||
|
||||
@@ -6,19 +6,17 @@ mod mac;
|
||||
mod test;
|
||||
|
||||
use crate::{
|
||||
point, size, Action, AnyWindowHandle, BackgroundExecutor, Bounds, DevicePixels, Font, FontId,
|
||||
FontMetrics, FontRun, ForegroundExecutor, GlobalPixels, GlyphId, InputEvent, Keymap,
|
||||
LineLayout, Pixels, Point, RenderGlyphParams, RenderImageParams, RenderSvgParams, Result,
|
||||
Scene, SharedString, Size, TaskLabel,
|
||||
Action, AnyWindowHandle, BackgroundExecutor, Bounds, DevicePixels, Font, FontId, FontMetrics,
|
||||
FontRun, ForegroundExecutor, GlobalPixels, GlyphId, InputEvent, Keymap, LineLayout, Pixels,
|
||||
Point, RenderGlyphParams, RenderImageParams, RenderSvgParams, Result, Scene, SharedString,
|
||||
Size, TaskLabel,
|
||||
};
|
||||
use anyhow::{anyhow, bail};
|
||||
use anyhow::anyhow;
|
||||
use async_task::Runnable;
|
||||
use futures::channel::oneshot;
|
||||
use parking::Unparker;
|
||||
use seahash::SeaHasher;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlez::bindable::{Bind, Column, StaticColumnCount};
|
||||
use sqlez::statement::Statement;
|
||||
use std::borrow::Cow;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::time::Duration;
|
||||
@@ -396,67 +394,6 @@ pub enum WindowBounds {
|
||||
Fixed(Bounds<GlobalPixels>),
|
||||
}
|
||||
|
||||
impl StaticColumnCount for WindowBounds {
|
||||
fn column_count() -> usize {
|
||||
5
|
||||
}
|
||||
}
|
||||
|
||||
impl Bind for WindowBounds {
|
||||
fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
|
||||
let (region, next_index) = match self {
|
||||
WindowBounds::Fullscreen => {
|
||||
let next_index = statement.bind(&"Fullscreen", start_index)?;
|
||||
(None, next_index)
|
||||
}
|
||||
WindowBounds::Maximized => {
|
||||
let next_index = statement.bind(&"Maximized", start_index)?;
|
||||
(None, next_index)
|
||||
}
|
||||
WindowBounds::Fixed(region) => {
|
||||
let next_index = statement.bind(&"Fixed", start_index)?;
|
||||
(Some(*region), next_index)
|
||||
}
|
||||
};
|
||||
|
||||
statement.bind(
|
||||
®ion.map(|region| {
|
||||
(
|
||||
region.origin.x,
|
||||
region.origin.y,
|
||||
region.size.width,
|
||||
region.size.height,
|
||||
)
|
||||
}),
|
||||
next_index,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Column for WindowBounds {
|
||||
fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
|
||||
let (window_state, next_index) = String::column(statement, start_index)?;
|
||||
let bounds = match window_state.as_str() {
|
||||
"Fullscreen" => WindowBounds::Fullscreen,
|
||||
"Maximized" => WindowBounds::Maximized,
|
||||
"Fixed" => {
|
||||
let ((x, y, width, height), _) = Column::column(statement, next_index)?;
|
||||
let x: f64 = x;
|
||||
let y: f64 = y;
|
||||
let width: f64 = width;
|
||||
let height: f64 = height;
|
||||
WindowBounds::Fixed(Bounds {
|
||||
origin: point(x.into(), y.into()),
|
||||
size: size(width.into(), height.into()),
|
||||
})
|
||||
}
|
||||
_ => bail!("Window State did not have a valid string"),
|
||||
};
|
||||
|
||||
Ok((bounds, next_index + 4))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum WindowAppearance {
|
||||
Light,
|
||||
|
||||
@@ -11,7 +11,7 @@ use objc::{
|
||||
};
|
||||
use parking::{Parker, Unparker};
|
||||
use parking_lot::Mutex;
|
||||
use std::{ffi::c_void, sync::Arc, time::Duration};
|
||||
use std::{ffi::c_void, ptr::NonNull, sync::Arc, time::Duration};
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/dispatch_sys.rs"));
|
||||
|
||||
@@ -47,7 +47,7 @@ impl PlatformDispatcher for MacDispatcher {
|
||||
unsafe {
|
||||
dispatch_async_f(
|
||||
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT.try_into().unwrap(), 0),
|
||||
runnable.into_raw() as *mut c_void,
|
||||
runnable.into_raw().as_ptr() as *mut c_void,
|
||||
Some(trampoline),
|
||||
);
|
||||
}
|
||||
@@ -57,7 +57,7 @@ impl PlatformDispatcher for MacDispatcher {
|
||||
unsafe {
|
||||
dispatch_async_f(
|
||||
dispatch_get_main_queue(),
|
||||
runnable.into_raw() as *mut c_void,
|
||||
runnable.into_raw().as_ptr() as *mut c_void,
|
||||
Some(trampoline),
|
||||
);
|
||||
}
|
||||
@@ -71,7 +71,7 @@ impl PlatformDispatcher for MacDispatcher {
|
||||
dispatch_after_f(
|
||||
when,
|
||||
queue,
|
||||
runnable.into_raw() as *mut c_void,
|
||||
runnable.into_raw().as_ptr() as *mut c_void,
|
||||
Some(trampoline),
|
||||
);
|
||||
}
|
||||
@@ -91,6 +91,6 @@ impl PlatformDispatcher for MacDispatcher {
|
||||
}
|
||||
|
||||
extern "C" fn trampoline(runnable: *mut c_void) {
|
||||
let task = unsafe { Runnable::from_raw(runnable as *mut ()) };
|
||||
let task = unsafe { Runnable::<()>::from_raw(NonNull::new_unchecked(runnable as *mut ())) };
|
||||
task.run();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ float gaussian(float x, float sigma);
|
||||
float2 erf(float2 x);
|
||||
float blur_along_x(float x, float y, float sigma, float corner,
|
||||
float2 half_size);
|
||||
float4 over(float4 below, float4 above);
|
||||
|
||||
struct QuadVertexOutput {
|
||||
float4 position [[position]];
|
||||
@@ -108,21 +109,11 @@ fragment float4 quad_fragment(QuadFragmentInput input [[stage_in]],
|
||||
color = input.background_color;
|
||||
} else {
|
||||
float inset_distance = distance + border_width;
|
||||
|
||||
// Decrease border's opacity as we move inside the background.
|
||||
input.border_color.a *= 1. - saturate(0.5 - inset_distance);
|
||||
|
||||
// Alpha-blend the border and the background.
|
||||
float output_alpha = input.border_color.a +
|
||||
input.background_color.a * (1. - input.border_color.a);
|
||||
float3 premultiplied_border_rgb =
|
||||
input.border_color.rgb * input.border_color.a;
|
||||
float3 premultiplied_background_rgb =
|
||||
input.background_color.rgb * input.background_color.a;
|
||||
float3 premultiplied_output_rgb =
|
||||
premultiplied_border_rgb +
|
||||
premultiplied_background_rgb * (1. - input.border_color.a);
|
||||
color = float4(premultiplied_output_rgb, output_alpha);
|
||||
// Blend the border on top of the background and then linearly interpolate
|
||||
// between the two as we slide inside the background.
|
||||
float4 blended_border = over(input.background_color, input.border_color);
|
||||
color = mix(blended_border, input.background_color,
|
||||
saturate(0.5 - inset_distance));
|
||||
}
|
||||
|
||||
return color * float4(1., 1., 1., saturate(0.5 - distance));
|
||||
@@ -653,3 +644,12 @@ float4 distance_from_clip_rect(float2 unit_vertex, Bounds_ScaledPixels bounds,
|
||||
position.y - clip_bounds.origin.y,
|
||||
clip_bounds.origin.y + clip_bounds.size.height - position.y);
|
||||
}
|
||||
|
||||
float4 over(float4 below, float4 above) {
|
||||
float4 result;
|
||||
float alpha = above.a + below.a * (1.0 - above.a);
|
||||
result.rgb =
|
||||
(above.rgb * above.a + below.rgb * below.a * (1.0 - above.a)) / alpha;
|
||||
result.a = alpha;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ pub struct TestPlatform {
|
||||
background_executor: BackgroundExecutor,
|
||||
foreground_executor: ForegroundExecutor,
|
||||
|
||||
active_window: Arc<Mutex<Option<AnyWindowHandle>>>,
|
||||
pub(crate) active_window: RefCell<Option<TestWindow>>,
|
||||
active_display: Rc<dyn PlatformDisplay>,
|
||||
active_cursor: Mutex<CursorStyle>,
|
||||
current_clipboard_item: Mutex<Option<ClipboardItem>>,
|
||||
@@ -79,6 +79,28 @@ impl TestPlatform {
|
||||
self.prompts.borrow_mut().multiple_choice.push_back(tx);
|
||||
rx
|
||||
}
|
||||
|
||||
pub(crate) fn set_active_window(&self, window: Option<TestWindow>) {
|
||||
let executor = self.foreground_executor().clone();
|
||||
let previous_window = self.active_window.borrow_mut().take();
|
||||
*self.active_window.borrow_mut() = window.clone();
|
||||
|
||||
executor
|
||||
.spawn(async move {
|
||||
if let Some(previous_window) = previous_window {
|
||||
if let Some(window) = window.as_ref() {
|
||||
if Arc::ptr_eq(&previous_window.0, &window.0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
previous_window.simulate_active_status_change(false);
|
||||
}
|
||||
if let Some(window) = window {
|
||||
window.simulate_active_status_change(true);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
|
||||
// todo!("implement out what our tests needed in GPUI 1")
|
||||
@@ -106,7 +128,7 @@ impl Platform for TestPlatform {
|
||||
}
|
||||
|
||||
fn activate(&self, _ignoring_other_apps: bool) {
|
||||
unimplemented!()
|
||||
//
|
||||
}
|
||||
|
||||
fn hide(&self) {
|
||||
@@ -130,7 +152,10 @@ impl Platform for TestPlatform {
|
||||
}
|
||||
|
||||
fn active_window(&self) -> Option<crate::AnyWindowHandle> {
|
||||
self.active_window.lock().clone()
|
||||
self.active_window
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.map(|window| window.0.lock().handle)
|
||||
}
|
||||
|
||||
fn open_window(
|
||||
@@ -139,12 +164,13 @@ impl Platform for TestPlatform {
|
||||
options: WindowOptions,
|
||||
_draw: Box<dyn FnMut() -> Result<Scene>>,
|
||||
) -> Box<dyn crate::PlatformWindow> {
|
||||
*self.active_window.lock() = Some(handle);
|
||||
Box::new(TestWindow::new(
|
||||
let window = TestWindow::new(
|
||||
options,
|
||||
handle,
|
||||
self.weak.clone(),
|
||||
self.active_display.clone(),
|
||||
))
|
||||
);
|
||||
Box::new(window)
|
||||
}
|
||||
|
||||
fn set_display_link_output_callback(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
px, AtlasKey, AtlasTextureId, AtlasTile, Pixels, PlatformAtlas, PlatformDisplay,
|
||||
PlatformInputHandler, PlatformWindow, Point, Size, TestPlatform, TileId, WindowAppearance,
|
||||
WindowBounds, WindowOptions,
|
||||
px, AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, Bounds, InputEvent, KeyDownEvent,
|
||||
Keystroke, Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point,
|
||||
Size, TestPlatform, TileId, WindowAppearance, WindowBounds, WindowOptions,
|
||||
};
|
||||
use collections::HashMap;
|
||||
use parking_lot::Mutex;
|
||||
@@ -10,51 +10,122 @@ use std::{
|
||||
sync::{self, Arc},
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct TestWindowHandlers {
|
||||
pub(crate) active_status_change: Vec<Box<dyn FnMut(bool)>>,
|
||||
pub(crate) input: Vec<Box<dyn FnMut(crate::InputEvent) -> bool>>,
|
||||
pub(crate) moved: Vec<Box<dyn FnMut()>>,
|
||||
pub(crate) resize: Vec<Box<dyn FnMut(Size<Pixels>, f32)>>,
|
||||
}
|
||||
|
||||
pub struct TestWindow {
|
||||
pub struct TestWindowState {
|
||||
pub(crate) bounds: WindowBounds,
|
||||
pub(crate) handle: AnyWindowHandle,
|
||||
display: Rc<dyn PlatformDisplay>,
|
||||
pub(crate) title: Option<String>,
|
||||
pub(crate) edited: bool,
|
||||
pub(crate) input_handler: Option<Arc<Mutex<Box<dyn PlatformInputHandler>>>>,
|
||||
pub(crate) handlers: Arc<Mutex<TestWindowHandlers>>,
|
||||
platform: Weak<TestPlatform>,
|
||||
sprite_atlas: Arc<dyn PlatformAtlas>,
|
||||
|
||||
input_callback: Option<Box<dyn FnMut(InputEvent) -> bool>>,
|
||||
active_status_change_callback: Option<Box<dyn FnMut(bool)>>,
|
||||
resize_callback: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
|
||||
moved_callback: Option<Box<dyn FnMut()>>,
|
||||
input_handler: Option<Box<dyn PlatformInputHandler>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TestWindow(pub(crate) Arc<Mutex<TestWindowState>>);
|
||||
|
||||
impl TestWindow {
|
||||
pub fn new(
|
||||
options: WindowOptions,
|
||||
handle: AnyWindowHandle,
|
||||
platform: Weak<TestPlatform>,
|
||||
display: Rc<dyn PlatformDisplay>,
|
||||
) -> Self {
|
||||
Self {
|
||||
Self(Arc::new(Mutex::new(TestWindowState {
|
||||
bounds: options.bounds,
|
||||
display,
|
||||
platform,
|
||||
input_handler: None,
|
||||
handle,
|
||||
sprite_atlas: Arc::new(TestAtlas::new()),
|
||||
handlers: Default::default(),
|
||||
title: Default::default(),
|
||||
edited: false,
|
||||
|
||||
input_callback: None,
|
||||
active_status_change_callback: None,
|
||||
resize_callback: None,
|
||||
moved_callback: None,
|
||||
input_handler: None,
|
||||
})))
|
||||
}
|
||||
|
||||
pub fn simulate_resize(&mut self, size: Size<Pixels>) {
|
||||
let scale_factor = self.scale_factor();
|
||||
let mut lock = self.0.lock();
|
||||
let Some(mut callback) = lock.resize_callback.take() else {
|
||||
return;
|
||||
};
|
||||
match &mut lock.bounds {
|
||||
WindowBounds::Fullscreen | WindowBounds::Maximized => {
|
||||
lock.bounds = WindowBounds::Fixed(Bounds {
|
||||
origin: Point::default(),
|
||||
size: size.map(|pixels| f64::from(pixels).into()),
|
||||
});
|
||||
}
|
||||
WindowBounds::Fixed(bounds) => {
|
||||
bounds.size = size.map(|pixels| f64::from(pixels).into());
|
||||
}
|
||||
}
|
||||
drop(lock);
|
||||
callback(size, scale_factor);
|
||||
self.0.lock().resize_callback = Some(callback);
|
||||
}
|
||||
|
||||
pub(crate) fn simulate_active_status_change(&self, active: bool) {
|
||||
let mut lock = self.0.lock();
|
||||
let Some(mut callback) = lock.active_status_change_callback.take() else {
|
||||
return;
|
||||
};
|
||||
drop(lock);
|
||||
callback(active);
|
||||
self.0.lock().active_status_change_callback = Some(callback);
|
||||
}
|
||||
|
||||
pub fn simulate_input(&mut self, event: InputEvent) -> bool {
|
||||
let mut lock = self.0.lock();
|
||||
let Some(mut callback) = lock.input_callback.take() else {
|
||||
return false;
|
||||
};
|
||||
drop(lock);
|
||||
let result = callback(event);
|
||||
self.0.lock().input_callback = Some(callback);
|
||||
result
|
||||
}
|
||||
|
||||
pub fn simulate_keystroke(&mut self, keystroke: Keystroke, is_held: bool) {
|
||||
if self.simulate_input(InputEvent::KeyDown(KeyDownEvent {
|
||||
keystroke: keystroke.clone(),
|
||||
is_held,
|
||||
})) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut lock = self.0.lock();
|
||||
let Some(mut input_handler) = lock.input_handler.take() else {
|
||||
panic!(
|
||||
"simulate_keystroke {:?} input event was not handled and there was no active input",
|
||||
&keystroke
|
||||
);
|
||||
};
|
||||
drop(lock);
|
||||
let text = keystroke.ime_key.unwrap_or(keystroke.key);
|
||||
input_handler.replace_text_in_range(None, &text);
|
||||
|
||||
self.0.lock().input_handler = Some(input_handler);
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformWindow for TestWindow {
|
||||
fn bounds(&self) -> WindowBounds {
|
||||
self.bounds
|
||||
self.0.lock().bounds
|
||||
}
|
||||
|
||||
fn content_size(&self) -> Size<Pixels> {
|
||||
let bounds = match self.bounds {
|
||||
let bounds = match self.bounds() {
|
||||
WindowBounds::Fixed(bounds) => bounds,
|
||||
WindowBounds::Maximized | WindowBounds::Fullscreen => self.display().bounds(),
|
||||
};
|
||||
@@ -74,7 +145,7 @@ impl PlatformWindow for TestWindow {
|
||||
}
|
||||
|
||||
fn display(&self) -> std::rc::Rc<dyn crate::PlatformDisplay> {
|
||||
self.display.clone()
|
||||
self.0.lock().display.clone()
|
||||
}
|
||||
|
||||
fn mouse_position(&self) -> Point<Pixels> {
|
||||
@@ -90,11 +161,11 @@ impl PlatformWindow for TestWindow {
|
||||
}
|
||||
|
||||
fn set_input_handler(&mut self, input_handler: Box<dyn crate::PlatformInputHandler>) {
|
||||
self.input_handler = Some(Arc::new(Mutex::new(input_handler)));
|
||||
self.0.lock().input_handler = Some(input_handler);
|
||||
}
|
||||
|
||||
fn clear_input_handler(&mut self) {
|
||||
self.input_handler = None;
|
||||
self.0.lock().input_handler = None;
|
||||
}
|
||||
|
||||
fn prompt(
|
||||
@@ -103,19 +174,29 @@ impl PlatformWindow for TestWindow {
|
||||
_msg: &str,
|
||||
_answers: &[&str],
|
||||
) -> futures::channel::oneshot::Receiver<usize> {
|
||||
self.platform.upgrade().expect("platform dropped").prompt()
|
||||
self.0
|
||||
.lock()
|
||||
.platform
|
||||
.upgrade()
|
||||
.expect("platform dropped")
|
||||
.prompt()
|
||||
}
|
||||
|
||||
fn activate(&self) {
|
||||
unimplemented!()
|
||||
self.0
|
||||
.lock()
|
||||
.platform
|
||||
.upgrade()
|
||||
.unwrap()
|
||||
.set_active_window(Some(self.clone()))
|
||||
}
|
||||
|
||||
fn set_title(&mut self, title: &str) {
|
||||
self.title = Some(title.to_owned());
|
||||
self.0.lock().title = Some(title.to_owned());
|
||||
}
|
||||
|
||||
fn set_edited(&mut self, edited: bool) {
|
||||
self.edited = edited;
|
||||
self.0.lock().edited = edited;
|
||||
}
|
||||
|
||||
fn show_character_palette(&self) {
|
||||
@@ -135,15 +216,15 @@ impl PlatformWindow for TestWindow {
|
||||
}
|
||||
|
||||
fn on_input(&self, callback: Box<dyn FnMut(crate::InputEvent) -> bool>) {
|
||||
self.handlers.lock().input.push(callback)
|
||||
self.0.lock().input_callback = Some(callback)
|
||||
}
|
||||
|
||||
fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
|
||||
self.handlers.lock().active_status_change.push(callback)
|
||||
self.0.lock().active_status_change_callback = Some(callback)
|
||||
}
|
||||
|
||||
fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
|
||||
self.handlers.lock().resize.push(callback)
|
||||
self.0.lock().resize_callback = Some(callback)
|
||||
}
|
||||
|
||||
fn on_fullscreen(&self, _callback: Box<dyn FnMut(bool)>) {
|
||||
@@ -151,7 +232,7 @@ impl PlatformWindow for TestWindow {
|
||||
}
|
||||
|
||||
fn on_moved(&self, callback: Box<dyn FnMut()>) {
|
||||
self.handlers.lock().moved.push(callback)
|
||||
self.0.lock().moved_callback = Some(callback)
|
||||
}
|
||||
|
||||
fn on_should_close(&self, _callback: Box<dyn FnMut() -> bool>) {
|
||||
@@ -175,7 +256,7 @@ impl PlatformWindow for TestWindow {
|
||||
}
|
||||
|
||||
fn sprite_atlas(&self) -> sync::Arc<dyn crate::PlatformAtlas> {
|
||||
self.sprite_atlas.clone()
|
||||
self.0.lock().sprite_atlas.clone()
|
||||
}
|
||||
|
||||
fn as_test(&mut self) -> Option<&mut TestWindow> {
|
||||
|
||||
@@ -15,8 +15,9 @@ use crate::{
|
||||
use anyhow::anyhow;
|
||||
use collections::FxHashMap;
|
||||
use core::fmt;
|
||||
use itertools::Itertools;
|
||||
use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
|
||||
use smallvec::SmallVec;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use std::{
|
||||
cmp,
|
||||
fmt::{Debug, Display, Formatter},
|
||||
@@ -42,6 +43,7 @@ pub struct TextSystem {
|
||||
raster_bounds: RwLock<FxHashMap<RenderGlyphParams, Bounds<DevicePixels>>>,
|
||||
wrapper_pool: Mutex<FxHashMap<FontIdWithSize, Vec<LineWrapper>>>,
|
||||
font_runs_pool: Mutex<Vec<Vec<FontRun>>>,
|
||||
fallback_font_stack: SmallVec<[Font; 2]>,
|
||||
}
|
||||
|
||||
impl TextSystem {
|
||||
@@ -54,6 +56,12 @@ impl TextSystem {
|
||||
font_ids_by_font: RwLock::default(),
|
||||
wrapper_pool: Mutex::default(),
|
||||
font_runs_pool: Mutex::default(),
|
||||
fallback_font_stack: smallvec![
|
||||
// TODO: This is currently Zed-specific.
|
||||
// We should allow GPUI users to provide their own fallback font stack.
|
||||
font("Zed Mono"),
|
||||
font("Helvetica")
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +80,33 @@ impl TextSystem {
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolves the specified font, falling back to the default font stack if
|
||||
/// the font fails to load.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the font and none of the fallbacks can be resolved.
|
||||
pub fn resolve_font(&self, font: &Font) -> FontId {
|
||||
if let Ok(font_id) = self.font_id(font) {
|
||||
return font_id;
|
||||
}
|
||||
|
||||
for fallback in &self.fallback_font_stack {
|
||||
if let Ok(font_id) = self.font_id(fallback) {
|
||||
return font_id;
|
||||
}
|
||||
}
|
||||
|
||||
panic!(
|
||||
"failed to resolve font '{}' or any of the fallbacks: {}",
|
||||
font.family,
|
||||
self.fallback_font_stack
|
||||
.iter()
|
||||
.map(|fallback| &fallback.family)
|
||||
.join(", ")
|
||||
);
|
||||
}
|
||||
|
||||
pub fn bounding_box(&self, font_id: FontId, font_size: Pixels) -> Bounds<Pixels> {
|
||||
self.read_metrics(font_id, |metrics| metrics.bounding_box(font_size))
|
||||
}
|
||||
@@ -159,7 +194,7 @@ impl TextSystem {
|
||||
) -> Result<Arc<LineLayout>> {
|
||||
let mut font_runs = self.font_runs_pool.lock().pop().unwrap_or_default();
|
||||
for run in runs.iter() {
|
||||
let font_id = self.font_id(&run.font)?;
|
||||
let font_id = self.resolve_font(&run.font);
|
||||
if let Some(last_run) = font_runs.last_mut() {
|
||||
if last_run.font_id == font_id {
|
||||
last_run.len += run.len;
|
||||
@@ -253,7 +288,7 @@ impl TextSystem {
|
||||
last_font = Some(run.font.clone());
|
||||
font_runs.push(FontRun {
|
||||
len: run_len_within_line,
|
||||
font_id: self.platform_text_system.font_id(&run.font)?,
|
||||
font_id: self.resolve_font(&run.font),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -143,6 +143,11 @@ impl<V: 'static> WeakView<V> {
|
||||
let view = self.upgrade().context("error upgrading view")?;
|
||||
Ok(view.update(cx, f)).flatten()
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn assert_dropped(&self) {
|
||||
self.model.assert_dropped()
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> Clone for WeakView<V> {
|
||||
|
||||
+26
-20
@@ -1583,36 +1583,16 @@ impl<'a> WindowContext<'a> {
|
||||
|
||||
let mut actions: Vec<Box<dyn Action>> = Vec::new();
|
||||
|
||||
// Capture phase
|
||||
let mut context_stack: SmallVec<[KeyContext; 16]> = SmallVec::new();
|
||||
self.propagate_event = true;
|
||||
|
||||
for node_id in &dispatch_path {
|
||||
let node = self.window.rendered_frame.dispatch_tree.node(*node_id);
|
||||
|
||||
if let Some(context) = node.context.clone() {
|
||||
context_stack.push(context);
|
||||
}
|
||||
|
||||
for key_listener in node.key_listeners.clone() {
|
||||
key_listener(event, DispatchPhase::Capture, self);
|
||||
if !self.propagate_event {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bubble phase
|
||||
for node_id in dispatch_path.iter().rev() {
|
||||
// Handle low level key events
|
||||
let node = self.window.rendered_frame.dispatch_tree.node(*node_id);
|
||||
for key_listener in node.key_listeners.clone() {
|
||||
key_listener(event, DispatchPhase::Bubble, self);
|
||||
if !self.propagate_event {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Match keystrokes
|
||||
let node = self.window.rendered_frame.dispatch_tree.node(*node_id);
|
||||
if node.context.is_some() {
|
||||
@@ -1633,6 +1613,7 @@ impl<'a> WindowContext<'a> {
|
||||
self.clear_pending_keystrokes();
|
||||
}
|
||||
|
||||
self.propagate_event = true;
|
||||
for action in actions {
|
||||
self.dispatch_action_on_node(node_id, action.boxed_clone());
|
||||
if !self.propagate_event {
|
||||
@@ -1640,6 +1621,31 @@ impl<'a> WindowContext<'a> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Capture phase
|
||||
for node_id in &dispatch_path {
|
||||
let node = self.window.rendered_frame.dispatch_tree.node(*node_id);
|
||||
|
||||
for key_listener in node.key_listeners.clone() {
|
||||
key_listener(event, DispatchPhase::Capture, self);
|
||||
if !self.propagate_event {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bubble phase
|
||||
for node_id in dispatch_path.iter().rev() {
|
||||
// Handle low level key events
|
||||
let node = self.window.rendered_frame.dispatch_tree.node(*node_id);
|
||||
for key_listener in node.key_listeners.clone() {
|
||||
key_listener(event, DispatchPhase::Bubble, self);
|
||||
if !self.propagate_event {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.dispatch_keystroke_observers(event, None);
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,12 @@ lazy_static! {
|
||||
pub static ref BUFFER_DIFF_TASK: TaskLabel = TaskLabel::new();
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Copy, Debug)]
|
||||
pub enum Capability {
|
||||
ReadWrite,
|
||||
ReadOnly,
|
||||
}
|
||||
|
||||
pub struct Buffer {
|
||||
text: TextBuffer,
|
||||
diff_base: Option<String>,
|
||||
@@ -90,6 +96,7 @@ pub struct Buffer {
|
||||
completion_triggers: Vec<String>,
|
||||
completion_triggers_timestamp: clock::Lamport,
|
||||
deferred_ops: OperationQueue<Operation>,
|
||||
capability: Capability,
|
||||
}
|
||||
|
||||
pub struct BufferSnapshot {
|
||||
@@ -405,19 +412,27 @@ impl Buffer {
|
||||
TextBuffer::new(replica_id, id, base_text.into()),
|
||||
None,
|
||||
None,
|
||||
Capability::ReadWrite,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn remote(remote_id: u64, replica_id: ReplicaId, base_text: String) -> Self {
|
||||
pub fn remote(
|
||||
remote_id: u64,
|
||||
replica_id: ReplicaId,
|
||||
capability: Capability,
|
||||
base_text: String,
|
||||
) -> Self {
|
||||
Self::build(
|
||||
TextBuffer::new(replica_id, remote_id, base_text),
|
||||
None,
|
||||
None,
|
||||
capability,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn from_proto(
|
||||
replica_id: ReplicaId,
|
||||
capability: Capability,
|
||||
message: proto::BufferState,
|
||||
file: Option<Arc<dyn File>>,
|
||||
) -> Result<Self> {
|
||||
@@ -426,6 +441,7 @@ impl Buffer {
|
||||
buffer,
|
||||
message.diff_base.map(|text| text.into_boxed_str().into()),
|
||||
file,
|
||||
capability,
|
||||
);
|
||||
this.text.set_line_ending(proto::deserialize_line_ending(
|
||||
rpc::proto::LineEnding::from_i32(message.line_ending)
|
||||
@@ -504,10 +520,19 @@ impl Buffer {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn capability(&self) -> Capability {
|
||||
self.capability
|
||||
}
|
||||
|
||||
pub fn read_only(&self) -> bool {
|
||||
self.capability == Capability::ReadOnly
|
||||
}
|
||||
|
||||
pub fn build(
|
||||
buffer: TextBuffer,
|
||||
diff_base: Option<String>,
|
||||
file: Option<Arc<dyn File>>,
|
||||
capability: Capability,
|
||||
) -> Self {
|
||||
let saved_mtime = if let Some(file) = file.as_ref() {
|
||||
file.mtime()
|
||||
@@ -526,6 +551,7 @@ impl Buffer {
|
||||
diff_base,
|
||||
git_diff: git::diff::BufferDiff::new(),
|
||||
file,
|
||||
capability,
|
||||
syntax_map: Mutex::new(SyntaxMap::new()),
|
||||
parsing_in_background: false,
|
||||
parse_count: 0,
|
||||
|
||||
@@ -1926,7 +1926,7 @@ fn test_serialization(cx: &mut gpui::AppContext) {
|
||||
.background_executor()
|
||||
.block(buffer1.read(cx).serialize_ops(None, cx));
|
||||
let buffer2 = cx.new_model(|cx| {
|
||||
let mut buffer = Buffer::from_proto(1, state, None).unwrap();
|
||||
let mut buffer = Buffer::from_proto(1, Capability::ReadWrite, state, None).unwrap();
|
||||
buffer
|
||||
.apply_ops(
|
||||
ops.into_iter()
|
||||
@@ -1967,7 +1967,8 @@ fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
|
||||
let ops = cx
|
||||
.background_executor()
|
||||
.block(base_buffer.read(cx).serialize_ops(None, cx));
|
||||
let mut buffer = Buffer::from_proto(i as ReplicaId, state, None).unwrap();
|
||||
let mut buffer =
|
||||
Buffer::from_proto(i as ReplicaId, Capability::ReadWrite, state, None).unwrap();
|
||||
buffer
|
||||
.apply_ops(
|
||||
ops.into_iter()
|
||||
@@ -2083,8 +2084,13 @@ fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
|
||||
replica_id
|
||||
);
|
||||
new_buffer = Some(cx.new_model(|cx| {
|
||||
let mut new_buffer =
|
||||
Buffer::from_proto(new_replica_id, old_buffer_state, None).unwrap();
|
||||
let mut new_buffer = Buffer::from_proto(
|
||||
new_replica_id,
|
||||
Capability::ReadWrite,
|
||||
old_buffer_state,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
new_buffer
|
||||
.apply_ops(
|
||||
old_buffer_ops
|
||||
|
||||
@@ -113,7 +113,6 @@ pub struct LanguageServerName(pub Arc<str>);
|
||||
pub struct CachedLspAdapter {
|
||||
pub name: LanguageServerName,
|
||||
pub short_name: &'static str,
|
||||
pub initialization_options: Option<Value>,
|
||||
pub disk_based_diagnostic_sources: Vec<String>,
|
||||
pub disk_based_diagnostics_progress_token: Option<String>,
|
||||
pub language_ids: HashMap<String, String>,
|
||||
@@ -125,7 +124,6 @@ impl CachedLspAdapter {
|
||||
pub async fn new(adapter: Arc<dyn LspAdapter>) -> Arc<Self> {
|
||||
let name = adapter.name().await;
|
||||
let short_name = adapter.short_name();
|
||||
let initialization_options = adapter.initialization_options().await;
|
||||
let disk_based_diagnostic_sources = adapter.disk_based_diagnostic_sources().await;
|
||||
let disk_based_diagnostics_progress_token =
|
||||
adapter.disk_based_diagnostics_progress_token().await;
|
||||
@@ -134,7 +132,6 @@ impl CachedLspAdapter {
|
||||
Arc::new(CachedLspAdapter {
|
||||
name,
|
||||
short_name,
|
||||
initialization_options,
|
||||
disk_based_diagnostic_sources,
|
||||
disk_based_diagnostics_progress_token,
|
||||
language_ids,
|
||||
|
||||
@@ -10,7 +10,7 @@ use language::{LanguageServerId, LanguageServerName};
|
||||
use lsp::IoKind;
|
||||
use project::{search::SearchQuery, Project};
|
||||
use std::{borrow::Cow, sync::Arc};
|
||||
use ui::{h_stack, popover_menu, Button, Checkbox, Clickable, ContextMenu, Label, Selection};
|
||||
use ui::{popover_menu, prelude::*, Button, Checkbox, ContextMenu, Label, Selection};
|
||||
use workspace::{
|
||||
item::{Item, ItemHandle},
|
||||
searchable::{SearchEvent, SearchableItem, SearchableItemHandle},
|
||||
@@ -614,8 +614,14 @@ impl Item for LspLogView {
|
||||
Editor::to_item_events(event, f)
|
||||
}
|
||||
|
||||
fn tab_content(&self, _: Option<usize>, _: bool, _: &WindowContext<'_>) -> AnyElement {
|
||||
Label::new("LSP Logs").into_any_element()
|
||||
fn tab_content(&self, _: Option<usize>, selected: bool, _: &WindowContext<'_>) -> AnyElement {
|
||||
Label::new("LSP Logs")
|
||||
.color(if selected {
|
||||
Color::Default
|
||||
} else {
|
||||
Color::Muted
|
||||
})
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn as_searchable(&self, handle: &View<Self>) -> Option<Box<dyn SearchableItemHandle>> {
|
||||
@@ -766,9 +772,10 @@ impl Render for LspLogToolbarItemView {
|
||||
}),
|
||||
);
|
||||
if server_selected && row.logs_selected {
|
||||
let selected_ix = menu.select_last();
|
||||
debug_assert_eq!(
|
||||
Some(ix * 3 + 1),
|
||||
menu.select_last(),
|
||||
selected_ix,
|
||||
"Could not scroll to a just added LSP menu item"
|
||||
);
|
||||
}
|
||||
@@ -816,9 +823,10 @@ impl Render for LspLogToolbarItemView {
|
||||
}),
|
||||
);
|
||||
if server_selected && row.rpc_trace_selected {
|
||||
let selected_ix = menu.select_last();
|
||||
debug_assert_eq!(
|
||||
Some(ix * 3 + 2),
|
||||
menu.select_last(),
|
||||
selected_ix,
|
||||
"Could not scroll to a just added LSP menu item"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -405,8 +405,14 @@ impl Item for SyntaxTreeView {
|
||||
|
||||
fn to_item_events(_: &Self::Event, _: impl FnMut(workspace::item::ItemEvent)) {}
|
||||
|
||||
fn tab_content(&self, _: Option<usize>, _: bool, _: &WindowContext<'_>) -> AnyElement {
|
||||
Label::new("Syntax Tree").into_any_element()
|
||||
fn tab_content(&self, _: Option<usize>, selected: bool, _: &WindowContext<'_>) -> AnyElement {
|
||||
Label::new("Syntax Tree")
|
||||
.color(if selected {
|
||||
Color::Default
|
||||
} else {
|
||||
Color::Muted
|
||||
})
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn clone_on_split(
|
||||
|
||||
@@ -62,6 +62,7 @@ impl<'a> VideoGrant<'a> {
|
||||
Self {
|
||||
room: Some(Cow::Borrowed(room)),
|
||||
room_join: Some(true),
|
||||
can_publish: Some(false),
|
||||
can_subscribe: Some(true),
|
||||
..Default::default()
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ pub use language::Completion;
|
||||
use language::{
|
||||
char_kind,
|
||||
language_settings::{language_settings, LanguageSettings},
|
||||
AutoindentMode, Buffer, BufferChunks, BufferSnapshot, CharKind, Chunk, CursorShape,
|
||||
AutoindentMode, Buffer, BufferChunks, BufferSnapshot, Capability, CharKind, Chunk, CursorShape,
|
||||
DiagnosticEntry, File, IndentSize, Language, LanguageScope, OffsetRangeExt, OffsetUtf16,
|
||||
Outline, OutlineItem, Point, PointUtf16, Selection, TextDimension, ToOffset as _,
|
||||
ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _, TransactionId, Unclipped,
|
||||
@@ -55,6 +55,7 @@ pub struct MultiBuffer {
|
||||
replica_id: ReplicaId,
|
||||
history: History,
|
||||
title: Option<String>,
|
||||
capability: Capability,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
@@ -225,13 +226,14 @@ struct ExcerptBytes<'a> {
|
||||
}
|
||||
|
||||
impl MultiBuffer {
|
||||
pub fn new(replica_id: ReplicaId) -> Self {
|
||||
pub fn new(replica_id: ReplicaId, capability: Capability) -> Self {
|
||||
Self {
|
||||
snapshot: Default::default(),
|
||||
buffers: Default::default(),
|
||||
next_excerpt_id: 1,
|
||||
subscriptions: Default::default(),
|
||||
singleton: false,
|
||||
capability,
|
||||
replica_id,
|
||||
history: History {
|
||||
next_transaction_id: Default::default(),
|
||||
@@ -271,6 +273,7 @@ impl MultiBuffer {
|
||||
next_excerpt_id: 1,
|
||||
subscriptions: Default::default(),
|
||||
singleton: self.singleton,
|
||||
capability: self.capability,
|
||||
replica_id: self.replica_id,
|
||||
history: self.history.clone(),
|
||||
title: self.title.clone(),
|
||||
@@ -282,8 +285,12 @@ impl MultiBuffer {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn read_only(&self) -> bool {
|
||||
self.capability == Capability::ReadOnly
|
||||
}
|
||||
|
||||
pub fn singleton(buffer: Model<Buffer>, cx: &mut ModelContext<Self>) -> Self {
|
||||
let mut this = Self::new(buffer.read(cx).replica_id());
|
||||
let mut this = Self::new(buffer.read(cx).replica_id(), buffer.read(cx).capability());
|
||||
this.singleton = true;
|
||||
this.push_excerpts(
|
||||
buffer,
|
||||
@@ -1657,7 +1664,7 @@ impl MultiBuffer {
|
||||
excerpts: [(&str, Vec<Range<Point>>); COUNT],
|
||||
cx: &mut gpui::AppContext,
|
||||
) -> Model<Self> {
|
||||
let multi = cx.new_model(|_| Self::new(0));
|
||||
let multi = cx.new_model(|_| Self::new(0, Capability::ReadWrite));
|
||||
for (text, ranges) in excerpts {
|
||||
let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text));
|
||||
let excerpt_ranges = ranges.into_iter().map(|range| ExcerptRange {
|
||||
@@ -1678,7 +1685,7 @@ impl MultiBuffer {
|
||||
|
||||
pub fn build_random(rng: &mut impl rand::Rng, cx: &mut gpui::AppContext) -> Model<Self> {
|
||||
cx.new_model(|cx| {
|
||||
let mut multibuffer = MultiBuffer::new(0);
|
||||
let mut multibuffer = MultiBuffer::new(0, Capability::ReadWrite);
|
||||
let mutation_count = rng.gen_range(1..=5);
|
||||
multibuffer.randomly_edit_excerpts(rng, mutation_count, cx);
|
||||
multibuffer
|
||||
@@ -4176,7 +4183,7 @@ mod tests {
|
||||
let ops = cx
|
||||
.background_executor()
|
||||
.block(host_buffer.read(cx).serialize_ops(None, cx));
|
||||
let mut buffer = Buffer::from_proto(1, state, None).unwrap();
|
||||
let mut buffer = Buffer::from_proto(1, Capability::ReadWrite, state, None).unwrap();
|
||||
buffer
|
||||
.apply_ops(
|
||||
ops.into_iter()
|
||||
@@ -4205,7 +4212,7 @@ mod tests {
|
||||
cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(6, 6, 'a')));
|
||||
let buffer_2 =
|
||||
cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(6, 6, 'g')));
|
||||
let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
|
||||
let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
|
||||
|
||||
let events = Arc::new(RwLock::new(Vec::<Event>::new()));
|
||||
multibuffer.update(cx, |_, cx| {
|
||||
@@ -4442,8 +4449,8 @@ mod tests {
|
||||
let buffer_2 =
|
||||
cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(10, 3, 'm')));
|
||||
|
||||
let leader_multibuffer = cx.new_model(|_| MultiBuffer::new(0));
|
||||
let follower_multibuffer = cx.new_model(|_| MultiBuffer::new(0));
|
||||
let leader_multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
|
||||
let follower_multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
|
||||
let follower_edit_event_count = Arc::new(RwLock::new(0));
|
||||
|
||||
follower_multibuffer.update(cx, |_, cx| {
|
||||
@@ -4547,7 +4554,7 @@ mod tests {
|
||||
fn test_push_excerpts_with_context_lines(cx: &mut AppContext) {
|
||||
let buffer =
|
||||
cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(20, 3, 'a')));
|
||||
let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
|
||||
let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
|
||||
let anchor_ranges = multibuffer.update(cx, |multibuffer, cx| {
|
||||
multibuffer.push_excerpts_with_context_lines(
|
||||
buffer.clone(),
|
||||
@@ -4584,7 +4591,7 @@ mod tests {
|
||||
async fn test_stream_excerpts_with_context_lines(cx: &mut TestAppContext) {
|
||||
let buffer =
|
||||
cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(20, 3, 'a')));
|
||||
let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
|
||||
let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
|
||||
let anchor_ranges = multibuffer.update(cx, |multibuffer, cx| {
|
||||
let snapshot = buffer.read(cx);
|
||||
let ranges = vec![
|
||||
@@ -4619,7 +4626,7 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_empty_multibuffer(cx: &mut AppContext) {
|
||||
let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
|
||||
let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
|
||||
|
||||
let snapshot = multibuffer.read(cx).snapshot(cx);
|
||||
assert_eq!(snapshot.text(), "");
|
||||
@@ -4652,7 +4659,7 @@ mod tests {
|
||||
let buffer_1 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abcd"));
|
||||
let buffer_2 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "efghi"));
|
||||
let multibuffer = cx.new_model(|cx| {
|
||||
let mut multibuffer = MultiBuffer::new(0);
|
||||
let mut multibuffer = MultiBuffer::new(0, Capability::ReadWrite);
|
||||
multibuffer.push_excerpts(
|
||||
buffer_1.clone(),
|
||||
[ExcerptRange {
|
||||
@@ -4710,7 +4717,7 @@ mod tests {
|
||||
let buffer_1 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abcd"));
|
||||
let buffer_2 =
|
||||
cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "ABCDEFGHIJKLMNOP"));
|
||||
let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
|
||||
let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
|
||||
|
||||
// Create an insertion id in buffer 1 that doesn't exist in buffer 2.
|
||||
// Add an excerpt from buffer 1 that spans this new insertion.
|
||||
@@ -4844,7 +4851,7 @@ mod tests {
|
||||
.unwrap_or(10);
|
||||
|
||||
let mut buffers: Vec<Model<Buffer>> = Vec::new();
|
||||
let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
|
||||
let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
|
||||
let mut excerpt_ids = Vec::<ExcerptId>::new();
|
||||
let mut expected_excerpts = Vec::<(Model<Buffer>, Range<text::Anchor>)>::new();
|
||||
let mut anchors = Vec::new();
|
||||
@@ -5266,7 +5273,7 @@ mod tests {
|
||||
|
||||
let buffer_1 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "1234"));
|
||||
let buffer_2 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "5678"));
|
||||
let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
|
||||
let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
|
||||
let group_interval = multibuffer.read(cx).history.group_interval;
|
||||
multibuffer.update(cx, |multibuffer, cx| {
|
||||
multibuffer.push_excerpts(
|
||||
|
||||
@@ -39,11 +39,11 @@ use language::{
|
||||
deserialize_anchor, deserialize_fingerprint, deserialize_line_ending, deserialize_version,
|
||||
serialize_anchor, serialize_version, split_operations,
|
||||
},
|
||||
range_from_lsp, range_to_lsp, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CodeAction,
|
||||
CodeLabel, Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Diff, Event as BufferEvent,
|
||||
File as _, Language, LanguageRegistry, LanguageServerName, LocalFile, LspAdapterDelegate,
|
||||
OffsetRangeExt, Operation, Patch, PendingLanguageServer, PointUtf16, TextBufferSnapshot,
|
||||
ToOffset, ToPointUtf16, Transaction, Unclipped,
|
||||
range_from_lsp, range_to_lsp, Bias, Buffer, BufferSnapshot, CachedLspAdapter, Capability,
|
||||
CodeAction, CodeLabel, Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Diff,
|
||||
Event as BufferEvent, File as _, Language, LanguageRegistry, LanguageServerName, LocalFile,
|
||||
LspAdapterDelegate, OffsetRangeExt, Operation, Patch, PendingLanguageServer, PointUtf16,
|
||||
TextBufferSnapshot, ToOffset, ToPointUtf16, Transaction, Unclipped,
|
||||
};
|
||||
use log::error;
|
||||
use lsp::{
|
||||
@@ -262,6 +262,7 @@ enum ProjectClientState {
|
||||
},
|
||||
Remote {
|
||||
sharing_has_stopped: bool,
|
||||
capability: Capability,
|
||||
remote_id: u64,
|
||||
replica_id: ReplicaId,
|
||||
},
|
||||
@@ -702,6 +703,7 @@ impl Project {
|
||||
user_store: Model<UserStore>,
|
||||
languages: Arc<LanguageRegistry>,
|
||||
fs: Arc<dyn Fs>,
|
||||
role: proto::ChannelRole,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> Result<Model<Self>> {
|
||||
client.authenticate_and_connect(true, &cx).await?;
|
||||
@@ -756,6 +758,7 @@ impl Project {
|
||||
client: client.clone(),
|
||||
client_state: Some(ProjectClientState::Remote {
|
||||
sharing_has_stopped: false,
|
||||
capability: Capability::ReadWrite,
|
||||
remote_id,
|
||||
replica_id,
|
||||
}),
|
||||
@@ -796,6 +799,7 @@ impl Project {
|
||||
prettiers_per_worktree: HashMap::default(),
|
||||
prettier_instances: HashMap::default(),
|
||||
};
|
||||
this.set_role(role);
|
||||
for worktree in worktrees {
|
||||
let _ = this.add_worktree(&worktree, cx);
|
||||
}
|
||||
@@ -1618,6 +1622,17 @@ impl Project {
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn set_role(&mut self, role: proto::ChannelRole) {
|
||||
if let Some(ProjectClientState::Remote { capability, .. }) = &mut self.client_state {
|
||||
*capability = if role == proto::ChannelRole::Member || role == proto::ChannelRole::Admin
|
||||
{
|
||||
Capability::ReadWrite
|
||||
} else {
|
||||
Capability::ReadOnly
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn disconnected_from_host_internal(&mut self, cx: &mut AppContext) {
|
||||
if let Some(ProjectClientState::Remote {
|
||||
sharing_has_stopped,
|
||||
@@ -1659,7 +1674,7 @@ impl Project {
|
||||
cx.emit(Event::Closed);
|
||||
}
|
||||
|
||||
pub fn is_read_only(&self) -> bool {
|
||||
pub fn is_disconnected(&self) -> bool {
|
||||
match &self.client_state {
|
||||
Some(ProjectClientState::Remote {
|
||||
sharing_has_stopped,
|
||||
@@ -1669,6 +1684,17 @@ impl Project {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn capability(&self) -> Capability {
|
||||
match &self.client_state {
|
||||
Some(ProjectClientState::Remote { capability, .. }) => *capability,
|
||||
Some(ProjectClientState::Local { .. }) | None => Capability::ReadWrite,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_read_only(&self) -> bool {
|
||||
self.is_disconnected() || self.capability() == Capability::ReadOnly
|
||||
}
|
||||
|
||||
pub fn is_local(&self) -> bool {
|
||||
match &self.client_state {
|
||||
Some(ProjectClientState::Remote { .. }) => false,
|
||||
@@ -2816,15 +2842,6 @@ impl Project {
|
||||
let lsp = project_settings.lsp.get(&adapter.name.0);
|
||||
let override_options = lsp.map(|s| s.initialization_options.clone()).flatten();
|
||||
|
||||
let mut initialization_options = adapter.initialization_options.clone();
|
||||
match (&mut initialization_options, override_options) {
|
||||
(Some(initialization_options), Some(override_options)) => {
|
||||
merge_json_value_into(override_options, initialization_options);
|
||||
}
|
||||
(None, override_options) => initialization_options = override_options,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let server_id = pending_server.server_id;
|
||||
let container_dir = pending_server.container_dir.clone();
|
||||
let state = LanguageServerState::Starting({
|
||||
@@ -2837,7 +2854,7 @@ impl Project {
|
||||
let result = Self::setup_and_insert_language_server(
|
||||
this.clone(),
|
||||
&worktree_path,
|
||||
initialization_options,
|
||||
override_options,
|
||||
pending_server,
|
||||
adapter.clone(),
|
||||
language.clone(),
|
||||
@@ -2958,7 +2975,7 @@ impl Project {
|
||||
async fn setup_and_insert_language_server(
|
||||
this: WeakModel<Self>,
|
||||
worktree_path: &Path,
|
||||
initialization_options: Option<serde_json::Value>,
|
||||
override_initialization_options: Option<serde_json::Value>,
|
||||
pending_server: PendingLanguageServer,
|
||||
adapter: Arc<CachedLspAdapter>,
|
||||
language: Arc<Language>,
|
||||
@@ -2968,7 +2985,7 @@ impl Project {
|
||||
) -> Result<Option<Arc<LanguageServer>>> {
|
||||
let language_server = Self::setup_pending_language_server(
|
||||
this.clone(),
|
||||
initialization_options,
|
||||
override_initialization_options,
|
||||
pending_server,
|
||||
worktree_path,
|
||||
adapter.clone(),
|
||||
@@ -2998,7 +3015,7 @@ impl Project {
|
||||
|
||||
async fn setup_pending_language_server(
|
||||
this: WeakModel<Self>,
|
||||
initialization_options: Option<serde_json::Value>,
|
||||
override_options: Option<serde_json::Value>,
|
||||
pending_server: PendingLanguageServer,
|
||||
worktree_path: &Path,
|
||||
adapter: Arc<CachedLspAdapter>,
|
||||
@@ -3164,7 +3181,14 @@ impl Project {
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
let mut initialization_options = adapter.adapter.initialization_options().await;
|
||||
match (&mut initialization_options, override_options) {
|
||||
(Some(initialization_options), Some(override_options)) => {
|
||||
merge_json_value_into(override_options, initialization_options);
|
||||
}
|
||||
(None, override_options) => initialization_options = override_options,
|
||||
_ => {}
|
||||
}
|
||||
let language_server = language_server.initialize(initialization_options).await?;
|
||||
|
||||
language_server
|
||||
@@ -6015,7 +6039,7 @@ impl Project {
|
||||
this.upgrade().context("project dropped")?;
|
||||
let response = rpc.request(message).await?;
|
||||
let this = this.upgrade().context("project dropped")?;
|
||||
if this.update(&mut cx, |this, _| this.is_read_only())? {
|
||||
if this.update(&mut cx, |this, _| this.is_disconnected())? {
|
||||
Err(anyhow!("disconnected before completing request"))
|
||||
} else {
|
||||
request
|
||||
@@ -7194,7 +7218,8 @@ impl Project {
|
||||
|
||||
let buffer_id = state.id;
|
||||
let buffer = cx.new_model(|_| {
|
||||
Buffer::from_proto(this.replica_id(), state, buffer_file).unwrap()
|
||||
Buffer::from_proto(this.replica_id(), this.capability(), state, buffer_file)
|
||||
.unwrap()
|
||||
});
|
||||
this.incomplete_remote_buffers
|
||||
.insert(buffer_id, Some(buffer));
|
||||
@@ -7942,7 +7967,7 @@ impl Project {
|
||||
|
||||
if let Some(buffer) = buffer {
|
||||
break buffer;
|
||||
} else if this.update(&mut cx, |this, _| this.is_read_only())? {
|
||||
} else if this.update(&mut cx, |this, _| this.is_disconnected())? {
|
||||
return Err(anyhow!("disconnected before buffer {} could be opened", id));
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,8 @@ use language::{
|
||||
deserialize_fingerprint, deserialize_version, serialize_fingerprint, serialize_line_ending,
|
||||
serialize_version,
|
||||
},
|
||||
Buffer, DiagnosticEntry, File as _, LineEnding, PointUtf16, Rope, RopeFingerprint, Unclipped,
|
||||
Buffer, Capability, DiagnosticEntry, File as _, LineEnding, PointUtf16, Rope, RopeFingerprint,
|
||||
Unclipped,
|
||||
};
|
||||
use lsp::LanguageServerId;
|
||||
use parking_lot::Mutex;
|
||||
@@ -682,7 +683,14 @@ impl LocalWorktree {
|
||||
.background_executor()
|
||||
.spawn(async move { text::Buffer::new(0, id, contents) })
|
||||
.await;
|
||||
cx.new_model(|_| Buffer::build(text_buffer, diff_base, Some(Arc::new(file))))
|
||||
cx.new_model(|_| {
|
||||
Buffer::build(
|
||||
text_buffer,
|
||||
diff_base,
|
||||
Some(Arc::new(file)),
|
||||
Capability::ReadWrite,
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -388,8 +388,18 @@ impl ProjectPanel {
|
||||
let is_dir = entry.is_dir();
|
||||
let worktree_id = worktree.id();
|
||||
let is_local = project.is_local();
|
||||
let is_read_only = project.is_read_only();
|
||||
|
||||
let context_menu = ContextMenu::build(cx, |mut menu, cx| {
|
||||
if is_read_only {
|
||||
menu = menu.action("Copy Relative Path", Box::new(CopyRelativePath));
|
||||
if is_dir {
|
||||
menu = menu.action("Search Inside", Box::new(NewSearchInDirectory))
|
||||
}
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
if is_local {
|
||||
menu = menu.action(
|
||||
"Add Folder to Project",
|
||||
@@ -971,25 +981,16 @@ impl ProjectPanel {
|
||||
}
|
||||
}
|
||||
|
||||
fn open_in_terminal(&mut self, _: &OpenInTerminal, _cx: &mut ViewContext<Self>) {
|
||||
todo!()
|
||||
// if let Some((worktree, entry)) = self.selected_entry(cx) {
|
||||
// let window = cx.window();
|
||||
// let view_id = cx.view_id();
|
||||
// let path = worktree.abs_path().join(&entry.path);
|
||||
|
||||
// cx.app_context()
|
||||
// .spawn(|mut cx| async move {
|
||||
// window.dispatch_action(
|
||||
// view_id,
|
||||
// &workspace::OpenTerminal {
|
||||
// working_directory: path,
|
||||
// },
|
||||
// &mut cx,
|
||||
// );
|
||||
// })
|
||||
// .detach();
|
||||
// }
|
||||
fn open_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext<Self>) {
|
||||
if let Some((worktree, entry)) = self.selected_entry(cx) {
|
||||
let path = worktree.abs_path().join(&entry.path);
|
||||
cx.dispatch_action(
|
||||
workspace::OpenTerminal {
|
||||
working_directory: path,
|
||||
}
|
||||
.boxed_clone(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_search_in_directory(
|
||||
@@ -1404,7 +1405,7 @@ impl ProjectPanel {
|
||||
.child(if let Some(icon) = &icon {
|
||||
div().child(IconElement::from_path(icon.to_string()).color(Color::Muted))
|
||||
} else {
|
||||
div()
|
||||
div().size(IconSize::default().rems()).invisible()
|
||||
})
|
||||
.child(
|
||||
if let (Some(editor), true) = (Some(&self.filename_editor), show_editor) {
|
||||
@@ -1482,6 +1483,7 @@ impl ProjectPanel {
|
||||
impl Render for ProjectPanel {
|
||||
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement {
|
||||
let has_worktree = self.visible_entries.len() != 0;
|
||||
let project = self.project.read(cx);
|
||||
|
||||
if has_worktree {
|
||||
div()
|
||||
@@ -1494,21 +1496,25 @@ impl Render for ProjectPanel {
|
||||
.on_action(cx.listener(Self::expand_selected_entry))
|
||||
.on_action(cx.listener(Self::collapse_selected_entry))
|
||||
.on_action(cx.listener(Self::collapse_all_entries))
|
||||
.on_action(cx.listener(Self::new_file))
|
||||
.on_action(cx.listener(Self::new_directory))
|
||||
.on_action(cx.listener(Self::rename))
|
||||
.on_action(cx.listener(Self::delete))
|
||||
.on_action(cx.listener(Self::confirm))
|
||||
.on_action(cx.listener(Self::open_file))
|
||||
.on_action(cx.listener(Self::confirm))
|
||||
.on_action(cx.listener(Self::cancel))
|
||||
.on_action(cx.listener(Self::cut))
|
||||
.on_action(cx.listener(Self::copy))
|
||||
.on_action(cx.listener(Self::copy_path))
|
||||
.on_action(cx.listener(Self::copy_relative_path))
|
||||
.on_action(cx.listener(Self::paste))
|
||||
.on_action(cx.listener(Self::reveal_in_finder))
|
||||
.on_action(cx.listener(Self::open_in_terminal))
|
||||
.on_action(cx.listener(Self::new_search_in_directory))
|
||||
.when(!project.is_read_only(), |el| {
|
||||
el.on_action(cx.listener(Self::new_file))
|
||||
.on_action(cx.listener(Self::new_directory))
|
||||
.on_action(cx.listener(Self::rename))
|
||||
.on_action(cx.listener(Self::delete))
|
||||
.on_action(cx.listener(Self::cut))
|
||||
.on_action(cx.listener(Self::copy))
|
||||
.on_action(cx.listener(Self::paste))
|
||||
})
|
||||
.when(project.is_local(), |el| {
|
||||
el.on_action(cx.listener(Self::reveal_in_finder))
|
||||
.on_action(cx.listener(Self::open_in_terminal))
|
||||
})
|
||||
.track_focus(&self.focus_handle)
|
||||
.child(
|
||||
uniform_list(
|
||||
|
||||
@@ -395,6 +395,7 @@ mod tests {
|
||||
language::init(cx);
|
||||
Project::init_settings(cx);
|
||||
workspace::init_settings(cx);
|
||||
editor::init(cx);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -269,6 +269,7 @@ message Participant {
|
||||
repeated ParticipantProject projects = 3;
|
||||
ParticipantLocation location = 4;
|
||||
uint32 participant_index = 5;
|
||||
ChannelRole role = 6;
|
||||
}
|
||||
|
||||
message PendingParticipant {
|
||||
|
||||
@@ -70,7 +70,7 @@ impl BufferSearchBar {
|
||||
fn render_text_input(&self, editor: &View<Editor>, cx: &ViewContext<Self>) -> impl IntoElement {
|
||||
let settings = ThemeSettings::get_global(cx);
|
||||
let text_style = TextStyle {
|
||||
color: if editor.read(cx).read_only() {
|
||||
color: if editor.read(cx).read_only(cx) {
|
||||
cx.theme().colors().text_disabled
|
||||
} else {
|
||||
cx.theme().colors().text
|
||||
@@ -223,6 +223,7 @@ impl Render for BufferSearchBar {
|
||||
.gap_2()
|
||||
.border_1()
|
||||
.border_color(editor_border)
|
||||
.min_w(rems(384. / 16.))
|
||||
.rounded_lg()
|
||||
.child(IconElement::new(Icon::MagnifyingGlass))
|
||||
.child(self.render_text_input(&self.query_editor, cx))
|
||||
@@ -422,89 +423,134 @@ impl ToolbarItemView for BufferSearchBar {
|
||||
}
|
||||
}
|
||||
|
||||
impl BufferSearchBar {
|
||||
fn register(workspace: &mut Workspace) {
|
||||
workspace.register_action(move |workspace, deploy: &Deploy, cx| {
|
||||
let pane = workspace.active_pane();
|
||||
/// Registrar inverts the dependency between search and it's downstream user, allowing said downstream user to register search action without knowing exactly what those actions are.
|
||||
pub trait SearchActionsRegistrar {
|
||||
fn register_handler<A: Action>(
|
||||
&mut self,
|
||||
callback: fn(&mut BufferSearchBar, &A, &mut ViewContext<BufferSearchBar>),
|
||||
);
|
||||
}
|
||||
|
||||
pane.update(cx, |this, cx| {
|
||||
this.toolbar().update(cx, |this, cx| {
|
||||
type GetSearchBar<T> =
|
||||
for<'a, 'b> fn(&'a T, &'a mut ViewContext<'b, T>) -> Option<View<BufferSearchBar>>;
|
||||
|
||||
/// Registers search actions on a div that can be taken out.
|
||||
pub struct DivRegistrar<'a, 'b, T: 'static> {
|
||||
div: Option<Div>,
|
||||
cx: &'a mut ViewContext<'b, T>,
|
||||
search_getter: GetSearchBar<T>,
|
||||
}
|
||||
|
||||
impl<'a, 'b, T: 'static> DivRegistrar<'a, 'b, T> {
|
||||
pub fn new(search_getter: GetSearchBar<T>, cx: &'a mut ViewContext<'b, T>) -> Self {
|
||||
Self {
|
||||
div: Some(div()),
|
||||
cx,
|
||||
search_getter,
|
||||
}
|
||||
}
|
||||
pub fn into_div(self) -> Div {
|
||||
// This option is always Some; it's an option in the first place because we want to call methods
|
||||
// on div that require ownership.
|
||||
self.div.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> SearchActionsRegistrar for DivRegistrar<'_, '_, T> {
|
||||
fn register_handler<A: gpui::Action>(
|
||||
&mut self,
|
||||
callback: fn(&mut BufferSearchBar, &A, &mut ViewContext<BufferSearchBar>),
|
||||
) {
|
||||
let getter = self.search_getter;
|
||||
self.div = self.div.take().map(|div| {
|
||||
div.on_action(self.cx.listener(move |this, action, cx| {
|
||||
(getter)(this, cx)
|
||||
.clone()
|
||||
.map(|search_bar| search_bar.update(cx, |this, cx| callback(this, action, cx)));
|
||||
}))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Register actions for an active pane.
|
||||
impl SearchActionsRegistrar for Workspace {
|
||||
fn register_handler<A: Action>(
|
||||
&mut self,
|
||||
callback: fn(&mut BufferSearchBar, &A, &mut ViewContext<BufferSearchBar>),
|
||||
) {
|
||||
self.register_action(move |workspace, action: &A, cx| {
|
||||
let pane = workspace.active_pane();
|
||||
pane.update(cx, move |this, cx| {
|
||||
this.toolbar().update(cx, move |this, cx| {
|
||||
if let Some(search_bar) = this.item_of_type::<BufferSearchBar>() {
|
||||
search_bar.update(cx, |this, cx| {
|
||||
this.deploy(deploy, cx);
|
||||
});
|
||||
return;
|
||||
search_bar.update(cx, move |this, cx| callback(this, action, cx));
|
||||
cx.notify();
|
||||
}
|
||||
let view = cx.new_view(|cx| BufferSearchBar::new(cx));
|
||||
this.add_item(view.clone(), cx);
|
||||
view.update(cx, |this, cx| this.deploy(deploy, cx));
|
||||
cx.notify();
|
||||
})
|
||||
});
|
||||
});
|
||||
fn register_action<A: Action>(
|
||||
workspace: &mut Workspace,
|
||||
update: fn(&mut BufferSearchBar, &A, &mut ViewContext<BufferSearchBar>),
|
||||
) {
|
||||
workspace.register_action(move |workspace, action: &A, cx| {
|
||||
let pane = workspace.active_pane();
|
||||
pane.update(cx, move |this, cx| {
|
||||
this.toolbar().update(cx, move |this, cx| {
|
||||
if let Some(search_bar) = this.item_of_type::<BufferSearchBar>() {
|
||||
search_bar.update(cx, move |this, cx| update(this, action, cx));
|
||||
cx.notify();
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
register_action(workspace, |this, action: &ToggleCaseSensitive, cx| {
|
||||
}
|
||||
}
|
||||
impl BufferSearchBar {
|
||||
pub fn register_inner(registrar: &mut impl SearchActionsRegistrar) {
|
||||
registrar.register_handler(|this, action: &ToggleCaseSensitive, cx| {
|
||||
if this.supported_options().case {
|
||||
this.toggle_case_sensitive(action, cx);
|
||||
}
|
||||
});
|
||||
register_action(workspace, |this, action: &ToggleWholeWord, cx| {
|
||||
|
||||
registrar.register_handler(|this, action: &ToggleWholeWord, cx| {
|
||||
if this.supported_options().word {
|
||||
this.toggle_whole_word(action, cx);
|
||||
}
|
||||
});
|
||||
register_action(workspace, |this, action: &ToggleReplace, cx| {
|
||||
|
||||
registrar.register_handler(|this, action: &ToggleReplace, cx| {
|
||||
if this.supported_options().replacement {
|
||||
this.toggle_replace(action, cx);
|
||||
}
|
||||
});
|
||||
register_action(workspace, |this, _: &ActivateRegexMode, cx| {
|
||||
|
||||
registrar.register_handler(|this, _: &ActivateRegexMode, cx| {
|
||||
if this.supported_options().regex {
|
||||
this.activate_search_mode(SearchMode::Regex, cx);
|
||||
}
|
||||
});
|
||||
register_action(workspace, |this, _: &ActivateTextMode, cx| {
|
||||
|
||||
registrar.register_handler(|this, _: &ActivateTextMode, cx| {
|
||||
this.activate_search_mode(SearchMode::Text, cx);
|
||||
});
|
||||
register_action(workspace, |this, action: &CycleMode, cx| {
|
||||
|
||||
registrar.register_handler(|this, action: &CycleMode, cx| {
|
||||
if this.supported_options().regex {
|
||||
// If regex is not supported then search has just one mode (text) - in that case there's no point in supporting
|
||||
// cycling.
|
||||
this.cycle_mode(action, cx)
|
||||
}
|
||||
});
|
||||
register_action(workspace, |this, action: &SelectNextMatch, cx| {
|
||||
|
||||
registrar.register_handler(|this, action: &SelectNextMatch, cx| {
|
||||
this.select_next_match(action, cx);
|
||||
});
|
||||
register_action(workspace, |this, action: &SelectPrevMatch, cx| {
|
||||
registrar.register_handler(|this, action: &SelectPrevMatch, cx| {
|
||||
this.select_prev_match(action, cx);
|
||||
});
|
||||
register_action(workspace, |this, action: &SelectAllMatches, cx| {
|
||||
registrar.register_handler(|this, action: &SelectAllMatches, cx| {
|
||||
this.select_all_matches(action, cx);
|
||||
});
|
||||
register_action(workspace, |this, _: &editor::Cancel, cx| {
|
||||
registrar.register_handler(|this, _: &editor::Cancel, cx| {
|
||||
if !this.dismissed {
|
||||
this.dismiss(&Dismiss, cx);
|
||||
return;
|
||||
}
|
||||
cx.propagate();
|
||||
});
|
||||
registrar.register_handler(|this, deploy, cx| {
|
||||
this.deploy(deploy, cx);
|
||||
})
|
||||
}
|
||||
fn register(workspace: &mut Workspace) {
|
||||
Self::register_inner(workspace);
|
||||
}
|
||||
pub fn new(cx: &mut ViewContext<Self>) -> Self {
|
||||
let query_editor = cx.new_view(|cx| Editor::single_line(cx));
|
||||
|
||||
@@ -38,8 +38,8 @@ use std::{
|
||||
use theme::ThemeSettings;
|
||||
|
||||
use ui::{
|
||||
h_stack, prelude::*, v_stack, Button, Icon, IconButton, IconElement, Label, LabelCommon,
|
||||
LabelSize, Selectable, Tooltip,
|
||||
h_stack, prelude::*, v_stack, Icon, IconButton, IconElement, Label, LabelCommon, LabelSize,
|
||||
Selectable, ToggleButton, Tooltip,
|
||||
};
|
||||
use util::{paths::PathMatcher, ResultExt as _};
|
||||
use workspace::{
|
||||
@@ -61,12 +61,12 @@ struct ActiveSearches(HashMap<WeakModel<Project>, WeakView<ProjectSearchView>>);
|
||||
struct ActiveSettings(HashMap<WeakModel<Project>, ProjectSearchSettings>);
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
// todo!() po
|
||||
cx.set_global(ActiveSearches::default());
|
||||
cx.set_global(ActiveSettings::default());
|
||||
cx.observe_new_views(|workspace: &mut Workspace, _cx| {
|
||||
workspace
|
||||
.register_action(ProjectSearchView::deploy)
|
||||
.register_action(ProjectSearchView::new_search)
|
||||
.register_action(ProjectSearchView::deploy_search)
|
||||
.register_action(ProjectSearchBar::search_in_new);
|
||||
})
|
||||
.detach();
|
||||
@@ -132,9 +132,11 @@ pub struct ProjectSearchBar {
|
||||
impl ProjectSearch {
|
||||
fn new(project: Model<Project>, cx: &mut ModelContext<Self>) -> Self {
|
||||
let replica_id = project.read(cx).replica_id();
|
||||
let capability = project.read(cx).capability();
|
||||
|
||||
Self {
|
||||
project,
|
||||
excerpts: cx.new_model(|_| MultiBuffer::new(replica_id)),
|
||||
excerpts: cx.new_model(|_| MultiBuffer::new(replica_id, capability)),
|
||||
pending_search: Default::default(),
|
||||
match_ranges: Default::default(),
|
||||
active_query: None,
|
||||
@@ -286,7 +288,6 @@ impl Render for ProjectSearchView {
|
||||
.size_full()
|
||||
.track_focus(&self.focus_handle)
|
||||
.child(self.results_editor.clone())
|
||||
.into_any()
|
||||
} else {
|
||||
let model = self.model.read(cx);
|
||||
let has_no_results = model.no_results.unwrap_or(false);
|
||||
@@ -363,6 +364,7 @@ impl Render for ProjectSearchView {
|
||||
.flex_1()
|
||||
.size_full()
|
||||
.justify_center()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.track_focus(&self.focus_handle)
|
||||
.child(
|
||||
h_stack()
|
||||
@@ -372,7 +374,6 @@ impl Render for ProjectSearchView {
|
||||
.child(v_stack().child(major_text).children(minor_text))
|
||||
.child(h_stack().flex_1()),
|
||||
)
|
||||
.into_any()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -941,11 +942,41 @@ impl ProjectSearchView {
|
||||
});
|
||||
}
|
||||
|
||||
// Re-activate the most recently activated search or the most recent if it has been closed.
|
||||
// If no search exists in the workspace, create a new one.
|
||||
fn deploy_search(
|
||||
workspace: &mut Workspace,
|
||||
_: &workspace::DeploySearch,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) {
|
||||
let active_search = cx
|
||||
.global::<ActiveSearches>()
|
||||
.0
|
||||
.get(&workspace.project().downgrade());
|
||||
let existing = active_search
|
||||
.and_then(|active_search| {
|
||||
workspace
|
||||
.items_of_type::<ProjectSearchView>(cx)
|
||||
.filter(|search| &search.downgrade() == active_search)
|
||||
.last()
|
||||
})
|
||||
.or_else(|| workspace.item_of_type::<ProjectSearchView>(cx));
|
||||
Self::existing_or_new_search(workspace, existing, cx)
|
||||
}
|
||||
|
||||
// Add another search tab to the workspace.
|
||||
fn deploy(
|
||||
fn new_search(
|
||||
workspace: &mut Workspace,
|
||||
_: &workspace::NewSearch,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) {
|
||||
Self::existing_or_new_search(workspace, None, cx)
|
||||
}
|
||||
|
||||
fn existing_or_new_search(
|
||||
workspace: &mut Workspace,
|
||||
existing: Option<View<ProjectSearchView>>,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) {
|
||||
// Clean up entries for dropped projects
|
||||
cx.update_global(|state: &mut ActiveSearches, _cx| {
|
||||
@@ -962,19 +993,27 @@ impl ProjectSearchView {
|
||||
}
|
||||
});
|
||||
|
||||
let settings = cx
|
||||
.global::<ActiveSettings>()
|
||||
.0
|
||||
.get(&workspace.project().downgrade());
|
||||
|
||||
let settings = if let Some(settings) = settings {
|
||||
Some(settings.clone())
|
||||
let search = if let Some(existing) = existing {
|
||||
workspace.activate_item(&existing, cx);
|
||||
existing
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let settings = cx
|
||||
.global::<ActiveSettings>()
|
||||
.0
|
||||
.get(&workspace.project().downgrade());
|
||||
|
||||
let model = cx.new_model(|cx| ProjectSearch::new(workspace.project().clone(), cx));
|
||||
let search = cx.new_view(|cx| ProjectSearchView::new(model, cx, settings));
|
||||
let settings = if let Some(settings) = settings {
|
||||
Some(settings.clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let model = cx.new_model(|cx| ProjectSearch::new(workspace.project().clone(), cx));
|
||||
let view = cx.new_view(|cx| ProjectSearchView::new(model, cx, settings));
|
||||
|
||||
workspace.add_item(Box::new(view.clone()), cx);
|
||||
view
|
||||
};
|
||||
|
||||
workspace.add_item(Box::new(search.clone()), cx);
|
||||
|
||||
@@ -1519,7 +1558,7 @@ impl ProjectSearchBar {
|
||||
fn render_text_input(&self, editor: &View<Editor>, cx: &ViewContext<Self>) -> impl IntoElement {
|
||||
let settings = ThemeSettings::get_global(cx);
|
||||
let text_style = TextStyle {
|
||||
color: if editor.read(cx).read_only() {
|
||||
color: if editor.read(cx).read_only(cx) {
|
||||
cx.theme().colors().text_disabled
|
||||
} else {
|
||||
cx.theme().colors().text
|
||||
@@ -1639,20 +1678,26 @@ impl Render for ProjectSearchBar {
|
||||
|
||||
let mode_column = v_stack().items_start().justify_start().child(
|
||||
h_stack()
|
||||
.gap_2()
|
||||
.child(
|
||||
h_stack()
|
||||
.child(
|
||||
Button::new("project-search-text-button", "Text")
|
||||
ToggleButton::new("project-search-text-button", "Text")
|
||||
.style(ButtonStyle::Filled)
|
||||
.size(ButtonSize::Large)
|
||||
.selected(search.current_mode == SearchMode::Text)
|
||||
.on_click(cx.listener(|this, _, cx| {
|
||||
this.activate_search_mode(SearchMode::Text, cx)
|
||||
}))
|
||||
.tooltip(|cx| {
|
||||
Tooltip::for_action("Toggle text search", &ActivateTextMode, cx)
|
||||
}),
|
||||
})
|
||||
.first(),
|
||||
)
|
||||
.child(
|
||||
Button::new("project-search-regex-button", "Regex")
|
||||
ToggleButton::new("project-search-regex-button", "Regex")
|
||||
.style(ButtonStyle::Filled)
|
||||
.size(ButtonSize::Large)
|
||||
.selected(search.current_mode == SearchMode::Regex)
|
||||
.on_click(cx.listener(|this, _, cx| {
|
||||
this.activate_search_mode(SearchMode::Regex, cx)
|
||||
@@ -1663,11 +1708,20 @@ impl Render for ProjectSearchBar {
|
||||
&ActivateRegexMode,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.map(|this| {
|
||||
if semantic_is_available {
|
||||
this.middle()
|
||||
} else {
|
||||
this.last()
|
||||
}
|
||||
}),
|
||||
)
|
||||
.when(semantic_is_available, |this| {
|
||||
this.child(
|
||||
Button::new("project-search-semantic-button", "Semantic")
|
||||
ToggleButton::new("project-search-semantic-button", "Semantic")
|
||||
.style(ButtonStyle::Filled)
|
||||
.size(ButtonSize::Large)
|
||||
.selected(search.current_mode == SearchMode::Semantic)
|
||||
.on_click(cx.listener(|this, _, cx| {
|
||||
this.activate_search_mode(SearchMode::Semantic, cx)
|
||||
@@ -1678,7 +1732,8 @@ impl Render for ProjectSearchBar {
|
||||
&ActivateSemanticMode,
|
||||
cx,
|
||||
)
|
||||
}),
|
||||
})
|
||||
.last(),
|
||||
)
|
||||
}),
|
||||
)
|
||||
@@ -1829,6 +1884,7 @@ impl Render for ProjectSearchBar {
|
||||
.child(
|
||||
h_stack()
|
||||
.justify_between()
|
||||
.gap_2()
|
||||
.child(query_column)
|
||||
.child(mode_column)
|
||||
.child(replace_column)
|
||||
@@ -2060,7 +2116,7 @@ pub mod tests {
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_project_search_focus(cx: &mut TestAppContext) {
|
||||
async fn test_deploy_project_search_focus(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.background_executor.clone());
|
||||
@@ -2101,7 +2157,237 @@ pub mod tests {
|
||||
.update(cx, |toolbar, cx| toolbar.add_item(search_bar, cx))
|
||||
});
|
||||
|
||||
ProjectSearchView::deploy(workspace, &workspace::NewSearch, cx)
|
||||
ProjectSearchView::deploy_search(workspace, &workspace::DeploySearch, cx)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let Some(search_view) = cx.read(|cx| {
|
||||
workspace
|
||||
.read(cx)
|
||||
.unwrap()
|
||||
.active_pane()
|
||||
.read(cx)
|
||||
.active_item()
|
||||
.and_then(|item| item.downcast::<ProjectSearchView>())
|
||||
}) else {
|
||||
panic!("Search view expected to appear after new search event trigger")
|
||||
};
|
||||
|
||||
cx.spawn(|mut cx| async move {
|
||||
window
|
||||
.update(&mut cx, |_, cx| {
|
||||
cx.dispatch_action(ToggleFocus.boxed_clone())
|
||||
})
|
||||
.unwrap();
|
||||
})
|
||||
.detach();
|
||||
cx.background_executor.run_until_parked();
|
||||
window
|
||||
.update(cx, |_, cx| {
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
assert!(
|
||||
search_view.query_editor.focus_handle(cx).is_focused(cx),
|
||||
"Empty search view should be focused after the toggle focus event: no results panel to focus on",
|
||||
);
|
||||
});
|
||||
}).unwrap();
|
||||
|
||||
window
|
||||
.update(cx, |_, cx| {
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
let query_editor = &search_view.query_editor;
|
||||
assert!(
|
||||
query_editor.focus_handle(cx).is_focused(cx),
|
||||
"Search view should be focused after the new search view is activated",
|
||||
);
|
||||
let query_text = query_editor.read(cx).text(cx);
|
||||
assert!(
|
||||
query_text.is_empty(),
|
||||
"New search query should be empty but got '{query_text}'",
|
||||
);
|
||||
let results_text = search_view
|
||||
.results_editor
|
||||
.update(cx, |editor, cx| editor.display_text(cx));
|
||||
assert!(
|
||||
results_text.is_empty(),
|
||||
"Empty search view should have no results but got '{results_text}'"
|
||||
);
|
||||
});
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
window
|
||||
.update(cx, |_, cx| {
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
search_view.query_editor.update(cx, |query_editor, cx| {
|
||||
query_editor.set_text("sOMETHINGtHATsURELYdOESnOTeXIST", cx)
|
||||
});
|
||||
search_view.search(cx);
|
||||
});
|
||||
})
|
||||
.unwrap();
|
||||
cx.background_executor.run_until_parked();
|
||||
window
|
||||
.update(cx, |_, cx| {
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
let results_text = search_view
|
||||
.results_editor
|
||||
.update(cx, |editor, cx| editor.display_text(cx));
|
||||
assert!(
|
||||
results_text.is_empty(),
|
||||
"Search view for mismatching query should have no results but got '{results_text}'"
|
||||
);
|
||||
assert!(
|
||||
search_view.query_editor.focus_handle(cx).is_focused(cx),
|
||||
"Search view should be focused after mismatching query had been used in search",
|
||||
);
|
||||
});
|
||||
}).unwrap();
|
||||
|
||||
cx.spawn(|mut cx| async move {
|
||||
window.update(&mut cx, |_, cx| {
|
||||
cx.dispatch_action(ToggleFocus.boxed_clone())
|
||||
})
|
||||
})
|
||||
.detach();
|
||||
cx.background_executor.run_until_parked();
|
||||
window.update(cx, |_, cx| {
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
assert!(
|
||||
search_view.query_editor.focus_handle(cx).is_focused(cx),
|
||||
"Search view with mismatching query should be focused after the toggle focus event: still no results panel to focus on",
|
||||
);
|
||||
});
|
||||
}).unwrap();
|
||||
|
||||
window
|
||||
.update(cx, |_, cx| {
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
search_view
|
||||
.query_editor
|
||||
.update(cx, |query_editor, cx| query_editor.set_text("TWO", cx));
|
||||
search_view.search(cx);
|
||||
});
|
||||
})
|
||||
.unwrap();
|
||||
cx.background_executor.run_until_parked();
|
||||
window.update(cx, |_, cx| {
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
assert_eq!(
|
||||
search_view
|
||||
.results_editor
|
||||
.update(cx, |editor, cx| editor.display_text(cx)),
|
||||
"\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\nconst TWO: usize = one::ONE + one::ONE;",
|
||||
"Search view results should match the query"
|
||||
);
|
||||
assert!(
|
||||
search_view.results_editor.focus_handle(cx).is_focused(cx),
|
||||
"Search view with mismatching query should be focused after search results are available",
|
||||
);
|
||||
});
|
||||
}).unwrap();
|
||||
cx.spawn(|mut cx| async move {
|
||||
window
|
||||
.update(&mut cx, |_, cx| {
|
||||
cx.dispatch_action(ToggleFocus.boxed_clone())
|
||||
})
|
||||
.unwrap();
|
||||
})
|
||||
.detach();
|
||||
cx.background_executor.run_until_parked();
|
||||
window.update(cx, |_, cx| {
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
assert!(
|
||||
search_view.results_editor.focus_handle(cx).is_focused(cx),
|
||||
"Search view with matching query should still have its results editor focused after the toggle focus event",
|
||||
);
|
||||
});
|
||||
}).unwrap();
|
||||
|
||||
workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
ProjectSearchView::deploy_search(workspace, &workspace::DeploySearch, cx)
|
||||
})
|
||||
.unwrap();
|
||||
window.update(cx, |_, cx| {
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
assert_eq!(search_view.query_editor.read(cx).text(cx), "two", "Query should be updated to first search result after search view 2nd open in a row");
|
||||
assert_eq!(
|
||||
search_view
|
||||
.results_editor
|
||||
.update(cx, |editor, cx| editor.display_text(cx)),
|
||||
"\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\nconst TWO: usize = one::ONE + one::ONE;",
|
||||
"Results should be unchanged after search view 2nd open in a row"
|
||||
);
|
||||
assert!(
|
||||
search_view.query_editor.focus_handle(cx).is_focused(cx),
|
||||
"Focus should be moved into query editor again after search view 2nd open in a row"
|
||||
);
|
||||
});
|
||||
}).unwrap();
|
||||
|
||||
cx.spawn(|mut cx| async move {
|
||||
window
|
||||
.update(&mut cx, |_, cx| {
|
||||
cx.dispatch_action(ToggleFocus.boxed_clone())
|
||||
})
|
||||
.unwrap();
|
||||
})
|
||||
.detach();
|
||||
cx.background_executor.run_until_parked();
|
||||
window.update(cx, |_, cx| {
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
assert!(
|
||||
search_view.results_editor.focus_handle(cx).is_focused(cx),
|
||||
"Search view with matching query should switch focus to the results editor after the toggle focus event",
|
||||
);
|
||||
});
|
||||
}).unwrap();
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_new_project_search_focus(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.background_executor.clone());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
json!({
|
||||
"one.rs": "const ONE: usize = 1;",
|
||||
"two.rs": "const TWO: usize = one::ONE + one::ONE;",
|
||||
"three.rs": "const THREE: usize = one::ONE + two::TWO;",
|
||||
"four.rs": "const FOUR: usize = one::ONE + three::THREE;",
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
|
||||
let window = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||
let workspace = window.clone();
|
||||
let search_bar = window.build_view(cx, |_| ProjectSearchBar::new());
|
||||
|
||||
let active_item = cx.read(|cx| {
|
||||
workspace
|
||||
.read(cx)
|
||||
.unwrap()
|
||||
.active_pane()
|
||||
.read(cx)
|
||||
.active_item()
|
||||
.and_then(|item| item.downcast::<ProjectSearchView>())
|
||||
});
|
||||
assert!(
|
||||
active_item.is_none(),
|
||||
"Expected no search panel to be active"
|
||||
);
|
||||
|
||||
window
|
||||
.update(cx, move |workspace, cx| {
|
||||
assert_eq!(workspace.panes().len(), 1);
|
||||
workspace.panes()[0].update(cx, move |pane, cx| {
|
||||
pane.toolbar()
|
||||
.update(cx, |toolbar, cx| toolbar.add_item(search_bar, cx))
|
||||
});
|
||||
|
||||
ProjectSearchView::new_search(workspace, &workspace::NewSearch, cx)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
@@ -2250,7 +2536,7 @@ pub mod tests {
|
||||
|
||||
workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
ProjectSearchView::deploy(workspace, &workspace::NewSearch, cx)
|
||||
ProjectSearchView::new_search(workspace, &workspace::NewSearch, cx)
|
||||
})
|
||||
.unwrap();
|
||||
cx.background_executor.run_until_parked();
|
||||
@@ -2536,7 +2822,7 @@ pub mod tests {
|
||||
.update(cx, |toolbar, cx| toolbar.add_item(search_bar, cx))
|
||||
});
|
||||
|
||||
ProjectSearchView::deploy(workspace, &workspace::NewSearch, cx)
|
||||
ProjectSearchView::new_search(workspace, &workspace::NewSearch, cx)
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
@@ -14,7 +14,6 @@ test-support = ["gpui/test-support", "fs/test-support"]
|
||||
[dependencies]
|
||||
collections = { path = "../collections" }
|
||||
gpui = { path = "../gpui" }
|
||||
sqlez = { path = "../sqlez" }
|
||||
fs = { path = "../fs" }
|
||||
feature_flags = { path = "../feature_flags" }
|
||||
util = { path = "../util" }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{settings_store::SettingsStore, KeymapFile, Settings};
|
||||
use crate::{settings_store::SettingsStore, Settings};
|
||||
use anyhow::Result;
|
||||
use fs::Fs;
|
||||
use futures::{channel::mpsc, StreamExt};
|
||||
@@ -77,7 +77,6 @@ pub fn handle_settings_file_changes(
|
||||
});
|
||||
cx.spawn(move |mut cx| async move {
|
||||
while let Some(user_settings_content) = user_settings_file_rx.next().await {
|
||||
eprintln!("settings file changed");
|
||||
let result = cx.update_global(|store: &mut SettingsStore, cx| {
|
||||
store
|
||||
.set_user_settings(&user_settings_content, cx)
|
||||
@@ -121,14 +120,3 @@ pub fn update_settings_file<T: Settings>(
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
|
||||
pub fn load_default_keymap(cx: &mut AppContext) {
|
||||
for path in ["keymaps/default.json", "keymaps/vim.json"] {
|
||||
KeymapFile::load_asset(path, cx).unwrap();
|
||||
}
|
||||
|
||||
// todo!()
|
||||
// if let Some(asset_path) = settings::get::<BaseKeymap>(cx).asset_path() {
|
||||
// KeymapFile::load_asset(asset_path, cx).unwrap();
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -158,39 +158,41 @@ pub fn mouse_moved_report(point: AlacPoint, e: &MouseMoveEvent, mode: TermMode)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mouse_side(
|
||||
pub fn grid_point(pos: Point<Pixels>, cur_size: TerminalSize, display_offset: usize) -> AlacPoint {
|
||||
grid_point_and_side(pos, cur_size, display_offset).0
|
||||
}
|
||||
|
||||
pub fn grid_point_and_side(
|
||||
pos: Point<Pixels>,
|
||||
cur_size: TerminalSize,
|
||||
) -> alacritty_terminal::index::Direction {
|
||||
let cell_width = cur_size.cell_width.floor();
|
||||
if cell_width == px(0.) {
|
||||
return Side::Right;
|
||||
}
|
||||
|
||||
let x = pos.x.floor();
|
||||
|
||||
let cell_x = cmp::max(px(0.), x - cell_width) % cell_width;
|
||||
let half_cell_width = (cur_size.cell_width / 2.0).floor();
|
||||
let additional_padding = (cur_size.width() - cur_size.cell_width * 2.) % cur_size.cell_width;
|
||||
let end_of_grid = cur_size.width() - cur_size.cell_width - additional_padding;
|
||||
|
||||
//Width: Pixels or columns?
|
||||
if cell_x > half_cell_width
|
||||
// Edge case when mouse leaves the window.
|
||||
|| x >= end_of_grid
|
||||
{
|
||||
display_offset: usize,
|
||||
) -> (AlacPoint, Side) {
|
||||
let mut col = GridCol((pos.x / cur_size.cell_width) as usize);
|
||||
let cell_x = cmp::max(px(0.), pos.x) % cur_size.cell_width;
|
||||
let half_cell_width = cur_size.cell_width / 2.0;
|
||||
let mut side = if cell_x > half_cell_width {
|
||||
Side::Right
|
||||
} else {
|
||||
Side::Left
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn grid_point(pos: Point<Pixels>, cur_size: TerminalSize, display_offset: usize) -> AlacPoint {
|
||||
let col = GridCol((pos.x / cur_size.cell_width) as usize);
|
||||
if col > cur_size.last_column() {
|
||||
col = cur_size.last_column();
|
||||
side = Side::Right;
|
||||
}
|
||||
let col = min(col, cur_size.last_column());
|
||||
let line = (pos.y / cur_size.line_height) as i32;
|
||||
let line = min(line, cur_size.bottommost_line().0);
|
||||
AlacPoint::new(GridLine(line - display_offset as i32), col)
|
||||
let mut line = (pos.y / cur_size.line_height) as i32;
|
||||
if line > cur_size.bottommost_line() {
|
||||
line = cur_size.bottommost_line().0 as i32;
|
||||
side = Side::Right;
|
||||
} else if line < 0 {
|
||||
side = Side::Left;
|
||||
}
|
||||
|
||||
(
|
||||
AlacPoint::new(GridLine(line - display_offset as i32), col),
|
||||
side,
|
||||
)
|
||||
}
|
||||
|
||||
///Generate the bytes to send to the terminal, from the cell location, a mouse event, and the terminal mode
|
||||
|
||||
@@ -28,7 +28,8 @@ use futures::{
|
||||
};
|
||||
|
||||
use mappings::mouse::{
|
||||
alt_scroll, grid_point, mouse_button_report, mouse_moved_report, mouse_side, scroll_report,
|
||||
alt_scroll, grid_point, grid_point_and_side, mouse_button_report, mouse_moved_report,
|
||||
scroll_report,
|
||||
};
|
||||
|
||||
use procinfo::LocalProcessInfo;
|
||||
@@ -61,15 +62,7 @@ use lazy_static::lazy_static;
|
||||
|
||||
actions!(
|
||||
terminal,
|
||||
[
|
||||
Clear,
|
||||
Copy,
|
||||
Paste,
|
||||
ShowCharacterPalette,
|
||||
SearchTest,
|
||||
SendText,
|
||||
SendKeystroke,
|
||||
]
|
||||
[Clear, Copy, Paste, ShowCharacterPalette, SearchTest,]
|
||||
);
|
||||
|
||||
///Scrolling is unbearably sluggish by default. Alacritty supports a configurable
|
||||
@@ -704,14 +697,12 @@ impl Terminal {
|
||||
}
|
||||
InternalEvent::UpdateSelection(position) => {
|
||||
if let Some(mut selection) = term.selection.take() {
|
||||
let point = grid_point(
|
||||
let (point, side) = grid_point_and_side(
|
||||
*position,
|
||||
self.last_content.size,
|
||||
term.grid().display_offset(),
|
||||
);
|
||||
|
||||
let side = mouse_side(*position, self.last_content.size);
|
||||
|
||||
selection.update(point, side);
|
||||
term.selection = Some(selection);
|
||||
|
||||
@@ -1088,12 +1079,11 @@ impl Terminal {
|
||||
let position = e.position - origin;
|
||||
self.last_mouse_position = Some(position);
|
||||
if self.mouse_mode(e.modifiers.shift) {
|
||||
let point = grid_point(
|
||||
let (point, side) = grid_point_and_side(
|
||||
position,
|
||||
self.last_content.size,
|
||||
self.last_content.display_offset,
|
||||
);
|
||||
let side = mouse_side(position, self.last_content.size);
|
||||
|
||||
if self.mouse_changed(point, side) {
|
||||
if let Some(bytes) = mouse_moved_report(point, e, self.last_content.mode) {
|
||||
@@ -1175,15 +1165,12 @@ impl Terminal {
|
||||
}
|
||||
} else if e.button == MouseButton::Left {
|
||||
let position = e.position - origin;
|
||||
let point = grid_point(
|
||||
let (point, side) = grid_point_and_side(
|
||||
position,
|
||||
self.last_content.size,
|
||||
self.last_content.display_offset,
|
||||
);
|
||||
|
||||
// Use .opposite so that selection is inclusive of the cell clicked.
|
||||
let side = mouse_side(position, self.last_content.size);
|
||||
|
||||
let selection_type = match e.click_count {
|
||||
0 => return, //This is a release
|
||||
1 => Some(SelectionType::Simple),
|
||||
|
||||
@@ -13,7 +13,7 @@ editor = { path = "../editor" }
|
||||
language = { path = "../language" }
|
||||
gpui = { path = "../gpui" }
|
||||
project = { path = "../project" }
|
||||
# search = { path = "../search" }
|
||||
search = { path = "../search" }
|
||||
settings = { path = "../settings" }
|
||||
theme = { path = "../theme" }
|
||||
util = { path = "../util" }
|
||||
|
||||
@@ -2,10 +2,11 @@ use editor::{Cursor, HighlightedRange, HighlightedRangeLine};
|
||||
use gpui::{
|
||||
div, fill, point, px, red, relative, AnyElement, AsyncWindowContext, AvailableSpace,
|
||||
BorrowWindow, Bounds, DispatchPhase, Element, ElementId, ExternalPaths, FocusHandle, Font,
|
||||
FontStyle, FontWeight, HighlightStyle, Hsla, InteractiveElement, InteractiveElementState,
|
||||
Interactivity, IntoElement, LayoutId, Model, ModelContext, ModifiersChangedEvent, MouseButton,
|
||||
Pixels, PlatformInputHandler, Point, ShapedLine, StatefulInteractiveElement, StyleRefinement,
|
||||
Styled, TextRun, TextStyle, TextSystem, UnderlineStyle, WhiteSpace, WindowContext,
|
||||
FontStyle, FontWeight, HighlightStyle, Hsla, InteractiveBounds, InteractiveElement,
|
||||
InteractiveElementState, Interactivity, IntoElement, LayoutId, Model, ModelContext,
|
||||
ModifiersChangedEvent, MouseButton, MouseMoveEvent, Pixels, PlatformInputHandler, Point,
|
||||
ShapedLine, StatefulInteractiveElement, StyleRefinement, Styled, TextRun, TextStyle,
|
||||
TextSystem, UnderlineStyle, WhiteSpace, WindowContext,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use language::CursorShape;
|
||||
@@ -421,7 +422,7 @@ impl TerminalElement {
|
||||
let rem_size = cx.rem_size();
|
||||
let font_pixels = text_style.font_size.to_pixels(rem_size);
|
||||
let line_height = font_pixels * line_height.to_pixels(rem_size);
|
||||
let font_id = cx.text_system().font_id(&text_style.font()).unwrap();
|
||||
let font_id = cx.text_system().resolve_font(&text_style.font());
|
||||
|
||||
// todo!(do we need to keep this unwrap?)
|
||||
let cell_width = text_system
|
||||
@@ -598,33 +599,48 @@ impl TerminalElement {
|
||||
) {
|
||||
let focus = self.focus.clone();
|
||||
let terminal = self.terminal.clone();
|
||||
let interactive_bounds = InteractiveBounds {
|
||||
bounds: bounds.intersect(&cx.content_mask().bounds),
|
||||
stacking_order: cx.stacking_order().clone(),
|
||||
};
|
||||
|
||||
self.interactivity.on_mouse_down(MouseButton::Left, {
|
||||
let terminal = terminal.clone();
|
||||
let focus = focus.clone();
|
||||
move |e, cx| {
|
||||
cx.focus(&focus);
|
||||
//todo!(context menu)
|
||||
// v.context_menu.update(cx, |menu, _cx| menu.delay_cancel());
|
||||
terminal.update(cx, |terminal, cx| {
|
||||
terminal.mouse_down(&e, origin);
|
||||
|
||||
cx.notify();
|
||||
})
|
||||
}
|
||||
});
|
||||
self.interactivity.on_mouse_move({
|
||||
let terminal = terminal.clone();
|
||||
let focus = focus.clone();
|
||||
move |e, cx| {
|
||||
if e.pressed_button.is_some() && focus.is_focused(cx) && !cx.has_active_drag() {
|
||||
|
||||
cx.on_mouse_event({
|
||||
let bounds = bounds.clone();
|
||||
let focus = self.focus.clone();
|
||||
let terminal = self.terminal.clone();
|
||||
move |e: &MouseMoveEvent, phase, cx| {
|
||||
if phase != DispatchPhase::Bubble || !focus.is_focused(cx) {
|
||||
return;
|
||||
}
|
||||
|
||||
if e.pressed_button.is_some() && !cx.has_active_drag() {
|
||||
terminal.update(cx, |terminal, cx| {
|
||||
terminal.mouse_drag(e, origin, bounds);
|
||||
cx.notify();
|
||||
})
|
||||
}
|
||||
|
||||
if interactive_bounds.visibly_contains(&e.position, cx) {
|
||||
terminal.update(cx, |terminal, cx| {
|
||||
terminal.mouse_move(&e, origin);
|
||||
cx.notify();
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
self.interactivity.on_mouse_up(
|
||||
MouseButton::Left,
|
||||
TerminalElement::generic_button_handler(
|
||||
@@ -651,19 +667,6 @@ impl TerminalElement {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
self.interactivity.on_mouse_move({
|
||||
let terminal = terminal.clone();
|
||||
let focus = focus.clone();
|
||||
move |e, cx| {
|
||||
if focus.is_focused(cx) {
|
||||
terminal.update(cx, |terminal, cx| {
|
||||
terminal.mouse_move(&e, origin);
|
||||
cx.notify();
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
self.interactivity.on_scroll_wheel({
|
||||
let terminal = terminal.clone();
|
||||
move |e, cx| {
|
||||
|
||||
@@ -3,11 +3,12 @@ use std::{path::PathBuf, sync::Arc};
|
||||
use crate::TerminalView;
|
||||
use db::kvp::KEY_VALUE_STORE;
|
||||
use gpui::{
|
||||
actions, div, serde_json, AppContext, AsyncWindowContext, Entity, EventEmitter, ExternalPaths,
|
||||
actions, serde_json, AppContext, AsyncWindowContext, Entity, EventEmitter, ExternalPaths,
|
||||
FocusHandle, FocusableView, IntoElement, ParentElement, Pixels, Render, Styled, Subscription,
|
||||
Task, View, ViewContext, VisualContext, WeakView, WindowContext,
|
||||
};
|
||||
use project::Fs;
|
||||
use search::{buffer_search::DivRegistrar, BufferSearchBar};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::{Settings, SettingsStore};
|
||||
use terminal::terminal_settings::{TerminalDockPosition, TerminalSettings};
|
||||
@@ -52,7 +53,7 @@ pub struct TerminalPanel {
|
||||
|
||||
impl TerminalPanel {
|
||||
fn new(workspace: &Workspace, cx: &mut ViewContext<Self>) -> Self {
|
||||
let terminal_panel = cx.view().clone();
|
||||
let terminal_panel = cx.view().downgrade();
|
||||
let pane = cx.new_view(|cx| {
|
||||
let mut pane = Pane::new(
|
||||
workspace.weak_handle(),
|
||||
@@ -76,14 +77,17 @@ impl TerminalPanel {
|
||||
pane.set_can_navigate(false, cx);
|
||||
pane.display_nav_history_buttons(false);
|
||||
pane.set_render_tab_bar_buttons(cx, move |pane, cx| {
|
||||
let terminal_panel = terminal_panel.clone();
|
||||
h_stack()
|
||||
.gap_2()
|
||||
.child(
|
||||
IconButton::new("plus", Icon::Plus)
|
||||
.icon_size(IconSize::Small)
|
||||
.on_click(cx.listener_for(&terminal_panel, |terminal_panel, _, cx| {
|
||||
terminal_panel.add_terminal(None, cx);
|
||||
}))
|
||||
.on_click(move |_, cx| {
|
||||
terminal_panel
|
||||
.update(cx, |panel, cx| panel.add_terminal(None, cx))
|
||||
.log_err();
|
||||
})
|
||||
.tooltip(|cx| Tooltip::text("New Terminal", cx)),
|
||||
)
|
||||
.child({
|
||||
@@ -101,9 +105,9 @@ impl TerminalPanel {
|
||||
})
|
||||
.into_any_element()
|
||||
});
|
||||
// let buffer_search_bar = cx.build_view(search::BufferSearchBar::new);
|
||||
// pane.toolbar()
|
||||
// .update(cx, |toolbar, cx| toolbar.add_item(buffer_search_bar, cx));
|
||||
let buffer_search_bar = cx.new_view(search::BufferSearchBar::new);
|
||||
pane.toolbar()
|
||||
.update(cx, |toolbar, cx| toolbar.add_item(buffer_search_bar, cx));
|
||||
pane
|
||||
});
|
||||
let subscriptions = vec![
|
||||
@@ -329,8 +333,20 @@ impl TerminalPanel {
|
||||
impl EventEmitter<PanelEvent> for TerminalPanel {}
|
||||
|
||||
impl Render for TerminalPanel {
|
||||
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
div().size_full().child(self.pane.clone())
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
let mut registrar = DivRegistrar::new(
|
||||
|panel, cx| {
|
||||
panel
|
||||
.pane
|
||||
.read(cx)
|
||||
.toolbar()
|
||||
.read(cx)
|
||||
.item_of_type::<BufferSearchBar>()
|
||||
},
|
||||
cx,
|
||||
);
|
||||
BufferSearchBar::register_inner(&mut registrar);
|
||||
registrar.into_div().size_full().child(self.pane.clone())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,11 +426,6 @@ impl Panel for TerminalPanel {
|
||||
"TerminalPanel"
|
||||
}
|
||||
|
||||
// todo!()
|
||||
// fn icon_tooltip(&self) -> (String, Option<Box<dyn Action>>) {
|
||||
// ("Terminal Panel".into(), Some(Box::new(ToggleFocus)))
|
||||
// }
|
||||
|
||||
fn icon(&self, _cx: &WindowContext) -> Option<Icon> {
|
||||
Some(Icon::Terminal)
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ use workspace::{
|
||||
item::{BreadcrumbText, Item, ItemEvent},
|
||||
notifications::NotifyResultExt,
|
||||
register_deserializable_item,
|
||||
searchable::{SearchEvent, SearchOptions, SearchableItem},
|
||||
searchable::{SearchEvent, SearchOptions, SearchableItem, SearchableItemHandle},
|
||||
CloseActiveItem, NewCenterTerminal, Pane, ToolbarItemLocation, Workspace, WorkspaceId,
|
||||
};
|
||||
|
||||
@@ -57,7 +57,7 @@ pub struct SendText(String);
|
||||
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
|
||||
pub struct SendKeystroke(String);
|
||||
|
||||
impl_actions!(terminal_view, [SendText, SendKeystroke]);
|
||||
impl_actions!(terminal, [SendText, SendKeystroke]);
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
terminal_panel::init(cx);
|
||||
@@ -714,10 +714,9 @@ impl Item for TerminalView {
|
||||
false
|
||||
}
|
||||
|
||||
// todo!(search)
|
||||
// fn as_searchable(&self, handle: &View<Self>) -> Option<Box<dyn SearchableItemHandle>> {
|
||||
// Some(Box::new(handle.clone()))
|
||||
// }
|
||||
fn as_searchable(&self, handle: &View<Self>) -> Option<Box<dyn SearchableItemHandle>> {
|
||||
Some(Box::new(handle.clone()))
|
||||
}
|
||||
|
||||
fn breadcrumb_location(&self) -> ToolbarItemLocation {
|
||||
ToolbarItemLocation::PrimaryLeft
|
||||
|
||||
@@ -131,6 +131,15 @@ impl PlayerColors {
|
||||
*self.0.last().unwrap()
|
||||
}
|
||||
|
||||
pub fn read_only(&self) -> PlayerColor {
|
||||
let local = self.local();
|
||||
PlayerColor {
|
||||
cursor: local.cursor.grayscale(),
|
||||
background: local.background.grayscale(),
|
||||
selection: local.selection.grayscale(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn color_for_participant(&self, participant_index: u32) -> PlayerColor {
|
||||
let len = self.0.len() - 1;
|
||||
self.0[(participant_index as usize % len) + 1]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+6402
-6402
File diff suppressed because it is too large
Load Diff
+803
-803
File diff suppressed because it is too large
Load Diff
+451
-451
@@ -15,457 +15,6 @@ pub fn one() -> UserThemeFamily {
|
||||
name: "One".into(),
|
||||
author: "Zed Industries".into(),
|
||||
themes: vec![
|
||||
UserTheme {
|
||||
name: "One Light".into(),
|
||||
appearance: Appearance::Light,
|
||||
styles: UserThemeStylesRefinement {
|
||||
colors: ThemeColorsRefinement {
|
||||
border: Some(rgba(0xc9c9caff).into()),
|
||||
border_variant: Some(rgba(0xdfdfe0ff).into()),
|
||||
border_focused: Some(rgba(0xcbcdf6ff).into()),
|
||||
border_selected: Some(rgba(0xcbcdf6ff).into()),
|
||||
border_transparent: Some(rgba(0x00000000).into()),
|
||||
border_disabled: Some(rgba(0xd3d3d4ff).into()),
|
||||
elevated_surface_background: Some(rgba(0xebebecff).into()),
|
||||
surface_background: Some(rgba(0xebebecff).into()),
|
||||
background: Some(rgba(0xdcdcddff).into()),
|
||||
panel_background: Some(rgba(0xebebecff).into()),
|
||||
element_background: Some(rgba(0xebebecff).into()),
|
||||
element_hover: Some(rgba(0xdfdfe0ff).into()),
|
||||
element_active: Some(rgba(0xcacacaff).into()),
|
||||
element_selected: Some(rgba(0xcacacaff).into()),
|
||||
element_disabled: Some(rgba(0xebebecff).into()),
|
||||
drop_target_background: Some(rgba(0x7f818880).into()),
|
||||
ghost_element_background: Some(rgba(0x00000000).into()),
|
||||
ghost_element_hover: Some(rgba(0xdfdfe0ff).into()),
|
||||
ghost_element_active: Some(rgba(0xcacacaff).into()),
|
||||
ghost_element_selected: Some(rgba(0xcacacaff).into()),
|
||||
ghost_element_disabled: Some(rgba(0xebebecff).into()),
|
||||
text: Some(rgba(0x383a41ff).into()),
|
||||
text_muted: Some(rgba(0x7f8188ff).into()),
|
||||
text_placeholder: Some(rgba(0xa1a1a3ff).into()),
|
||||
text_disabled: Some(rgba(0xa1a1a3ff).into()),
|
||||
text_accent: Some(rgba(0x5c79e2ff).into()),
|
||||
icon: Some(rgba(0x383a41ff).into()),
|
||||
icon_muted: Some(rgba(0x7f8188ff).into()),
|
||||
icon_disabled: Some(rgba(0xa1a1a3ff).into()),
|
||||
icon_placeholder: Some(rgba(0x7f8188ff).into()),
|
||||
icon_accent: Some(rgba(0x5c79e2ff).into()),
|
||||
status_bar_background: Some(rgba(0xdcdcddff).into()),
|
||||
title_bar_background: Some(rgba(0xdcdcddff).into()),
|
||||
toolbar_background: Some(rgba(0xfafafaff).into()),
|
||||
tab_bar_background: Some(rgba(0xebebecff).into()),
|
||||
tab_inactive_background: Some(rgba(0xebebecff).into()),
|
||||
tab_active_background: Some(rgba(0xfafafaff).into()),
|
||||
scrollbar_thumb_background: Some(rgba(0x383a414c).into()),
|
||||
scrollbar_thumb_hover_background: Some(rgba(0xdfdfe0ff).into()),
|
||||
scrollbar_thumb_border: Some(rgba(0xdfdfe0ff).into()),
|
||||
scrollbar_track_background: Some(rgba(0x00000000).into()),
|
||||
scrollbar_track_border: Some(rgba(0xeeeeeeff).into()),
|
||||
editor_foreground: Some(rgba(0x383a41ff).into()),
|
||||
editor_background: Some(rgba(0xfafafaff).into()),
|
||||
editor_gutter_background: Some(rgba(0xfafafaff).into()),
|
||||
editor_subheader_background: Some(rgba(0xebebecff).into()),
|
||||
editor_active_line_background: Some(rgba(0xebebecbf).into()),
|
||||
editor_highlighted_line_background: Some(rgba(0xebebecff).into()),
|
||||
editor_line_number: Some(rgba(0x383a4159).into()),
|
||||
editor_active_line_number: Some(rgba(0x383a41ff).into()),
|
||||
editor_invisible: Some(rgba(0x7f8188ff).into()),
|
||||
editor_wrap_guide: Some(rgba(0x383a410d).into()),
|
||||
editor_active_wrap_guide: Some(rgba(0x383a411a).into()),
|
||||
editor_document_highlight_read_background: Some(rgba(0x5c79e21a).into()),
|
||||
editor_document_highlight_write_background: Some(rgba(0xa3a3a466).into()),
|
||||
terminal_background: Some(rgba(0xfafafaff).into()),
|
||||
terminal_ansi_bright_black: Some(rgba(0xaaaaaaff).into()),
|
||||
terminal_ansi_bright_red: Some(rgba(0xf0b0a4ff).into()),
|
||||
terminal_ansi_bright_green: Some(rgba(0xb2cfa9ff).into()),
|
||||
terminal_ansi_bright_yellow: Some(rgba(0xf1dfc1ff).into()),
|
||||
terminal_ansi_bright_blue: Some(rgba(0xb5baf2ff).into()),
|
||||
terminal_ansi_bright_magenta: Some(rgba(0xcea6d3ff).into()),
|
||||
terminal_ansi_bright_cyan: Some(rgba(0xa4bfdbff).into()),
|
||||
terminal_ansi_bright_white: Some(rgba(0x383a41ff).into()),
|
||||
terminal_ansi_black: Some(rgba(0xfafafaff).into()),
|
||||
terminal_ansi_red: Some(rgba(0xd36151ff).into()),
|
||||
terminal_ansi_green: Some(rgba(0x669f59ff).into()),
|
||||
terminal_ansi_yellow: Some(rgba(0xdec184ff).into()),
|
||||
terminal_ansi_blue: Some(rgba(0x5c79e2ff).into()),
|
||||
terminal_ansi_magenta: Some(rgba(0x994fa6ff).into()),
|
||||
terminal_ansi_cyan: Some(rgba(0x3b82b7ff).into()),
|
||||
terminal_ansi_white: Some(rgba(0x383a41ff).into()),
|
||||
link_text_hover: Some(rgba(0x5c79e2ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
status: StatusColorsRefinement {
|
||||
conflict: Some(rgba(0xdec184ff).into()),
|
||||
conflict_background: Some(rgba(0xfaf2e6ff).into()),
|
||||
conflict_border: Some(rgba(0xf5e8d2ff).into()),
|
||||
created: Some(rgba(0x669f59ff).into()),
|
||||
created_background: Some(rgba(0xe0ebdcff).into()),
|
||||
created_border: Some(rgba(0xc8dcc1ff).into()),
|
||||
deleted: Some(rgba(0xd36151ff).into()),
|
||||
deleted_background: Some(rgba(0xfbdfd9ff).into()),
|
||||
deleted_border: Some(rgba(0xf6c6bdff).into()),
|
||||
error: Some(rgba(0xd36151ff).into()),
|
||||
error_background: Some(rgba(0xfbdfd9ff).into()),
|
||||
error_border: Some(rgba(0xf6c6bdff).into()),
|
||||
hidden: Some(rgba(0xa1a1a3ff).into()),
|
||||
hidden_background: Some(rgba(0xdcdcddff).into()),
|
||||
hidden_border: Some(rgba(0xd3d3d4ff).into()),
|
||||
hint: Some(rgba(0x9295beff).into()),
|
||||
hint_background: Some(rgba(0xe2e2faff).into()),
|
||||
hint_border: Some(rgba(0xcbcdf6ff).into()),
|
||||
ignored: Some(rgba(0x7f8188ff).into()),
|
||||
ignored_background: Some(rgba(0xdcdcddff).into()),
|
||||
ignored_border: Some(rgba(0xc9c9caff).into()),
|
||||
info: Some(rgba(0x5c79e2ff).into()),
|
||||
info_background: Some(rgba(0xe2e2faff).into()),
|
||||
info_border: Some(rgba(0xcbcdf6ff).into()),
|
||||
modified: Some(rgba(0xdec184ff).into()),
|
||||
modified_background: Some(rgba(0xfaf2e6ff).into()),
|
||||
modified_border: Some(rgba(0xf5e8d2ff).into()),
|
||||
predictive: Some(rgba(0x9c9fc7ff).into()),
|
||||
predictive_background: Some(rgba(0xe0ebdcff).into()),
|
||||
predictive_border: Some(rgba(0xc8dcc1ff).into()),
|
||||
renamed: Some(rgba(0x5c79e2ff).into()),
|
||||
renamed_background: Some(rgba(0xe2e2faff).into()),
|
||||
renamed_border: Some(rgba(0xcbcdf6ff).into()),
|
||||
success: Some(rgba(0x669f59ff).into()),
|
||||
success_background: Some(rgba(0xe0ebdcff).into()),
|
||||
success_border: Some(rgba(0xc8dcc1ff).into()),
|
||||
unreachable: Some(rgba(0x7f8188ff).into()),
|
||||
unreachable_background: Some(rgba(0xdcdcddff).into()),
|
||||
unreachable_border: Some(rgba(0xc9c9caff).into()),
|
||||
warning: Some(rgba(0xdec184ff).into()),
|
||||
warning_background: Some(rgba(0xfaf2e6ff).into()),
|
||||
warning_border: Some(rgba(0xf5e8d2ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
player: Some(PlayerColors(vec![
|
||||
PlayerColor {
|
||||
cursor: rgba(0x5c79e2ff).into(),
|
||||
background: rgba(0x5c79e2ff).into(),
|
||||
selection: rgba(0x5c79e23d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0x994fa6ff).into(),
|
||||
background: rgba(0x994fa6ff).into(),
|
||||
selection: rgba(0x994fa63d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0xad6f27ff).into(),
|
||||
background: rgba(0xad6f27ff).into(),
|
||||
selection: rgba(0xad6f273d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0xa44aabff).into(),
|
||||
background: rgba(0xa44aabff).into(),
|
||||
selection: rgba(0xa44aab3d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0x3b82b7ff).into(),
|
||||
background: rgba(0x3b82b7ff).into(),
|
||||
selection: rgba(0x3b82b73d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0xd36151ff).into(),
|
||||
background: rgba(0xd36151ff).into(),
|
||||
selection: rgba(0xd361513d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0xdec184ff).into(),
|
||||
background: rgba(0xdec184ff).into(),
|
||||
selection: rgba(0xdec1843d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0x669f59ff).into(),
|
||||
background: rgba(0x669f59ff).into(),
|
||||
selection: rgba(0x669f593d).into(),
|
||||
},
|
||||
])),
|
||||
syntax: Some(UserSyntaxTheme {
|
||||
highlights: vec![
|
||||
(
|
||||
"attribute".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5c79e2ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"boolean".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xad6f26ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"comment".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xa2a3a7ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"comment.doc".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x7c7e86ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constant".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x669f59ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constructor".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5c79e2ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"embedded".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x383a41ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"emphasis".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5c79e2ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"emphasis.strong".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xad6f26ff).into()),
|
||||
font_weight: Some(UserFontWeight(700.0)),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"enum".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xd36050ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"function".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5b79e3ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"hint".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9295beff).into()),
|
||||
font_weight: Some(UserFontWeight(700.0)),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"keyword".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xa449abff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"label".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5c79e2ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"link_text".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5b79e3ff).into()),
|
||||
font_style: Some(UserFontStyle::Italic),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"link_uri".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x3982b7ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"number".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xad6f26ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"operator".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x3982b7ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"predictive".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9c9fc7ff).into()),
|
||||
font_style: Some(UserFontStyle::Italic),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"preproc".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x383a41ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"primary".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x383a41ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"property".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xd36050ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x383a41ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.bracket".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x4d4f52ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.delimiter".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x4d4f52ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.list_marker".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xd36050ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xb92c46ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x659f58ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.escape".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x7c7e86ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.regex".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xad6f27ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xad6f27ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special.symbol".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xad6f27ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"tag".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5c79e2ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"text.literal".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x659f58ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"title".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xd36050ff).into()),
|
||||
font_weight: Some(UserFontWeight(400.0)),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"type".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x3982b7ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"variable".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x383a41ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"variable.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xad6f26ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"variant".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5b79e3ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
],
|
||||
}),
|
||||
},
|
||||
},
|
||||
UserTheme {
|
||||
name: "One Dark".into(),
|
||||
appearance: Appearance::Dark,
|
||||
@@ -917,6 +466,457 @@ pub fn one() -> UserThemeFamily {
|
||||
}),
|
||||
},
|
||||
},
|
||||
UserTheme {
|
||||
name: "One Light".into(),
|
||||
appearance: Appearance::Light,
|
||||
styles: UserThemeStylesRefinement {
|
||||
colors: ThemeColorsRefinement {
|
||||
border: Some(rgba(0xc9c9caff).into()),
|
||||
border_variant: Some(rgba(0xdfdfe0ff).into()),
|
||||
border_focused: Some(rgba(0xcbcdf6ff).into()),
|
||||
border_selected: Some(rgba(0xcbcdf6ff).into()),
|
||||
border_transparent: Some(rgba(0x00000000).into()),
|
||||
border_disabled: Some(rgba(0xd3d3d4ff).into()),
|
||||
elevated_surface_background: Some(rgba(0xebebecff).into()),
|
||||
surface_background: Some(rgba(0xebebecff).into()),
|
||||
background: Some(rgba(0xdcdcddff).into()),
|
||||
panel_background: Some(rgba(0xebebecff).into()),
|
||||
element_background: Some(rgba(0xebebecff).into()),
|
||||
element_hover: Some(rgba(0xdfdfe0ff).into()),
|
||||
element_active: Some(rgba(0xcacacaff).into()),
|
||||
element_selected: Some(rgba(0xcacacaff).into()),
|
||||
element_disabled: Some(rgba(0xebebecff).into()),
|
||||
drop_target_background: Some(rgba(0x7f818880).into()),
|
||||
ghost_element_background: Some(rgba(0x00000000).into()),
|
||||
ghost_element_hover: Some(rgba(0xdfdfe0ff).into()),
|
||||
ghost_element_active: Some(rgba(0xcacacaff).into()),
|
||||
ghost_element_selected: Some(rgba(0xcacacaff).into()),
|
||||
ghost_element_disabled: Some(rgba(0xebebecff).into()),
|
||||
text: Some(rgba(0x383a41ff).into()),
|
||||
text_muted: Some(rgba(0x7f8188ff).into()),
|
||||
text_placeholder: Some(rgba(0xa1a1a3ff).into()),
|
||||
text_disabled: Some(rgba(0xa1a1a3ff).into()),
|
||||
text_accent: Some(rgba(0x5c79e2ff).into()),
|
||||
icon: Some(rgba(0x383a41ff).into()),
|
||||
icon_muted: Some(rgba(0x7f8188ff).into()),
|
||||
icon_disabled: Some(rgba(0xa1a1a3ff).into()),
|
||||
icon_placeholder: Some(rgba(0x7f8188ff).into()),
|
||||
icon_accent: Some(rgba(0x5c79e2ff).into()),
|
||||
status_bar_background: Some(rgba(0xdcdcddff).into()),
|
||||
title_bar_background: Some(rgba(0xdcdcddff).into()),
|
||||
toolbar_background: Some(rgba(0xfafafaff).into()),
|
||||
tab_bar_background: Some(rgba(0xebebecff).into()),
|
||||
tab_inactive_background: Some(rgba(0xebebecff).into()),
|
||||
tab_active_background: Some(rgba(0xfafafaff).into()),
|
||||
scrollbar_thumb_background: Some(rgba(0x383a414c).into()),
|
||||
scrollbar_thumb_hover_background: Some(rgba(0xdfdfe0ff).into()),
|
||||
scrollbar_thumb_border: Some(rgba(0xdfdfe0ff).into()),
|
||||
scrollbar_track_background: Some(rgba(0x00000000).into()),
|
||||
scrollbar_track_border: Some(rgba(0xeeeeeeff).into()),
|
||||
editor_foreground: Some(rgba(0x383a41ff).into()),
|
||||
editor_background: Some(rgba(0xfafafaff).into()),
|
||||
editor_gutter_background: Some(rgba(0xfafafaff).into()),
|
||||
editor_subheader_background: Some(rgba(0xebebecff).into()),
|
||||
editor_active_line_background: Some(rgba(0xebebecbf).into()),
|
||||
editor_highlighted_line_background: Some(rgba(0xebebecff).into()),
|
||||
editor_line_number: Some(rgba(0x383a4159).into()),
|
||||
editor_active_line_number: Some(rgba(0x383a41ff).into()),
|
||||
editor_invisible: Some(rgba(0x7f8188ff).into()),
|
||||
editor_wrap_guide: Some(rgba(0x383a410d).into()),
|
||||
editor_active_wrap_guide: Some(rgba(0x383a411a).into()),
|
||||
editor_document_highlight_read_background: Some(rgba(0x5c79e21a).into()),
|
||||
editor_document_highlight_write_background: Some(rgba(0xa3a3a466).into()),
|
||||
terminal_background: Some(rgba(0xfafafaff).into()),
|
||||
terminal_ansi_bright_black: Some(rgba(0xaaaaaaff).into()),
|
||||
terminal_ansi_bright_red: Some(rgba(0xf0b0a4ff).into()),
|
||||
terminal_ansi_bright_green: Some(rgba(0xb2cfa9ff).into()),
|
||||
terminal_ansi_bright_yellow: Some(rgba(0xf1dfc1ff).into()),
|
||||
terminal_ansi_bright_blue: Some(rgba(0xb5baf2ff).into()),
|
||||
terminal_ansi_bright_magenta: Some(rgba(0xcea6d3ff).into()),
|
||||
terminal_ansi_bright_cyan: Some(rgba(0xa4bfdbff).into()),
|
||||
terminal_ansi_bright_white: Some(rgba(0x383a41ff).into()),
|
||||
terminal_ansi_black: Some(rgba(0xfafafaff).into()),
|
||||
terminal_ansi_red: Some(rgba(0xd36151ff).into()),
|
||||
terminal_ansi_green: Some(rgba(0x669f59ff).into()),
|
||||
terminal_ansi_yellow: Some(rgba(0xdec184ff).into()),
|
||||
terminal_ansi_blue: Some(rgba(0x5c79e2ff).into()),
|
||||
terminal_ansi_magenta: Some(rgba(0x994fa6ff).into()),
|
||||
terminal_ansi_cyan: Some(rgba(0x3b82b7ff).into()),
|
||||
terminal_ansi_white: Some(rgba(0x383a41ff).into()),
|
||||
link_text_hover: Some(rgba(0x5c79e2ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
status: StatusColorsRefinement {
|
||||
conflict: Some(rgba(0xdec184ff).into()),
|
||||
conflict_background: Some(rgba(0xfaf2e6ff).into()),
|
||||
conflict_border: Some(rgba(0xf5e8d2ff).into()),
|
||||
created: Some(rgba(0x669f59ff).into()),
|
||||
created_background: Some(rgba(0xe0ebdcff).into()),
|
||||
created_border: Some(rgba(0xc8dcc1ff).into()),
|
||||
deleted: Some(rgba(0xd36151ff).into()),
|
||||
deleted_background: Some(rgba(0xfbdfd9ff).into()),
|
||||
deleted_border: Some(rgba(0xf6c6bdff).into()),
|
||||
error: Some(rgba(0xd36151ff).into()),
|
||||
error_background: Some(rgba(0xfbdfd9ff).into()),
|
||||
error_border: Some(rgba(0xf6c6bdff).into()),
|
||||
hidden: Some(rgba(0xa1a1a3ff).into()),
|
||||
hidden_background: Some(rgba(0xdcdcddff).into()),
|
||||
hidden_border: Some(rgba(0xd3d3d4ff).into()),
|
||||
hint: Some(rgba(0x9295beff).into()),
|
||||
hint_background: Some(rgba(0xe2e2faff).into()),
|
||||
hint_border: Some(rgba(0xcbcdf6ff).into()),
|
||||
ignored: Some(rgba(0x7f8188ff).into()),
|
||||
ignored_background: Some(rgba(0xdcdcddff).into()),
|
||||
ignored_border: Some(rgba(0xc9c9caff).into()),
|
||||
info: Some(rgba(0x5c79e2ff).into()),
|
||||
info_background: Some(rgba(0xe2e2faff).into()),
|
||||
info_border: Some(rgba(0xcbcdf6ff).into()),
|
||||
modified: Some(rgba(0xdec184ff).into()),
|
||||
modified_background: Some(rgba(0xfaf2e6ff).into()),
|
||||
modified_border: Some(rgba(0xf5e8d2ff).into()),
|
||||
predictive: Some(rgba(0x9c9fc7ff).into()),
|
||||
predictive_background: Some(rgba(0xe0ebdcff).into()),
|
||||
predictive_border: Some(rgba(0xc8dcc1ff).into()),
|
||||
renamed: Some(rgba(0x5c79e2ff).into()),
|
||||
renamed_background: Some(rgba(0xe2e2faff).into()),
|
||||
renamed_border: Some(rgba(0xcbcdf6ff).into()),
|
||||
success: Some(rgba(0x669f59ff).into()),
|
||||
success_background: Some(rgba(0xe0ebdcff).into()),
|
||||
success_border: Some(rgba(0xc8dcc1ff).into()),
|
||||
unreachable: Some(rgba(0x7f8188ff).into()),
|
||||
unreachable_background: Some(rgba(0xdcdcddff).into()),
|
||||
unreachable_border: Some(rgba(0xc9c9caff).into()),
|
||||
warning: Some(rgba(0xdec184ff).into()),
|
||||
warning_background: Some(rgba(0xfaf2e6ff).into()),
|
||||
warning_border: Some(rgba(0xf5e8d2ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
player: Some(PlayerColors(vec![
|
||||
PlayerColor {
|
||||
cursor: rgba(0x5c79e2ff).into(),
|
||||
background: rgba(0x5c79e2ff).into(),
|
||||
selection: rgba(0x5c79e23d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0x994fa6ff).into(),
|
||||
background: rgba(0x994fa6ff).into(),
|
||||
selection: rgba(0x994fa63d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0xad6f27ff).into(),
|
||||
background: rgba(0xad6f27ff).into(),
|
||||
selection: rgba(0xad6f273d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0xa44aabff).into(),
|
||||
background: rgba(0xa44aabff).into(),
|
||||
selection: rgba(0xa44aab3d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0x3b82b7ff).into(),
|
||||
background: rgba(0x3b82b7ff).into(),
|
||||
selection: rgba(0x3b82b73d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0xd36151ff).into(),
|
||||
background: rgba(0xd36151ff).into(),
|
||||
selection: rgba(0xd361513d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0xdec184ff).into(),
|
||||
background: rgba(0xdec184ff).into(),
|
||||
selection: rgba(0xdec1843d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0x669f59ff).into(),
|
||||
background: rgba(0x669f59ff).into(),
|
||||
selection: rgba(0x669f593d).into(),
|
||||
},
|
||||
])),
|
||||
syntax: Some(UserSyntaxTheme {
|
||||
highlights: vec![
|
||||
(
|
||||
"attribute".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5c79e2ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"boolean".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xad6f26ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"comment".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xa2a3a7ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"comment.doc".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x7c7e86ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constant".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x669f59ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constructor".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5c79e2ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"embedded".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x383a41ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"emphasis".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5c79e2ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"emphasis.strong".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xad6f26ff).into()),
|
||||
font_weight: Some(UserFontWeight(700.0)),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"enum".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xd36050ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"function".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5b79e3ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"hint".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9295beff).into()),
|
||||
font_weight: Some(UserFontWeight(700.0)),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"keyword".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xa449abff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"label".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5c79e2ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"link_text".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5b79e3ff).into()),
|
||||
font_style: Some(UserFontStyle::Italic),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"link_uri".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x3982b7ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"number".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xad6f26ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"operator".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x3982b7ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"predictive".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9c9fc7ff).into()),
|
||||
font_style: Some(UserFontStyle::Italic),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"preproc".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x383a41ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"primary".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x383a41ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"property".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xd36050ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x383a41ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.bracket".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x4d4f52ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.delimiter".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x4d4f52ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.list_marker".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xd36050ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xb92c46ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x659f58ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.escape".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x7c7e86ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.regex".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xad6f27ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xad6f27ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special.symbol".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xad6f27ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"tag".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5c79e2ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"text.literal".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x659f58ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"title".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xd36050ff).into()),
|
||||
font_weight: Some(UserFontWeight(400.0)),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"type".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x3982b7ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"variable".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x383a41ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"variable.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xad6f26ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"variant".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5b79e3ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
],
|
||||
}),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,464 @@ pub fn rose_pine() -> UserThemeFamily {
|
||||
name: "Rosé Pine".into(),
|
||||
author: "Zed Industries".into(),
|
||||
themes: vec![
|
||||
UserTheme {
|
||||
name: "Rosé Pine".into(),
|
||||
appearance: Appearance::Dark,
|
||||
styles: UserThemeStylesRefinement {
|
||||
colors: ThemeColorsRefinement {
|
||||
border: Some(rgba(0x423f55ff).into()),
|
||||
border_variant: Some(rgba(0x232132ff).into()),
|
||||
border_focused: Some(rgba(0x435255ff).into()),
|
||||
border_selected: Some(rgba(0x435255ff).into()),
|
||||
border_transparent: Some(rgba(0x00000000).into()),
|
||||
border_disabled: Some(rgba(0x353347ff).into()),
|
||||
elevated_surface_background: Some(rgba(0x1d1b2aff).into()),
|
||||
surface_background: Some(rgba(0x1d1b2aff).into()),
|
||||
background: Some(rgba(0x292739ff).into()),
|
||||
panel_background: Some(rgba(0x1d1b2aff).into()),
|
||||
element_background: Some(rgba(0x1d1b2aff).into()),
|
||||
element_hover: Some(rgba(0x232132ff).into()),
|
||||
element_active: Some(rgba(0x403e53ff).into()),
|
||||
element_selected: Some(rgba(0x403e53ff).into()),
|
||||
element_disabled: Some(rgba(0x1d1b2aff).into()),
|
||||
drop_target_background: Some(rgba(0x75718e80).into()),
|
||||
ghost_element_background: Some(rgba(0x00000000).into()),
|
||||
ghost_element_hover: Some(rgba(0x232132ff).into()),
|
||||
ghost_element_active: Some(rgba(0x403e53ff).into()),
|
||||
ghost_element_selected: Some(rgba(0x403e53ff).into()),
|
||||
ghost_element_disabled: Some(rgba(0x1d1b2aff).into()),
|
||||
text: Some(rgba(0xe0def4ff).into()),
|
||||
text_muted: Some(rgba(0x75718eff).into()),
|
||||
text_placeholder: Some(rgba(0x2f2b43ff).into()),
|
||||
text_disabled: Some(rgba(0x2f2b43ff).into()),
|
||||
text_accent: Some(rgba(0x9cced7ff).into()),
|
||||
icon: Some(rgba(0xe0def4ff).into()),
|
||||
icon_muted: Some(rgba(0x75718eff).into()),
|
||||
icon_disabled: Some(rgba(0x2f2b43ff).into()),
|
||||
icon_placeholder: Some(rgba(0x75718eff).into()),
|
||||
icon_accent: Some(rgba(0x9cced7ff).into()),
|
||||
status_bar_background: Some(rgba(0x292739ff).into()),
|
||||
title_bar_background: Some(rgba(0x292739ff).into()),
|
||||
toolbar_background: Some(rgba(0x191724ff).into()),
|
||||
tab_bar_background: Some(rgba(0x1d1b2aff).into()),
|
||||
tab_inactive_background: Some(rgba(0x1d1b2aff).into()),
|
||||
tab_active_background: Some(rgba(0x191724ff).into()),
|
||||
scrollbar_thumb_background: Some(rgba(0xe0def44c).into()),
|
||||
scrollbar_thumb_hover_background: Some(rgba(0x232132ff).into()),
|
||||
scrollbar_thumb_border: Some(rgba(0x232132ff).into()),
|
||||
scrollbar_track_background: Some(rgba(0x00000000).into()),
|
||||
scrollbar_track_border: Some(rgba(0x1c1a29ff).into()),
|
||||
editor_foreground: Some(rgba(0xe0def4ff).into()),
|
||||
editor_background: Some(rgba(0x191724ff).into()),
|
||||
editor_gutter_background: Some(rgba(0x191724ff).into()),
|
||||
editor_subheader_background: Some(rgba(0x1d1b2aff).into()),
|
||||
editor_active_line_background: Some(rgba(0x1d1b2abf).into()),
|
||||
editor_highlighted_line_background: Some(rgba(0x1d1b2aff).into()),
|
||||
editor_line_number: Some(rgba(0xe0def459).into()),
|
||||
editor_active_line_number: Some(rgba(0xe0def4ff).into()),
|
||||
editor_invisible: Some(rgba(0x75718eff).into()),
|
||||
editor_wrap_guide: Some(rgba(0xe0def40d).into()),
|
||||
editor_active_wrap_guide: Some(rgba(0xe0def41a).into()),
|
||||
editor_document_highlight_read_background: Some(rgba(0x9cced71a).into()),
|
||||
editor_document_highlight_write_background: Some(rgba(0x28253c66).into()),
|
||||
terminal_background: Some(rgba(0x191724ff).into()),
|
||||
terminal_ansi_bright_black: Some(rgba(0x403d55ff).into()),
|
||||
terminal_ansi_bright_red: Some(rgba(0x7e3647ff).into()),
|
||||
terminal_ansi_bright_green: Some(rgba(0x31614fff).into()),
|
||||
terminal_ansi_bright_yellow: Some(rgba(0x8a653bff).into()),
|
||||
terminal_ansi_bright_blue: Some(rgba(0x566c70ff).into()),
|
||||
terminal_ansi_bright_magenta: Some(rgba(0x4c3b47ff).into()),
|
||||
terminal_ansi_bright_cyan: Some(rgba(0x203a46ff).into()),
|
||||
terminal_ansi_bright_white: Some(rgba(0xe0def4ff).into()),
|
||||
terminal_ansi_black: Some(rgba(0x191724ff).into()),
|
||||
terminal_ansi_red: Some(rgba(0xea6f92ff).into()),
|
||||
terminal_ansi_green: Some(rgba(0x5dc2a3ff).into()),
|
||||
terminal_ansi_yellow: Some(rgba(0xf5c177ff).into()),
|
||||
terminal_ansi_blue: Some(rgba(0x9cced7ff).into()),
|
||||
terminal_ansi_magenta: Some(rgba(0x9d7691ff).into()),
|
||||
terminal_ansi_cyan: Some(rgba(0x32748fff).into()),
|
||||
terminal_ansi_white: Some(rgba(0xe0def4ff).into()),
|
||||
link_text_hover: Some(rgba(0x9cced7ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
status: StatusColorsRefinement {
|
||||
conflict: Some(rgba(0xf5c177ff).into()),
|
||||
conflict_background: Some(rgba(0x50341aff).into()),
|
||||
conflict_border: Some(rgba(0x6d4d2bff).into()),
|
||||
created: Some(rgba(0x5dc2a3ff).into()),
|
||||
created_background: Some(rgba(0x182e23ff).into()),
|
||||
created_border: Some(rgba(0x254839ff).into()),
|
||||
deleted: Some(rgba(0xea6f92ff).into()),
|
||||
deleted_background: Some(rgba(0x431820ff).into()),
|
||||
deleted_border: Some(rgba(0x612834ff).into()),
|
||||
error: Some(rgba(0xea6f92ff).into()),
|
||||
error_background: Some(rgba(0x431820ff).into()),
|
||||
error_border: Some(rgba(0x612834ff).into()),
|
||||
hidden: Some(rgba(0x2f2b43ff).into()),
|
||||
hidden_background: Some(rgba(0x292739ff).into()),
|
||||
hidden_border: Some(rgba(0x353347ff).into()),
|
||||
hint: Some(rgba(0x5e768cff).into()),
|
||||
hint_background: Some(rgba(0x2f3739ff).into()),
|
||||
hint_border: Some(rgba(0x435255ff).into()),
|
||||
ignored: Some(rgba(0x75718eff).into()),
|
||||
ignored_background: Some(rgba(0x292739ff).into()),
|
||||
ignored_border: Some(rgba(0x423f55ff).into()),
|
||||
info: Some(rgba(0x9cced7ff).into()),
|
||||
info_background: Some(rgba(0x2f3739ff).into()),
|
||||
info_border: Some(rgba(0x435255ff).into()),
|
||||
modified: Some(rgba(0xf5c177ff).into()),
|
||||
modified_background: Some(rgba(0x50341aff).into()),
|
||||
modified_border: Some(rgba(0x6d4d2bff).into()),
|
||||
predictive: Some(rgba(0x556b81ff).into()),
|
||||
predictive_background: Some(rgba(0x182e23ff).into()),
|
||||
predictive_border: Some(rgba(0x254839ff).into()),
|
||||
renamed: Some(rgba(0x9cced7ff).into()),
|
||||
renamed_background: Some(rgba(0x2f3739ff).into()),
|
||||
renamed_border: Some(rgba(0x435255ff).into()),
|
||||
success: Some(rgba(0x5dc2a3ff).into()),
|
||||
success_background: Some(rgba(0x182e23ff).into()),
|
||||
success_border: Some(rgba(0x254839ff).into()),
|
||||
unreachable: Some(rgba(0x75718eff).into()),
|
||||
unreachable_background: Some(rgba(0x292739ff).into()),
|
||||
unreachable_border: Some(rgba(0x423f55ff).into()),
|
||||
warning: Some(rgba(0xf5c177ff).into()),
|
||||
warning_background: Some(rgba(0x50341aff).into()),
|
||||
warning_border: Some(rgba(0x6d4d2bff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
player: Some(PlayerColors(vec![
|
||||
PlayerColor {
|
||||
cursor: rgba(0x9cced7ff).into(),
|
||||
background: rgba(0x9cced7ff).into(),
|
||||
selection: rgba(0x9cced73d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0x9d7691ff).into(),
|
||||
background: rgba(0x9d7691ff).into(),
|
||||
selection: rgba(0x9d76913d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0xc4a7e6ff).into(),
|
||||
background: rgba(0xc4a7e6ff).into(),
|
||||
selection: rgba(0xc4a7e63d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0xc4a7e6ff).into(),
|
||||
background: rgba(0xc4a7e6ff).into(),
|
||||
selection: rgba(0xc4a7e63d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0x32748fff).into(),
|
||||
background: rgba(0x32748fff).into(),
|
||||
selection: rgba(0x32748f3d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0xea6f92ff).into(),
|
||||
background: rgba(0xea6f92ff).into(),
|
||||
selection: rgba(0xea6f923d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0xf5c177ff).into(),
|
||||
background: rgba(0xf5c177ff).into(),
|
||||
selection: rgba(0xf5c1773d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0x5dc2a3ff).into(),
|
||||
background: rgba(0x5dc2a3ff).into(),
|
||||
selection: rgba(0x5dc2a33d).into(),
|
||||
},
|
||||
])),
|
||||
syntax: Some(UserSyntaxTheme {
|
||||
highlights: vec![
|
||||
(
|
||||
"attribute".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9cced7ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"boolean".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xebbcbaff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"comment".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x6e6a86ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"comment.doc".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x777390ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constant".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5dc2a3ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constructor".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9cced7ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"embedded".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xe0def4ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"emphasis".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9cced7ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"emphasis.strong".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9cced7ff).into()),
|
||||
font_weight: Some(UserFontWeight(700.0)),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"enum".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xc4a7e6ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"function".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xebbcbaff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"function.method".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xebbcbaff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"hint".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5e768cff).into()),
|
||||
font_weight: Some(UserFontWeight(700.0)),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"keyword".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x31748fff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"label".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9cced7ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"link_text".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9ccfd8ff).into()),
|
||||
font_style: Some(UserFontStyle::Normal),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"link_uri".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xebbcbaff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"number".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5dc2a3ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"operator".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x31748fff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"predictive".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x556b81ff).into()),
|
||||
font_style: Some(UserFontStyle::Italic),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"preproc".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xe0def4ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"primary".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xe0def4ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"property".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9cced7ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x908caaff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.bracket".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9d99b6ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.delimiter".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9d99b6ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.list_marker".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9d99b6ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9d99b6ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xf6c177ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.escape".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x777390ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.regex".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xc4a7e6ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xc4a7e6ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special.symbol".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xc4a7e6ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"tag".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9ccfd8ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"text.literal".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xc4a7e6ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"title".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xf6c177ff).into()),
|
||||
font_weight: Some(UserFontWeight(700.0)),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"type".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9ccfd8ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"type.builtin".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9ccfd8ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"variable".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xe0def4ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"variant".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9cced7ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
],
|
||||
}),
|
||||
},
|
||||
},
|
||||
UserTheme {
|
||||
name: "Rosé Pine Dawn".into(),
|
||||
appearance: Appearance::Light,
|
||||
@@ -931,464 +1389,6 @@ pub fn rose_pine() -> UserThemeFamily {
|
||||
}),
|
||||
},
|
||||
},
|
||||
UserTheme {
|
||||
name: "Rosé Pine".into(),
|
||||
appearance: Appearance::Dark,
|
||||
styles: UserThemeStylesRefinement {
|
||||
colors: ThemeColorsRefinement {
|
||||
border: Some(rgba(0x423f55ff).into()),
|
||||
border_variant: Some(rgba(0x232132ff).into()),
|
||||
border_focused: Some(rgba(0x435255ff).into()),
|
||||
border_selected: Some(rgba(0x435255ff).into()),
|
||||
border_transparent: Some(rgba(0x00000000).into()),
|
||||
border_disabled: Some(rgba(0x353347ff).into()),
|
||||
elevated_surface_background: Some(rgba(0x1d1b2aff).into()),
|
||||
surface_background: Some(rgba(0x1d1b2aff).into()),
|
||||
background: Some(rgba(0x292739ff).into()),
|
||||
panel_background: Some(rgba(0x1d1b2aff).into()),
|
||||
element_background: Some(rgba(0x1d1b2aff).into()),
|
||||
element_hover: Some(rgba(0x232132ff).into()),
|
||||
element_active: Some(rgba(0x403e53ff).into()),
|
||||
element_selected: Some(rgba(0x403e53ff).into()),
|
||||
element_disabled: Some(rgba(0x1d1b2aff).into()),
|
||||
drop_target_background: Some(rgba(0x75718e80).into()),
|
||||
ghost_element_background: Some(rgba(0x00000000).into()),
|
||||
ghost_element_hover: Some(rgba(0x232132ff).into()),
|
||||
ghost_element_active: Some(rgba(0x403e53ff).into()),
|
||||
ghost_element_selected: Some(rgba(0x403e53ff).into()),
|
||||
ghost_element_disabled: Some(rgba(0x1d1b2aff).into()),
|
||||
text: Some(rgba(0xe0def4ff).into()),
|
||||
text_muted: Some(rgba(0x75718eff).into()),
|
||||
text_placeholder: Some(rgba(0x2f2b43ff).into()),
|
||||
text_disabled: Some(rgba(0x2f2b43ff).into()),
|
||||
text_accent: Some(rgba(0x9cced7ff).into()),
|
||||
icon: Some(rgba(0xe0def4ff).into()),
|
||||
icon_muted: Some(rgba(0x75718eff).into()),
|
||||
icon_disabled: Some(rgba(0x2f2b43ff).into()),
|
||||
icon_placeholder: Some(rgba(0x75718eff).into()),
|
||||
icon_accent: Some(rgba(0x9cced7ff).into()),
|
||||
status_bar_background: Some(rgba(0x292739ff).into()),
|
||||
title_bar_background: Some(rgba(0x292739ff).into()),
|
||||
toolbar_background: Some(rgba(0x191724ff).into()),
|
||||
tab_bar_background: Some(rgba(0x1d1b2aff).into()),
|
||||
tab_inactive_background: Some(rgba(0x1d1b2aff).into()),
|
||||
tab_active_background: Some(rgba(0x191724ff).into()),
|
||||
scrollbar_thumb_background: Some(rgba(0xe0def44c).into()),
|
||||
scrollbar_thumb_hover_background: Some(rgba(0x232132ff).into()),
|
||||
scrollbar_thumb_border: Some(rgba(0x232132ff).into()),
|
||||
scrollbar_track_background: Some(rgba(0x00000000).into()),
|
||||
scrollbar_track_border: Some(rgba(0x1c1a29ff).into()),
|
||||
editor_foreground: Some(rgba(0xe0def4ff).into()),
|
||||
editor_background: Some(rgba(0x191724ff).into()),
|
||||
editor_gutter_background: Some(rgba(0x191724ff).into()),
|
||||
editor_subheader_background: Some(rgba(0x1d1b2aff).into()),
|
||||
editor_active_line_background: Some(rgba(0x1d1b2abf).into()),
|
||||
editor_highlighted_line_background: Some(rgba(0x1d1b2aff).into()),
|
||||
editor_line_number: Some(rgba(0xe0def459).into()),
|
||||
editor_active_line_number: Some(rgba(0xe0def4ff).into()),
|
||||
editor_invisible: Some(rgba(0x75718eff).into()),
|
||||
editor_wrap_guide: Some(rgba(0xe0def40d).into()),
|
||||
editor_active_wrap_guide: Some(rgba(0xe0def41a).into()),
|
||||
editor_document_highlight_read_background: Some(rgba(0x9cced71a).into()),
|
||||
editor_document_highlight_write_background: Some(rgba(0x28253c66).into()),
|
||||
terminal_background: Some(rgba(0x191724ff).into()),
|
||||
terminal_ansi_bright_black: Some(rgba(0x403d55ff).into()),
|
||||
terminal_ansi_bright_red: Some(rgba(0x7e3647ff).into()),
|
||||
terminal_ansi_bright_green: Some(rgba(0x31614fff).into()),
|
||||
terminal_ansi_bright_yellow: Some(rgba(0x8a653bff).into()),
|
||||
terminal_ansi_bright_blue: Some(rgba(0x566c70ff).into()),
|
||||
terminal_ansi_bright_magenta: Some(rgba(0x4c3b47ff).into()),
|
||||
terminal_ansi_bright_cyan: Some(rgba(0x203a46ff).into()),
|
||||
terminal_ansi_bright_white: Some(rgba(0xe0def4ff).into()),
|
||||
terminal_ansi_black: Some(rgba(0x191724ff).into()),
|
||||
terminal_ansi_red: Some(rgba(0xea6f92ff).into()),
|
||||
terminal_ansi_green: Some(rgba(0x5dc2a3ff).into()),
|
||||
terminal_ansi_yellow: Some(rgba(0xf5c177ff).into()),
|
||||
terminal_ansi_blue: Some(rgba(0x9cced7ff).into()),
|
||||
terminal_ansi_magenta: Some(rgba(0x9d7691ff).into()),
|
||||
terminal_ansi_cyan: Some(rgba(0x32748fff).into()),
|
||||
terminal_ansi_white: Some(rgba(0xe0def4ff).into()),
|
||||
link_text_hover: Some(rgba(0x9cced7ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
status: StatusColorsRefinement {
|
||||
conflict: Some(rgba(0xf5c177ff).into()),
|
||||
conflict_background: Some(rgba(0x50341aff).into()),
|
||||
conflict_border: Some(rgba(0x6d4d2bff).into()),
|
||||
created: Some(rgba(0x5dc2a3ff).into()),
|
||||
created_background: Some(rgba(0x182e23ff).into()),
|
||||
created_border: Some(rgba(0x254839ff).into()),
|
||||
deleted: Some(rgba(0xea6f92ff).into()),
|
||||
deleted_background: Some(rgba(0x431820ff).into()),
|
||||
deleted_border: Some(rgba(0x612834ff).into()),
|
||||
error: Some(rgba(0xea6f92ff).into()),
|
||||
error_background: Some(rgba(0x431820ff).into()),
|
||||
error_border: Some(rgba(0x612834ff).into()),
|
||||
hidden: Some(rgba(0x2f2b43ff).into()),
|
||||
hidden_background: Some(rgba(0x292739ff).into()),
|
||||
hidden_border: Some(rgba(0x353347ff).into()),
|
||||
hint: Some(rgba(0x5e768cff).into()),
|
||||
hint_background: Some(rgba(0x2f3739ff).into()),
|
||||
hint_border: Some(rgba(0x435255ff).into()),
|
||||
ignored: Some(rgba(0x75718eff).into()),
|
||||
ignored_background: Some(rgba(0x292739ff).into()),
|
||||
ignored_border: Some(rgba(0x423f55ff).into()),
|
||||
info: Some(rgba(0x9cced7ff).into()),
|
||||
info_background: Some(rgba(0x2f3739ff).into()),
|
||||
info_border: Some(rgba(0x435255ff).into()),
|
||||
modified: Some(rgba(0xf5c177ff).into()),
|
||||
modified_background: Some(rgba(0x50341aff).into()),
|
||||
modified_border: Some(rgba(0x6d4d2bff).into()),
|
||||
predictive: Some(rgba(0x556b81ff).into()),
|
||||
predictive_background: Some(rgba(0x182e23ff).into()),
|
||||
predictive_border: Some(rgba(0x254839ff).into()),
|
||||
renamed: Some(rgba(0x9cced7ff).into()),
|
||||
renamed_background: Some(rgba(0x2f3739ff).into()),
|
||||
renamed_border: Some(rgba(0x435255ff).into()),
|
||||
success: Some(rgba(0x5dc2a3ff).into()),
|
||||
success_background: Some(rgba(0x182e23ff).into()),
|
||||
success_border: Some(rgba(0x254839ff).into()),
|
||||
unreachable: Some(rgba(0x75718eff).into()),
|
||||
unreachable_background: Some(rgba(0x292739ff).into()),
|
||||
unreachable_border: Some(rgba(0x423f55ff).into()),
|
||||
warning: Some(rgba(0xf5c177ff).into()),
|
||||
warning_background: Some(rgba(0x50341aff).into()),
|
||||
warning_border: Some(rgba(0x6d4d2bff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
player: Some(PlayerColors(vec![
|
||||
PlayerColor {
|
||||
cursor: rgba(0x9cced7ff).into(),
|
||||
background: rgba(0x9cced7ff).into(),
|
||||
selection: rgba(0x9cced73d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0x9d7691ff).into(),
|
||||
background: rgba(0x9d7691ff).into(),
|
||||
selection: rgba(0x9d76913d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0xc4a7e6ff).into(),
|
||||
background: rgba(0xc4a7e6ff).into(),
|
||||
selection: rgba(0xc4a7e63d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0xc4a7e6ff).into(),
|
||||
background: rgba(0xc4a7e6ff).into(),
|
||||
selection: rgba(0xc4a7e63d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0x32748fff).into(),
|
||||
background: rgba(0x32748fff).into(),
|
||||
selection: rgba(0x32748f3d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0xea6f92ff).into(),
|
||||
background: rgba(0xea6f92ff).into(),
|
||||
selection: rgba(0xea6f923d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0xf5c177ff).into(),
|
||||
background: rgba(0xf5c177ff).into(),
|
||||
selection: rgba(0xf5c1773d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0x5dc2a3ff).into(),
|
||||
background: rgba(0x5dc2a3ff).into(),
|
||||
selection: rgba(0x5dc2a33d).into(),
|
||||
},
|
||||
])),
|
||||
syntax: Some(UserSyntaxTheme {
|
||||
highlights: vec![
|
||||
(
|
||||
"attribute".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9cced7ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"boolean".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xebbcbaff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"comment".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x6e6a86ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"comment.doc".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x777390ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constant".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5dc2a3ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constructor".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9cced7ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"embedded".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xe0def4ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"emphasis".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9cced7ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"emphasis.strong".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9cced7ff).into()),
|
||||
font_weight: Some(UserFontWeight(700.0)),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"enum".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xc4a7e6ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"function".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xebbcbaff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"function.method".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xebbcbaff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"hint".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5e768cff).into()),
|
||||
font_weight: Some(UserFontWeight(700.0)),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"keyword".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x31748fff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"label".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9cced7ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"link_text".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9ccfd8ff).into()),
|
||||
font_style: Some(UserFontStyle::Normal),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"link_uri".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xebbcbaff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"number".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5dc2a3ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"operator".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x31748fff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"predictive".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x556b81ff).into()),
|
||||
font_style: Some(UserFontStyle::Italic),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"preproc".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xe0def4ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"primary".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xe0def4ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"property".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9cced7ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x908caaff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.bracket".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9d99b6ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.delimiter".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9d99b6ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.list_marker".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9d99b6ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9d99b6ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xf6c177ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.escape".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x777390ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.regex".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xc4a7e6ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xc4a7e6ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special.symbol".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xc4a7e6ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"tag".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9ccfd8ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"text.literal".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xc4a7e6ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"title".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xf6c177ff).into()),
|
||||
font_weight: Some(UserFontWeight(700.0)),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"type".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9ccfd8ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"type.builtin".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9ccfd8ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"variable".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xe0def4ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"variant".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x9cced7ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
],
|
||||
}),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,450 +15,6 @@ pub fn solarized() -> UserThemeFamily {
|
||||
name: "Solarized".into(),
|
||||
author: "Zed Industries".into(),
|
||||
themes: vec![
|
||||
UserTheme {
|
||||
name: "Solarized Light".into(),
|
||||
appearance: Appearance::Light,
|
||||
styles: UserThemeStylesRefinement {
|
||||
colors: ThemeColorsRefinement {
|
||||
border: Some(rgba(0x9faaa8ff).into()),
|
||||
border_variant: Some(rgba(0xdcdacbff).into()),
|
||||
border_focused: Some(rgba(0xbfd3efff).into()),
|
||||
border_selected: Some(rgba(0xbfd3efff).into()),
|
||||
border_transparent: Some(rgba(0x00000000).into()),
|
||||
border_disabled: Some(rgba(0xb7bdb6ff).into()),
|
||||
elevated_surface_background: Some(rgba(0xf3eddaff).into()),
|
||||
surface_background: Some(rgba(0xf3eddaff).into()),
|
||||
background: Some(rgba(0xcfd0c4ff).into()),
|
||||
panel_background: Some(rgba(0xf3eddaff).into()),
|
||||
element_background: Some(rgba(0xf3eddaff).into()),
|
||||
element_hover: Some(rgba(0xdcdacbff).into()),
|
||||
element_active: Some(rgba(0xa2aca9ff).into()),
|
||||
element_selected: Some(rgba(0xa2aca9ff).into()),
|
||||
element_disabled: Some(rgba(0xf3eddaff).into()),
|
||||
drop_target_background: Some(rgba(0x34555e80).into()),
|
||||
ghost_element_background: Some(rgba(0x00000000).into()),
|
||||
ghost_element_hover: Some(rgba(0xdcdacbff).into()),
|
||||
ghost_element_active: Some(rgba(0xa2aca9ff).into()),
|
||||
ghost_element_selected: Some(rgba(0xa2aca9ff).into()),
|
||||
ghost_element_disabled: Some(rgba(0xf3eddaff).into()),
|
||||
text: Some(rgba(0x002b36ff).into()),
|
||||
text_muted: Some(rgba(0x34555eff).into()),
|
||||
text_placeholder: Some(rgba(0x6a7f86ff).into()),
|
||||
text_disabled: Some(rgba(0x6a7f86ff).into()),
|
||||
text_accent: Some(rgba(0x298bd1ff).into()),
|
||||
icon: Some(rgba(0x002b36ff).into()),
|
||||
icon_muted: Some(rgba(0x34555eff).into()),
|
||||
icon_disabled: Some(rgba(0x6a7f86ff).into()),
|
||||
icon_placeholder: Some(rgba(0x34555eff).into()),
|
||||
icon_accent: Some(rgba(0x298bd1ff).into()),
|
||||
status_bar_background: Some(rgba(0xcfd0c4ff).into()),
|
||||
title_bar_background: Some(rgba(0xcfd0c4ff).into()),
|
||||
toolbar_background: Some(rgba(0xfdf6e3ff).into()),
|
||||
tab_bar_background: Some(rgba(0xf3eddaff).into()),
|
||||
tab_inactive_background: Some(rgba(0xf3eddaff).into()),
|
||||
tab_active_background: Some(rgba(0xfdf6e3ff).into()),
|
||||
scrollbar_thumb_background: Some(rgba(0x002b364c).into()),
|
||||
scrollbar_thumb_hover_background: Some(rgba(0xdcdacbff).into()),
|
||||
scrollbar_thumb_border: Some(rgba(0xdcdacbff).into()),
|
||||
scrollbar_track_background: Some(rgba(0x00000000).into()),
|
||||
scrollbar_track_border: Some(rgba(0xf5eedbff).into()),
|
||||
editor_foreground: Some(rgba(0x002b36ff).into()),
|
||||
editor_background: Some(rgba(0xfdf6e3ff).into()),
|
||||
editor_gutter_background: Some(rgba(0xfdf6e3ff).into()),
|
||||
editor_subheader_background: Some(rgba(0xf3eddaff).into()),
|
||||
editor_active_line_background: Some(rgba(0xf3eddabf).into()),
|
||||
editor_highlighted_line_background: Some(rgba(0xf3eddaff).into()),
|
||||
editor_line_number: Some(rgba(0x002b3659).into()),
|
||||
editor_active_line_number: Some(rgba(0x002b36ff).into()),
|
||||
editor_invisible: Some(rgba(0x34555eff).into()),
|
||||
editor_wrap_guide: Some(rgba(0x002b360d).into()),
|
||||
editor_active_wrap_guide: Some(rgba(0x002b361a).into()),
|
||||
editor_document_highlight_read_background: Some(rgba(0x298bd11a).into()),
|
||||
editor_document_highlight_write_background: Some(rgba(0x6d828866).into()),
|
||||
terminal_background: Some(rgba(0xfdf6e3ff).into()),
|
||||
terminal_ansi_bright_black: Some(rgba(0x7b8e91ff).into()),
|
||||
terminal_ansi_bright_red: Some(rgba(0xfaa091ff).into()),
|
||||
terminal_ansi_bright_green: Some(rgba(0xc6cb8bff).into()),
|
||||
terminal_ansi_bright_yellow: Some(rgba(0xe1c28aff).into()),
|
||||
terminal_ansi_bright_blue: Some(rgba(0xa5c3e9ff).into()),
|
||||
terminal_ansi_bright_magenta: Some(rgba(0xf0a2bfff).into()),
|
||||
terminal_ansi_bright_cyan: Some(rgba(0x9fd0cbff).into()),
|
||||
terminal_ansi_bright_white: Some(rgba(0x002b36ff).into()),
|
||||
terminal_ansi_black: Some(rgba(0xfdf6e3ff).into()),
|
||||
terminal_ansi_red: Some(rgba(0xdc3330ff).into()),
|
||||
terminal_ansi_green: Some(rgba(0x859904ff).into()),
|
||||
terminal_ansi_yellow: Some(rgba(0xb58904ff).into()),
|
||||
terminal_ansi_blue: Some(rgba(0x298bd1ff).into()),
|
||||
terminal_ansi_magenta: Some(rgba(0xd33882ff).into()),
|
||||
terminal_ansi_cyan: Some(rgba(0x2ca198ff).into()),
|
||||
terminal_ansi_white: Some(rgba(0x002b36ff).into()),
|
||||
link_text_hover: Some(rgba(0x298bd1ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
status: StatusColorsRefinement {
|
||||
conflict: Some(rgba(0xb58904ff).into()),
|
||||
conflict_background: Some(rgba(0xf5e6d0ff).into()),
|
||||
conflict_border: Some(rgba(0xebd3aaff).into()),
|
||||
created: Some(rgba(0x859904ff).into()),
|
||||
created_background: Some(rgba(0xe9ead0ff).into()),
|
||||
created_border: Some(rgba(0xd6d9abff).into()),
|
||||
deleted: Some(rgba(0xdc3330ff).into()),
|
||||
deleted_background: Some(rgba(0xffd9d2ff).into()),
|
||||
deleted_border: Some(rgba(0xffbbafff).into()),
|
||||
error: Some(rgba(0xdc3330ff).into()),
|
||||
error_background: Some(rgba(0xffd9d2ff).into()),
|
||||
error_border: Some(rgba(0xffbbafff).into()),
|
||||
hidden: Some(rgba(0x6a7f86ff).into()),
|
||||
hidden_background: Some(rgba(0xcfd0c4ff).into()),
|
||||
hidden_border: Some(rgba(0xb7bdb6ff).into()),
|
||||
hint: Some(rgba(0x5889a3ff).into()),
|
||||
hint_background: Some(rgba(0xdbe6f6ff).into()),
|
||||
hint_border: Some(rgba(0xbfd3efff).into()),
|
||||
ignored: Some(rgba(0x34555eff).into()),
|
||||
ignored_background: Some(rgba(0xcfd0c4ff).into()),
|
||||
ignored_border: Some(rgba(0x9faaa8ff).into()),
|
||||
info: Some(rgba(0x298bd1ff).into()),
|
||||
info_background: Some(rgba(0xdbe6f6ff).into()),
|
||||
info_border: Some(rgba(0xbfd3efff).into()),
|
||||
modified: Some(rgba(0xb58904ff).into()),
|
||||
modified_background: Some(rgba(0xf5e6d0ff).into()),
|
||||
modified_border: Some(rgba(0xebd3aaff).into()),
|
||||
predictive: Some(rgba(0x679aafff).into()),
|
||||
predictive_background: Some(rgba(0xe9ead0ff).into()),
|
||||
predictive_border: Some(rgba(0xd6d9abff).into()),
|
||||
renamed: Some(rgba(0x298bd1ff).into()),
|
||||
renamed_background: Some(rgba(0xdbe6f6ff).into()),
|
||||
renamed_border: Some(rgba(0xbfd3efff).into()),
|
||||
success: Some(rgba(0x859904ff).into()),
|
||||
success_background: Some(rgba(0xe9ead0ff).into()),
|
||||
success_border: Some(rgba(0xd6d9abff).into()),
|
||||
unreachable: Some(rgba(0x34555eff).into()),
|
||||
unreachable_background: Some(rgba(0xcfd0c4ff).into()),
|
||||
unreachable_border: Some(rgba(0x9faaa8ff).into()),
|
||||
warning: Some(rgba(0xb58904ff).into()),
|
||||
warning_background: Some(rgba(0xf5e6d0ff).into()),
|
||||
warning_border: Some(rgba(0xebd3aaff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
player: Some(PlayerColors(vec![
|
||||
PlayerColor {
|
||||
cursor: rgba(0x298bd1ff).into(),
|
||||
background: rgba(0x298bd1ff).into(),
|
||||
selection: rgba(0x298bd13d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0xd33882ff).into(),
|
||||
background: rgba(0xd33882ff).into(),
|
||||
selection: rgba(0xd338823d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0xcb4c18ff).into(),
|
||||
background: rgba(0xcb4c18ff).into(),
|
||||
selection: rgba(0xcb4c183d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0x6d71c4ff).into(),
|
||||
background: rgba(0x6d71c4ff).into(),
|
||||
selection: rgba(0x6d71c43d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0x2ca198ff).into(),
|
||||
background: rgba(0x2ca198ff).into(),
|
||||
selection: rgba(0x2ca1983d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0xdc3330ff).into(),
|
||||
background: rgba(0xdc3330ff).into(),
|
||||
selection: rgba(0xdc33303d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0xb58904ff).into(),
|
||||
background: rgba(0xb58904ff).into(),
|
||||
selection: rgba(0xb589043d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0x859904ff).into(),
|
||||
background: rgba(0x859904ff).into(),
|
||||
selection: rgba(0x8599043d).into(),
|
||||
},
|
||||
])),
|
||||
syntax: Some(UserSyntaxTheme {
|
||||
highlights: vec![
|
||||
(
|
||||
"attribute".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x298bd1ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"boolean".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x859904ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"comment".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x30525bff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"comment.doc".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x30525bff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constant".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x859904ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constructor".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x298bd1ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"embedded".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x002b36ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"emphasis".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x298bd1ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"emphasis.strong".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x298bd1ff).into()),
|
||||
font_weight: Some(UserFontWeight(700.0)),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"enum".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xcb4c18ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"function".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xb58904ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"hint".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5889a3ff).into()),
|
||||
font_weight: Some(UserFontWeight(700.0)),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"keyword".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x298bd1ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"label".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x298bd1ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"link_text".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xcb4c18ff).into()),
|
||||
font_style: Some(UserFontStyle::Italic),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"link_uri".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x859904ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"number".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x859904ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"operator".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xcb4c18ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"predictive".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x679aafff).into()),
|
||||
font_style: Some(UserFontStyle::Italic),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"preproc".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x002b36ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"primary".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x002b36ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"property".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x298bd1ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x05333eff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.bracket".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x05333eff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.delimiter".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x05333eff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.list_marker".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x05333eff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x05333eff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xcb4c18ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.escape".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x30525bff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.regex".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xcb4c18ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xcb4c18ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special.symbol".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xcb4c18ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"tag".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x298bd1ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"text.literal".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xcb4c18ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"title".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x002b36ff).into()),
|
||||
font_weight: Some(UserFontWeight(700.0)),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"type".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x2ca198ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"variable".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x002b36ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"variant".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x298bd1ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
],
|
||||
}),
|
||||
},
|
||||
},
|
||||
UserTheme {
|
||||
name: "Solarized Dark".into(),
|
||||
appearance: Appearance::Dark,
|
||||
@@ -903,6 +459,450 @@ pub fn solarized() -> UserThemeFamily {
|
||||
}),
|
||||
},
|
||||
},
|
||||
UserTheme {
|
||||
name: "Solarized Light".into(),
|
||||
appearance: Appearance::Light,
|
||||
styles: UserThemeStylesRefinement {
|
||||
colors: ThemeColorsRefinement {
|
||||
border: Some(rgba(0x9faaa8ff).into()),
|
||||
border_variant: Some(rgba(0xdcdacbff).into()),
|
||||
border_focused: Some(rgba(0xbfd3efff).into()),
|
||||
border_selected: Some(rgba(0xbfd3efff).into()),
|
||||
border_transparent: Some(rgba(0x00000000).into()),
|
||||
border_disabled: Some(rgba(0xb7bdb6ff).into()),
|
||||
elevated_surface_background: Some(rgba(0xf3eddaff).into()),
|
||||
surface_background: Some(rgba(0xf3eddaff).into()),
|
||||
background: Some(rgba(0xcfd0c4ff).into()),
|
||||
panel_background: Some(rgba(0xf3eddaff).into()),
|
||||
element_background: Some(rgba(0xf3eddaff).into()),
|
||||
element_hover: Some(rgba(0xdcdacbff).into()),
|
||||
element_active: Some(rgba(0xa2aca9ff).into()),
|
||||
element_selected: Some(rgba(0xa2aca9ff).into()),
|
||||
element_disabled: Some(rgba(0xf3eddaff).into()),
|
||||
drop_target_background: Some(rgba(0x34555e80).into()),
|
||||
ghost_element_background: Some(rgba(0x00000000).into()),
|
||||
ghost_element_hover: Some(rgba(0xdcdacbff).into()),
|
||||
ghost_element_active: Some(rgba(0xa2aca9ff).into()),
|
||||
ghost_element_selected: Some(rgba(0xa2aca9ff).into()),
|
||||
ghost_element_disabled: Some(rgba(0xf3eddaff).into()),
|
||||
text: Some(rgba(0x002b36ff).into()),
|
||||
text_muted: Some(rgba(0x34555eff).into()),
|
||||
text_placeholder: Some(rgba(0x6a7f86ff).into()),
|
||||
text_disabled: Some(rgba(0x6a7f86ff).into()),
|
||||
text_accent: Some(rgba(0x298bd1ff).into()),
|
||||
icon: Some(rgba(0x002b36ff).into()),
|
||||
icon_muted: Some(rgba(0x34555eff).into()),
|
||||
icon_disabled: Some(rgba(0x6a7f86ff).into()),
|
||||
icon_placeholder: Some(rgba(0x34555eff).into()),
|
||||
icon_accent: Some(rgba(0x298bd1ff).into()),
|
||||
status_bar_background: Some(rgba(0xcfd0c4ff).into()),
|
||||
title_bar_background: Some(rgba(0xcfd0c4ff).into()),
|
||||
toolbar_background: Some(rgba(0xfdf6e3ff).into()),
|
||||
tab_bar_background: Some(rgba(0xf3eddaff).into()),
|
||||
tab_inactive_background: Some(rgba(0xf3eddaff).into()),
|
||||
tab_active_background: Some(rgba(0xfdf6e3ff).into()),
|
||||
scrollbar_thumb_background: Some(rgba(0x002b364c).into()),
|
||||
scrollbar_thumb_hover_background: Some(rgba(0xdcdacbff).into()),
|
||||
scrollbar_thumb_border: Some(rgba(0xdcdacbff).into()),
|
||||
scrollbar_track_background: Some(rgba(0x00000000).into()),
|
||||
scrollbar_track_border: Some(rgba(0xf5eedbff).into()),
|
||||
editor_foreground: Some(rgba(0x002b36ff).into()),
|
||||
editor_background: Some(rgba(0xfdf6e3ff).into()),
|
||||
editor_gutter_background: Some(rgba(0xfdf6e3ff).into()),
|
||||
editor_subheader_background: Some(rgba(0xf3eddaff).into()),
|
||||
editor_active_line_background: Some(rgba(0xf3eddabf).into()),
|
||||
editor_highlighted_line_background: Some(rgba(0xf3eddaff).into()),
|
||||
editor_line_number: Some(rgba(0x002b3659).into()),
|
||||
editor_active_line_number: Some(rgba(0x002b36ff).into()),
|
||||
editor_invisible: Some(rgba(0x34555eff).into()),
|
||||
editor_wrap_guide: Some(rgba(0x002b360d).into()),
|
||||
editor_active_wrap_guide: Some(rgba(0x002b361a).into()),
|
||||
editor_document_highlight_read_background: Some(rgba(0x298bd11a).into()),
|
||||
editor_document_highlight_write_background: Some(rgba(0x6d828866).into()),
|
||||
terminal_background: Some(rgba(0xfdf6e3ff).into()),
|
||||
terminal_ansi_bright_black: Some(rgba(0x7b8e91ff).into()),
|
||||
terminal_ansi_bright_red: Some(rgba(0xfaa091ff).into()),
|
||||
terminal_ansi_bright_green: Some(rgba(0xc6cb8bff).into()),
|
||||
terminal_ansi_bright_yellow: Some(rgba(0xe1c28aff).into()),
|
||||
terminal_ansi_bright_blue: Some(rgba(0xa5c3e9ff).into()),
|
||||
terminal_ansi_bright_magenta: Some(rgba(0xf0a2bfff).into()),
|
||||
terminal_ansi_bright_cyan: Some(rgba(0x9fd0cbff).into()),
|
||||
terminal_ansi_bright_white: Some(rgba(0x002b36ff).into()),
|
||||
terminal_ansi_black: Some(rgba(0xfdf6e3ff).into()),
|
||||
terminal_ansi_red: Some(rgba(0xdc3330ff).into()),
|
||||
terminal_ansi_green: Some(rgba(0x859904ff).into()),
|
||||
terminal_ansi_yellow: Some(rgba(0xb58904ff).into()),
|
||||
terminal_ansi_blue: Some(rgba(0x298bd1ff).into()),
|
||||
terminal_ansi_magenta: Some(rgba(0xd33882ff).into()),
|
||||
terminal_ansi_cyan: Some(rgba(0x2ca198ff).into()),
|
||||
terminal_ansi_white: Some(rgba(0x002b36ff).into()),
|
||||
link_text_hover: Some(rgba(0x298bd1ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
status: StatusColorsRefinement {
|
||||
conflict: Some(rgba(0xb58904ff).into()),
|
||||
conflict_background: Some(rgba(0xf5e6d0ff).into()),
|
||||
conflict_border: Some(rgba(0xebd3aaff).into()),
|
||||
created: Some(rgba(0x859904ff).into()),
|
||||
created_background: Some(rgba(0xe9ead0ff).into()),
|
||||
created_border: Some(rgba(0xd6d9abff).into()),
|
||||
deleted: Some(rgba(0xdc3330ff).into()),
|
||||
deleted_background: Some(rgba(0xffd9d2ff).into()),
|
||||
deleted_border: Some(rgba(0xffbbafff).into()),
|
||||
error: Some(rgba(0xdc3330ff).into()),
|
||||
error_background: Some(rgba(0xffd9d2ff).into()),
|
||||
error_border: Some(rgba(0xffbbafff).into()),
|
||||
hidden: Some(rgba(0x6a7f86ff).into()),
|
||||
hidden_background: Some(rgba(0xcfd0c4ff).into()),
|
||||
hidden_border: Some(rgba(0xb7bdb6ff).into()),
|
||||
hint: Some(rgba(0x5889a3ff).into()),
|
||||
hint_background: Some(rgba(0xdbe6f6ff).into()),
|
||||
hint_border: Some(rgba(0xbfd3efff).into()),
|
||||
ignored: Some(rgba(0x34555eff).into()),
|
||||
ignored_background: Some(rgba(0xcfd0c4ff).into()),
|
||||
ignored_border: Some(rgba(0x9faaa8ff).into()),
|
||||
info: Some(rgba(0x298bd1ff).into()),
|
||||
info_background: Some(rgba(0xdbe6f6ff).into()),
|
||||
info_border: Some(rgba(0xbfd3efff).into()),
|
||||
modified: Some(rgba(0xb58904ff).into()),
|
||||
modified_background: Some(rgba(0xf5e6d0ff).into()),
|
||||
modified_border: Some(rgba(0xebd3aaff).into()),
|
||||
predictive: Some(rgba(0x679aafff).into()),
|
||||
predictive_background: Some(rgba(0xe9ead0ff).into()),
|
||||
predictive_border: Some(rgba(0xd6d9abff).into()),
|
||||
renamed: Some(rgba(0x298bd1ff).into()),
|
||||
renamed_background: Some(rgba(0xdbe6f6ff).into()),
|
||||
renamed_border: Some(rgba(0xbfd3efff).into()),
|
||||
success: Some(rgba(0x859904ff).into()),
|
||||
success_background: Some(rgba(0xe9ead0ff).into()),
|
||||
success_border: Some(rgba(0xd6d9abff).into()),
|
||||
unreachable: Some(rgba(0x34555eff).into()),
|
||||
unreachable_background: Some(rgba(0xcfd0c4ff).into()),
|
||||
unreachable_border: Some(rgba(0x9faaa8ff).into()),
|
||||
warning: Some(rgba(0xb58904ff).into()),
|
||||
warning_background: Some(rgba(0xf5e6d0ff).into()),
|
||||
warning_border: Some(rgba(0xebd3aaff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
player: Some(PlayerColors(vec![
|
||||
PlayerColor {
|
||||
cursor: rgba(0x298bd1ff).into(),
|
||||
background: rgba(0x298bd1ff).into(),
|
||||
selection: rgba(0x298bd13d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0xd33882ff).into(),
|
||||
background: rgba(0xd33882ff).into(),
|
||||
selection: rgba(0xd338823d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0xcb4c18ff).into(),
|
||||
background: rgba(0xcb4c18ff).into(),
|
||||
selection: rgba(0xcb4c183d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0x6d71c4ff).into(),
|
||||
background: rgba(0x6d71c4ff).into(),
|
||||
selection: rgba(0x6d71c43d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0x2ca198ff).into(),
|
||||
background: rgba(0x2ca198ff).into(),
|
||||
selection: rgba(0x2ca1983d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0xdc3330ff).into(),
|
||||
background: rgba(0xdc3330ff).into(),
|
||||
selection: rgba(0xdc33303d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0xb58904ff).into(),
|
||||
background: rgba(0xb58904ff).into(),
|
||||
selection: rgba(0xb589043d).into(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: rgba(0x859904ff).into(),
|
||||
background: rgba(0x859904ff).into(),
|
||||
selection: rgba(0x8599043d).into(),
|
||||
},
|
||||
])),
|
||||
syntax: Some(UserSyntaxTheme {
|
||||
highlights: vec![
|
||||
(
|
||||
"attribute".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x298bd1ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"boolean".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x859904ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"comment".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x30525bff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"comment.doc".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x30525bff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constant".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x859904ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constructor".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x298bd1ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"embedded".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x002b36ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"emphasis".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x298bd1ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"emphasis.strong".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x298bd1ff).into()),
|
||||
font_weight: Some(UserFontWeight(700.0)),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"enum".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xcb4c18ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"function".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xb58904ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"hint".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5889a3ff).into()),
|
||||
font_weight: Some(UserFontWeight(700.0)),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"keyword".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x298bd1ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"label".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x298bd1ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"link_text".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xcb4c18ff).into()),
|
||||
font_style: Some(UserFontStyle::Italic),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"link_uri".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x859904ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"number".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x859904ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"operator".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xcb4c18ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"predictive".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x679aafff).into()),
|
||||
font_style: Some(UserFontStyle::Italic),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"preproc".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x002b36ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"primary".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x002b36ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"property".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x298bd1ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x05333eff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.bracket".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x05333eff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.delimiter".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x05333eff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.list_marker".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x05333eff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x05333eff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xcb4c18ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.escape".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x30525bff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.regex".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xcb4c18ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xcb4c18ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special.symbol".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xcb4c18ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"tag".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x298bd1ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"text.literal".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xcb4c18ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"title".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x002b36ff).into()),
|
||||
font_weight: Some(UserFontWeight(700.0)),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"type".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x2ca198ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"variable".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x002b36ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"variant".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x298bd1ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
],
|
||||
}),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ clap = { version = "4.4", features = ["derive"] }
|
||||
convert_case = "0.6.0"
|
||||
gpui = { path = "../gpui" }
|
||||
indexmap = { version = "1.6.2", features = ["serde"] }
|
||||
indoc.workspace = true
|
||||
json_comments = "0.2.2"
|
||||
log.workspace = true
|
||||
palette = { version = "0.7.3", default-features = false, features = ["std"] }
|
||||
|
||||
@@ -18,6 +18,7 @@ use clap::Parser;
|
||||
use convert_case::{Case, Casing};
|
||||
use gpui::serde_json;
|
||||
use indexmap::IndexMap;
|
||||
use indoc::formatdoc;
|
||||
use json_comments::StripComments;
|
||||
use log::LevelFilter;
|
||||
use serde::Deserialize;
|
||||
@@ -28,7 +29,7 @@ use crate::theme_printer::UserThemeFamilyPrinter;
|
||||
use crate::vscode::VsCodeTheme;
|
||||
use crate::vscode::VsCodeThemeConverter;
|
||||
use crate::zed1::theme::Theme as Zed1Theme;
|
||||
use crate::zed1::Zed1ThemeConverter;
|
||||
use crate::zed1::{zed1_theme_licenses, Zed1ThemeConverter};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct FamilyMetadata {
|
||||
@@ -200,7 +201,13 @@ fn main() -> Result<()> {
|
||||
"Summercamp",
|
||||
];
|
||||
|
||||
let mut zed1_themes_by_family: HashMap<String, Vec<UserTheme>> = HashMap::from_iter(
|
||||
let zed1_licenses_by_theme: HashMap<String, zed1::Zed1ThemeLicense> = HashMap::from_iter(
|
||||
zed1_theme_licenses()
|
||||
.into_iter()
|
||||
.map(|theme_license| (theme_license.theme.clone(), theme_license)),
|
||||
);
|
||||
|
||||
let mut zed1_themes_by_family: IndexMap<String, Vec<UserTheme>> = IndexMap::from_iter(
|
||||
zed1_theme_familes
|
||||
.into_iter()
|
||||
.map(|family| (family.to_string(), Vec::new())),
|
||||
@@ -254,13 +261,44 @@ fn main() -> Result<()> {
|
||||
themes_for_family.push(theme);
|
||||
}
|
||||
|
||||
zed1_themes_by_family.sort_keys();
|
||||
|
||||
let mut licenses = Vec::new();
|
||||
|
||||
for (family, themes) in zed1_themes_by_family {
|
||||
let theme_family = UserThemeFamily {
|
||||
let mut theme_family = UserThemeFamily {
|
||||
name: family,
|
||||
author: "Zed Industries".to_string(),
|
||||
themes,
|
||||
};
|
||||
|
||||
theme_family
|
||||
.themes
|
||||
.sort_unstable_by_key(|theme| theme.name.clone());
|
||||
|
||||
for theme in &theme_family.themes {
|
||||
let license = zed1_licenses_by_theme
|
||||
.get(&theme.name)
|
||||
.ok_or_else(|| anyhow!("missing license for theme: '{}'", theme.name))?;
|
||||
|
||||
let license_header = match license.license_url.as_ref() {
|
||||
Some(license_url) => {
|
||||
format!("[{theme_name}]({license_url})", theme_name = theme.name)
|
||||
}
|
||||
None => theme.name.clone(),
|
||||
};
|
||||
|
||||
licenses.push(formatdoc!(
|
||||
"
|
||||
## {license_header}
|
||||
|
||||
{license_text}
|
||||
********************************************************************************
|
||||
",
|
||||
license_text = license.license_text
|
||||
));
|
||||
}
|
||||
|
||||
theme_families.push(theme_family);
|
||||
}
|
||||
|
||||
@@ -357,6 +395,12 @@ fn main() -> Result<()> {
|
||||
|
||||
mod_rs_file.write_all(mod_rs_contents.as_bytes())?;
|
||||
|
||||
log::info!("Writing LICENSES file...");
|
||||
|
||||
let mut licenses_file = File::create(themes_output_path.join(format!("LICENSES")))?;
|
||||
|
||||
licenses_file.write_all(licenses.join("\n").as_bytes())?;
|
||||
|
||||
log::info!("Formatting themes...");
|
||||
|
||||
let format_result = format_themes_crate()
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
mod converter;
|
||||
mod licenses;
|
||||
pub mod theme;
|
||||
|
||||
pub use converter::*;
|
||||
pub use licenses::*;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -92,6 +92,13 @@ impl Selectable for Button {
|
||||
}
|
||||
}
|
||||
|
||||
impl SelectableButton for Button {
|
||||
fn selected_style(mut self, style: ButtonStyle) -> Self {
|
||||
self.base = self.base.selected_style(style);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Disableable for Button {
|
||||
fn disabled(mut self, disabled: bool) -> Self {
|
||||
self.base = self.base.disabled(disabled);
|
||||
|
||||
@@ -12,6 +12,7 @@ pub(super) struct ButtonIcon {
|
||||
disabled: bool,
|
||||
selected: bool,
|
||||
selected_icon: Option<Icon>,
|
||||
selected_style: Option<ButtonStyle>,
|
||||
}
|
||||
|
||||
impl ButtonIcon {
|
||||
@@ -23,6 +24,7 @@ impl ButtonIcon {
|
||||
disabled: false,
|
||||
selected: false,
|
||||
selected_icon: None,
|
||||
selected_style: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +64,13 @@ impl Selectable for ButtonIcon {
|
||||
}
|
||||
}
|
||||
|
||||
impl SelectableButton for ButtonIcon {
|
||||
fn selected_style(mut self, style: ButtonStyle) -> Self {
|
||||
self.selected_style = Some(style);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for ButtonIcon {
|
||||
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
|
||||
let icon = self
|
||||
@@ -71,6 +80,8 @@ impl RenderOnce for ButtonIcon {
|
||||
|
||||
let icon_color = if self.disabled {
|
||||
Color::Disabled
|
||||
} else if self.selected_style.is_some() && self.selected {
|
||||
self.selected_style.unwrap().into()
|
||||
} else if self.selected {
|
||||
Color::Selected
|
||||
} else {
|
||||
|
||||
@@ -4,6 +4,10 @@ use smallvec::SmallVec;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
pub trait SelectableButton: Selectable {
|
||||
fn selected_style(self, style: ButtonStyle) -> Self;
|
||||
}
|
||||
|
||||
pub trait ButtonCommon: Clickable + Disableable {
|
||||
/// A unique element ID to identify the button.
|
||||
fn id(&self) -> &ElementId;
|
||||
@@ -36,17 +40,68 @@ pub enum IconPosition {
|
||||
End,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
|
||||
pub enum TintColor {
|
||||
#[default]
|
||||
Accent,
|
||||
Negative,
|
||||
Warning,
|
||||
}
|
||||
|
||||
impl TintColor {
|
||||
fn button_like_style(self, cx: &mut WindowContext) -> ButtonLikeStyles {
|
||||
match self {
|
||||
TintColor::Accent => ButtonLikeStyles {
|
||||
background: cx.theme().status().info_background,
|
||||
border_color: cx.theme().status().info_border,
|
||||
label_color: cx.theme().colors().text,
|
||||
icon_color: cx.theme().colors().text,
|
||||
},
|
||||
TintColor::Negative => ButtonLikeStyles {
|
||||
background: cx.theme().status().error_background,
|
||||
border_color: cx.theme().status().error_border,
|
||||
label_color: cx.theme().colors().text,
|
||||
icon_color: cx.theme().colors().text,
|
||||
},
|
||||
TintColor::Warning => ButtonLikeStyles {
|
||||
background: cx.theme().status().warning_background,
|
||||
border_color: cx.theme().status().warning_border,
|
||||
label_color: cx.theme().colors().text,
|
||||
icon_color: cx.theme().colors().text,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TintColor> for Color {
|
||||
fn from(tint: TintColor) -> Self {
|
||||
match tint {
|
||||
TintColor::Accent => Color::Accent,
|
||||
TintColor::Negative => Color::Error,
|
||||
TintColor::Warning => Color::Warning,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Used to go from ButtonStyle -> Color through tint colors.
|
||||
impl From<ButtonStyle> for Color {
|
||||
fn from(style: ButtonStyle) -> Self {
|
||||
match style {
|
||||
ButtonStyle::Tinted(tint) => tint.into(),
|
||||
_ => Color::Default,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
|
||||
pub enum ButtonStyle {
|
||||
/// A filled button with a solid background color. Provides emphasis versus
|
||||
/// the more common subtle button.
|
||||
Filled,
|
||||
|
||||
/// 🚧 Under construction 🚧
|
||||
///
|
||||
/// Used to emphasize a button in some way, like a selected state, or a semantic
|
||||
/// coloring like an error or success button.
|
||||
Tinted,
|
||||
Tinted(TintColor),
|
||||
|
||||
/// The default button style, used for most buttons. Has a transparent background,
|
||||
/// but has a background color to indicate states like hover and active.
|
||||
@@ -86,12 +141,7 @@ impl ButtonStyle {
|
||||
label_color: Color::Default.color(cx),
|
||||
icon_color: Color::Default.color(cx),
|
||||
},
|
||||
ButtonStyle::Tinted => ButtonLikeStyles {
|
||||
background: gpui::red(),
|
||||
border_color: gpui::red(),
|
||||
label_color: gpui::red(),
|
||||
icon_color: gpui::red(),
|
||||
},
|
||||
ButtonStyle::Tinted(tint) => tint.button_like_style(cx),
|
||||
ButtonStyle::Subtle => ButtonLikeStyles {
|
||||
background: cx.theme().colors().ghost_element_background,
|
||||
border_color: transparent_black(),
|
||||
@@ -115,12 +165,7 @@ impl ButtonStyle {
|
||||
label_color: Color::Default.color(cx),
|
||||
icon_color: Color::Default.color(cx),
|
||||
},
|
||||
ButtonStyle::Tinted => ButtonLikeStyles {
|
||||
background: gpui::red(),
|
||||
border_color: gpui::red(),
|
||||
label_color: gpui::red(),
|
||||
icon_color: gpui::red(),
|
||||
},
|
||||
ButtonStyle::Tinted(tint) => tint.button_like_style(cx),
|
||||
ButtonStyle::Subtle => ButtonLikeStyles {
|
||||
background: cx.theme().colors().ghost_element_hover,
|
||||
border_color: transparent_black(),
|
||||
@@ -146,12 +191,7 @@ impl ButtonStyle {
|
||||
label_color: Color::Default.color(cx),
|
||||
icon_color: Color::Default.color(cx),
|
||||
},
|
||||
ButtonStyle::Tinted => ButtonLikeStyles {
|
||||
background: gpui::red(),
|
||||
border_color: gpui::red(),
|
||||
label_color: gpui::red(),
|
||||
icon_color: gpui::red(),
|
||||
},
|
||||
ButtonStyle::Tinted(tint) => tint.button_like_style(cx),
|
||||
ButtonStyle::Subtle => ButtonLikeStyles {
|
||||
background: cx.theme().colors().ghost_element_active,
|
||||
border_color: transparent_black(),
|
||||
@@ -178,12 +218,7 @@ impl ButtonStyle {
|
||||
label_color: Color::Default.color(cx),
|
||||
icon_color: Color::Default.color(cx),
|
||||
},
|
||||
ButtonStyle::Tinted => ButtonLikeStyles {
|
||||
background: gpui::red(),
|
||||
border_color: gpui::red(),
|
||||
label_color: gpui::red(),
|
||||
icon_color: gpui::red(),
|
||||
},
|
||||
ButtonStyle::Tinted(tint) => tint.button_like_style(cx),
|
||||
ButtonStyle::Subtle => ButtonLikeStyles {
|
||||
background: cx.theme().colors().ghost_element_background,
|
||||
border_color: cx.theme().colors().border_focused,
|
||||
@@ -208,12 +243,7 @@ impl ButtonStyle {
|
||||
label_color: Color::Disabled.color(cx),
|
||||
icon_color: Color::Disabled.color(cx),
|
||||
},
|
||||
ButtonStyle::Tinted => ButtonLikeStyles {
|
||||
background: gpui::red(),
|
||||
border_color: gpui::red(),
|
||||
label_color: gpui::red(),
|
||||
icon_color: gpui::red(),
|
||||
},
|
||||
ButtonStyle::Tinted(tint) => tint.button_like_style(cx),
|
||||
ButtonStyle::Subtle => ButtonLikeStyles {
|
||||
background: cx.theme().colors().ghost_element_disabled,
|
||||
border_color: cx.theme().colors().border_disabled,
|
||||
@@ -264,6 +294,7 @@ pub struct ButtonLike {
|
||||
pub(super) style: ButtonStyle,
|
||||
pub(super) disabled: bool,
|
||||
pub(super) selected: bool,
|
||||
pub(super) selected_style: Option<ButtonStyle>,
|
||||
pub(super) width: Option<DefiniteLength>,
|
||||
size: ButtonSize,
|
||||
rounding: Option<ButtonLikeRounding>,
|
||||
@@ -280,6 +311,7 @@ impl ButtonLike {
|
||||
style: ButtonStyle::default(),
|
||||
disabled: false,
|
||||
selected: false,
|
||||
selected_style: None,
|
||||
width: None,
|
||||
size: ButtonSize::Default,
|
||||
rounding: Some(ButtonLikeRounding::All),
|
||||
@@ -309,6 +341,13 @@ impl Selectable for ButtonLike {
|
||||
}
|
||||
}
|
||||
|
||||
impl SelectableButton for ButtonLike {
|
||||
fn selected_style(mut self, style: ButtonStyle) -> Self {
|
||||
self.selected_style = Some(style);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Clickable for ButtonLike {
|
||||
fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
|
||||
self.on_click = Some(Box::new(handler));
|
||||
@@ -364,6 +403,11 @@ impl ParentElement for ButtonLike {
|
||||
|
||||
impl RenderOnce for ButtonLike {
|
||||
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
||||
let style = self
|
||||
.selected_style
|
||||
.filter(|_| self.selected)
|
||||
.unwrap_or(self.style);
|
||||
|
||||
self.base
|
||||
.h_flex()
|
||||
.id(self.id.clone())
|
||||
@@ -382,12 +426,12 @@ impl RenderOnce for ButtonLike {
|
||||
ButtonSize::Default | ButtonSize::Compact => this.px_1(),
|
||||
ButtonSize::None => this,
|
||||
})
|
||||
.bg(self.style.enabled(cx).background)
|
||||
.bg(style.enabled(cx).background)
|
||||
.when(self.disabled, |this| this.cursor_not_allowed())
|
||||
.when(!self.disabled, |this| {
|
||||
this.cursor_pointer()
|
||||
.hover(|hover| hover.bg(self.style.hovered(cx).background))
|
||||
.active(|active| active.bg(self.style.active(cx).background))
|
||||
.hover(|hover| hover.bg(style.hovered(cx).background))
|
||||
.active(|active| active.bg(style.active(cx).background))
|
||||
})
|
||||
.when_some(
|
||||
self.on_click.filter(|_| !self.disabled),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use gpui::{AnyView, DefiniteLength};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{prelude::*, SelectableButton};
|
||||
use crate::{ButtonCommon, ButtonLike, ButtonSize, ButtonStyle, Icon, IconSize};
|
||||
|
||||
use super::button_icon::ButtonIcon;
|
||||
@@ -55,6 +55,13 @@ impl Selectable for IconButton {
|
||||
}
|
||||
}
|
||||
|
||||
impl SelectableButton for IconButton {
|
||||
fn selected_style(mut self, style: ButtonStyle) -> Self {
|
||||
self.base = self.base.selected_style(style);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Clickable for IconButton {
|
||||
fn on_click(
|
||||
mut self,
|
||||
@@ -109,12 +116,14 @@ impl RenderOnce for IconButton {
|
||||
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
|
||||
let is_disabled = self.base.disabled;
|
||||
let is_selected = self.base.selected;
|
||||
let selected_style = self.base.selected_style;
|
||||
|
||||
self.base.child(
|
||||
ButtonIcon::new(self.icon)
|
||||
.disabled(is_disabled)
|
||||
.selected(is_selected)
|
||||
.selected_icon(self.selected_icon)
|
||||
.when_some(selected_style, |this, style| this.selected_style(style))
|
||||
.size(self.icon_size)
|
||||
.color(self.icon_color),
|
||||
)
|
||||
|
||||
@@ -63,6 +63,13 @@ impl Selectable for ToggleButton {
|
||||
}
|
||||
}
|
||||
|
||||
impl SelectableButton for ToggleButton {
|
||||
fn selected_style(mut self, style: ButtonStyle) -> Self {
|
||||
self.base.selected_style = Some(style);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Disableable for ToggleButton {
|
||||
fn disabled(mut self, disabled: bool) -> Self {
|
||||
self.base = self.base.disabled(disabled);
|
||||
|
||||
@@ -126,13 +126,14 @@ impl RenderOnce for Tab {
|
||||
if self.selected {
|
||||
this.border_l().border_r().pb_px()
|
||||
} else {
|
||||
this.pr_px().pl_px().border_b()
|
||||
this.pr_px().pl_px().border_b().border_r()
|
||||
}
|
||||
}
|
||||
TabPosition::Middle(Ordering::Equal) => this.border_l().border_r().pb_px(),
|
||||
TabPosition::Middle(Ordering::Less) => this.border_l().pr_px().border_b(),
|
||||
TabPosition::Middle(Ordering::Greater) => this.border_r().pl_px().border_b(),
|
||||
})
|
||||
.cursor_pointer()
|
||||
.child(
|
||||
h_stack()
|
||||
.group("")
|
||||
|
||||
@@ -12,7 +12,7 @@ pub use crate::selectable::*;
|
||||
pub use crate::styles::{vh, vw};
|
||||
pub use crate::visible_on_hover::*;
|
||||
pub use crate::{h_stack, v_stack};
|
||||
pub use crate::{Button, ButtonSize, ButtonStyle, IconButton};
|
||||
pub use crate::{Button, ButtonSize, ButtonStyle, IconButton, SelectableButton};
|
||||
pub use crate::{ButtonCommon, Color, StyledExt};
|
||||
pub use crate::{Icon, IconElement, IconPosition, IconSize};
|
||||
pub use crate::{Label, LabelCommon, LabelSize, LineHeightStyle};
|
||||
|
||||
+149
-143
@@ -59,153 +59,159 @@ pub struct WelcomePage {
|
||||
|
||||
impl Render for WelcomePage {
|
||||
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement {
|
||||
h_stack().full().track_focus(&self.focus_handle).child(
|
||||
v_stack()
|
||||
.w_96()
|
||||
.gap_4()
|
||||
.mx_auto()
|
||||
.child(
|
||||
svg()
|
||||
.path("icons/logo_96.svg")
|
||||
.text_color(gpui::white())
|
||||
.w(px(96.))
|
||||
.h(px(96.))
|
||||
.mx_auto(),
|
||||
)
|
||||
.child(
|
||||
h_stack()
|
||||
.justify_center()
|
||||
.child(Label::new("Code at the speed of thought")),
|
||||
)
|
||||
.child(
|
||||
v_stack()
|
||||
.gap_2()
|
||||
.child(
|
||||
Button::new("choose-theme", "Choose a theme")
|
||||
.full_width()
|
||||
.on_click(cx.listener(|this, _, cx| {
|
||||
this.workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
theme_selector::toggle(
|
||||
workspace,
|
||||
&Default::default(),
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.ok();
|
||||
})),
|
||||
)
|
||||
.child(
|
||||
Button::new("choose-keymap", "Choose a keymap")
|
||||
.full_width()
|
||||
.on_click(cx.listener(|this, _, cx| {
|
||||
this.workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
base_keymap_picker::toggle(
|
||||
workspace,
|
||||
&Default::default(),
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.ok();
|
||||
})),
|
||||
)
|
||||
.child(
|
||||
Button::new("install-cli", "Install the CLI")
|
||||
.full_width()
|
||||
.on_click(cx.listener(|_, _, cx| {
|
||||
cx.app_mut()
|
||||
.spawn(
|
||||
|cx| async move { install_cli::install_cli(&cx).await },
|
||||
h_stack()
|
||||
.full()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.track_focus(&self.focus_handle)
|
||||
.child(
|
||||
v_stack()
|
||||
.w_96()
|
||||
.gap_4()
|
||||
.mx_auto()
|
||||
.child(
|
||||
svg()
|
||||
.path("icons/logo_96.svg")
|
||||
.text_color(gpui::white())
|
||||
.w(px(96.))
|
||||
.h(px(96.))
|
||||
.mx_auto(),
|
||||
)
|
||||
.child(
|
||||
h_stack()
|
||||
.justify_center()
|
||||
.child(Label::new("Code at the speed of thought")),
|
||||
)
|
||||
.child(
|
||||
v_stack()
|
||||
.gap_2()
|
||||
.child(
|
||||
Button::new("choose-theme", "Choose a theme")
|
||||
.full_width()
|
||||
.on_click(cx.listener(|this, _, cx| {
|
||||
this.workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
theme_selector::toggle(
|
||||
workspace,
|
||||
&Default::default(),
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.ok();
|
||||
})),
|
||||
)
|
||||
.child(
|
||||
Button::new("choose-keymap", "Choose a keymap")
|
||||
.full_width()
|
||||
.on_click(cx.listener(|this, _, cx| {
|
||||
this.workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
base_keymap_picker::toggle(
|
||||
workspace,
|
||||
&Default::default(),
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.ok();
|
||||
})),
|
||||
)
|
||||
.child(
|
||||
Button::new("install-cli", "Install the CLI")
|
||||
.full_width()
|
||||
.on_click(cx.listener(|_, _, cx| {
|
||||
cx.app_mut()
|
||||
.spawn(|cx| async move {
|
||||
install_cli::install_cli(&cx).await
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
})),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
v_stack()
|
||||
.p_3()
|
||||
.gap_2()
|
||||
.bg(cx.theme().colors().elevated_surface_background)
|
||||
.border_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.rounded_md()
|
||||
.child(
|
||||
h_stack()
|
||||
.gap_2()
|
||||
.child(
|
||||
Checkbox::new(
|
||||
"enable-vim",
|
||||
if VimModeSetting::get_global(cx).0 {
|
||||
ui::Selection::Selected
|
||||
} else {
|
||||
ui::Selection::Unselected
|
||||
},
|
||||
)
|
||||
.detach_and_log_err(cx);
|
||||
})),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
v_stack()
|
||||
.p_3()
|
||||
.gap_2()
|
||||
.bg(cx.theme().colors().elevated_surface_background)
|
||||
.border_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.rounded_md()
|
||||
.child(
|
||||
h_stack()
|
||||
.gap_2()
|
||||
.child(
|
||||
Checkbox::new(
|
||||
"enable-vim",
|
||||
if VimModeSetting::get_global(cx).0 {
|
||||
ui::Selection::Selected
|
||||
} else {
|
||||
ui::Selection::Unselected
|
||||
},
|
||||
.on_click(
|
||||
cx.listener(move |this, selection, cx| {
|
||||
this.update_settings::<VimModeSetting>(
|
||||
selection,
|
||||
cx,
|
||||
|setting, value| *setting = Some(value),
|
||||
);
|
||||
}),
|
||||
),
|
||||
)
|
||||
.on_click(cx.listener(
|
||||
move |this, selection, cx| {
|
||||
this.update_settings::<VimModeSetting>(
|
||||
selection,
|
||||
cx,
|
||||
|setting, value| *setting = Some(value),
|
||||
);
|
||||
},
|
||||
)),
|
||||
)
|
||||
.child(Label::new("Enable vim mode")),
|
||||
)
|
||||
.child(
|
||||
h_stack()
|
||||
.gap_2()
|
||||
.child(
|
||||
Checkbox::new(
|
||||
"enable-telemetry",
|
||||
if TelemetrySettings::get_global(cx).metrics {
|
||||
ui::Selection::Selected
|
||||
} else {
|
||||
ui::Selection::Unselected
|
||||
},
|
||||
.child(Label::new("Enable vim mode")),
|
||||
)
|
||||
.child(
|
||||
h_stack()
|
||||
.gap_2()
|
||||
.child(
|
||||
Checkbox::new(
|
||||
"enable-telemetry",
|
||||
if TelemetrySettings::get_global(cx).metrics {
|
||||
ui::Selection::Selected
|
||||
} else {
|
||||
ui::Selection::Unselected
|
||||
},
|
||||
)
|
||||
.on_click(
|
||||
cx.listener(move |this, selection, cx| {
|
||||
this.update_settings::<TelemetrySettings>(
|
||||
selection,
|
||||
cx,
|
||||
|settings, value| {
|
||||
settings.metrics = Some(value)
|
||||
},
|
||||
);
|
||||
}),
|
||||
),
|
||||
)
|
||||
.on_click(cx.listener(
|
||||
move |this, selection, cx| {
|
||||
this.update_settings::<TelemetrySettings>(
|
||||
selection,
|
||||
cx,
|
||||
|settings, value| settings.metrics = Some(value),
|
||||
);
|
||||
},
|
||||
)),
|
||||
)
|
||||
.child(Label::new("Send anonymous usage data")),
|
||||
)
|
||||
.child(
|
||||
h_stack()
|
||||
.gap_2()
|
||||
.child(
|
||||
Checkbox::new(
|
||||
"enable-crash",
|
||||
if TelemetrySettings::get_global(cx).diagnostics {
|
||||
ui::Selection::Selected
|
||||
} else {
|
||||
ui::Selection::Unselected
|
||||
},
|
||||
.child(Label::new("Send anonymous usage data")),
|
||||
)
|
||||
.child(
|
||||
h_stack()
|
||||
.gap_2()
|
||||
.child(
|
||||
Checkbox::new(
|
||||
"enable-crash",
|
||||
if TelemetrySettings::get_global(cx).diagnostics {
|
||||
ui::Selection::Selected
|
||||
} else {
|
||||
ui::Selection::Unselected
|
||||
},
|
||||
)
|
||||
.on_click(
|
||||
cx.listener(move |this, selection, cx| {
|
||||
this.update_settings::<TelemetrySettings>(
|
||||
selection,
|
||||
cx,
|
||||
|settings, value| {
|
||||
settings.diagnostics = Some(value)
|
||||
},
|
||||
);
|
||||
}),
|
||||
),
|
||||
)
|
||||
.on_click(cx.listener(
|
||||
move |this, selection, cx| {
|
||||
this.update_settings::<TelemetrySettings>(
|
||||
selection,
|
||||
cx,
|
||||
|settings, value| {
|
||||
settings.diagnostics = Some(value)
|
||||
},
|
||||
);
|
||||
},
|
||||
)),
|
||||
)
|
||||
.child(Label::new("Send crash reports")),
|
||||
),
|
||||
),
|
||||
)
|
||||
.child(Label::new("Send crash reports")),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ language = { path = "../language" }
|
||||
node_runtime = { path = "../node_runtime" }
|
||||
project = { path = "../project" }
|
||||
settings = { path = "../settings" }
|
||||
sqlez = { path = "../sqlez" }
|
||||
terminal = { path = "../terminal" }
|
||||
theme = { path = "../theme" }
|
||||
util = { path = "../util" }
|
||||
|
||||
@@ -651,9 +651,13 @@ impl Render for PanelButtons {
|
||||
&& panel.position_is_valid(position, cx)
|
||||
{
|
||||
let panel = panel.clone();
|
||||
menu = menu.entry(position.to_label(), None, move |cx| {
|
||||
panel.set_position(position, cx);
|
||||
})
|
||||
menu = menu.entry(
|
||||
format!("Dock {}", position.to_label()),
|
||||
None,
|
||||
move |cx| {
|
||||
panel.set_position(position, cx);
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
menu
|
||||
|
||||
@@ -246,7 +246,15 @@ impl Member {
|
||||
.size_full()
|
||||
.child(pane.clone())
|
||||
.when_some(leader_border, |this, color| {
|
||||
this.border_2().border_color(color)
|
||||
this.child(
|
||||
div()
|
||||
.absolute()
|
||||
.size_full()
|
||||
.left_0()
|
||||
.top_0()
|
||||
.border_2()
|
||||
.border_color(color),
|
||||
)
|
||||
})
|
||||
.when_some(leader_status_box, |this, status_box| {
|
||||
this.child(
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user