Merge branch 'main' into channel-guests

This commit is contained in:
Conrad Irwin 2024-01-05 10:02:22 -07:00
commit 3c0052850c
79 changed files with 3501 additions and 3265 deletions

419
Cargo.lock generated
View file

@ -268,12 +268,6 @@ dependencies = [
"num-traits",
]
[[package]]
name = "arbitrary"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
[[package]]
name = "arrayref"
version = "0.3.7"
@ -582,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"
@ -1832,105 +1827,6 @@ dependencies = [
"libc",
]
[[package]]
name = "cranelift-bforest"
version = "0.103.0"
source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
dependencies = [
"cranelift-entity",
]
[[package]]
name = "cranelift-codegen"
version = "0.103.0"
source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
dependencies = [
"bumpalo",
"cranelift-bforest",
"cranelift-codegen-meta",
"cranelift-codegen-shared",
"cranelift-control",
"cranelift-entity",
"cranelift-isle",
"gimli",
"hashbrown 0.14.0",
"log",
"regalloc2",
"smallvec",
"target-lexicon",
]
[[package]]
name = "cranelift-codegen-meta"
version = "0.103.0"
source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
dependencies = [
"cranelift-codegen-shared",
]
[[package]]
name = "cranelift-codegen-shared"
version = "0.103.0"
source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
[[package]]
name = "cranelift-control"
version = "0.103.0"
source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
dependencies = [
"arbitrary",
]
[[package]]
name = "cranelift-entity"
version = "0.103.0"
source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
dependencies = [
"serde",
"serde_derive",
]
[[package]]
name = "cranelift-frontend"
version = "0.103.0"
source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
dependencies = [
"cranelift-codegen",
"log",
"smallvec",
"target-lexicon",
]
[[package]]
name = "cranelift-isle"
version = "0.103.0"
source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
[[package]]
name = "cranelift-native"
version = "0.103.0"
source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
dependencies = [
"cranelift-codegen",
"libc",
"target-lexicon",
]
[[package]]
name = "cranelift-wasm"
version = "0.103.0"
source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
dependencies = [
"cranelift-codegen",
"cranelift-entity",
"cranelift-frontend",
"itertools 0.10.5",
"log",
"smallvec",
"wasmparser",
"wasmtime-types",
]
[[package]]
name = "crc"
version = "3.0.1"
@ -2526,12 +2422,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
[[package]]
name = "fallible-iterator"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
[[package]]
name = "fallible-streaming-iterator"
version = "0.1.9"
@ -2597,7 +2487,6 @@ dependencies = [
"postage",
"project",
"regex",
"search",
"serde",
"serde_derive",
"settings",
@ -3042,11 +2931,6 @@ name = "gimli"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
dependencies = [
"fallible-iterator 0.3.0",
"indexmap 2.0.0",
"stable_deref_trait",
]
[[package]]
name = "git"
@ -3559,7 +3443,6 @@ checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
dependencies = [
"equivalent",
"hashbrown 0.14.0",
"serde",
]
[[package]]
@ -3925,12 +3808,6 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "leb128"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
version = "0.2.148"
@ -4162,15 +4039,6 @@ dependencies = [
"url",
]
[[package]]
name = "mach"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
dependencies = [
"libc",
]
[[package]]
name = "mach2"
version = "0.4.1"
@ -4249,15 +4117,6 @@ version = "2.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c"
[[package]]
name = "memfd"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64"
dependencies = [
"rustix 0.38.21",
]
[[package]]
name = "memmap2"
version = "0.2.3"
@ -4913,9 +4772,6 @@ version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
dependencies = [
"crc32fast",
"hashbrown 0.14.0",
"indexmap 2.0.0",
"memchr",
]
@ -5770,15 +5626,6 @@ version = "2.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94"
[[package]]
name = "psm"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874"
dependencies = [
"cc",
]
[[package]]
name = "ptr_meta"
version = "0.1.4"
@ -6052,19 +5899,6 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "regalloc2"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad156d539c879b7a24a363a2016d77961786e71f48f2e2fc8302a92abd2429a6"
dependencies = [
"hashbrown 0.13.2",
"log",
"rustc-hash",
"slice-group-by",
"smallvec",
]
[[package]]
name = "regex"
version = "1.9.5"
@ -6384,7 +6218,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2"
dependencies = [
"bitflags 2.4.1",
"fallible-iterator 0.2.0",
"fallible-iterator",
"fallible-streaming-iterator",
"hashlink",
"libsqlite3-sys",
@ -7209,12 +7043,6 @@ dependencies = [
"autocfg",
]
[[package]]
name = "slice-group-by"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7"
[[package]]
name = "slotmap"
version = "1.0.6"
@ -7327,12 +7155,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be6c3f39c37a4283ee4b43d1311c828f2e1fb0541e76ea0cb1a2abd9ef2f5b3b"
[[package]]
name = "sptr"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a"
[[package]]
name = "sqlez"
version = "0.1.0"
@ -7588,12 +7410,6 @@ dependencies = [
"uuid 1.4.1",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "static_assertions"
version = "1.1.0"
@ -7865,12 +7681,6 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "target-lexicon"
version = "0.12.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a"
[[package]]
name = "tempdir"
version = "0.3.7"
@ -7952,6 +7762,7 @@ dependencies = [
"procinfo",
"project",
"rand 0.8.5",
"search",
"serde",
"serde_derive",
"settings",
@ -8524,8 +8335,6 @@ source = "git+https://github.com/tree-sitter/tree-sitter?rev=31c40449749c4263a91
dependencies = [
"cc",
"regex",
"wasmtime",
"wasmtime-c-api-impl",
]
[[package]]
@ -9290,224 +9099,6 @@ version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
[[package]]
name = "wasm-encoder"
version = "0.38.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ad2b51884de9c7f4fe2fd1043fccb8dcad4b1e29558146ee57a144d15779f3f"
dependencies = [
"leb128",
]
[[package]]
name = "wasmparser"
version = "0.118.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95ee9723b928e735d53000dec9eae7b07a60e490c85ab54abb66659fc61bfcd9"
dependencies = [
"indexmap 2.0.0",
"semver",
]
[[package]]
name = "wasmtime"
version = "16.0.0"
source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
dependencies = [
"anyhow",
"bincode",
"bumpalo",
"cfg-if 1.0.0",
"indexmap 2.0.0",
"libc",
"log",
"object",
"once_cell",
"paste",
"serde",
"serde_derive",
"serde_json",
"target-lexicon",
"wasmparser",
"wasmtime-cranelift",
"wasmtime-environ",
"wasmtime-jit",
"wasmtime-runtime",
"windows-sys 0.48.0",
]
[[package]]
name = "wasmtime-asm-macros"
version = "16.0.0"
source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "wasmtime-c-api-impl"
version = "16.0.0"
source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
dependencies = [
"anyhow",
"log",
"once_cell",
"tracing",
"wasmtime",
"wasmtime-c-api-macros",
]
[[package]]
name = "wasmtime-c-api-macros"
version = "0.0.0"
source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
dependencies = [
"proc-macro2",
"quote",
]
[[package]]
name = "wasmtime-cranelift"
version = "16.0.0"
source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
dependencies = [
"anyhow",
"cfg-if 1.0.0",
"cranelift-codegen",
"cranelift-control",
"cranelift-entity",
"cranelift-frontend",
"cranelift-native",
"cranelift-wasm",
"gimli",
"log",
"object",
"target-lexicon",
"thiserror",
"wasmparser",
"wasmtime-cranelift-shared",
"wasmtime-environ",
"wasmtime-versioned-export-macros",
]
[[package]]
name = "wasmtime-cranelift-shared"
version = "16.0.0"
source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
dependencies = [
"anyhow",
"cranelift-codegen",
"cranelift-control",
"cranelift-native",
"gimli",
"object",
"target-lexicon",
"wasmtime-environ",
]
[[package]]
name = "wasmtime-environ"
version = "16.0.0"
source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
dependencies = [
"anyhow",
"cranelift-entity",
"gimli",
"indexmap 2.0.0",
"log",
"object",
"serde",
"serde_derive",
"target-lexicon",
"thiserror",
"wasmparser",
"wasmtime-types",
]
[[package]]
name = "wasmtime-jit"
version = "16.0.0"
source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
dependencies = [
"anyhow",
"bincode",
"cfg-if 1.0.0",
"gimli",
"log",
"object",
"rustix 0.38.21",
"serde",
"serde_derive",
"target-lexicon",
"wasmtime-environ",
"wasmtime-jit-icache-coherence",
"wasmtime-runtime",
"windows-sys 0.48.0",
]
[[package]]
name = "wasmtime-jit-icache-coherence"
version = "16.0.0"
source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
dependencies = [
"cfg-if 1.0.0",
"libc",
"windows-sys 0.48.0",
]
[[package]]
name = "wasmtime-runtime"
version = "16.0.0"
source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
dependencies = [
"anyhow",
"cc",
"cfg-if 1.0.0",
"indexmap 2.0.0",
"libc",
"log",
"mach",
"memfd",
"memoffset 0.9.0",
"paste",
"psm",
"rustix 0.38.21",
"sptr",
"wasm-encoder",
"wasmtime-asm-macros",
"wasmtime-environ",
"wasmtime-versioned-export-macros",
"wasmtime-wmemcheck",
"windows-sys 0.48.0",
]
[[package]]
name = "wasmtime-types"
version = "16.0.0"
source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
dependencies = [
"cranelift-entity",
"serde",
"serde_derive",
"thiserror",
"wasmparser",
]
[[package]]
name = "wasmtime-versioned-export-macros"
version = "16.0.0"
source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.37",
]
[[package]]
name = "wasmtime-wmemcheck"
version = "16.0.0"
source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
[[package]]
name = "web-sys"
version = "0.3.64"

View file

@ -127,12 +127,11 @@ thiserror = { version = "1.0.29" }
time = { version = "0.3", features = ["serde", "serde-well-known"] }
toml = { version = "0.5" }
tiktoken-rs = "0.5.7"
tree-sitter = { version = "0.20", features = ["wasm"] }
tree-sitter = { version = "0.20" }
unindent = { version = "0.1.7" }
pretty_assertions = "1.3.0"
git2 = { version = "0.15", default-features = false}
uuid = { version = "1.1.2", features = ["v4"] }
wasmtime = "16"
tree-sitter-bash = { git = "https://github.com/tree-sitter/tree-sitter-bash", rev = "7331995b19b8f8aba2d5e26deb51d2195c18bc94" }
tree-sitter-c = "0.20.1"
@ -165,8 +164,7 @@ tree-sitter-uiua = {git = "https://github.com/shnarazk/tree-sitter-uiua", rev =
[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" }
# 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
cocoa = { git = "https://github.com/servo/core-foundation-rs", rev = "079665882507dd5e2ff77db3de5070c1f6c0fb85" }

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-refresh-cw"><path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"/><path d="M8 16H3v5"/></svg>

After

Width:  |  Height:  |  Size: 389 B

View file

@ -402,7 +402,7 @@
"cmd-r": "workspace::ToggleRightDock",
"cmd-j": "workspace::ToggleBottomDock",
"alt-cmd-y": "workspace::CloseAllDocks",
"cmd-shift-f": "workspace::NewSearch",
"cmd-shift-f": "workspace::DeploySearch",
"cmd-k cmd-t": "theme_selector::Toggle",
"cmd-k cmd-s": "zed::OpenKeymap",
"cmd-t": "project_symbols::Toggle",

View file

@ -16,7 +16,7 @@ use ai::{
use ai::prompts::repository_context::PromptCodeSnippet;
use anyhow::{anyhow, Result};
use chrono::{DateTime, Local};
use client::{telemetry::AssistantKind, TelemetrySettings};
use client::telemetry::AssistantKind;
use collections::{hash_map, HashMap, HashSet, VecDeque};
use editor::{
display_map::{
@ -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::{
@ -1125,8 +1125,6 @@ impl Render for AssistantPanel {
.child(Label::new(
"Click on the Z button in the status bar to close this panel."
))
.border()
.border_color(gpui::red())
} else {
let header = TabBar::new("assistant_header")
.start_child(
@ -1158,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);
@ -1177,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()
@ -1277,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> {
@ -3529,12 +3538,5 @@ fn report_assistant_event(
.default_open_ai_model
.clone();
let telemetry_settings = TelemetrySettings::get_global(cx).clone();
telemetry.report_assistant_event(
telemetry_settings,
conversation_id,
assistant_kind,
model.full_name(),
)
telemetry.report_assistant_event(conversation_id, assistant_kind, model.full_name(), cx)
}

View file

@ -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 {

View file

@ -5,7 +5,7 @@ pub mod room;
use anyhow::{anyhow, Result};
use audio::Audio;
use call_settings::CallSettings;
use client::{proto, Client, TelemetrySettings, TypedEnvelope, User, UserStore, ZED_ALWAYS_ACTIVE};
use client::{proto, Client, TypedEnvelope, User, UserStore, ZED_ALWAYS_ACTIVE};
use collections::HashSet;
use futures::{channel::oneshot, future::Shared, Future, FutureExt};
use gpui::{
@ -480,9 +480,8 @@ pub fn report_call_event_for_room(
cx: &mut AppContext,
) {
let telemetry = client.telemetry();
let telemetry_settings = *TelemetrySettings::get_global(cx);
telemetry.report_call_event(telemetry_settings, operation, Some(room_id), channel_id)
telemetry.report_call_event(operation, Some(room_id), channel_id, cx)
}
pub fn report_call_event_for_channel(
@ -495,13 +494,11 @@ pub fn report_call_event_for_channel(
let telemetry = client.telemetry();
let telemetry_settings = *TelemetrySettings::get_global(cx);
telemetry.report_call_event(
telemetry_settings,
operation,
room.map(|r| r.read(cx).id()),
Some(channel_id),
cx,
)
}

View file

@ -177,8 +177,7 @@ impl Telemetry {
// TestAppContext ends up calling this function on shutdown and it panics when trying to find the TelemetrySettings
#[cfg(not(any(test, feature = "test-support")))]
fn shutdown_telemetry(self: &Arc<Self>, cx: &mut AppContext) -> impl Future<Output = ()> {
let telemetry_settings = TelemetrySettings::get_global(cx).clone();
self.report_app_event(telemetry_settings, "close", true);
self.report_app_event("close", true, cx);
Task::ready(())
}
@ -227,24 +226,11 @@ impl Telemetry {
return;
};
let telemetry_settings = if let Ok(telemetry_settings) =
cx.update(|cx| *TelemetrySettings::get_global(cx))
{
telemetry_settings
} else {
break;
};
this.report_memory_event(
telemetry_settings,
process.memory(),
process.virtual_memory(),
);
this.report_cpu_event(
telemetry_settings,
process.cpu_usage(),
system.cpus().len() as u32,
);
cx.update(|cx| {
this.report_memory_event(process.memory(), process.virtual_memory(), cx);
this.report_cpu_event(process.cpu_usage(), system.cpus().len() as u32, cx);
})
.ok();
}
})
.detach();
@ -269,12 +255,12 @@ impl Telemetry {
pub fn report_editor_event(
self: &Arc<Self>,
telemetry_settings: TelemetrySettings,
file_extension: Option<String>,
vim_mode: bool,
operation: &'static str,
copilot_enabled: bool,
copilot_enabled_for_language: bool,
cx: &AppContext,
) {
let event = ClickhouseEvent::Editor {
file_extension,
@ -285,15 +271,15 @@ impl Telemetry {
milliseconds_since_first_event: self.milliseconds_since_first_event(),
};
self.report_clickhouse_event(event, telemetry_settings, false)
self.report_clickhouse_event(event, false, cx)
}
pub fn report_copilot_event(
self: &Arc<Self>,
telemetry_settings: TelemetrySettings,
suggestion_id: Option<String>,
suggestion_accepted: bool,
file_extension: Option<String>,
cx: &AppContext,
) {
let event = ClickhouseEvent::Copilot {
suggestion_id,
@ -302,15 +288,15 @@ impl Telemetry {
milliseconds_since_first_event: self.milliseconds_since_first_event(),
};
self.report_clickhouse_event(event, telemetry_settings, false)
self.report_clickhouse_event(event, false, cx)
}
pub fn report_assistant_event(
self: &Arc<Self>,
telemetry_settings: TelemetrySettings,
conversation_id: Option<String>,
kind: AssistantKind,
model: &'static str,
cx: &AppContext,
) {
let event = ClickhouseEvent::Assistant {
conversation_id,
@ -319,15 +305,15 @@ impl Telemetry {
milliseconds_since_first_event: self.milliseconds_since_first_event(),
};
self.report_clickhouse_event(event, telemetry_settings, false)
self.report_clickhouse_event(event, false, cx)
}
pub fn report_call_event(
self: &Arc<Self>,
telemetry_settings: TelemetrySettings,
operation: &'static str,
room_id: Option<u64>,
channel_id: Option<u64>,
cx: &AppContext,
) {
let event = ClickhouseEvent::Call {
operation,
@ -336,14 +322,14 @@ impl Telemetry {
milliseconds_since_first_event: self.milliseconds_since_first_event(),
};
self.report_clickhouse_event(event, telemetry_settings, false)
self.report_clickhouse_event(event, false, cx)
}
pub fn report_cpu_event(
self: &Arc<Self>,
telemetry_settings: TelemetrySettings,
usage_as_percentage: f32,
core_count: u32,
cx: &AppContext,
) {
let event = ClickhouseEvent::Cpu {
usage_as_percentage,
@ -351,14 +337,14 @@ impl Telemetry {
milliseconds_since_first_event: self.milliseconds_since_first_event(),
};
self.report_clickhouse_event(event, telemetry_settings, false)
self.report_clickhouse_event(event, false, cx)
}
pub fn report_memory_event(
self: &Arc<Self>,
telemetry_settings: TelemetrySettings,
memory_in_bytes: u64,
virtual_memory_in_bytes: u64,
cx: &AppContext,
) {
let event = ClickhouseEvent::Memory {
memory_in_bytes,
@ -366,28 +352,28 @@ impl Telemetry {
milliseconds_since_first_event: self.milliseconds_since_first_event(),
};
self.report_clickhouse_event(event, telemetry_settings, false)
self.report_clickhouse_event(event, false, cx)
}
pub fn report_app_event(
self: &Arc<Self>,
telemetry_settings: TelemetrySettings,
operation: &'static str,
immediate_flush: bool,
cx: &AppContext,
) {
let event = ClickhouseEvent::App {
operation,
milliseconds_since_first_event: self.milliseconds_since_first_event(),
};
self.report_clickhouse_event(event, telemetry_settings, immediate_flush)
self.report_clickhouse_event(event, immediate_flush, cx)
}
pub fn report_setting_event(
self: &Arc<Self>,
telemetry_settings: TelemetrySettings,
setting: &'static str,
value: String,
cx: &AppContext,
) {
let event = ClickhouseEvent::Setting {
setting,
@ -395,7 +381,7 @@ impl Telemetry {
milliseconds_since_first_event: self.milliseconds_since_first_event(),
};
self.report_clickhouse_event(event, telemetry_settings, false)
self.report_clickhouse_event(event, false, cx)
}
fn milliseconds_since_first_event(&self) -> i64 {
@ -415,10 +401,10 @@ impl Telemetry {
fn report_clickhouse_event(
self: &Arc<Self>,
event: ClickhouseEvent,
telemetry_settings: TelemetrySettings,
immediate_flush: bool,
cx: &AppContext,
) {
if !telemetry_settings.metrics {
if !TelemetrySettings::get_global(cx).metrics {
return;
}

File diff suppressed because it is too large Load diff

View file

@ -607,8 +607,12 @@ impl Panel for ChatPanel {
"ChatPanel"
}
fn icon(&self, _cx: &WindowContext) -> Option<ui::Icon> {
Some(ui::Icon::MessageBubbles)
fn icon(&self, cx: &WindowContext) -> Option<ui::Icon> {
if !is_channels_feature_enabled(cx) {
return None;
}
Some(ui::Icon::MessageBubbles).filter(|_| ChatPanelSettings::get_global(cx).button)
}
fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> {

View file

@ -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::{
@ -1829,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,

View file

@ -164,8 +164,14 @@ impl Render for ChannelModal {
.py_1()
.rounded_t(px(8.))
.bg(cx.theme().colors().element_background)
.child(IconElement::new(Icon::Hash).size(IconSize::Medium))
.child(Label::new(channel_name))
.child(
h_stack()
.w_px()
.flex_1()
.gap_1()
.child(IconElement::new(Icon::Hash).size(IconSize::Medium))
.child(Label::new(channel_name)),
)
.child(
h_stack()
.w_full()

View file

@ -15,7 +15,7 @@ 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};
@ -111,6 +111,7 @@ impl Render for CollabTitlebarItem {
&room,
project_id,
&current_user,
cx,
))
.children(
remote_participants.iter().filter_map(|collaborator| {
@ -128,6 +129,7 @@ impl Render for CollabTitlebarItem {
&room,
project_id,
&current_user,
cx,
)?;
Some(
@ -183,6 +185,8 @@ impl Render for CollabTitlebarItem {
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| {
@ -218,6 +222,7 @@ impl Render for CollabTitlebarItem {
.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)),
)
})
@ -231,6 +236,7 @@ impl Render for CollabTitlebarItem {
},
)
.style(ButtonStyle::Subtle)
.selected_style(ButtonStyle::Tinted(TintColor::Negative))
.icon_size(IconSize::Small)
.selected(is_deafened)
.tooltip(move |cx| {
@ -253,6 +259,7 @@ impl Render for CollabTitlebarItem {
.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)
}),
@ -420,6 +427,7 @@ impl CollabTitlebarItem {
room: &Room,
project_id: Option<u64>,
current_user: &Arc<User>,
cx: &ViewContext<Self>,
) -> Option<FacePile> {
if room.role_for_user(user.id) == Some(proto::ChannelRole::Guest) {
return None;
@ -432,9 +440,9 @@ impl CollabTitlebarItem {
Avatar::new(user.avatar_uri.clone())
.grayscale(!is_present)
.border_color(if is_speaking {
gpui::blue()
cx.theme().status().info_border
} else if is_muted {
gpui::red()
cx.theme().status().error_border
} else {
Hsla::default()
}),

View file

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

View file

@ -646,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()
@ -1572,6 +1577,7 @@ mod tests {
workspace::init_settings(cx);
Project::init_settings(cx);
crate::init(cx);
editor::init(cx);
});
}

View file

@ -23,11 +23,21 @@ pub struct DiagnosticIndicator {
impl Render for DiagnosticIndicator {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let diagnostic_indicator = match (self.summary.error_count, self.summary.warning_count) {
(0, 0) => h_stack().child(
IconElement::new(Icon::Check)
.size(IconSize::Small)
.color(Color::Success),
),
(0, 0) => h_stack().map(|this| {
if !self.in_progress_checks.is_empty() {
this.child(
IconElement::new(Icon::ArrowCircle)
.size(IconSize::Small)
.color(Color::Muted),
)
} else {
this.child(
IconElement::new(Icon::Check)
.size(IconSize::Small)
.color(Color::Default),
)
}
}),
(0, warning_count) => h_stack()
.gap_1()
.child(
@ -64,6 +74,7 @@ impl Render for DiagnosticIndicator {
Some(
Label::new("Checking…")
.size(LabelSize::Small)
.color(Color::Muted)
.into_any_element(),
)
} else if let Some(diagnostic) = &self.current_diagnostic {

View file

@ -24,7 +24,7 @@ use ::git::diff::DiffHunk;
use aho_corasick::AhoCorasick;
use anyhow::{anyhow, Context as _, Result};
use blink_manager::BlinkManager;
use client::{Client, Collaborator, ParticipantIndex, TelemetrySettings};
use client::{Client, Collaborator, ParticipantIndex};
use clock::ReplicaId;
use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
use convert_case::{Case, Casing};
@ -99,9 +99,9 @@ use sum_tree::TreeMap;
use text::{OffsetUtf16, Rope};
use theme::{ActiveTheme, PlayerColor, StatusColors, SyntaxTheme, ThemeColors, ThemeSettings};
use ui::{
h_stack, ButtonSize, ButtonStyle, Icon, IconButton, ListItem, ListItemSpacing, Popover, Tooltip,
h_stack, prelude::*, ButtonSize, ButtonStyle, Icon, IconButton, IconSize, ListItem, Popover,
Tooltip,
};
use ui::{prelude::*, IconSize};
use util::{post_inc, RangeExt, ResultExt, TryFutureExt};
use workspace::{searchable::SearchEvent, ItemNavHistory, Pane, SplitDirection, ViewId, Workspace};
@ -1259,7 +1259,6 @@ impl CompletionsMenu {
div().min_w(px(220.)).max_w(px(540.)).child(
ListItem::new(mat.candidate_id)
.inset(true)
.spacing(ListItemSpacing::Sparse)
.selected(item_ix == selected_item)
.on_click(cx.listener(move |editor, _event, cx| {
cx.stop_propagation();
@ -8451,6 +8450,12 @@ impl Editor {
})
}
pub fn has_background_highlights<T: 'static>(&self) -> bool {
self.background_highlights
.get(&TypeId::of::<T>())
.map_or(false, |(_, highlights)| !highlights.is_empty())
}
pub fn background_highlights_in_range(
&self,
search_range: Range<Anchor>,
@ -8868,14 +8873,8 @@ impl Editor {
.map(|a| a.to_string());
let telemetry = project.read(cx).client().telemetry().clone();
let telemetry_settings = *TelemetrySettings::get_global(cx);
telemetry.report_copilot_event(
telemetry_settings,
suggestion_id,
suggestion_accepted,
file_extension,
)
telemetry.report_copilot_event(suggestion_id, suggestion_accepted, file_extension, cx)
}
#[cfg(any(test, feature = "test-support"))]
@ -8913,7 +8912,6 @@ impl Editor {
.raw_user_settings()
.get("vim_mode")
== Some(&serde_json::Value::Bool(true));
let telemetry_settings = *TelemetrySettings::get_global(cx);
let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
let copilot_enabled_for_language = self
.buffer
@ -8923,12 +8921,12 @@ impl Editor {
let telemetry = project.read(cx).client().telemetry().clone();
telemetry.report_editor_event(
telemetry_settings,
file_extension,
vim_mode,
operation,
copilot_enabled,
copilot_enabled_for_language,
cx,
)
}
@ -9570,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

View file

@ -8,6 +8,7 @@ use crate::{
hover_popover::{
self, hover_at, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH, MIN_POPOVER_LINE_HEIGHT,
},
items::BufferSearchHighlights,
link_go_to_definition::{
go_to_fetched_definition, go_to_fetched_type_definition, show_link_definition,
update_go_to_definition_link, update_inlay_link_and_hover_points, GoToDefinitionTrigger,
@ -1229,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;
@ -1275,7 +1284,7 @@ impl EditorElement {
let background_ranges = self
.editor
.read(cx)
.background_highlight_row_ranges::<crate::items::BufferSearchHighlights>(
.background_highlight_row_ranges::<BufferSearchHighlights>(
start_anchor..end_anchor,
&layout.position_map.snapshot,
50000,
@ -1766,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
@ -1972,7 +1981,7 @@ impl EditorElement {
(is_singleton && scrollbar_settings.git_diff && snapshot.buffer_snapshot.has_git_diffs())
||
// Selections
(is_singleton && scrollbar_settings.selections && !highlighted_ranges.is_empty())
(is_singleton && scrollbar_settings.selections && editor.has_background_highlights::<BufferSearchHighlights>())
// Scrollmanager
|| editor.scroll_manager.scrollbars_visible()
}
@ -3779,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

View file

@ -15,9 +15,11 @@ use language::{
proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, CharKind, OffsetRangeExt,
Point, SelectionGoal,
};
use project::repository::GitFileStatus;
use project::{search::SearchQuery, FormatTrigger, Item as _, Project, ProjectPath};
use rpc::proto::{self, update_view, PeerId};
use settings::Settings;
use workspace::item::ItemSettings;
use std::fmt::Write;
use std::{
@ -29,7 +31,7 @@ use std::{
sync::Arc,
};
use text::Selection;
use theme::{ActiveTheme, Theme};
use theme::Theme;
use ui::{h_stack, prelude::*, Label};
use util::{paths::PathExt, paths::FILE_ROW_COLUMN_DELIMITER, ResultExt, TryFutureExt};
use workspace::{
@ -580,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)?;
@ -596,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)

View file

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

View file

@ -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" }

View file

@ -19,7 +19,7 @@ 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"

View file

@ -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

View file

@ -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;

View file

@ -1,14 +1,13 @@
use crate::{
div, Action, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext,
BackgroundExecutor, Bounds, ClipboardItem, Context, Entity, EventEmitter, ForegroundExecutor,
InputEvent, IntoElement, KeyDownEvent, Keystroke, Model, ModelContext, Pixels, Platform,
PlatformWindow, Point, Render, Result, Size, Task, TestDispatcher, TestPlatform, TestWindow,
TestWindowHandlers, TextSystem, View, ViewContext, VisualContext, WindowBounds, WindowContext,
WindowHandle, WindowOptions,
BackgroundExecutor, ClipboardItem, Context, Entity, EventEmitter, ForegroundExecutor,
IntoElement, Keystroke, Model, ModelContext, Pixels, Platform, Render, Result, Size, Task,
TestDispatcher, TestPlatform, TestWindow, TextSystem, View, ViewContext, VisualContext,
WindowContext, WindowHandle, WindowOptions,
};
use anyhow::{anyhow, bail};
use futures::{Stream, StreamExt};
use std::{future::Future, mem, ops::Deref, rc::Rc, sync::Arc, time::Duration};
use std::{future::Future, ops::Deref, rc::Rc, sync::Arc, time::Duration};
#[derive(Clone)]
pub struct TestAppContext {
@ -185,42 +184,7 @@ impl TestAppContext {
}
pub fn simulate_window_resize(&self, window_handle: AnyWindowHandle, size: Size<Pixels>) {
let (mut handlers, scale_factor) = self
.app
.borrow_mut()
.update_window(window_handle, |_, cx| {
let platform_window = cx.window.platform_window.as_test().unwrap();
let scale_factor = platform_window.scale_factor();
match &mut platform_window.bounds {
WindowBounds::Fullscreen | WindowBounds::Maximized => {
platform_window.bounds = WindowBounds::Fixed(Bounds {
origin: Point::default(),
size: size.map(|pixels| f64::from(pixels).into()),
});
}
WindowBounds::Fixed(bounds) => {
bounds.size = size.map(|pixels| f64::from(pixels).into());
}
}
(
mem::take(&mut platform_window.handlers.lock().resize),
scale_factor,
)
})
.unwrap();
for handler in &mut handlers {
handler(size, scale_factor);
}
self.app
.borrow_mut()
.update_window(window_handle, |_, cx| {
let platform_window = cx.window.platform_window.as_test().unwrap();
platform_window.handlers.lock().resize = handlers;
})
.unwrap();
self.test_window(window_handle).simulate_resize(size);
}
pub fn 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();
}
}

View file

@ -1,7 +1,7 @@
use crate::{
point, px, AnyElement, AvailableSpace, BorrowAppContext, Bounds, DispatchPhase, Element,
IntoElement, Pixels, Point, ScrollWheelEvent, Size, Style, StyleRefinement, Styled,
WindowContext,
point, px, AnyElement, AvailableSpace, BorrowAppContext, BorrowWindow, Bounds, ContentMask,
DispatchPhase, Element, IntoElement, Pixels, Point, ScrollWheelEvent, Size, Style,
StyleRefinement, Styled, WindowContext,
};
use collections::VecDeque;
use refineable::Refineable as _;
@ -317,7 +317,7 @@ impl Element for List {
fn paint(
&mut self,
bounds: crate::Bounds<crate::Pixels>,
bounds: Bounds<crate::Pixels>,
_state: &mut Self::State,
cx: &mut crate::WindowContext,
) {
@ -444,13 +444,15 @@ impl Element for List {
new_items.append(cursor.suffix(&()), &());
// Paint the visible items
let mut item_origin = bounds.origin;
item_origin.y -= scroll_top.offset_in_item;
for item_element in &mut item_elements {
let item_height = item_element.measure(available_item_space, cx).height;
item_element.draw(item_origin, available_item_space, cx);
item_origin.y += item_height;
}
cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
let mut item_origin = bounds.origin;
item_origin.y -= scroll_top.offset_in_item;
for item_element in &mut item_elements {
let item_height = item_element.measure(available_item_space, cx).height;
item_element.draw(item_origin, available_item_space, cx);
item_origin.y += item_height;
}
});
state.items = new_items;
state.last_layout_bounds = Some(bounds);

View file

@ -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

View file

@ -11,7 +11,7 @@ use objc::{
};
use parking::{Parker, Unparker};
use parking_lot::Mutex;
use std::{ffi::c_void, sync::Arc, time::Duration};
use std::{ffi::c_void, ptr::NonNull, sync::Arc, time::Duration};
include!(concat!(env!("OUT_DIR"), "/dispatch_sys.rs"));
@ -47,7 +47,7 @@ impl PlatformDispatcher for MacDispatcher {
unsafe {
dispatch_async_f(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT.try_into().unwrap(), 0),
runnable.into_raw() as *mut c_void,
runnable.into_raw().as_ptr() as *mut c_void,
Some(trampoline),
);
}
@ -57,7 +57,7 @@ impl PlatformDispatcher for MacDispatcher {
unsafe {
dispatch_async_f(
dispatch_get_main_queue(),
runnable.into_raw() as *mut c_void,
runnable.into_raw().as_ptr() as *mut c_void,
Some(trampoline),
);
}
@ -71,7 +71,7 @@ impl PlatformDispatcher for MacDispatcher {
dispatch_after_f(
when,
queue,
runnable.into_raw() as *mut c_void,
runnable.into_raw().as_ptr() as *mut c_void,
Some(trampoline),
);
}
@ -91,6 +91,6 @@ impl PlatformDispatcher for MacDispatcher {
}
extern "C" fn trampoline(runnable: *mut c_void) {
let task = unsafe { Runnable::from_raw(runnable as *mut ()) };
let task = unsafe { Runnable::<()>::from_raw(NonNull::new_unchecked(runnable as *mut ())) };
task.run();
}

View file

@ -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;
}

View file

@ -19,7 +19,7 @@ pub struct TestPlatform {
background_executor: BackgroundExecutor,
foreground_executor: ForegroundExecutor,
pub(crate) 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")
@ -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,13 +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(

View file

@ -1,7 +1,7 @@
use crate::{
px, AnyWindowHandle, 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,26 +10,25 @@ 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,
@ -37,27 +36,96 @@ impl TestWindow {
platform: Weak<TestPlatform>,
display: Rc<dyn PlatformDisplay>,
) -> Self {
Self {
Self(Arc::new(Mutex::new(TestWindowState {
bounds: options.bounds,
display,
platform,
handle,
input_handler: None,
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(),
};
@ -77,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> {
@ -93,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(
@ -106,24 +174,29 @@ impl PlatformWindow for TestWindow {
_msg: &str,
_answers: &[&str],
) -> futures::channel::oneshot::Receiver<usize> {
self.platform.upgrade().expect("platform dropped").prompt()
}
fn activate(&self) {
*self
self.0
.lock()
.platform
.upgrade()
.expect("platform dropped")
.active_window
.lock() = Some(self.handle);
.prompt()
}
fn activate(&self) {
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) {
@ -143,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)>) {
@ -159,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>) {
@ -183,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> {

View file

@ -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),
});
}

View file

@ -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> {

View file

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

View file

@ -44,7 +44,7 @@ use std::{
};
use syntax_map::SyntaxSnapshot;
use theme::{SyntaxTheme, Theme};
use tree_sitter::{self, wasmtime, Query, WasmStore};
use tree_sitter::{self, Query};
use unicase::UniCase;
use util::{http::HttpClient, paths::PathExt};
use util::{post_inc, ResultExt, TryFutureExt as _, UnwrapFuture};
@ -85,14 +85,11 @@ impl LspBinaryStatusSender {
thread_local! {
static PARSER: RefCell<Parser> = {
let mut parser = Parser::new();
parser.set_wasm_store(WasmStore::new(WASM_ENGINE.clone()).unwrap()).unwrap();
RefCell::new(parser)
RefCell::new(Parser::new())
};
}
lazy_static! {
pub static ref WASM_ENGINE: wasmtime::Engine = wasmtime::Engine::default();
pub static ref NEXT_GRAMMAR_ID: AtomicUsize = Default::default();
pub static ref PLAIN_TEXT: Arc<Language> = Arc::new(Language::new(
LanguageConfig {
@ -116,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>,
@ -128,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;
@ -137,7 +132,6 @@ impl CachedLspAdapter {
Arc::new(CachedLspAdapter {
name,
short_name,
initialization_options,
disk_based_diagnostic_sources,
disk_based_diagnostics_progress_token,
language_ids,
@ -641,8 +635,8 @@ enum AvailableGrammar {
get_queries: fn(&str) -> LanguageQueries,
},
Wasm {
grammar_name: Arc<str>,
path: Arc<Path>,
_grammar_name: Arc<str>,
_path: Arc<Path>,
},
}
@ -742,7 +736,10 @@ impl LanguageRegistry {
state.available_languages.push(AvailableLanguage {
id: post_inc(&mut state.next_available_language_id),
config,
grammar: AvailableGrammar::Wasm { grammar_name, path },
grammar: AvailableGrammar::Wasm {
_grammar_name: grammar_name,
_path: path,
},
lsp_adapters: Vec::new(),
loaded: false,
});
@ -876,25 +873,8 @@ impl LanguageRegistry {
asset_dir,
get_queries,
} => (grammar, (get_queries)(asset_dir)),
AvailableGrammar::Wasm { grammar_name, path } => {
let mut wasm_path = path.join(grammar_name.as_ref());
wasm_path.set_extension("wasm");
let wasm_bytes = std::fs::read(&wasm_path)?;
let grammar = PARSER.with(|parser| {
let mut parser = parser.borrow_mut();
let mut store = parser.take_wasm_store().unwrap();
let grammar =
store.load_language(&grammar_name, &wasm_bytes);
parser.set_wasm_store(store).unwrap();
grammar
})?;
let mut queries = LanguageQueries::default();
if let Ok(contents) = std::fs::read_to_string(
&path.join("highlights.scm"),
) {
queries.highlights = Some(contents.into());
}
(grammar, queries)
AvailableGrammar::Wasm { .. } => {
Err(anyhow!("not supported"))?
}
};
Language::new(language.config, Some(grammar))

View file

@ -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>> {

View file

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

View file

@ -5,7 +5,7 @@ edition = "2021"
publish = false
[lib]
path = "src/notification_store2.rs"
path = "src/notification_store.rs"
doctest = false
[features]

View file

@ -2842,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({
@ -2863,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(),
@ -2984,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>,
@ -2994,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(),
@ -3024,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>,
@ -3190,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

View file

@ -981,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(
@ -1414,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) {

View file

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

View file

@ -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));

View file

@ -38,8 +38,8 @@ use std::{
use theme::ThemeSettings;
use ui::{
h_stack, prelude::*, v_stack, Button, Icon, IconButton, IconElement, Label, LabelCommon,
LabelSize, Selectable, Tooltip,
h_stack, prelude::*, v_stack, Icon, IconButton, IconElement, Label, LabelCommon, LabelSize,
Selectable, ToggleButton, Tooltip,
};
use util::{paths::PathMatcher, ResultExt as _};
use workspace::{
@ -61,12 +61,12 @@ struct ActiveSearches(HashMap<WeakModel<Project>, WeakView<ProjectSearchView>>);
struct ActiveSettings(HashMap<WeakModel<Project>, ProjectSearchSettings>);
pub fn init(cx: &mut AppContext) {
// todo!() po
cx.set_global(ActiveSearches::default());
cx.set_global(ActiveSettings::default());
cx.observe_new_views(|workspace: &mut Workspace, _cx| {
workspace
.register_action(ProjectSearchView::deploy)
.register_action(ProjectSearchView::new_search)
.register_action(ProjectSearchView::deploy_search)
.register_action(ProjectSearchBar::search_in_new);
})
.detach();
@ -288,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);
@ -365,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()
@ -374,7 +374,6 @@ impl Render for ProjectSearchView {
.child(v_stack().child(major_text).children(minor_text))
.child(h_stack().flex_1()),
)
.into_any()
}
}
}
@ -943,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| {
@ -964,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);
@ -1641,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)
@ -1665,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)
@ -1680,7 +1732,8 @@ impl Render for ProjectSearchBar {
&ActivateSemanticMode,
cx,
)
}),
})
.last(),
)
}),
)
@ -1831,6 +1884,7 @@ impl Render for ProjectSearchBar {
.child(
h_stack()
.justify_between()
.gap_2()
.child(query_column)
.child(mode_column)
.child(replace_column)
@ -2062,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());
@ -2103,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();
@ -2252,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();
@ -2538,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();

View file

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

View file

@ -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

View file

@ -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),

View file

@ -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" }

View file

@ -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| {

View file

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

View file

@ -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

View file

@ -20,7 +20,7 @@ pub fn andromeda() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x2b2f39ff).into()),
border_variant: Some(rgba(0x2b2f39ff).into()),
border_variant: Some(rgba(0x252931ff).into()),
border_focused: Some(rgba(0x183a34ff).into()),
border_selected: Some(rgba(0x183a34ff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -59,7 +59,7 @@ pub fn andromeda() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0xf7f7f84c).into()),
scrollbar_thumb_hover_background: Some(rgba(0x252931ff).into()),
scrollbar_thumb_border: Some(rgba(0x252931ff).into()),
scrollbar_track_background: Some(rgba(0x1e2025ff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0x21232aff).into()),
editor_foreground: Some(rgba(0xf7f7f8ff).into()),
editor_background: Some(rgba(0x1e2025ff).into()),

View file

@ -21,7 +21,7 @@ pub fn atelier() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x969585ff).into()),
border_variant: Some(rgba(0x969585ff).into()),
border_variant: Some(rgba(0xd1d0c6ff).into()),
border_focused: Some(rgba(0xbbddc6ff).into()),
border_selected: Some(rgba(0xbbddc6ff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -60,7 +60,7 @@ pub fn atelier() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0x22221b4c).into()),
scrollbar_thumb_hover_background: Some(rgba(0xd1d0c6ff).into()),
scrollbar_thumb_border: Some(rgba(0xd1d0c6ff).into()),
scrollbar_track_background: Some(rgba(0xf4f3ecff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0xedece5ff).into()),
editor_foreground: Some(rgba(0x302f27ff).into()),
editor_background: Some(rgba(0xf4f3ecff).into()),
@ -486,7 +486,7 @@ pub fn atelier() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x665f5cff).into()),
border_variant: Some(rgba(0x665f5cff).into()),
border_variant: Some(rgba(0x3b3431ff).into()),
border_focused: Some(rgba(0x192e5bff).into()),
border_selected: Some(rgba(0x192e5bff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -525,7 +525,7 @@ pub fn atelier() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0xf1efee4c).into()),
scrollbar_thumb_hover_background: Some(rgba(0x3b3431ff).into()),
scrollbar_thumb_border: Some(rgba(0x3b3431ff).into()),
scrollbar_track_background: Some(rgba(0x1b1918ff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0x251f1dff).into()),
editor_foreground: Some(rgba(0xe6e2e0ff).into()),
editor_background: Some(rgba(0x1b1918ff).into()),
@ -951,7 +951,7 @@ pub fn atelier() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x8b968eff).into()),
border_variant: Some(rgba(0x8b968eff).into()),
border_variant: Some(rgba(0xc8d1cbff).into()),
border_focused: Some(rgba(0xbed4d6ff).into()),
border_selected: Some(rgba(0xbed4d6ff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -990,7 +990,7 @@ pub fn atelier() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0x171c194c).into()),
scrollbar_thumb_hover_background: Some(rgba(0xc8d1cbff).into()),
scrollbar_thumb_border: Some(rgba(0xc8d1cbff).into()),
scrollbar_track_background: Some(rgba(0xecf4eeff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0xe5ede7ff).into()),
editor_foreground: Some(rgba(0x232a25ff).into()),
editor_background: Some(rgba(0xecf4eeff).into()),
@ -1416,7 +1416,7 @@ pub fn atelier() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x56505eff).into()),
border_variant: Some(rgba(0x56505eff).into()),
border_variant: Some(rgba(0x332f38ff).into()),
border_focused: Some(rgba(0x222953ff).into()),
border_selected: Some(rgba(0x222953ff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -1455,7 +1455,7 @@ pub fn atelier() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0xefecf44c).into()),
scrollbar_thumb_hover_background: Some(rgba(0x332f38ff).into()),
scrollbar_thumb_border: Some(rgba(0x332f38ff).into()),
scrollbar_track_background: Some(rgba(0x19171cff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0x201e24ff).into()),
editor_foreground: Some(rgba(0xe2dfe7ff).into()),
editor_background: Some(rgba(0x19171cff).into()),
@ -1881,7 +1881,7 @@ pub fn atelier() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x5d5c4cff).into()),
border_variant: Some(rgba(0x5d5c4cff).into()),
border_variant: Some(rgba(0x3c3b31ff).into()),
border_focused: Some(rgba(0x1c3927ff).into()),
border_selected: Some(rgba(0x1c3927ff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -1920,7 +1920,7 @@ pub fn atelier() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0xf4f3ec4c).into()),
scrollbar_thumb_hover_background: Some(rgba(0x3c3b31ff).into()),
scrollbar_thumb_border: Some(rgba(0x3c3b31ff).into()),
scrollbar_track_background: Some(rgba(0x22221bff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0x2a2922ff).into()),
editor_foreground: Some(rgba(0xe7e6dfff).into()),
editor_background: Some(rgba(0x22221bff).into()),
@ -2346,7 +2346,7 @@ pub fn atelier() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x5c6485ff).into()),
border_variant: Some(rgba(0x5c6485ff).into()),
border_variant: Some(rgba(0x363f62ff).into()),
border_focused: Some(rgba(0x203348ff).into()),
border_selected: Some(rgba(0x203348ff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -2385,7 +2385,7 @@ pub fn atelier() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0xf5f7ff4c).into()),
scrollbar_thumb_hover_background: Some(rgba(0x363f62ff).into()),
scrollbar_thumb_border: Some(rgba(0x363f62ff).into()),
scrollbar_track_background: Some(rgba(0x202746ff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0x252d4fff).into()),
editor_foreground: Some(rgba(0xdfe2f1ff).into()),
editor_background: Some(rgba(0x202746ff).into()),
@ -2811,7 +2811,7 @@ pub fn atelier() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x9a9fb6ff).into()),
border_variant: Some(rgba(0x9a9fb6ff).into()),
border_variant: Some(rgba(0xccd0e1ff).into()),
border_focused: Some(rgba(0xc2d5efff).into()),
border_selected: Some(rgba(0xc2d5efff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -2850,7 +2850,7 @@ pub fn atelier() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0x2027464c).into()),
scrollbar_thumb_hover_background: Some(rgba(0xccd0e1ff).into()),
scrollbar_thumb_border: Some(rgba(0xccd0e1ff).into()),
scrollbar_track_background: Some(rgba(0xf5f7ffff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0xe9ebf7ff).into()),
editor_foreground: Some(rgba(0x293256ff).into()),
editor_background: Some(rgba(0xf5f7ffff).into()),
@ -3276,7 +3276,7 @@ pub fn atelier() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x6c695cff).into()),
border_variant: Some(rgba(0x6c695cff).into()),
border_variant: Some(rgba(0x3b3933ff).into()),
border_focused: Some(rgba(0x263056ff).into()),
border_selected: Some(rgba(0x263056ff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -3315,7 +3315,7 @@ pub fn atelier() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0xfefbec4c).into()),
scrollbar_thumb_hover_background: Some(rgba(0x3b3933ff).into()),
scrollbar_thumb_border: Some(rgba(0x3b3933ff).into()),
scrollbar_track_background: Some(rgba(0x20201dff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0x252521ff).into()),
editor_foreground: Some(rgba(0xe8e4cfff).into()),
editor_background: Some(rgba(0x20201dff).into()),
@ -3741,7 +3741,7 @@ pub fn atelier() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x5c6c5cff).into()),
border_variant: Some(rgba(0x5c6c5cff).into()),
border_variant: Some(rgba(0x333b33ff).into()),
border_focused: Some(rgba(0x102668ff).into()),
border_selected: Some(rgba(0x102668ff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -3780,7 +3780,7 @@ pub fn atelier() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0xf4fbf44c).into()),
scrollbar_thumb_hover_background: Some(rgba(0x333b33ff).into()),
scrollbar_thumb_border: Some(rgba(0x333b33ff).into()),
scrollbar_track_background: Some(rgba(0x131513ff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0x1d201dff).into()),
editor_foreground: Some(rgba(0xcfe8cfff).into()),
editor_background: Some(rgba(0x131513ff).into()),
@ -4206,7 +4206,7 @@ pub fn atelier() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x8f8b96ff).into()),
border_variant: Some(rgba(0x8f8b96ff).into()),
border_variant: Some(rgba(0xcbc8d1ff).into()),
border_focused: Some(rgba(0xc9c8f3ff).into()),
border_selected: Some(rgba(0xc9c8f3ff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -4245,7 +4245,7 @@ pub fn atelier() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0x19171c4c).into()),
scrollbar_thumb_hover_background: Some(rgba(0xcbc8d1ff).into()),
scrollbar_thumb_border: Some(rgba(0xcbc8d1ff).into()),
scrollbar_track_background: Some(rgba(0xefecf4ff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0xe8e5edff).into()),
editor_foreground: Some(rgba(0x26232aff).into()),
editor_background: Some(rgba(0xefecf4ff).into()),
@ -4671,7 +4671,7 @@ pub fn atelier() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x564e4eff).into()),
border_variant: Some(rgba(0x564e4eff).into()),
border_variant: Some(rgba(0x352f2fff).into()),
border_focused: Some(rgba(0x2c2b45ff).into()),
border_selected: Some(rgba(0x2c2b45ff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -4710,7 +4710,7 @@ pub fn atelier() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0xf4ecec4c).into()),
scrollbar_thumb_hover_background: Some(rgba(0x352f2fff).into()),
scrollbar_thumb_border: Some(rgba(0x352f2fff).into()),
scrollbar_track_background: Some(rgba(0x1b1818ff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0x231f1fff).into()),
editor_foreground: Some(rgba(0xe7dfdfff).into()),
editor_background: Some(rgba(0x1b1818ff).into()),
@ -5136,7 +5136,7 @@ pub fn atelier() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x675b67ff).into()),
border_variant: Some(rgba(0x675b67ff).into()),
border_variant: Some(rgba(0x393239ff).into()),
border_focused: Some(rgba(0x1a2961ff).into()),
border_selected: Some(rgba(0x1a2961ff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -5175,7 +5175,7 @@ pub fn atelier() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0xf7f3f74c).into()),
scrollbar_thumb_hover_background: Some(rgba(0x393239ff).into()),
scrollbar_thumb_border: Some(rgba(0x393239ff).into()),
scrollbar_track_background: Some(rgba(0x1b181bff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0x231e23ff).into()),
editor_foreground: Some(rgba(0xd8cad8ff).into()),
editor_background: Some(rgba(0x1b181bff).into()),
@ -5601,7 +5601,7 @@ pub fn atelier() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x4f6b78ff).into()),
border_variant: Some(rgba(0x4f6b78ff).into()),
border_variant: Some(rgba(0x2c3b42ff).into()),
border_focused: Some(rgba(0x1a2f3cff).into()),
border_selected: Some(rgba(0x1a2f3cff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -5640,7 +5640,7 @@ pub fn atelier() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0xebf8ff4c).into()),
scrollbar_thumb_hover_background: Some(rgba(0x2c3b42ff).into()),
scrollbar_thumb_border: Some(rgba(0x2c3b42ff).into()),
scrollbar_track_background: Some(rgba(0x161b1dff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0x1b2327ff).into()),
editor_foreground: Some(rgba(0xc1e4f6ff).into()),
editor_background: Some(rgba(0x161b1dff).into()),
@ -6066,7 +6066,7 @@ pub fn atelier() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0xaaa3a1ff).into()),
border_variant: Some(rgba(0xaaa3a1ff).into()),
border_variant: Some(rgba(0xd6d1cfff).into()),
border_focused: Some(rgba(0xc6cef7ff).into()),
border_selected: Some(rgba(0xc6cef7ff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -6105,7 +6105,7 @@ pub fn atelier() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0x1b19184c).into()),
scrollbar_thumb_hover_background: Some(rgba(0xd6d1cfff).into()),
scrollbar_thumb_border: Some(rgba(0xd6d1cfff).into()),
scrollbar_track_background: Some(rgba(0xf1efeeff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0xebe8e6ff).into()),
editor_foreground: Some(rgba(0x2c2421ff).into()),
editor_background: Some(rgba(0xf1efeeff).into()),
@ -6531,7 +6531,7 @@ pub fn atelier() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0xa8a48eff).into()),
border_variant: Some(rgba(0xa8a48eff).into()),
border_variant: Some(rgba(0xd7d3beff).into()),
border_focused: Some(rgba(0xcdd1f5ff).into()),
border_selected: Some(rgba(0xcdd1f5ff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -6570,7 +6570,7 @@ pub fn atelier() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0x20201d4c).into()),
scrollbar_thumb_hover_background: Some(rgba(0xd7d3beff).into()),
scrollbar_thumb_border: Some(rgba(0xd7d3beff).into()),
scrollbar_track_background: Some(rgba(0xfefbecff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0xf2eedcff).into()),
editor_foreground: Some(rgba(0x292824ff).into()),
editor_background: Some(rgba(0xfefbecff).into()),
@ -6996,7 +6996,7 @@ pub fn atelier() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x8e8989ff).into()),
border_variant: Some(rgba(0x8e8989ff).into()),
border_variant: Some(rgba(0xcfc7c7ff).into()),
border_focused: Some(rgba(0xcecaecff).into()),
border_selected: Some(rgba(0xcecaecff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -7035,7 +7035,7 @@ pub fn atelier() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0x1b18184c).into()),
scrollbar_thumb_hover_background: Some(rgba(0xcfc7c7ff).into()),
scrollbar_thumb_border: Some(rgba(0xcfc7c7ff).into()),
scrollbar_track_background: Some(rgba(0xf4ececff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0xede5e5ff).into()),
editor_foreground: Some(rgba(0x292424ff).into()),
editor_background: Some(rgba(0xf4ececff).into()),
@ -7461,7 +7461,7 @@ pub fn atelier() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x8ea88eff).into()),
border_variant: Some(rgba(0x8ea88eff).into()),
border_variant: Some(rgba(0xbed7beff).into()),
border_focused: Some(rgba(0xc9c4fdff).into()),
border_selected: Some(rgba(0xc9c4fdff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -7500,7 +7500,7 @@ pub fn atelier() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0x1315134c).into()),
scrollbar_thumb_hover_background: Some(rgba(0xbed7beff).into()),
scrollbar_thumb_border: Some(rgba(0xbed7beff).into()),
scrollbar_track_background: Some(rgba(0xf4fbf4ff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0xdff0dfff).into()),
editor_foreground: Some(rgba(0x242924ff).into()),
editor_background: Some(rgba(0xf4fbf4ff).into()),
@ -7926,7 +7926,7 @@ pub fn atelier() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x505e55ff).into()),
border_variant: Some(rgba(0x505e55ff).into()),
border_variant: Some(rgba(0x2f3832ff).into()),
border_focused: Some(rgba(0x1f3233ff).into()),
border_selected: Some(rgba(0x1f3233ff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -7965,7 +7965,7 @@ pub fn atelier() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0xecf4ee4c).into()),
scrollbar_thumb_hover_background: Some(rgba(0x2f3832ff).into()),
scrollbar_thumb_border: Some(rgba(0x2f3832ff).into()),
scrollbar_track_background: Some(rgba(0x171c19ff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0x1e2420ff).into()),
editor_foreground: Some(rgba(0xdfe7e2ff).into()),
editor_background: Some(rgba(0x171c19ff).into()),
@ -8391,7 +8391,7 @@ pub fn atelier() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0xad9dadff).into()),
border_variant: Some(rgba(0xad9dadff).into()),
border_variant: Some(rgba(0xcdbecdff).into()),
border_focused: Some(rgba(0xcac7faff).into()),
border_selected: Some(rgba(0xcac7faff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -8430,7 +8430,7 @@ pub fn atelier() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0x1b181b4c).into()),
scrollbar_thumb_hover_background: Some(rgba(0xcdbecdff).into()),
scrollbar_thumb_border: Some(rgba(0xcdbecdff).into()),
scrollbar_track_background: Some(rgba(0xf7f3f7ff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0xe5dce5ff).into()),
editor_foreground: Some(rgba(0x292329ff).into()),
editor_background: Some(rgba(0xf7f3f7ff).into()),
@ -8856,7 +8856,7 @@ pub fn atelier() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x80a4b6ff).into()),
border_variant: Some(rgba(0x80a4b6ff).into()),
border_variant: Some(rgba(0xb0d3e5ff).into()),
border_focused: Some(rgba(0xbacfe1ff).into()),
border_selected: Some(rgba(0xbacfe1ff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -8895,7 +8895,7 @@ pub fn atelier() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0x161b1d4c).into()),
scrollbar_thumb_hover_background: Some(rgba(0xb0d3e5ff).into()),
scrollbar_thumb_border: Some(rgba(0xb0d3e5ff).into()),
scrollbar_track_background: Some(rgba(0xebf8ffff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0xd3edfaff).into()),
editor_foreground: Some(rgba(0x1f292eff).into()),
editor_background: Some(rgba(0xebf8ffff).into()),

View file

@ -21,7 +21,7 @@ pub fn ayu() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x3f4043ff).into()),
border_variant: Some(rgba(0x3f4043ff).into()),
border_variant: Some(rgba(0x2d2f34ff).into()),
border_focused: Some(rgba(0x1b4a6eff).into()),
border_selected: Some(rgba(0x1b4a6eff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -60,7 +60,7 @@ pub fn ayu() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0xbfbdb64c).into()),
scrollbar_thumb_hover_background: Some(rgba(0x2d2f34ff).into()),
scrollbar_thumb_border: Some(rgba(0x2d2f34ff).into()),
scrollbar_track_background: Some(rgba(0x0d1017ff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0x1b1e24ff).into()),
editor_foreground: Some(rgba(0xbfbdb6ff).into()),
editor_background: Some(rgba(0x0d1017ff).into()),
@ -465,7 +465,7 @@ pub fn ayu() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0xcfd1d2ff).into()),
border_variant: Some(rgba(0xcfd1d2ff).into()),
border_variant: Some(rgba(0xdfe0e1ff).into()),
border_focused: Some(rgba(0xc4daf6ff).into()),
border_selected: Some(rgba(0xc4daf6ff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -504,7 +504,7 @@ pub fn ayu() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0x5c61664c).into()),
scrollbar_thumb_hover_background: Some(rgba(0xdfe0e1ff).into()),
scrollbar_thumb_border: Some(rgba(0xdfe0e1ff).into()),
scrollbar_track_background: Some(rgba(0xfcfcfcff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0xefeff0ff).into()),
editor_foreground: Some(rgba(0x5c6166ff).into()),
editor_background: Some(rgba(0xfcfcfcff).into()),
@ -909,7 +909,7 @@ pub fn ayu() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x53565dff).into()),
border_variant: Some(rgba(0x53565dff).into()),
border_variant: Some(rgba(0x43464fff).into()),
border_focused: Some(rgba(0x24556fff).into()),
border_selected: Some(rgba(0x24556fff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -948,7 +948,7 @@ pub fn ayu() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0xcccac24c).into()),
scrollbar_thumb_hover_background: Some(rgba(0x43464fff).into()),
scrollbar_thumb_border: Some(rgba(0x43464fff).into()),
scrollbar_track_background: Some(rgba(0x242936ff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0x323641ff).into()),
editor_foreground: Some(rgba(0xcccac2ff).into()),
editor_background: Some(rgba(0x242936ff).into()),

View file

@ -21,7 +21,7 @@ pub fn gruvbox() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0xc9b99aff).into()),
border_variant: Some(rgba(0xc9b99aff).into()),
border_variant: Some(rgba(0xddcca7ff).into()),
border_focused: Some(rgba(0xaec6cdff).into()),
border_selected: Some(rgba(0xaec6cdff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -60,7 +60,7 @@ pub fn gruvbox() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0x2828284c).into()),
scrollbar_thumb_hover_background: Some(rgba(0xddcca7ff).into()),
scrollbar_thumb_border: Some(rgba(0xddcca7ff).into()),
scrollbar_track_background: Some(rgba(0xf9f5d7ff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0xefe2bcff).into()),
editor_foreground: Some(rgba(0x282828ff).into()),
editor_background: Some(rgba(0xf9f5d7ff).into()),
@ -472,7 +472,7 @@ pub fn gruvbox() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x5b534dff).into()),
border_variant: Some(rgba(0x5b534dff).into()),
border_variant: Some(rgba(0x494340ff).into()),
border_focused: Some(rgba(0x303a36ff).into()),
border_selected: Some(rgba(0x303a36ff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -511,7 +511,7 @@ pub fn gruvbox() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0xfbf1c74c).into()),
scrollbar_thumb_hover_background: Some(rgba(0x494340ff).into()),
scrollbar_thumb_border: Some(rgba(0x494340ff).into()),
scrollbar_track_background: Some(rgba(0x32302fff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0x393634ff).into()),
editor_foreground: Some(rgba(0xebdbb2ff).into()),
editor_background: Some(rgba(0x32302fff).into()),
@ -923,7 +923,7 @@ pub fn gruvbox() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0xc9b99aff).into()),
border_variant: Some(rgba(0xc9b99aff).into()),
border_variant: Some(rgba(0xddcca7ff).into()),
border_focused: Some(rgba(0xaec6cdff).into()),
border_selected: Some(rgba(0xaec6cdff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -962,7 +962,7 @@ pub fn gruvbox() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0x2828284c).into()),
scrollbar_thumb_hover_background: Some(rgba(0xddcca7ff).into()),
scrollbar_thumb_border: Some(rgba(0xddcca7ff).into()),
scrollbar_track_background: Some(rgba(0xfbf1c7ff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0xefe1b8ff).into()),
editor_foreground: Some(rgba(0x282828ff).into()),
editor_background: Some(rgba(0xfbf1c7ff).into()),
@ -1374,7 +1374,7 @@ pub fn gruvbox() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x5b534dff).into()),
border_variant: Some(rgba(0x5b534dff).into()),
border_variant: Some(rgba(0x494340ff).into()),
border_focused: Some(rgba(0x303a36ff).into()),
border_selected: Some(rgba(0x303a36ff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -1413,7 +1413,7 @@ pub fn gruvbox() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0xfbf1c74c).into()),
scrollbar_thumb_hover_background: Some(rgba(0x494340ff).into()),
scrollbar_thumb_border: Some(rgba(0x494340ff).into()),
scrollbar_track_background: Some(rgba(0x282828ff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0x373432ff).into()),
editor_foreground: Some(rgba(0xebdbb2ff).into()),
editor_background: Some(rgba(0x282828ff).into()),
@ -1825,7 +1825,7 @@ pub fn gruvbox() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0xc9b99aff).into()),
border_variant: Some(rgba(0xc9b99aff).into()),
border_variant: Some(rgba(0xddcca7ff).into()),
border_focused: Some(rgba(0xaec6cdff).into()),
border_selected: Some(rgba(0xaec6cdff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -1864,7 +1864,7 @@ pub fn gruvbox() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0x2828284c).into()),
scrollbar_thumb_hover_background: Some(rgba(0xddcca7ff).into()),
scrollbar_thumb_border: Some(rgba(0xddcca7ff).into()),
scrollbar_track_background: Some(rgba(0xf2e5bcff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0xeddeb5ff).into()),
editor_foreground: Some(rgba(0x282828ff).into()),
editor_background: Some(rgba(0xf2e5bcff).into()),
@ -2276,7 +2276,7 @@ pub fn gruvbox() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x5b534dff).into()),
border_variant: Some(rgba(0x5b534dff).into()),
border_variant: Some(rgba(0x494340ff).into()),
border_focused: Some(rgba(0x303a36ff).into()),
border_selected: Some(rgba(0x303a36ff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -2315,7 +2315,7 @@ pub fn gruvbox() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0xfbf1c74c).into()),
scrollbar_thumb_hover_background: Some(rgba(0x494340ff).into()),
scrollbar_thumb_border: Some(rgba(0x494340ff).into()),
scrollbar_track_background: Some(rgba(0x1d2021ff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0x343130ff).into()),
editor_foreground: Some(rgba(0xebdbb2ff).into()),
editor_background: Some(rgba(0x1d2021ff).into()),

View file

@ -21,7 +21,7 @@ pub fn one() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0xc9c9caff).into()),
border_variant: 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()),
@ -60,7 +60,7 @@ pub fn one() -> UserThemeFamily {
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(0xfafafaff).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()),
@ -472,7 +472,7 @@ pub fn one() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x464b57ff).into()),
border_variant: Some(rgba(0x464b57ff).into()),
border_variant: Some(rgba(0x363c46ff).into()),
border_focused: Some(rgba(0x293c5bff).into()),
border_selected: Some(rgba(0x293c5bff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -511,7 +511,7 @@ pub fn one() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0xc8ccd44c).into()),
scrollbar_thumb_hover_background: Some(rgba(0x363c46ff).into()),
scrollbar_thumb_border: Some(rgba(0x363c46ff).into()),
scrollbar_track_background: Some(rgba(0x282c34ff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0x2e333cff).into()),
editor_foreground: Some(rgba(0xacb2beff).into()),
editor_background: Some(rgba(0x282c34ff).into()),

View file

@ -21,7 +21,7 @@ pub fn rose_pine() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0xdcd6d5ff).into()),
border_variant: Some(rgba(0xdcd6d5ff).into()),
border_variant: Some(rgba(0xe5e0dfff).into()),
border_focused: Some(rgba(0xc3d7dbff).into()),
border_selected: Some(rgba(0xc3d7dbff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -60,7 +60,7 @@ pub fn rose_pine() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0x5752794c).into()),
scrollbar_thumb_hover_background: Some(rgba(0xe5e0dfff).into()),
scrollbar_thumb_border: Some(rgba(0xe5e0dfff).into()),
scrollbar_track_background: Some(rgba(0xfaf4edff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0xfdf8f1ff).into()),
editor_foreground: Some(rgba(0x575279ff).into()),
editor_background: Some(rgba(0xfaf4edff).into()),
@ -479,7 +479,7 @@ pub fn rose_pine() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x504c68ff).into()),
border_variant: Some(rgba(0x504c68ff).into()),
border_variant: Some(rgba(0x322f48ff).into()),
border_focused: Some(rgba(0x435255ff).into()),
border_selected: Some(rgba(0x435255ff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -518,7 +518,7 @@ pub fn rose_pine() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0xe0def44c).into()),
scrollbar_thumb_hover_background: Some(rgba(0x322f48ff).into()),
scrollbar_thumb_border: Some(rgba(0x322f48ff).into()),
scrollbar_track_background: Some(rgba(0x232136ff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0x27243bff).into()),
editor_foreground: Some(rgba(0xe0def4ff).into()),
editor_background: Some(rgba(0x232136ff).into()),
@ -937,7 +937,7 @@ pub fn rose_pine() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x423f55ff).into()),
border_variant: 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()),
@ -976,7 +976,7 @@ pub fn rose_pine() -> UserThemeFamily {
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(0x191724ff).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()),

View file

@ -20,7 +20,7 @@ pub fn sandcastle() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x3d4350ff).into()),
border_variant: Some(rgba(0x3d4350ff).into()),
border_variant: Some(rgba(0x313741ff).into()),
border_focused: Some(rgba(0x223232ff).into()),
border_selected: Some(rgba(0x223232ff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -59,7 +59,7 @@ pub fn sandcastle() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0xfdf4c14c).into()),
scrollbar_thumb_hover_background: Some(rgba(0x313741ff).into()),
scrollbar_thumb_border: Some(rgba(0x313741ff).into()),
scrollbar_track_background: Some(rgba(0x282c34ff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0x2a2f38ff).into()),
editor_foreground: Some(rgba(0xfdf4c1ff).into()),
editor_background: Some(rgba(0x282c34ff).into()),

View file

@ -21,7 +21,7 @@ pub fn solarized() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x9faaa8ff).into()),
border_variant: 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()),
@ -60,7 +60,7 @@ pub fn solarized() -> UserThemeFamily {
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(0xfdf6e3ff).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()),
@ -465,7 +465,7 @@ pub fn solarized() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x2b4f58ff).into()),
border_variant: Some(rgba(0x2b4f58ff).into()),
border_variant: Some(rgba(0x063541ff).into()),
border_focused: Some(rgba(0x1c3249ff).into()),
border_selected: Some(rgba(0x1c3249ff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -504,7 +504,7 @@ pub fn solarized() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0xfdf6e34c).into()),
scrollbar_thumb_hover_background: Some(rgba(0x063541ff).into()),
scrollbar_thumb_border: Some(rgba(0x063541ff).into()),
scrollbar_track_background: Some(rgba(0x002b36ff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0x032f3bff).into()),
editor_foreground: Some(rgba(0xfdf6e3ff).into()),
editor_background: Some(rgba(0x002b36ff).into()),

View file

@ -20,7 +20,7 @@ pub fn summercamp() -> UserThemeFamily {
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x312d21ff).into()),
border_variant: Some(rgba(0x312d21ff).into()),
border_variant: Some(rgba(0x29251bff).into()),
border_focused: Some(rgba(0x193761ff).into()),
border_selected: Some(rgba(0x193761ff).into()),
border_transparent: Some(rgba(0x00000000).into()),
@ -59,7 +59,7 @@ pub fn summercamp() -> UserThemeFamily {
scrollbar_thumb_background: Some(rgba(0xf8f5de4c).into()),
scrollbar_thumb_hover_background: Some(rgba(0x29251bff).into()),
scrollbar_thumb_border: Some(rgba(0x29251bff).into()),
scrollbar_track_background: Some(rgba(0x1c1810ff).into()),
scrollbar_track_background: Some(rgba(0x00000000).into()),
scrollbar_track_border: Some(rgba(0x221e15ff).into()),
editor_foreground: Some(rgba(0xf8f5deff).into()),
editor_background: Some(rgba(0x1c1810ff).into()),

View file

@ -187,7 +187,7 @@ impl Zed1ThemeConverter {
Ok(ThemeColorsRefinement {
border: convert(lowest.base.default.border),
border_variant: convert(lowest.variant.default.border),
border_variant: convert(middle.variant.default.border),
border_focused: convert(lowest.accent.hovered.border),
border_selected: convert(lowest.accent.default.border),
border_transparent: Some(gpui::transparent_black()),
@ -230,7 +230,7 @@ impl Zed1ThemeConverter {
.map(|color| color_alpha(color, 0.3)),
scrollbar_thumb_hover_background: convert(middle.base.hovered.background),
scrollbar_thumb_border: convert(middle.base.default.border),
scrollbar_track_background: convert(highest.base.default.background),
scrollbar_track_background: Some(gpui::transparent_black()),
scrollbar_track_border: convert(highest.variant.default.border),
editor_foreground: convert(editor.text_color),
editor_background: convert(editor.background),

View file

@ -1,4 +1,4 @@
use client::{telemetry::Telemetry, TelemetrySettings};
use client::telemetry::Telemetry;
use feature_flags::FeatureFlagAppExt;
use fs::Fs;
use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
@ -7,7 +7,7 @@ use gpui::{
VisualContext, WeakView,
};
use picker::{Picker, PickerDelegate};
use settings::{update_settings_file, Settings, SettingsStore};
use settings::{update_settings_file, SettingsStore};
use std::sync::Arc;
use theme::{Theme, ThemeMeta, ThemeRegistry, ThemeSettings};
use ui::{prelude::*, v_stack, ListItem, ListItemSpacing};
@ -181,9 +181,8 @@ impl PickerDelegate for ThemeSelectorDelegate {
let theme_name = cx.theme().name.clone();
let telemetry_settings = TelemetrySettings::get_global(cx).clone();
self.telemetry
.report_setting_event(telemetry_settings, "theme", theme_name.to_string());
.report_setting_event("theme", theme_name.to_string(), cx);
update_settings_file::<ThemeSettings>(self.fs.clone(), cx, move |settings| {
settings.theme = Some(theme_name.to_string());

View file

@ -1,25 +0,0 @@
## Documentation priorities:
These are the priorities to get documented, in a rough stack rank order:
- [ ] label
- [ ] button
- [ ] icon_button
- [ ] icon
- [ ] list
- [ ] avatar
- [ ] panel
- [ ] modal
- [ ] palette
- [ ] input
- [ ] facepile
- [ ] player
- [ ] stacks
- [ ] context menu
- [ ] input
- [ ] textarea/multiline input (not built - not an editor)
- [ ] indicator
- [ ] public actor
- [ ] keybinding
- [ ] tab
- [ ] toast

View file

@ -92,6 +92,13 @@ impl Selectable for Button {
}
}
impl SelectableButton for Button {
fn selected_style(mut self, style: ButtonStyle) -> Self {
self.base = self.base.selected_style(style);
self
}
}
impl Disableable for Button {
fn disabled(mut self, disabled: bool) -> Self {
self.base = self.base.disabled(disabled);

View file

@ -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 {

View file

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

View file

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

View file

@ -63,6 +63,13 @@ impl Selectable for ToggleButton {
}
}
impl SelectableButton for ToggleButton {
fn selected_style(mut self, style: ButtonStyle) -> Self {
self.base.selected_style = Some(style);
self
}
}
impl Disableable for ToggleButton {
fn disabled(mut self, disabled: bool) -> Self {
self.base = self.base.disabled(disabled);

View file

@ -29,6 +29,7 @@ pub enum Icon {
ArrowRight,
ArrowUp,
ArrowUpRight,
ArrowCircle,
AtSign,
AudioOff,
AudioOn,
@ -119,6 +120,7 @@ impl Icon {
Icon::ArrowRight => "icons/arrow_right.svg",
Icon::ArrowUp => "icons/arrow_up.svg",
Icon::ArrowUpRight => "icons/arrow_up_right.svg",
Icon::ArrowCircle => "icons/arrow_circle.svg",
Icon::AtSign => "icons/at_sign.svg",
Icon::AudioOff => "icons/speaker_off.svg",
Icon::AudioOn => "icons/speaker_loud.svg",

View file

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

View file

@ -12,7 +12,7 @@ pub use crate::selectable::*;
pub use crate::styles::{vh, vw};
pub use crate::visible_on_hover::*;
pub use crate::{h_stack, v_stack};
pub use crate::{Button, ButtonSize, ButtonStyle, IconButton};
pub use crate::{Button, ButtonSize, ButtonStyle, IconButton, SelectableButton};
pub use crate::{ButtonCommon, Color, StyledExt};
pub use crate::{Icon, IconElement, IconPosition, IconSize};
pub use crate::{Label, LabelCommon, LabelSize, LineHeightStyle};

View file

@ -2,15 +2,8 @@
//!
//! This crate provides a set of UI primitives and components that are used to build all of the elements in Zed's UI.
//!
//! ## Work in Progress
//!
//! This crate is still a work in progress. The initial primitives and components are built for getting all the UI on the screen,
//! much of the state and functionality is mocked or hard codeded, and performance has not been a focus.
//!
#![doc = include_str!("../docs/hello-world.md")]
#![doc = include_str!("../docs/building-ui.md")]
#![doc = include_str!("../docs/todo.md")]
mod clickable;
mod components;

View file

@ -59,153 +59,159 @@ pub struct WelcomePage {
impl Render for WelcomePage {
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement {
h_stack().full().track_focus(&self.focus_handle).child(
v_stack()
.w_96()
.gap_4()
.mx_auto()
.child(
svg()
.path("icons/logo_96.svg")
.text_color(gpui::white())
.w(px(96.))
.h(px(96.))
.mx_auto(),
)
.child(
h_stack()
.justify_center()
.child(Label::new("Code at the speed of thought")),
)
.child(
v_stack()
.gap_2()
.child(
Button::new("choose-theme", "Choose a theme")
.full_width()
.on_click(cx.listener(|this, _, cx| {
this.workspace
.update(cx, |workspace, cx| {
theme_selector::toggle(
workspace,
&Default::default(),
cx,
)
})
.ok();
})),
)
.child(
Button::new("choose-keymap", "Choose a keymap")
.full_width()
.on_click(cx.listener(|this, _, cx| {
this.workspace
.update(cx, |workspace, cx| {
base_keymap_picker::toggle(
workspace,
&Default::default(),
cx,
)
})
.ok();
})),
)
.child(
Button::new("install-cli", "Install the CLI")
.full_width()
.on_click(cx.listener(|_, _, cx| {
cx.app_mut()
.spawn(
|cx| async move { install_cli::install_cli(&cx).await },
h_stack()
.full()
.bg(cx.theme().colors().editor_background)
.track_focus(&self.focus_handle)
.child(
v_stack()
.w_96()
.gap_4()
.mx_auto()
.child(
svg()
.path("icons/logo_96.svg")
.text_color(gpui::white())
.w(px(96.))
.h(px(96.))
.mx_auto(),
)
.child(
h_stack()
.justify_center()
.child(Label::new("Code at the speed of thought")),
)
.child(
v_stack()
.gap_2()
.child(
Button::new("choose-theme", "Choose a theme")
.full_width()
.on_click(cx.listener(|this, _, cx| {
this.workspace
.update(cx, |workspace, cx| {
theme_selector::toggle(
workspace,
&Default::default(),
cx,
)
})
.ok();
})),
)
.child(
Button::new("choose-keymap", "Choose a keymap")
.full_width()
.on_click(cx.listener(|this, _, cx| {
this.workspace
.update(cx, |workspace, cx| {
base_keymap_picker::toggle(
workspace,
&Default::default(),
cx,
)
})
.ok();
})),
)
.child(
Button::new("install-cli", "Install the CLI")
.full_width()
.on_click(cx.listener(|_, _, cx| {
cx.app_mut()
.spawn(|cx| async move {
install_cli::install_cli(&cx).await
})
.detach_and_log_err(cx);
})),
),
)
.child(
v_stack()
.p_3()
.gap_2()
.bg(cx.theme().colors().elevated_surface_background)
.border_1()
.border_color(cx.theme().colors().border)
.rounded_md()
.child(
h_stack()
.gap_2()
.child(
Checkbox::new(
"enable-vim",
if VimModeSetting::get_global(cx).0 {
ui::Selection::Selected
} else {
ui::Selection::Unselected
},
)
.detach_and_log_err(cx);
})),
),
)
.child(
v_stack()
.p_3()
.gap_2()
.bg(cx.theme().colors().elevated_surface_background)
.border_1()
.border_color(cx.theme().colors().border)
.rounded_md()
.child(
h_stack()
.gap_2()
.child(
Checkbox::new(
"enable-vim",
if VimModeSetting::get_global(cx).0 {
ui::Selection::Selected
} else {
ui::Selection::Unselected
},
.on_click(
cx.listener(move |this, selection, cx| {
this.update_settings::<VimModeSetting>(
selection,
cx,
|setting, value| *setting = Some(value),
);
}),
),
)
.on_click(cx.listener(
move |this, selection, cx| {
this.update_settings::<VimModeSetting>(
selection,
cx,
|setting, value| *setting = Some(value),
);
},
)),
)
.child(Label::new("Enable vim mode")),
)
.child(
h_stack()
.gap_2()
.child(
Checkbox::new(
"enable-telemetry",
if TelemetrySettings::get_global(cx).metrics {
ui::Selection::Selected
} else {
ui::Selection::Unselected
},
.child(Label::new("Enable vim mode")),
)
.child(
h_stack()
.gap_2()
.child(
Checkbox::new(
"enable-telemetry",
if TelemetrySettings::get_global(cx).metrics {
ui::Selection::Selected
} else {
ui::Selection::Unselected
},
)
.on_click(
cx.listener(move |this, selection, cx| {
this.update_settings::<TelemetrySettings>(
selection,
cx,
|settings, value| {
settings.metrics = Some(value)
},
);
}),
),
)
.on_click(cx.listener(
move |this, selection, cx| {
this.update_settings::<TelemetrySettings>(
selection,
cx,
|settings, value| settings.metrics = Some(value),
);
},
)),
)
.child(Label::new("Send anonymous usage data")),
)
.child(
h_stack()
.gap_2()
.child(
Checkbox::new(
"enable-crash",
if TelemetrySettings::get_global(cx).diagnostics {
ui::Selection::Selected
} else {
ui::Selection::Unselected
},
.child(Label::new("Send anonymous usage data")),
)
.child(
h_stack()
.gap_2()
.child(
Checkbox::new(
"enable-crash",
if TelemetrySettings::get_global(cx).diagnostics {
ui::Selection::Selected
} else {
ui::Selection::Unselected
},
)
.on_click(
cx.listener(move |this, selection, cx| {
this.update_settings::<TelemetrySettings>(
selection,
cx,
|settings, value| {
settings.diagnostics = Some(value)
},
);
}),
),
)
.on_click(cx.listener(
move |this, selection, cx| {
this.update_settings::<TelemetrySettings>(
selection,
cx,
|settings, value| {
settings.diagnostics = Some(value)
},
);
},
)),
)
.child(Label::new("Send crash reports")),
),
),
)
.child(Label::new("Send crash reports")),
),
),
)
}
}

View file

@ -246,7 +246,15 @@ impl Member {
.size_full()
.child(pane.clone())
.when_some(leader_border, |this, color| {
this.border_2().border_color(color)
this.child(
div()
.absolute()
.size_full()
.left_0()
.top_0()
.border_2()
.border_color(color),
)
})
.when_some(leader_status_box, |this, status_box| {
this.child(

View file

@ -15,7 +15,7 @@ use anyhow::{anyhow, Context as _, Result};
use call::ActiveCall;
use client::{
proto::{self, PeerId},
Client, Status, TelemetrySettings, TypedEnvelope, UserStore,
Client, Status, TypedEnvelope, UserStore,
};
use collections::{hash_map, HashMap, HashSet};
use dock::{Dock, DockPosition, Panel, PanelButtons, PanelHandle};
@ -107,6 +107,7 @@ actions!(
NewCenterTerminal,
ToggleTerminalFocus,
NewSearch,
DeploySearch,
Feedback,
Restart,
Welcome,
@ -1095,19 +1096,21 @@ impl Workspace {
}
pub fn close_global(_: &CloseWindow, cx: &mut AppContext) {
cx.windows().iter().find(|window| {
window
.update(cx, |_, window| {
if window.is_window_active() {
//This can only get called when the window's project connection has been lost
//so we don't need to prompt the user for anything and instead just close the window
window.remove_window();
true
} else {
false
}
})
.unwrap_or(false)
cx.defer(|cx| {
cx.windows().iter().find(|window| {
window
.update(cx, |_, window| {
if window.is_window_active() {
//This can only get called when the window's project connection has been lost
//so we don't need to prompt the user for anything and instead just close the window
window.remove_window();
true
} else {
false
}
})
.unwrap_or(false)
});
});
}
@ -1250,10 +1253,9 @@ impl Workspace {
}
pub fn open(&mut self, _: &Open, cx: &mut ViewContext<Self>) {
let telemetry_settings = TelemetrySettings::get_global(cx).clone();
self.client()
.telemetry()
.report_app_event(telemetry_settings, "open project", false);
.report_app_event("open project", false, cx);
let paths = cx.prompt_for_paths(PathPromptOptions {
files: true,
directories: true,
@ -3264,6 +3266,7 @@ impl Workspace {
let user_store = project.read(cx).user_store();
let workspace_store = cx.new_model(|cx| WorkspaceStore::new(client.clone(), cx));
cx.activate_window();
let app_state = Arc::new(AppState {
languages: project.read(cx).languages().clone(),
workspace_store,
@ -4762,8 +4765,7 @@ mod tests {
});
// Deactivating the window saves the file.
cx.simulate_deactivation();
cx.executor().run_until_parked();
cx.deactivate_window();
item.update(cx, |item, _| assert_eq!(item.save_count, 1));
// Autosave on focus change.
@ -4783,14 +4785,13 @@ mod tests {
item.update(cx, |item, _| assert_eq!(item.save_count, 2));
// Deactivating the window still saves the file.
cx.simulate_activation();
cx.update(|cx| cx.activate_window());
item.update(cx, |item, cx| {
cx.focus_self();
item.is_dirty = true;
});
cx.simulate_deactivation();
cx.deactivate_window();
cx.executor().run_until_parked();
item.update(cx, |item, _| assert_eq!(item.save_count, 3));
// Autosave after delay.

View file

@ -11,7 +11,7 @@ path = "src/zed.rs"
doctest = false
[[bin]]
name = "zed"
name = "Zed"
path = "src/main.rs"
[dependencies]

View file

@ -150,14 +150,6 @@ pub fn app_menus() -> Vec<Menu<'static>> {
MenuItem::action("View Dependency Licenses", crate::OpenLicenses),
MenuItem::action("Show Welcome", workspace::Welcome),
MenuItem::separator(),
// todo!(): Needs `feedback` crate.
// MenuItem::action("Give us feedback", feedback::feedback_editor::GiveFeedback),
// MenuItem::action(
// "Copy System Specs Into Clipboard",
// feedback::CopySystemSpecsIntoClipboard,
// ),
// MenuItem::action("File Bug Report", feedback::FileBugReport),
// MenuItem::action("Request Feature", feedback::RequestFeature),
MenuItem::separator(),
MenuItem::action(
"Documentation",

View file

@ -144,6 +144,7 @@ fn main() {
cx.set_global(client.clone());
zed::init(cx);
theme::init(theme::LoadThemes::All, cx);
project::Project::init(&client, cx);
client::init(&client, cx);
@ -158,7 +159,6 @@ fn main() {
cx,
);
assistant::init(cx);
// component_test::init(cx);
cx.spawn(|_| watch_languages(fs.clone(), languages.clone()))
.detach();
@ -172,19 +172,16 @@ fn main() {
.detach();
client.telemetry().start(installation_id, session_id, cx);
let telemetry_settings = *client::TelemetrySettings::get_global(cx);
client.telemetry().report_setting_event(
telemetry_settings,
"theme",
cx.theme().name.to_string(),
);
client
.telemetry()
.report_setting_event("theme", cx.theme().name.to_string(), cx);
let event_operation = match existing_installation_id_found {
Some(false) => "first open",
_ => "open",
};
client
.telemetry()
.report_app_event(telemetry_settings, event_operation, true);
.report_app_event(event_operation, true, cx);
let app_state = Arc::new(AppState {
languages: languages.clone(),

View file

@ -18,11 +18,11 @@ pub use only_instance::*;
pub use open_listener::*;
use anyhow::{anyhow, Context as _};
use futures::{channel::mpsc, StreamExt};
use futures::{channel::mpsc, select_biased, StreamExt};
use project_panel::ProjectPanel;
use quick_action_bar::QuickActionBar;
use search::project_search::ProjectSearchBar;
use settings::{initial_local_settings_content, load_default_keymap, KeymapFile, Settings};
use settings::{initial_local_settings_content, KeymapFile, Settings, SettingsStore};
use std::{borrow::Cow, ops::Deref, sync::Arc};
use terminal_view::terminal_panel::TerminalPanel;
use util::{
@ -32,6 +32,7 @@ use util::{
ResultExt,
};
use uuid::Uuid;
use welcome::BaseKeymap;
use workspace::Pane;
use workspace::{
create_and_open_local_file, notifications::simple_message_notification::MessageNotification,
@ -64,6 +65,13 @@ actions!(
]
);
pub fn init(cx: &mut AppContext) {
cx.on_action(|_: &Hide, cx| cx.hide());
cx.on_action(|_: &HideOthers, cx| cx.hide_other_apps());
cx.on_action(|_: &ShowAll, cx| cx.unhide_other_apps());
cx.on_action(quit);
}
pub fn build_window_options(
bounds: Option<WindowBounds>,
display_uuid: Option<Uuid>,
@ -130,7 +138,6 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
status_bar.add_left_item(diagnostic_summary, cx);
status_bar.add_left_item(activity_indicator, cx);
status_bar.add_right_item(feedback_button, cx);
// status_bar.add_right_item(copilot, cx);
status_bar.add_right_item(copilot, cx);
status_bar.add_right_item(active_buffer_language, cx);
status_bar.add_right_item(vim_mode_indicator, cx);
@ -207,15 +214,6 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
workspace
.register_action(about)
.register_action(|_, _: &Hide, cx| {
cx.hide();
})
.register_action(|_, _: &HideOthers, cx| {
cx.hide_other_apps();
})
.register_action(|_, _: &ShowAll, cx| {
cx.unhide_other_apps();
})
.register_action(|_, _: &Minimize, cx| {
cx.minimize_window();
})
@ -225,7 +223,6 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
.register_action(|_, _: &ToggleFullScreen, cx| {
cx.toggle_full_screen();
})
.register_action(quit)
.register_action(|_, action: &OpenZedURL, cx| {
cx.global::<Arc<OpenListener>>()
.open_urls(&[action.url.clone()])
@ -403,8 +400,6 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
});
workspace.focus_handle(cx).focus(cx);
//todo!()
// load_default_keymap(cx);
})
.detach();
}
@ -451,10 +446,10 @@ fn about(_: &mut Workspace, _: &About, cx: &mut gpui::ViewContext<Workspace>) {
.detach();
}
fn quit(_: &mut Workspace, _: &Quit, cx: &mut gpui::ViewContext<Workspace>) {
fn quit(_: &Quit, cx: &mut AppContext) {
let should_confirm = WorkspaceSettings::get_global(cx).confirm_quit;
cx.spawn(|_, mut cx| async move {
let mut workspace_windows = cx.update(|_, cx| {
cx.spawn(|mut cx| async move {
let mut workspace_windows = cx.update(|cx| {
cx.windows()
.into_iter()
.filter_map(|window| window.downcast::<Workspace>())
@ -463,14 +458,14 @@ fn quit(_: &mut Workspace, _: &Quit, cx: &mut gpui::ViewContext<Workspace>) {
// If multiple windows have unsaved changes, and need a save prompt,
// prompt in the active window before switching to a different window.
cx.update(|_, cx| {
cx.update(|cx| {
workspace_windows.sort_by_key(|window| window.is_active(&cx) == Some(false));
})
.log_err();
if let (true, Some(_)) = (should_confirm, workspace_windows.first().copied()) {
let answer = cx
.update(|_, cx| {
if let (true, Some(workspace)) = (should_confirm, workspace_windows.first().copied()) {
let answer = workspace
.update(&mut cx, |_, cx| {
cx.prompt(
PromptLevel::Info,
"Are you sure you want to quit?",
@ -500,9 +495,7 @@ fn quit(_: &mut Workspace, _: &Quit, cx: &mut gpui::ViewContext<Workspace>) {
}
}
}
cx.update(|_, cx| {
cx.quit();
})?;
cx.update(|cx| cx.quit())?;
anyhow::Ok(())
})
.detach_and_log_err(cx);
@ -564,38 +557,60 @@ pub fn handle_keymap_file_changes(
mut user_keymap_file_rx: mpsc::UnboundedReceiver<String>,
cx: &mut AppContext,
) {
cx.spawn(move |cx| async move {
// let mut settings_subscription = None;
while let Some(user_keymap_content) = user_keymap_file_rx.next().await {
if let Some(keymap_content) = KeymapFile::parse(&user_keymap_content).log_err() {
cx.update(|cx| reload_keymaps(cx, &keymap_content)).ok();
BaseKeymap::register(cx);
// todo!()
// let mut old_base_keymap = cx.read(|cx| *settings::get::<BaseKeymap>(cx));
// drop(settings_subscription);
// settings_subscription = Some(cx.update(|cx| {
// cx.observe_global::<SettingsStore, _>(move |cx| {
// let new_base_keymap = *settings::get::<BaseKeymap>(cx);
// if new_base_keymap != old_base_keymap {
// old_base_keymap = new_base_keymap.clone();
// reload_keymaps(cx, &keymap_content);
// }
// })
// }));
let (base_keymap_tx, mut base_keymap_rx) = mpsc::unbounded();
let mut old_base_keymap = *BaseKeymap::get_global(cx);
cx.observe_global::<SettingsStore>(move |cx| {
let new_base_keymap = *BaseKeymap::get_global(cx);
if new_base_keymap != old_base_keymap {
old_base_keymap = new_base_keymap.clone();
base_keymap_tx.unbounded_send(()).unwrap();
}
})
.detach();
load_default_keymap(cx);
cx.spawn(move |cx| async move {
let mut user_keymap = KeymapFile::default();
loop {
select_biased! {
_ = base_keymap_rx.next() => {}
user_keymap_content = user_keymap_file_rx.next() => {
if let Some(user_keymap_content) = user_keymap_content {
if let Some(keymap_content) = KeymapFile::parse(&user_keymap_content).log_err() {
user_keymap = keymap_content;
} else {
continue
}
}
}
}
cx.update(|cx| reload_keymaps(cx, &user_keymap)).ok();
}
})
.detach();
}
fn reload_keymaps(cx: &mut AppContext, keymap_content: &KeymapFile) {
// todo!()
// cx.clear_bindings();
cx.clear_key_bindings();
load_default_keymap(cx);
keymap_content.clone().add_to_cx(cx).log_err();
cx.set_menus(app_menus());
}
pub fn load_default_keymap(cx: &mut AppContext) {
for path in ["keymaps/default.json", "keymaps/vim.json"] {
KeymapFile::load_asset(path, cx).unwrap();
}
if let Some(asset_path) = BaseKeymap::get_global(cx).asset_path() {
KeymapFile::load_asset(asset_path, cx).unwrap();
}
}
fn open_local_settings_file(
workspace: &mut Workspace,
_: &OpenLocalSettings,