Compare commits

..

11 Commits

Author SHA1 Message Date
Max Brunsfeld
7a3a520c6b zed 0.91.4 2023-06-27 15:25:29 -07:00
Max Brunsfeld
b8339a9908 Fix bugs in handling combined injections exposed by HEEx (#2652)
Fixes
https://linear.app/zed-industries/issue/Z-2481/heex-this-snippet-triggers-a-hard-crash

Release Notes:

- Fixed a crash that would sometimes occur when editing a HEEx file
([#1703](https://github.com/zed-industries/community/issues/1703)).
2023-06-27 15:22:06 -07:00
Joseph T. Lyons
eab4bd5720 v0.91.x stable 2023-06-21 13:58:53 -04:00
Antonio Scandurra
707bd7c156 zed 0.91.3 2023-06-16 17:27:42 +02:00
Antonio Scandurra
656f68dc69 Activate screen-sharing when leader activates a panel (#2614)
Fixes
https://linear.app/zed-industries/issue/Z-1875/screen-sharing-tab-is-not-activated-when-leader-is-on-a-panel

Release Notes:

- Fixed a bug that caused followers to not see the leader's screen when
they activated a panel.
2023-06-16 17:26:33 +02:00
Max Brunsfeld
186334bb12 zed 0.91.2 2023-06-15 15:34:43 -07:00
Max Brunsfeld
b1324ebe1f Fix failure to upload panics when multiple panics happen at the same time (#2616)
When multiple panics occur at the same time (usually because one thread
panics, and another thread joins it), multiple panic JSON objects can
get written to the same panic file. The resulting file won't be valid
JSON.

This PR addresses that problem via two changes:
* Format panic files as single-line JSON objects
* When a panic file  isn't valid JSON, try taking the first line

In the future, we could try combining all of the backtraces, but for
now, I just want to avoid a problem of not reporting a panic at all.

Release Notes:

- Fixed a problem with Zed's internal crash reporting.
2023-06-15 15:34:09 -07:00
Max Brunsfeld
e9aec1d67a Don't rely on debug symbols for panic reporting (#2615)
This fixes a regression introduced in
https://github.com/zed-industries/zed/pull/2560, where panic reports did
not include backtraces. The problem was that in that PR, I assumed we
could retrieve file paths for symbols in our backtraces. But actually,
that functionality only works when the app is built locally, and a
`.dSYM` file can be magically found by the OS. We don't ship those dSYM
files with Zed, so panic symbols do not have file paths available.

Panic backtraces will still be more useful and less noisy than before
though: we will strip out frames for which we don't have symbol names,
and remove leading panic-handling stack frames from the backtraces.

Release Notes:

- N/A
2023-06-15 15:34:01 -07:00
Mikayla Maki
32ed547c2c zed 0.91.1 2023-06-14 18:10:16 -07:00
Mikayla Maki
39915f7c96 Add entitlements file to bundle step (#2611)
This completes the bundle changes that will be needed to access voice,
as well as adds permissions for accessing other MacOS services, the
camera, and the necessary permissions for plugins. This was developed by
combining the entitlements of iTerm and VSCode, cross-referenced with
the entitlements of Firefox. 

Release Notes:

- Fixed a bug in enabling authorization for macOS services (preview
only)
2023-06-14 18:09:15 -07:00
Joseph Lyons
2c2cea1c92 v0.91.x preview 2023-06-14 13:27:18 -04:00
154 changed files with 2230 additions and 7927 deletions

1
.gitignore vendored
View File

@@ -18,5 +18,4 @@ DerivedData/
.swiftpm/config/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
.swiftpm
**/*.db

61
Cargo.lock generated
View File

@@ -109,14 +109,11 @@ dependencies = [
"isahc",
"language",
"menu",
"project",
"regex",
"schemars",
"search",
"serde",
"serde_json",
"settings",
"smol",
"theme",
"tiktoken-rs",
"util",
@@ -596,7 +593,7 @@ dependencies = [
"http",
"http-body",
"hyper",
"itoa 1.0.6",
"itoa",
"matchit",
"memchr",
"mime",
@@ -3014,7 +3011,7 @@ checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
dependencies = [
"bytes 1.4.0",
"fnv",
"itoa 1.0.6",
"itoa",
]
[[package]]
@@ -3073,7 +3070,7 @@ dependencies = [
"http-body",
"httparse",
"httpdate",
"itoa 1.0.6",
"itoa",
"pin-project-lite 0.2.9",
"socket2",
"tokio",
@@ -3339,12 +3336,6 @@ dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
[[package]]
name = "itoa"
version = "1.0.6"
@@ -3405,6 +3396,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "json_comments"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41ee439ee368ba4a77ac70d04f14015415af8600d6c894dc1f11bd79758c57d5"
[[package]]
name = "jwt"
version = "0.16.0"
@@ -5670,7 +5667,7 @@ dependencies = [
"bitflags",
"errno 0.2.8",
"io-lifetimes 0.5.3",
"itoa 1.0.6",
"itoa",
"libc",
"linux-raw-sys 0.0.42",
"once_cell",
@@ -6102,19 +6099,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
dependencies = [
"indexmap",
"itoa 1.0.6",
"ryu",
"serde",
]
[[package]]
name = "serde_json_lenient"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d7b9ce5b0a63c6269b9623ed828b39259545a6ec0d8a35d6135ad6af6232add"
dependencies = [
"indexmap",
"itoa 0.4.8",
"itoa",
"ryu",
"serde",
]
@@ -6137,7 +6122,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [
"form_urlencoded",
"itoa 1.0.6",
"itoa",
"ryu",
"serde",
]
@@ -6163,7 +6148,7 @@ dependencies = [
"fs",
"futures 0.3.28",
"gpui",
"indoc",
"json_comments",
"lazy_static",
"postage",
"pretty_assertions",
@@ -6172,7 +6157,6 @@ dependencies = [
"serde",
"serde_derive",
"serde_json",
"serde_json_lenient",
"smallvec",
"sqlez",
"staff_mode",
@@ -6523,7 +6507,7 @@ dependencies = [
"hkdf",
"hmac 0.12.1",
"indexmap",
"itoa 1.0.6",
"itoa",
"libc",
"libsqlite3-sys",
"log",
@@ -6918,6 +6902,18 @@ dependencies = [
"workspace",
]
[[package]]
name = "theme_testbench"
version = "0.1.0"
dependencies = [
"gpui",
"project",
"settings",
"smallvec",
"theme",
"workspace",
]
[[package]]
name = "thiserror"
version = "1.0.40"
@@ -6997,7 +6993,7 @@ version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc"
dependencies = [
"itoa 1.0.6",
"itoa",
"serde",
"time-core",
"time-macros",
@@ -8799,7 +8795,7 @@ dependencies = [
[[package]]
name = "zed"
version = "0.92.2"
version = "0.91.4"
dependencies = [
"activity_indicator",
"ai",
@@ -8878,6 +8874,7 @@ dependencies = [
"text",
"theme",
"theme_selector",
"theme_testbench",
"thiserror",
"tiny_http",
"toml",

View File

@@ -61,6 +61,7 @@ members = [
"crates/text",
"crates/theme",
"crates/theme_selector",
"crates/theme_testbench",
"crates/util",
"crates/vim",
"crates/workspace",

View File

@@ -1 +0,0 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M13.9 0.499976C13.9 0.279062 13.7209 0.0999756 13.5 0.0999756C13.2791 0.0999756 13.1 0.279062 13.1 0.499976V1.09998H12.5C12.2791 1.09998 12.1 1.27906 12.1 1.49998C12.1 1.72089 12.2791 1.89998 12.5 1.89998H13.1V2.49998C13.1 2.72089 13.2791 2.89998 13.5 2.89998C13.7209 2.89998 13.9 2.72089 13.9 2.49998V1.89998H14.5C14.7209 1.89998 14.9 1.72089 14.9 1.49998C14.9 1.27906 14.7209 1.09998 14.5 1.09998H13.9V0.499976ZM11.8536 3.14642C12.0488 3.34168 12.0488 3.65826 11.8536 3.85353L10.8536 4.85353C10.6583 5.04879 10.3417 5.04879 10.1465 4.85353C9.9512 4.65827 9.9512 4.34169 10.1465 4.14642L11.1464 3.14643C11.3417 2.95116 11.6583 2.95116 11.8536 3.14642ZM9.85357 5.14642C10.0488 5.34168 10.0488 5.65827 9.85357 5.85353L2.85355 12.8535C2.65829 13.0488 2.34171 13.0488 2.14645 12.8535C1.95118 12.6583 1.95118 12.3417 2.14645 12.1464L9.14646 5.14642C9.34172 4.95116 9.65831 4.95116 9.85357 5.14642ZM13.5 5.09998C13.7209 5.09998 13.9 5.27906 13.9 5.49998V6.09998H14.5C14.7209 6.09998 14.9 6.27906 14.9 6.49998C14.9 6.72089 14.7209 6.89998 14.5 6.89998H13.9V7.49998C13.9 7.72089 13.7209 7.89998 13.5 7.89998C13.2791 7.89998 13.1 7.72089 13.1 7.49998V6.89998H12.5C12.2791 6.89998 12.1 6.72089 12.1 6.49998C12.1 6.27906 12.2791 6.09998 12.5 6.09998H13.1V5.49998C13.1 5.27906 13.2791 5.09998 13.5 5.09998ZM8.90002 0.499976C8.90002 0.279062 8.72093 0.0999756 8.50002 0.0999756C8.2791 0.0999756 8.10002 0.279062 8.10002 0.499976V1.09998H7.50002C7.2791 1.09998 7.10002 1.27906 7.10002 1.49998C7.10002 1.72089 7.2791 1.89998 7.50002 1.89998H8.10002V2.49998C8.10002 2.72089 8.2791 2.89998 8.50002 2.89998C8.72093 2.89998 8.90002 2.72089 8.90002 2.49998V1.89998H9.50002C9.72093 1.89998 9.90002 1.72089 9.90002 1.49998C9.90002 1.27906 9.72093 1.09998 9.50002 1.09998H8.90002V0.499976Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -1,3 +0,0 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 3C1.22386 3 1 3.22386 1 3.5C1 3.77614 1.22386 4 1.5 4H13.5C13.7761 4 14 3.77614 14 3.5C14 3.22386 13.7761 3 13.5 3H1.5ZM1 7.5C1 7.22386 1.22386 7 1.5 7H13.5C13.7761 7 14 7.22386 14 7.5C14 7.77614 13.7761 8 13.5 8H1.5C1.22386 8 1 7.77614 1 7.5ZM1 11.5C1 11.2239 1.22386 11 1.5 11H13.5C13.7761 11 14 11.2239 14 11.5C14 11.7761 13.7761 12 13.5 12H1.5C1.22386 12 1 11.7761 1 11.5Z" fill="#CCCAC2"/>
</svg>

Before

Width:  |  Height:  |  Size: 552 B

View File

@@ -1 +0,0 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9.42503 3.44136C10.0561 3.23654 10.7837 3.2402 11.3792 3.54623C12.7532 4.25224 13.3477 6.07191 12.7946 8C12.5465 8.8649 12.1102 9.70472 11.1861 10.5524C10.262 11.4 8.98034 11.9 8.38571 11.9C8.17269 11.9 8 11.7321 8 11.525C8 11.3179 8.17644 11.15 8.38571 11.15C9.06497 11.15 9.67189 10.7804 10.3906 10.236C10.9406 9.8193 11.3701 9.28633 11.608 8.82191C12.0628 7.93367 12.0782 6.68174 11.3433 6.34901C10.9904 6.73455 10.5295 6.95946 9.97725 6.95946C8.7773 6.95946 8.0701 5.99412 8.10051 5.12009C8.12957 4.28474 8.66032 3.68954 9.42503 3.44136ZM3.42503 3.44136C4.05614 3.23654 4.78366 3.2402 5.37923 3.54623C6.7532 4.25224 7.34766 6.07191 6.79462 8C6.54654 8.8649 6.11019 9.70472 5.1861 10.5524C4.26201 11.4 2.98034 11.9 2.38571 11.9C2.17269 11.9 2 11.7321 2 11.525C2 11.3179 2.17644 11.15 2.38571 11.15C3.06497 11.15 3.67189 10.7804 4.39058 10.236C4.94065 9.8193 5.37014 9.28633 5.60797 8.82191C6.06282 7.93367 6.07821 6.68174 5.3433 6.34901C4.99037 6.73455 4.52948 6.95946 3.97725 6.95946C2.7773 6.95946 2.0701 5.99412 2.10051 5.12009C2.12957 4.28474 2.66032 3.68954 3.42503 3.44136Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1 +0,0 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7.81832 0.68179C7.64258 0.506054 7.35766 0.506054 7.18192 0.68179L5.18192 2.68179C5.00619 2.85753 5.00619 3.14245 5.18192 3.31819C5.35766 3.49392 5.64258 3.49392 5.81832 3.31819L7.05012 2.08638L7.05012 5.50023C7.05012 5.74876 7.25159 5.95023 7.50012 5.95023C7.74865 5.95023 7.95012 5.74876 7.95012 5.50023L7.95012 2.08638L9.18192 3.31819C9.35766 3.49392 9.64258 3.49392 9.81832 3.31819C9.99406 3.14245 9.99406 2.85753 9.81832 2.68179L7.81832 0.68179ZM7.95012 12.9136V9.50023C7.95012 9.2517 7.74865 9.05023 7.50012 9.05023C7.25159 9.05023 7.05012 9.2517 7.05012 9.50023V12.9136L5.81832 11.6818C5.64258 11.5061 5.35766 11.5061 5.18192 11.6818C5.00619 11.8575 5.00619 12.1424 5.18192 12.3182L7.18192 14.3182C7.26632 14.4026 7.38077 14.45 7.50012 14.45C7.61947 14.45 7.73393 14.4026 7.81832 14.3182L9.81832 12.3182C9.99406 12.1424 9.99406 11.8575 9.81832 11.6818C9.64258 11.5061 9.35766 11.5061 9.18192 11.6818L7.95012 12.9136ZM1.49994 7.00017C1.2238 7.00017 0.999939 7.22403 0.999939 7.50017C0.999939 7.77631 1.2238 8.00017 1.49994 8.00017L13.4999 8.00017C13.7761 8.00017 13.9999 7.77631 13.9999 7.50017C13.9999 7.22403 13.7761 7.00017 13.4999 7.00017L1.49994 7.00017Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -55,40 +55,7 @@
"context": "Pane",
"bindings": {
"alt-cmd-/": "search::ToggleRegex",
"ctrl-0": "project_panel::ToggleFocus",
"cmd-1": [
"pane::ActivateItem",
0
],
"cmd-2": [
"pane::ActivateItem",
1
],
"cmd-3": [
"pane::ActivateItem",
2
],
"cmd-4": [
"pane::ActivateItem",
3
],
"cmd-5": [
"pane::ActivateItem",
4
],
"cmd-6": [
"pane::ActivateItem",
5
],
"cmd-7": [
"pane::ActivateItem",
6
],
"cmd-8": [
"pane::ActivateItem",
7
],
"cmd-9": "pane::ActivateLastItem"
"ctrl-0": "project_panel::ToggleFocus"
}
},
{

View File

@@ -40,8 +40,7 @@
"cmd-o": "workspace::Open",
"alt-cmd-o": "projects::OpenRecent",
"ctrl-~": "workspace::NewTerminal",
"ctrl-`": "terminal_panel::ToggleFocus",
"shift-escape": "workspace::ToggleZoom"
"ctrl-`": "terminal_panel::ToggleFocus"
}
},
{
@@ -198,20 +197,10 @@
}
},
{
"context": "AssistantPanel",
"bindings": {
"cmd-g": "search::SelectNextMatch",
"cmd-shift-g": "search::SelectPrevMatch"
}
},
{
"context": "ConversationEditor > Editor",
"context": "AssistantEditor > Editor",
"bindings": {
"cmd-enter": "assistant::Assist",
"cmd-s": "workspace::Save",
"cmd->": "assistant::QuoteSelection",
"shift-enter": "assistant::Split",
"ctrl-r": "assistant::CycleMessageRole"
"cmd->": "assistant::QuoteSelection"
}
},
{
@@ -243,7 +232,8 @@
"cmd-shift-g": "search::SelectPrevMatch",
"alt-cmd-c": "search::ToggleCaseSensitive",
"alt-cmd-w": "search::ToggleWholeWord",
"alt-cmd-r": "search::ToggleRegex"
"alt-cmd-r": "search::ToggleRegex",
"shift-escape": "workspace::ToggleZoom"
}
},
// Bindings from VS Code
@@ -408,7 +398,6 @@
"cmd-shift-p": "command_palette::Toggle",
"cmd-shift-m": "diagnostics::Deploy",
"cmd-shift-e": "project_panel::ToggleFocus",
"cmd-?": "assistant::ToggleFocus",
"cmd-alt-s": "workspace::SaveAll",
"cmd-k m": "language_selector::Toggle"
}

View File

@@ -57,37 +57,37 @@
"show_whitespaces": "selection",
// Scrollbar related settings
"scrollbar": {
// When to show the scrollbar in the editor.
// This setting can take four values:
//
// 1. Show the scrollbar if there's important information or
// follow the system's configured behavior (default):
// "auto"
// 2. Match the system's configured behavior:
// "system"
// 3. Always show the scrollbar:
// "always"
// 4. Never show the scrollbar:
// "never"
"show": "auto",
// Whether to show git diff indicators in the scrollbar.
"git_diff": true
// When to show the scrollbar in the editor.
// This setting can take four values:
//
// 1. Show the scrollbar if there's important information or
// follow the system's configured behavior (default):
// "auto"
// 2. Match the system's configured behavior:
// "system"
// 3. Always show the scrollbar:
// "always"
// 4. Never show the scrollbar:
// "never"
"show": "auto",
// Whether to show git diff indicators in the scrollbar.
"git_diff": true
},
"project_panel": {
// Whether to show the git status in the project panel.
"git_status": true,
// Where to dock project panel. Can be 'left' or 'right'.
"dock": "left",
// Default width of the project panel.
"default_width": 240
// Whether to show the git status in the project panel.
"git_status": true,
// Where to dock project panel. Can be 'left' or 'right'.
"dock": "left",
// Default width of the project panel.
"default_width": 240
},
"assistant": {
// Where to dock the assistant. Can be 'left', 'right' or 'bottom'.
"dock": "right",
// Default width when the assistant is docked to the left or right.
"default_width": 640,
// Default height when the assistant is docked to the bottom.
"default_height": 320
// Where to dock the assistant. Can be 'left', 'right' or 'bottom'.
"dock": "right",
// Default width when the assistant is docked to the left or right.
"default_width": 450,
// Default height when the assistant is docked to the bottom.
"default_height": 320
},
// Whether the screen sharing icon is shown in the os status bar.
"show_call_status_icon": true,

View File

@@ -326,7 +326,7 @@ impl View for ActivityIndicator {
let mut element = MouseEventHandler::<Self, _>::new(0, cx, |state, cx| {
let theme = &theme::current(cx).workspace.status_bar.lsp_status;
let style = if state.hovered() && on_click.is_some() {
theme.hovered.as_ref().unwrap_or(&theme.default)
theme.hover.as_ref().unwrap_or(&theme.default)
} else {
&theme.default
};

View File

@@ -22,16 +22,13 @@ util = { path = "../util" }
workspace = { path = "../workspace" }
anyhow.workspace = true
chrono = { version = "0.4", features = ["serde"] }
chrono = "0.4"
futures.workspace = true
isahc.workspace = true
regex.workspace = true
schemars.workspace = true
serde.workspace = true
serde_json.workspace = true
smol.workspace = true
tiktoken-rs = "0.4"
[dev-dependencies]
editor = { path = "../editor", features = ["test-support"] }
project = { path = "../project", features = ["test-support"] }

View File

@@ -1,109 +1,19 @@
pub mod assistant;
mod assistant_settings;
use anyhow::Result;
pub use assistant::AssistantPanel;
use chrono::{DateTime, Local};
use collections::HashMap;
use fs::Fs;
use futures::StreamExt;
use gpui::AppContext;
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::{
cmp::Reverse,
fmt::{self, Display},
path::PathBuf,
sync::Arc,
};
use util::paths::CONVERSATIONS_DIR;
use std::fmt::{self, Display};
// Data types for chat completion requests
#[derive(Debug, Serialize)]
#[derive(Serialize)]
struct OpenAIRequest {
model: String,
messages: Vec<RequestMessage>,
stream: bool,
}
#[derive(
Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize,
)]
struct MessageId(usize);
#[derive(Clone, Debug, Serialize, Deserialize)]
struct MessageMetadata {
role: Role,
sent_at: DateTime<Local>,
status: MessageStatus,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
enum MessageStatus {
Pending,
Done,
Error(Arc<str>),
}
#[derive(Serialize, Deserialize)]
struct SavedMessage {
id: MessageId,
start: usize,
}
#[derive(Serialize, Deserialize)]
struct SavedConversation {
zed: String,
version: String,
text: String,
messages: Vec<SavedMessage>,
message_metadata: HashMap<MessageId, MessageMetadata>,
summary: String,
model: String,
}
impl SavedConversation {
const VERSION: &'static str = "0.1.0";
}
struct SavedConversationMetadata {
title: String,
path: PathBuf,
mtime: chrono::DateTime<chrono::Local>,
}
impl SavedConversationMetadata {
pub async fn list(fs: Arc<dyn Fs>) -> Result<Vec<Self>> {
fs.create_dir(&CONVERSATIONS_DIR).await?;
let mut paths = fs.read_dir(&CONVERSATIONS_DIR).await?;
let mut conversations = Vec::<SavedConversationMetadata>::new();
while let Some(path) = paths.next().await {
let path = path?;
let pattern = r" - \d+.zed.json$";
let re = Regex::new(pattern).unwrap();
let metadata = fs.metadata(&path).await?;
if let Some((file_name, metadata)) = path
.file_name()
.and_then(|name| name.to_str())
.zip(metadata)
{
let title = re.replace(file_name, "");
conversations.push(Self {
title: title.into_owned(),
path,
mtime: metadata.mtime.into(),
});
}
}
conversations.sort_unstable_by_key(|conversation| Reverse(conversation.mtime));
Ok(conversations)
}
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
struct RequestMessage {
role: Role,

File diff suppressed because it is too large Load Diff

View File

@@ -49,7 +49,7 @@ impl View for UpdateNotification {
)
.with_child(
MouseEventHandler::<Cancel, _>::new(0, cx, |state, _| {
let style = theme.dismiss_button.style_for(state);
let style = theme.dismiss_button.style_for(state, false);
Svg::new("icons/x_mark_8.svg")
.with_color(style.color)
.constrained()
@@ -74,7 +74,7 @@ impl View for UpdateNotification {
),
)
.with_child({
let style = theme.action_message.style_for(state);
let style = theme.action_message.style_for(state, false);
Text::new("View the release notes", style.text.clone())
.contained()
.with_style(style.container)

View File

@@ -83,7 +83,7 @@ impl View for Breadcrumbs {
}
MouseEventHandler::<Breadcrumbs, Breadcrumbs>::new(0, cx, |state, _| {
let style = style.style_for(state);
let style = style.style_for(state, false);
crumbs.with_style(style.container)
})
.on_click(MouseButton::Left, |_, this, cx| {

View File

@@ -299,12 +299,7 @@ impl CollabTitlebarItem {
pub fn toggle_user_menu(&mut self, _: &ToggleUserMenu, cx: &mut ViewContext<Self>) {
let theme = theme::current(cx).clone();
let avatar_style = theme.workspace.titlebar.leader_avatar.clone();
let item_style = theme
.context_menu
.item
.inactive_state()
.disabled_style()
.clone();
let item_style = theme.context_menu.item.disabled_style().clone();
self.user_menu.update(cx, |user_menu, cx| {
let items = if let Some(user) = self.user_store.read(cx).current_user() {
vec![
@@ -366,20 +361,8 @@ impl CollabTitlebarItem {
.contained()
.with_style(titlebar.toggle_contacts_badge)
.contained()
.with_margin_left(
titlebar
.toggle_contacts_button
.inactive_state()
.default
.icon_width,
)
.with_margin_top(
titlebar
.toggle_contacts_button
.inactive_state()
.default
.icon_width,
)
.with_margin_left(titlebar.toggle_contacts_button.default.icon_width)
.with_margin_top(titlebar.toggle_contacts_button.default.icon_width)
.aligned(),
)
};
@@ -389,8 +372,7 @@ impl CollabTitlebarItem {
MouseEventHandler::<ToggleContactsMenu, Self>::new(0, cx, |state, _| {
let style = titlebar
.toggle_contacts_button
.in_state(self.contacts_popover.is_some())
.style_for(state);
.style_for(state, self.contacts_popover.is_some());
Svg::new("icons/user_plus_16.svg")
.with_color(style.color)
.constrained()
@@ -437,7 +419,7 @@ impl CollabTitlebarItem {
let titlebar = &theme.workspace.titlebar;
MouseEventHandler::<ToggleScreenSharing, Self>::new(0, cx, |state, _| {
let style = titlebar.call_control.style_for(state);
let style = titlebar.call_control.style_for(state, false);
Svg::new(icon)
.with_color(style.color)
.constrained()
@@ -491,7 +473,7 @@ impl CollabTitlebarItem {
.with_child(
MouseEventHandler::<ShareUnshare, Self>::new(0, cx, |state, _| {
//TODO: Ensure this button has consistent width for both text variations
let style = titlebar.share_button.inactive_state().style_for(state);
let style = titlebar.share_button.style_for(state, false);
Label::new(label, style.text.clone())
.contained()
.with_style(style.container)
@@ -529,7 +511,7 @@ impl CollabTitlebarItem {
Stack::new()
.with_child(
MouseEventHandler::<ToggleUserMenu, Self>::new(0, cx, |state, _| {
let style = titlebar.call_control.style_for(state);
let style = titlebar.call_control.style_for(state, false);
Svg::new("icons/ellipsis_14.svg")
.with_color(style.color)
.constrained()
@@ -567,7 +549,7 @@ impl CollabTitlebarItem {
fn render_sign_in_button(&self, theme: &Theme, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
let titlebar = &theme.workspace.titlebar;
MouseEventHandler::<SignIn, Self>::new(0, cx, |state, _| {
let style = titlebar.sign_in_prompt.inactive_state().style_for(state);
let style = titlebar.sign_in_prompt.style_for(state, false);
Label::new("Sign In", style.text.clone())
.contained()
.with_style(style.container)

View File

@@ -117,8 +117,7 @@ impl PickerDelegate for ContactFinderDelegate {
.contact_finder
.picker
.item
.in_state(selected)
.style_for(mouse_state);
.style_for(mouse_state, selected);
Flex::row()
.with_children(user.avatar.clone().map(|avatar| {
Image::from_data(avatar)

View File

@@ -774,8 +774,7 @@ impl ContactList {
.with_style(
*theme
.contact_row
.in_state(is_selected)
.style_for(&mut Default::default()),
.style_for(&mut Default::default(), is_selected),
)
.into_any()
}
@@ -798,7 +797,7 @@ impl ContactList {
.width
.or(theme.contact_avatar.height)
.unwrap_or(0.);
let row = &theme.project_row.inactive_state().default;
let row = &theme.project_row.default;
let tree_branch = theme.tree_branch;
let line_height = row.name.text.line_height(font_cache);
let cap_height = row.name.text.cap_height(font_cache);
@@ -811,11 +810,8 @@ impl ContactList {
};
MouseEventHandler::<JoinProject, Self>::new(project_id as usize, cx, |mouse_state, _| {
let tree_branch = *tree_branch.in_state(is_selected).style_for(mouse_state);
let row = theme
.project_row
.in_state(is_selected)
.style_for(mouse_state);
let tree_branch = *tree_branch.style_for(mouse_state, is_selected);
let row = theme.project_row.style_for(mouse_state, is_selected);
Flex::row()
.with_child(
@@ -897,7 +893,7 @@ impl ContactList {
.width
.or(theme.contact_avatar.height)
.unwrap_or(0.);
let row = &theme.project_row.inactive_state().default;
let row = &theme.project_row.default;
let tree_branch = theme.tree_branch;
let line_height = row.name.text.line_height(font_cache);
let cap_height = row.name.text.cap_height(font_cache);
@@ -908,11 +904,8 @@ impl ContactList {
peer_id.as_u64() as usize,
cx,
|mouse_state, _| {
let tree_branch = *tree_branch.in_state(is_selected).style_for(mouse_state);
let row = theme
.project_row
.in_state(is_selected)
.style_for(mouse_state);
let tree_branch = *tree_branch.style_for(mouse_state, is_selected);
let row = theme.project_row.style_for(mouse_state, is_selected);
Flex::row()
.with_child(
@@ -996,8 +989,7 @@ impl ContactList {
let header_style = theme
.header_row
.in_state(is_selected)
.style_for(&mut Default::default());
.style_for(&mut Default::default(), is_selected);
let text = match section {
Section::ActiveCall => "Collaborators",
Section::Requests => "Contact Requests",
@@ -1007,7 +999,7 @@ impl ContactList {
let leave_call = if section == Section::ActiveCall {
Some(
MouseEventHandler::<LeaveCallContactList, Self>::new(0, cx, |state, _| {
let style = theme.leave_call.style_for(state);
let style = theme.leave_call.style_for(state, false);
Label::new("Leave Call", style.text.clone())
.contained()
.with_style(style.container)
@@ -1118,7 +1110,8 @@ impl ContactList {
contact.user.id as usize,
cx,
|mouse_state, _| {
let button_style = theme.contact_button.style_for(mouse_state);
let button_style =
theme.contact_button.style_for(mouse_state, false);
render_icon_button(button_style, "icons/x_mark_8.svg")
.aligned()
.flex_float()
@@ -1153,8 +1146,7 @@ impl ContactList {
.with_style(
*theme
.contact_row
.in_state(is_selected)
.style_for(&mut Default::default()),
.style_for(&mut Default::default(), is_selected),
)
})
.on_click(MouseButton::Left, move |_, this, cx| {
@@ -1212,7 +1204,7 @@ impl ContactList {
let button_style = if is_contact_request_pending {
&theme.disabled_button
} else {
theme.contact_button.style_for(mouse_state)
theme.contact_button.style_for(mouse_state, false)
};
render_icon_button(button_style, "icons/x_mark_8.svg").aligned()
})
@@ -1235,7 +1227,7 @@ impl ContactList {
let button_style = if is_contact_request_pending {
&theme.disabled_button
} else {
theme.contact_button.style_for(mouse_state)
theme.contact_button.style_for(mouse_state, false)
};
render_icon_button(button_style, "icons/check_8.svg")
.aligned()
@@ -1258,7 +1250,7 @@ impl ContactList {
let button_style = if is_contact_request_pending {
&theme.disabled_button
} else {
theme.contact_button.style_for(mouse_state)
theme.contact_button.style_for(mouse_state, false)
};
render_icon_button(button_style, "icons/x_mark_8.svg")
.aligned()
@@ -1285,8 +1277,7 @@ impl ContactList {
.with_style(
*theme
.contact_row
.in_state(is_selected)
.style_for(&mut Default::default()),
.style_for(&mut Default::default(), is_selected),
)
.into_any()
}

View File

@@ -53,7 +53,7 @@ where
)
.with_child(
MouseEventHandler::<Dismiss, V>::new(user.id as usize, cx, |state, _| {
let style = theme.dismiss_button.style_for(state);
let style = theme.dismiss_button.style_for(state, false);
Svg::new("icons/x_mark_8.svg")
.with_color(style.color)
.constrained()
@@ -93,7 +93,7 @@ where
.with_children(buttons.into_iter().enumerate().map(
|(ix, (message, handler))| {
MouseEventHandler::<Button, V>::new(ix, cx, |state, _| {
let button = theme.button.style_for(state);
let button = theme.button.style_for(state, false);
Label::new(message, button.text.clone())
.contained()
.with_style(button.container)

View File

@@ -185,8 +185,8 @@ impl PickerDelegate for CommandPaletteDelegate {
let mat = &self.matches[ix];
let command = &self.actions[mat.candidate_id];
let theme = theme::current(cx);
let style = theme.picker.item.in_state(selected).style_for(mouse_state);
let key_style = &theme.command_palette.key.in_state(selected);
let style = theme.picker.item.style_for(mouse_state, selected);
let key_style = &theme.command_palette.key.style_for(mouse_state, selected);
let keystroke_spacing = theme.command_palette.keystroke_spacing;
Flex::row()

View File

@@ -328,8 +328,10 @@ impl ContextMenu {
Flex::column().with_children(self.items.iter().enumerate().map(|(ix, item)| {
match item {
ContextMenuItem::Item { label, .. } => {
let style = style.item.in_state(self.selected_index == Some(ix));
let style = style.style_for(&mut Default::default());
let style = style.item.style_for(
&mut Default::default(),
Some(ix) == self.selected_index,
);
match label {
ContextMenuItemLabel::String(label) => {
@@ -361,8 +363,10 @@ impl ContextMenu {
.with_children(self.items.iter().enumerate().map(|(ix, item)| {
match item {
ContextMenuItem::Item { action, .. } => {
let style = style.item.in_state(self.selected_index == Some(ix));
let style = style.style_for(&mut Default::default());
let style = style.item.style_for(
&mut Default::default(),
Some(ix) == self.selected_index,
);
match action {
ContextMenuItemAction::Action(action) => KeystrokeLabel::new(
@@ -408,8 +412,8 @@ impl ContextMenu {
let action = action.clone();
let view_id = self.parent_view_id;
MouseEventHandler::<MenuItem, ContextMenu>::new(ix, cx, |state, _| {
let style = style.item.in_state(self.selected_index == Some(ix));
let style = style.style_for(state);
let style =
style.item.style_for(state, Some(ix) == self.selected_index);
let keystroke = match &action {
ContextMenuItemAction::Action(action) => Some(
KeystrokeLabel::new(

View File

@@ -127,16 +127,16 @@ impl CopilotCodeVerification {
.with_child(
Label::new(
if copied { "Copied!" } else { "Copy" },
device_code_style.cta.style_for(state).text.clone(),
device_code_style.cta.style_for(state, false).text.clone(),
)
.aligned()
.contained()
.with_style(*device_code_style.right_container.style_for(state))
.with_style(*device_code_style.right_container.style_for(state, false))
.constrained()
.with_width(device_code_style.right),
)
.contained()
.with_style(device_code_style.cta.style_for(state).container)
.with_style(device_code_style.cta.style_for(state, false).container)
})
.on_click(gpui::platform::MouseButton::Left, {
let user_code = data.user_code.clone();

View File

@@ -71,8 +71,7 @@ impl View for CopilotButton {
.status_bar
.panel_buttons
.button
.in_state(active)
.style_for(state);
.style_for(state, active);
Flex::row()
.with_child(
@@ -256,7 +255,7 @@ impl CopilotButton {
move |state: &mut MouseState, style: &theme::ContextMenuItem| {
Flex::row()
.with_child(Label::new("Copilot Settings", style.label.clone()))
.with_child(theme::ui::icon(icon_style.style_for(state)))
.with_child(theme::ui::icon(icon_style.style_for(state, false)))
.align_children_center()
.into_any()
},

View File

@@ -1509,8 +1509,7 @@ mod tests {
let snapshot = editor.snapshot(cx);
snapshot
.blocks_in_range(0..snapshot.max_point().row())
.enumerate()
.filter_map(|(ix, (row, block))| {
.filter_map(|(row, block)| {
let name = match block {
TransformBlock::Custom(block) => block
.render(&mut BlockContext {
@@ -1521,7 +1520,6 @@ mod tests {
gutter_width: 0.,
line_height: 0.,
em_width: 0.,
block_id: ix,
})
.name()?
.to_string(),

View File

@@ -100,7 +100,7 @@ impl View for DiagnosticIndicator {
.workspace
.status_bar
.diagnostic_summary
.style_for(state);
.style_for(state, false);
let mut summary_row = Flex::row();
if self.summary.error_count > 0 {
@@ -198,7 +198,7 @@ impl View for DiagnosticIndicator {
MouseEventHandler::<Message, _>::new(1, cx, |state, _| {
Label::new(
diagnostic.message.split('\n').next().unwrap().to_string(),
message_style.style_for(state).text.clone(),
message_style.style_for(state, false).text.clone(),
)
.aligned()
.contained()

View File

@@ -88,7 +88,6 @@ pub struct BlockContext<'a, 'b, 'c> {
pub gutter_padding: f32,
pub em_width: f32,
pub line_height: f32,
pub block_id: usize,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
@@ -244,7 +243,7 @@ impl BlockMap {
// Preserve any old transforms that precede this edit.
let old_start = WrapRow(edit.old.start);
let new_start = WrapRow(edit.new.start);
new_transforms.append(cursor.slice(&old_start, Bias::Left, &()), &());
new_transforms.push_tree(cursor.slice(&old_start, Bias::Left, &()), &());
if let Some(transform) = cursor.item() {
if transform.is_isomorphic() && old_start == cursor.end(&()) {
new_transforms.push(transform.clone(), &());
@@ -426,7 +425,7 @@ impl BlockMap {
push_isomorphic(&mut new_transforms, extent_after_edit);
}
new_transforms.append(cursor.suffix(&()), &());
new_transforms.push_tree(cursor.suffix(&()), &());
debug_assert_eq!(
new_transforms.summary().input_rows,
wrap_snapshot.max_point().row() + 1

View File

@@ -115,10 +115,10 @@ impl<'a> FoldMapWriter<'a> {
let mut new_tree = SumTree::new();
let mut cursor = self.0.folds.cursor::<Fold>();
for fold in folds {
new_tree.append(cursor.slice(&fold, Bias::Right, &buffer), &buffer);
new_tree.push_tree(cursor.slice(&fold, Bias::Right, &buffer), &buffer);
new_tree.push(fold, &buffer);
}
new_tree.append(cursor.suffix(&buffer), &buffer);
new_tree.push_tree(cursor.suffix(&buffer), &buffer);
new_tree
};
@@ -165,10 +165,10 @@ impl<'a> FoldMapWriter<'a> {
let mut cursor = self.0.folds.cursor::<usize>();
let mut folds = SumTree::new();
for fold_ix in fold_ixs_to_delete {
folds.append(cursor.slice(&fold_ix, Bias::Right, &buffer), &buffer);
folds.push_tree(cursor.slice(&fold_ix, Bias::Right, &buffer), &buffer);
cursor.next(&buffer);
}
folds.append(cursor.suffix(&buffer), &buffer);
folds.push_tree(cursor.suffix(&buffer), &buffer);
folds
};
@@ -302,7 +302,7 @@ impl FoldMap {
cursor.seek(&0, Bias::Right, &());
while let Some(mut edit) = buffer_edits_iter.next() {
new_transforms.append(cursor.slice(&edit.old.start, Bias::Left, &()), &());
new_transforms.push_tree(cursor.slice(&edit.old.start, Bias::Left, &()), &());
edit.new.start -= edit.old.start - cursor.start();
edit.old.start = *cursor.start();
@@ -412,7 +412,7 @@ impl FoldMap {
}
}
new_transforms.append(cursor.suffix(&()), &());
new_transforms.push_tree(cursor.suffix(&()), &());
if new_transforms.is_empty() {
let text_summary = new_buffer.text_summary();
new_transforms.push(

View File

@@ -353,7 +353,7 @@ impl WrapSnapshot {
}
old_cursor.next(&());
new_transforms.append(
new_transforms.push_tree(
old_cursor.slice(&next_edit.old.start, Bias::Right, &()),
&(),
);
@@ -366,7 +366,7 @@ impl WrapSnapshot {
new_transforms.push_or_extend(Transform::isomorphic(summary));
}
old_cursor.next(&());
new_transforms.append(old_cursor.suffix(&()), &());
new_transforms.push_tree(old_cursor.suffix(&()), &());
}
}
}
@@ -500,7 +500,7 @@ impl WrapSnapshot {
new_transforms.push_or_extend(Transform::isomorphic(summary));
}
old_cursor.next(&());
new_transforms.append(
new_transforms.push_tree(
old_cursor.slice(
&TabPoint::new(next_edit.old_rows.start, 0),
Bias::Right,
@@ -517,7 +517,7 @@ impl WrapSnapshot {
new_transforms.push_or_extend(Transform::isomorphic(summary));
}
old_cursor.next(&());
new_transforms.append(old_cursor.suffix(&()), &());
new_transforms.push_tree(old_cursor.suffix(&()), &());
}
}
}

View File

@@ -3320,21 +3320,15 @@ impl Editor {
pub fn render_code_actions_indicator(
&self,
style: &EditorStyle,
is_active: bool,
active: bool,
cx: &mut ViewContext<Self>,
) -> Option<AnyElement<Self>> {
if self.available_code_actions.is_some() {
enum CodeActions {}
Some(
MouseEventHandler::<CodeActions, _>::new(0, cx, |state, _| {
Svg::new("icons/bolt_8.svg").with_color(
style
.code_actions
.indicator
.in_state(is_active)
.style_for(state)
.color,
)
Svg::new("icons/bolt_8.svg")
.with_color(style.code_actions.indicator.style_for(state, active).color)
})
.with_cursor_style(CursorStyle::PointingHand)
.with_padding(Padding::uniform(3.))
@@ -3384,8 +3378,10 @@ impl Editor {
.with_color(
style
.indicator
.in_state(fold_status == FoldStatus::Folded)
.style_for(mouse_state)
.style_for(
mouse_state,
fold_status == FoldStatus::Folded,
)
.color,
)
.constrained()
@@ -7953,7 +7949,6 @@ impl Deref for EditorStyle {
pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
let mut highlighted_lines = Vec::new();
for (index, line) in diagnostic.message.lines().enumerate() {
let line = match &diagnostic.source {
Some(source) if index == 0 => {
@@ -7965,44 +7960,25 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> Rend
};
highlighted_lines.push(line);
}
let message = diagnostic.message;
Arc::new(move |cx: &mut BlockContext| {
let message = message.clone();
let settings = settings::get::<ThemeSettings>(cx);
let tooltip_style = settings.theme.tooltip.clone();
let theme = &settings.theme.editor;
let style = diagnostic_style(diagnostic.severity, is_valid, theme);
let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
let anchor_x = cx.anchor_x;
enum BlockContextToolip {}
MouseEventHandler::<BlockContext, _>::new(cx.block_id, cx, |_, _| {
Flex::column()
.with_children(highlighted_lines.iter().map(|(line, highlights)| {
Label::new(
line.clone(),
style.message.clone().with_font_size(font_size),
)
.with_highlights(highlights.clone())
.contained()
.with_margin_left(anchor_x)
}))
.aligned()
.left()
.into_any()
})
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, move |_, _, cx| {
cx.write_to_clipboard(ClipboardItem::new(message.clone()));
})
// We really need to rethink this ID system...
.with_tooltip::<BlockContextToolip>(
cx.block_id,
"Copy diagnostic message".to_string(),
None,
tooltip_style,
cx,
)
.into_any()
Flex::column()
.with_children(highlighted_lines.iter().map(|(line, highlights)| {
Label::new(
line.clone(),
style.message.clone().with_font_size(font_size),
)
.with_highlights(highlights.clone())
.contained()
.with_margin_left(cx.anchor_x)
}))
.aligned()
.left()
.into_any()
})
}

View File

@@ -1467,7 +1467,6 @@ impl EditorElement {
editor: &mut Editor,
cx: &mut LayoutContext<Editor>,
) -> (f32, Vec<BlockLayout>) {
let mut block_id = 0;
let scroll_x = snapshot.scroll_anchor.offset.x();
let (fixed_blocks, non_fixed_blocks) = snapshot
.blocks_in_range(rows.clone())
@@ -1475,7 +1474,7 @@ impl EditorElement {
TransformBlock::ExcerptHeader { .. } => false,
TransformBlock::Custom(block) => block.style() == BlockStyle::Fixed,
});
let mut render_block = |block: &TransformBlock, width: f32, block_id: usize| {
let mut render_block = |block: &TransformBlock, width: f32| {
let mut element = match block {
TransformBlock::Custom(block) => {
let align_to = block
@@ -1500,7 +1499,6 @@ impl EditorElement {
scroll_x,
gutter_width,
em_width,
block_id,
})
}
TransformBlock::ExcerptHeader {
@@ -1529,7 +1527,7 @@ impl EditorElement {
enum JumpIcon {}
MouseEventHandler::<JumpIcon, _>::new((*id).into(), cx, |state, _| {
let style = style.jump_icon.style_for(state);
let style = style.jump_icon.style_for(state, false);
Svg::new("icons/arrow_up_right_8.svg")
.with_color(style.color)
.constrained()
@@ -1636,8 +1634,7 @@ impl EditorElement {
let mut fixed_block_max_width = 0f32;
let mut blocks = Vec::new();
for (row, block) in fixed_blocks {
let element = render_block(block, f32::INFINITY, block_id);
block_id += 1;
let element = render_block(block, f32::INFINITY);
fixed_block_max_width = fixed_block_max_width.max(element.size().x() + em_width);
blocks.push(BlockLayout {
row,
@@ -1657,8 +1654,7 @@ impl EditorElement {
.max(gutter_width + scroll_width),
BlockStyle::Fixed => unreachable!(),
};
let element = render_block(block, width, block_id);
block_id += 1;
let element = render_block(block, width);
blocks.push(BlockLayout {
row,
element,
@@ -2094,7 +2090,7 @@ impl Element<Editor> for EditorElement {
.folds
.ellipses
.background
.style_for(&mut cx.mouse_state::<FoldMarkers>(id as usize))
.style_for(&mut cx.mouse_state::<FoldMarkers>(id as usize), false)
.color;
(id, fold, color)

View File

@@ -1010,7 +1010,7 @@ impl MultiBuffer {
let suffix = cursor.suffix(&());
let changed_trailing_excerpt = suffix.is_empty();
new_excerpts.append(suffix, &());
new_excerpts.push_tree(suffix, &());
drop(cursor);
snapshot.excerpts = new_excerpts;
snapshot.excerpt_ids = new_excerpt_ids;
@@ -1193,7 +1193,7 @@ impl MultiBuffer {
while let Some(excerpt_id) = excerpt_ids.next() {
// Seek to the next excerpt to remove, preserving any preceding excerpts.
let locator = snapshot.excerpt_locator_for_id(excerpt_id);
new_excerpts.append(cursor.slice(&Some(locator), Bias::Left, &()), &());
new_excerpts.push_tree(cursor.slice(&Some(locator), Bias::Left, &()), &());
if let Some(mut excerpt) = cursor.item() {
if excerpt.id != excerpt_id {
@@ -1245,7 +1245,7 @@ impl MultiBuffer {
}
let suffix = cursor.suffix(&());
let changed_trailing_excerpt = suffix.is_empty();
new_excerpts.append(suffix, &());
new_excerpts.push_tree(suffix, &());
drop(cursor);
snapshot.excerpts = new_excerpts;
@@ -1509,7 +1509,7 @@ impl MultiBuffer {
let mut cursor = snapshot.excerpts.cursor::<(Option<&Locator>, usize)>();
for (locator, buffer, buffer_edited) in excerpts_to_edit {
new_excerpts.append(cursor.slice(&Some(locator), Bias::Left, &()), &());
new_excerpts.push_tree(cursor.slice(&Some(locator), Bias::Left, &()), &());
let old_excerpt = cursor.item().unwrap();
let buffer = buffer.read(cx);
let buffer_id = buffer.remote_id();
@@ -1549,7 +1549,7 @@ impl MultiBuffer {
new_excerpts.push(new_excerpt, &());
cursor.next(&());
}
new_excerpts.append(cursor.suffix(&()), &());
new_excerpts.push_tree(cursor.suffix(&()), &());
drop(cursor);
snapshot.excerpts = new_excerpts;

View File

@@ -41,8 +41,7 @@ impl View for DeployFeedbackButton {
.status_bar
.panel_buttons
.button
.in_state(active)
.style_for(state);
.style_for(state, active);
Svg::new("icons/feedback_16.svg")
.with_color(style.icon_color)

View File

@@ -48,7 +48,7 @@ impl View for SubmitFeedbackButton {
let theme = theme::current(cx).clone();
enum SubmitFeedbackButton {}
MouseEventHandler::<SubmitFeedbackButton, Self>::new(0, cx, |state, _| {
let style = theme.feedback.submit_button.style_for(state);
let style = theme.feedback.submit_button.style_for(state, false);
Label::new("Submit as Markdown", style.text.clone())
.contained()
.with_style(style.container)

View File

@@ -546,7 +546,7 @@ impl PickerDelegate for FileFinderDelegate {
.get(ix)
.expect("Invalid matches state: no element for index {ix}");
let theme = theme::current(cx);
let style = theme.picker.item.in_state(selected).style_for(mouse_state);
let style = theme.picker.item.style_for(mouse_state, selected);
let (file_name, file_name_positions, full_path, full_path_positions) =
self.labels_for_match(path_match, cx, ix);
Flex::column()

View File

@@ -152,29 +152,6 @@ impl App {
asset_source,
))));
foreground_platform.on_event(Box::new({
let cx = app.0.clone();
move |event| {
if let Event::KeyDown(KeyDownEvent { keystroke, .. }) = &event {
// Allow system menu "cmd-?" shortcut to be overridden
if keystroke.cmd
&& !keystroke.shift
&& !keystroke.alt
&& !keystroke.function
&& keystroke.key == "?"
{
if cx
.borrow_mut()
.update_active_window(|cx| cx.dispatch_keystroke(keystroke))
.unwrap_or(false)
{
return true;
}
}
}
false
}
}));
foreground_platform.on_quit(Box::new({
let cx = app.0.clone();
move || {
@@ -468,7 +445,7 @@ type WindowBoundsCallback = Box<dyn FnMut(WindowBounds, Uuid, &mut WindowContext
type KeystrokeCallback =
Box<dyn FnMut(&Keystroke, &MatchResult, Option<&Box<dyn Action>>, &mut WindowContext) -> bool>;
type ActiveLabeledTasksCallback = Box<dyn FnMut(&mut AppContext) -> bool>;
type DeserializeActionCallback = fn(json: serde_json::Value) -> anyhow::Result<Box<dyn Action>>;
type DeserializeActionCallback = fn(json: &str) -> anyhow::Result<Box<dyn Action>>;
type WindowShouldCloseSubscriptionCallback = Box<dyn FnMut(&mut AppContext) -> bool>;
pub struct AppContext {
@@ -647,14 +624,14 @@ impl AppContext {
pub fn deserialize_action(
&self,
name: &str,
argument: Option<serde_json::Value>,
argument: Option<&str>,
) -> Result<Box<dyn Action>> {
let callback = self
.action_deserializers
.get(name)
.ok_or_else(|| anyhow!("unknown action {}", name))?
.1;
callback(argument.unwrap_or_else(|| serde_json::Value::Object(Default::default())))
callback(argument.unwrap_or("{}"))
.with_context(|| format!("invalid data for action {}", name))
}
@@ -5596,7 +5573,7 @@ mod tests {
let action1 = cx
.deserialize_action(
"test::something::ComplexAction",
Some(serde_json::from_str(r#"{"arg": "a", "count": 5}"#).unwrap()),
Some(r#"{"arg": "a", "count": 5}"#),
)
.unwrap();
let action2 = cx

View File

@@ -11,7 +11,7 @@ pub trait Action: 'static {
fn qualified_name() -> &'static str
where
Self: Sized;
fn from_json_str(json: serde_json::Value) -> anyhow::Result<Box<dyn Action>>
fn from_json_str(json: &str) -> anyhow::Result<Box<dyn Action>>
where
Self: Sized;
}
@@ -38,7 +38,7 @@ macro_rules! actions {
$crate::__impl_action! {
$namespace,
$name,
fn from_json_str(_: $crate::serde_json::Value) -> $crate::anyhow::Result<Box<dyn $crate::Action>> {
fn from_json_str(_: &str) -> $crate::anyhow::Result<Box<dyn $crate::Action>> {
Ok(Box::new(Self))
}
}
@@ -58,8 +58,8 @@ macro_rules! impl_actions {
$crate::__impl_action! {
$namespace,
$name,
fn from_json_str(json: $crate::serde_json::Value) -> $crate::anyhow::Result<Box<dyn $crate::Action>> {
Ok(Box::new($crate::serde_json::from_value::<Self>(json)?))
fn from_json_str(json: &str) -> $crate::anyhow::Result<Box<dyn $crate::Action>> {
Ok(Box::new($crate::serde_json::from_str::<Self>(json)?))
}
}
)*

View File

@@ -394,7 +394,7 @@ impl<'a> WindowContext<'a> {
.iter()
.filter_map(move |(name, (type_id, deserialize))| {
if let Some(action_depth) = handler_depths_by_action_type.get(type_id).copied() {
let action = deserialize(serde_json::Value::Object(Default::default())).ok()?;
let action = deserialize("{}").ok()?;
let bindings = self
.keystroke_matcher
.bindings_for_action_type(*type_id)

View File

@@ -164,7 +164,6 @@ impl<V: View> Element<V> for Label {
_: &mut V,
cx: &mut ViewContext<V>,
) -> Self::PaintState {
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
line.paint(
scene,
bounds.origin(),

View File

@@ -211,7 +211,7 @@ impl<V: View> Element<V> for List<V> {
let mut cursor = old_items.cursor::<Count>();
if state.rendered_range.start < new_rendered_range.start {
new_items.append(
new_items.push_tree(
cursor.slice(&Count(state.rendered_range.start), Bias::Right, &()),
&(),
);
@@ -221,7 +221,7 @@ impl<V: View> Element<V> for List<V> {
cursor.next(&());
}
}
new_items.append(
new_items.push_tree(
cursor.slice(&Count(new_rendered_range.start), Bias::Right, &()),
&(),
);
@@ -230,7 +230,7 @@ impl<V: View> Element<V> for List<V> {
cursor.seek(&Count(new_rendered_range.end), Bias::Right, &());
if new_rendered_range.end < state.rendered_range.start {
new_items.append(
new_items.push_tree(
cursor.slice(&Count(state.rendered_range.start), Bias::Right, &()),
&(),
);
@@ -240,7 +240,7 @@ impl<V: View> Element<V> for List<V> {
cursor.next(&());
}
new_items.append(cursor.suffix(&()), &());
new_items.push_tree(cursor.suffix(&()), &());
state.items = new_items;
state.rendered_range = new_rendered_range;
@@ -413,7 +413,7 @@ impl<V: View> ListState<V> {
old_heights.seek_forward(&Count(old_range.end), Bias::Right, &());
new_heights.extend((0..count).map(|_| ListItem::Unrendered), &());
new_heights.append(old_heights.suffix(&()), &());
new_heights.push_tree(old_heights.suffix(&()), &());
drop(old_heights);
state.items = new_heights;
}

View File

@@ -1,5 +1,7 @@
use super::constrain_size_preserving_aspect_ratio;
use crate::json::ToJson;
use std::{borrow::Cow, ops::Range};
use serde_json::json;
use crate::{
color::Color,
geometry::{
@@ -8,9 +10,6 @@ use crate::{
},
scene, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
};
use serde_derive::Deserialize;
use serde_json::json;
use std::{borrow::Cow, ops::Range};
pub struct Svg {
path: Cow<'static, str>,
@@ -25,14 +24,6 @@ impl Svg {
}
}
pub fn for_style<V: View>(style: SvgStyle) -> impl Element<V> {
Self::new(style.asset)
.with_color(style.color)
.constrained()
.with_width(style.dimensions.width)
.with_height(style.dimensions.height)
}
pub fn with_color(mut self, color: Color) -> Self {
self.color = color;
self
@@ -114,24 +105,9 @@ impl<V: View> Element<V> for Svg {
}
}
#[derive(Clone, Deserialize, Default)]
pub struct SvgStyle {
pub color: Color,
pub asset: String,
pub dimensions: Dimensions,
}
use crate::json::ToJson;
#[derive(Clone, Deserialize, Default)]
pub struct Dimensions {
pub width: f32,
pub height: f32,
}
impl Dimensions {
pub fn to_vec(&self) -> Vector2F {
vec2f(self.width, self.height)
}
}
use super::constrain_size_preserving_aspect_ratio;
fn from_usvg_rect(rect: usvg::Rect) -> RectF {
RectF::new(

View File

@@ -786,7 +786,7 @@ impl platform::Platform for MacPlatform {
fn set_cursor_style(&self, style: CursorStyle) {
unsafe {
let new_cursor: id = match style {
let cursor: id = match style {
CursorStyle::Arrow => msg_send![class!(NSCursor), arrowCursor],
CursorStyle::ResizeLeftRight => {
msg_send![class!(NSCursor), resizeLeftRightCursor]
@@ -795,11 +795,7 @@ impl platform::Platform for MacPlatform {
CursorStyle::PointingHand => msg_send![class!(NSCursor), pointingHandCursor],
CursorStyle::IBeam => msg_send![class!(NSCursor), IBeamCursor],
};
let old_cursor: id = msg_send![class!(NSCursor), currentCursor];
if new_cursor != old_cursor {
let _: () = msg_send![new_cursor, set];
}
let _: () = msg_send![cursor, set];
}
}
@@ -939,6 +935,7 @@ extern "C" fn send_event(this: &mut Object, _sel: Sel, native_event: id) {
}
}
}
msg_send![super(this, class!(NSApplication)), sendEvent: native_event]
}
}

View File

@@ -17,7 +17,7 @@ use futures::{
future::{BoxFuture, Shared},
FutureExt, TryFutureExt as _,
};
use gpui::{executor::Background, AppContext, AsyncAppContext, Task};
use gpui::{executor::Background, AppContext, Task};
use highlight_map::HighlightMap;
use lazy_static::lazy_static;
use lsp::CodeActionKind;
@@ -125,46 +125,27 @@ impl CachedLspAdapter {
pub async fn fetch_latest_server_version(
&self,
delegate: &dyn LspAdapterDelegate,
http: Arc<dyn HttpClient>,
) -> Result<Box<dyn 'static + Send + Any>> {
self.adapter.fetch_latest_server_version(delegate).await
}
pub fn will_fetch_server(
&self,
delegate: &Arc<dyn LspAdapterDelegate>,
cx: &mut AsyncAppContext,
) -> Option<Task<Result<()>>> {
self.adapter.will_fetch_server(delegate, cx)
}
pub fn will_start_server(
&self,
delegate: &Arc<dyn LspAdapterDelegate>,
cx: &mut AsyncAppContext,
) -> Option<Task<Result<()>>> {
self.adapter.will_start_server(delegate, cx)
self.adapter.fetch_latest_server_version(http).await
}
pub async fn fetch_server_binary(
&self,
version: Box<dyn 'static + Send + Any>,
http: Arc<dyn HttpClient>,
container_dir: PathBuf,
delegate: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary> {
self.adapter
.fetch_server_binary(version, container_dir, delegate)
.fetch_server_binary(version, http, container_dir)
.await
}
pub async fn cached_server_binary(
&self,
container_dir: PathBuf,
delegate: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
self.adapter
.cached_server_binary(container_dir, delegate)
.await
self.adapter.cached_server_binary(container_dir).await
}
pub fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
@@ -206,48 +187,23 @@ impl CachedLspAdapter {
}
}
pub trait LspAdapterDelegate: Send + Sync {
fn show_notification(&self, message: &str, cx: &mut AppContext);
fn http_client(&self) -> Arc<dyn HttpClient>;
}
#[async_trait]
pub trait LspAdapter: 'static + Send + Sync {
async fn name(&self) -> LanguageServerName;
async fn fetch_latest_server_version(
&self,
delegate: &dyn LspAdapterDelegate,
http: Arc<dyn HttpClient>,
) -> Result<Box<dyn 'static + Send + Any>>;
fn will_fetch_server(
&self,
_: &Arc<dyn LspAdapterDelegate>,
_: &mut AsyncAppContext,
) -> Option<Task<Result<()>>> {
None
}
fn will_start_server(
&self,
_: &Arc<dyn LspAdapterDelegate>,
_: &mut AsyncAppContext,
) -> Option<Task<Result<()>>> {
None
}
async fn fetch_server_binary(
&self,
version: Box<dyn 'static + Send + Any>,
http: Arc<dyn HttpClient>,
container_dir: PathBuf,
delegate: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary>;
async fn cached_server_binary(
&self,
container_dir: PathBuf,
delegate: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary>;
async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<LanguageServerBinary>;
async fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {}
@@ -557,7 +513,10 @@ pub struct LanguageRegistry {
login_shell_env_loaded: Shared<Task<()>>,
#[allow(clippy::type_complexity)]
lsp_binary_paths: Mutex<
HashMap<LanguageServerName, Shared<Task<Result<LanguageServerBinary, Arc<anyhow::Error>>>>>,
HashMap<
LanguageServerName,
Shared<BoxFuture<'static, Result<LanguageServerBinary, Arc<anyhow::Error>>>>,
>,
>,
executor: Option<Arc<Background>>,
}
@@ -853,7 +812,7 @@ impl LanguageRegistry {
language: Arc<Language>,
adapter: Arc<CachedLspAdapter>,
root_path: Arc<Path>,
delegate: Arc<dyn LspAdapterDelegate>,
http_client: Arc<dyn HttpClient>,
cx: &mut AppContext,
) -> Option<PendingLanguageServer> {
let server_id = self.state.write().next_language_server_id();
@@ -901,40 +860,35 @@ impl LanguageRegistry {
.log_err()?;
let this = self.clone();
let language = language.clone();
let http_client = http_client.clone();
let download_dir = download_dir.clone();
let root_path = root_path.clone();
let adapter = adapter.clone();
let lsp_binary_statuses = self.lsp_binary_statuses_tx.clone();
let login_shell_env_loaded = self.login_shell_env_loaded.clone();
let task = cx.spawn(|mut cx| async move {
let task = cx.spawn(|cx| async move {
login_shell_env_loaded.await;
let entry = this
.lsp_binary_paths
.lock()
let mut lock = this.lsp_binary_paths.lock();
let entry = lock
.entry(adapter.name.clone())
.or_insert_with(|| {
cx.spawn(|cx| {
get_binary(
adapter.clone(),
language.clone(),
delegate.clone(),
download_dir,
lsp_binary_statuses,
cx,
)
.map_err(Arc::new)
})
get_binary(
adapter.clone(),
language.clone(),
http_client,
download_dir,
lsp_binary_statuses,
)
.map_err(Arc::new)
.boxed()
.shared()
})
.clone();
drop(lock);
let binary = entry.clone().map_err(|e| anyhow!(e)).await?;
if let Some(task) = adapter.will_start_server(&delegate, &mut cx) {
task.await?;
}
let server = lsp::LanguageServer::new(
server_id,
&binary.path,
@@ -1004,10 +958,9 @@ impl Default for LanguageRegistry {
async fn get_binary(
adapter: Arc<CachedLspAdapter>,
language: Arc<Language>,
delegate: Arc<dyn LspAdapterDelegate>,
http_client: Arc<dyn HttpClient>,
download_dir: Arc<Path>,
statuses: async_broadcast::Sender<(Arc<Language>, LanguageServerBinaryStatus)>,
mut cx: AsyncAppContext,
) -> Result<LanguageServerBinary> {
let container_dir = download_dir.join(adapter.name.0.as_ref());
if !container_dir.exists() {
@@ -1016,24 +969,17 @@ async fn get_binary(
.context("failed to create container directory")?;
}
if let Some(task) = adapter.will_fetch_server(&delegate, &mut cx) {
task.await?;
}
let binary = fetch_latest_binary(
adapter.clone(),
language.clone(),
delegate.as_ref(),
http_client,
&container_dir,
statuses.clone(),
)
.await;
if let Err(error) = binary.as_ref() {
if let Some(cached) = adapter
.cached_server_binary(container_dir, delegate.as_ref())
.await
{
if let Some(cached) = adapter.cached_server_binary(container_dir).await {
statuses
.broadcast((language.clone(), LanguageServerBinaryStatus::Cached))
.await?;
@@ -1055,7 +1001,7 @@ async fn get_binary(
async fn fetch_latest_binary(
adapter: Arc<CachedLspAdapter>,
language: Arc<Language>,
delegate: &dyn LspAdapterDelegate,
http_client: Arc<dyn HttpClient>,
container_dir: &Path,
lsp_binary_statuses_tx: async_broadcast::Sender<(Arc<Language>, LanguageServerBinaryStatus)>,
) -> Result<LanguageServerBinary> {
@@ -1066,12 +1012,14 @@ async fn fetch_latest_binary(
LanguageServerBinaryStatus::CheckingForUpdate,
))
.await?;
let version_info = adapter.fetch_latest_server_version(delegate).await?;
let version_info = adapter
.fetch_latest_server_version(http_client.clone())
.await?;
lsp_binary_statuses_tx
.broadcast((language.clone(), LanguageServerBinaryStatus::Downloading))
.await?;
let binary = adapter
.fetch_server_binary(version_info, container_dir.to_path_buf(), delegate)
.fetch_server_binary(version_info, http_client, container_dir.to_path_buf())
.await?;
lsp_binary_statuses_tx
.broadcast((language.clone(), LanguageServerBinaryStatus::Downloaded))
@@ -1595,7 +1543,7 @@ impl LspAdapter for Arc<FakeLspAdapter> {
async fn fetch_latest_server_version(
&self,
_: &dyn LspAdapterDelegate,
_: Arc<dyn HttpClient>,
) -> Result<Box<dyn 'static + Send + Any>> {
unreachable!();
}
@@ -1603,17 +1551,13 @@ impl LspAdapter for Arc<FakeLspAdapter> {
async fn fetch_server_binary(
&self,
_: Box<dyn 'static + Send + Any>,
_: Arc<dyn HttpClient>,
_: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary> {
unreachable!();
}
async fn cached_server_binary(
&self,
_: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
async fn cached_server_binary(&self, _: PathBuf) -> Option<LanguageServerBinary> {
unreachable!();
}

View File

@@ -288,7 +288,7 @@ impl SyntaxSnapshot {
};
if target.cmp(&cursor.start(), text).is_gt() {
let slice = cursor.slice(&target, Bias::Left, text);
layers.append(slice, text);
layers.push_tree(slice, text);
}
}
// If this layer follows all of the edits, then preserve it and any
@@ -303,7 +303,7 @@ impl SyntaxSnapshot {
Bias::Left,
text,
);
layers.append(slice, text);
layers.push_tree(slice, text);
continue;
};
@@ -369,7 +369,7 @@ impl SyntaxSnapshot {
cursor.next(text);
}
layers.append(cursor.suffix(&text), &text);
layers.push_tree(cursor.suffix(&text), &text);
drop(cursor);
self.layers = layers;
}
@@ -480,7 +480,7 @@ impl SyntaxSnapshot {
if bounded_position.cmp(&cursor.start(), &text).is_gt() {
let slice = cursor.slice(&bounded_position, Bias::Left, text);
if !slice.is_empty() {
layers.append(slice, &text);
layers.push_tree(slice, &text);
if changed_regions.prune(cursor.end(text), text) {
done = false;
}

View File

@@ -55,7 +55,7 @@ impl View for ActiveBufferLanguage {
MouseEventHandler::<Self, Self>::new(0, cx, |state, cx| {
let theme = &theme::current(cx).workspace.status_bar;
let style = theme.active_language.style_for(state);
let style = theme.active_language.style_for(state, false);
Label::new(active_language_text, style.text.clone())
.contained()
.with_style(style.container)

View File

@@ -180,7 +180,7 @@ impl PickerDelegate for LanguageSelectorDelegate {
) -> AnyElement<Picker<Self>> {
let theme = theme::current(cx);
let mat = &self.matches[ix];
let style = theme.picker.item.in_state(selected).style_for(mouse_state);
let style = theme.picker.item.style_for(mouse_state, selected);
let buffer_language_name = self.buffer.read(cx).language().map(|l| l.name());
let mut label = mat.string.clone();
if buffer_language_name.as_deref() == Some(mat.string.as_str()) {

View File

@@ -681,7 +681,7 @@ impl LspLogToolbarItemView {
)
})
.unwrap_or_else(|| "No server selected".into());
let style = theme.toolbar_dropdown_menu.header.style_for(state);
let style = theme.toolbar_dropdown_menu.header.style_for(state, false);
Label::new(label, style.text.clone())
.contained()
.with_style(style.container)
@@ -722,8 +722,7 @@ impl LspLogToolbarItemView {
let style = theme
.toolbar_dropdown_menu
.item
.in_state(logs_selected)
.style_for(state);
.style_for(state, logs_selected);
Label::new(SERVER_LOGS, style.text.clone())
.contained()
.with_style(style.container)
@@ -740,8 +739,7 @@ impl LspLogToolbarItemView {
let style = theme
.toolbar_dropdown_menu
.item
.in_state(rpc_trace_selected)
.style_for(state);
.style_for(state, rpc_trace_selected);
Flex::row()
.with_child(
Label::new(RPC_MESSAGES, style.text.clone())

View File

@@ -565,7 +565,7 @@ impl SyntaxTreeToolbarItemView {
) -> impl Element<Self> {
enum ToggleMenu {}
MouseEventHandler::<ToggleMenu, Self>::new(0, cx, move |state, _| {
let style = theme.toolbar_dropdown_menu.header.style_for(state);
let style = theme.toolbar_dropdown_menu.header.style_for(state, false);
Flex::row()
.with_child(
Label::new(active_layer.language.name().to_string(), style.text.clone())
@@ -601,8 +601,7 @@ impl SyntaxTreeToolbarItemView {
let style = theme
.toolbar_dropdown_menu
.item
.in_state(is_selected)
.style_for(state);
.style_for(state, is_selected);
Flex::row()
.with_child(
Label::new(layer.language.name().to_string(), style.text.clone())

View File

@@ -6,23 +6,17 @@ import ScreenCaptureKit
class LKRoomDelegate: RoomDelegate {
var data: UnsafeRawPointer
var onDidDisconnect: @convention(c) (UnsafeRawPointer) -> Void
var onDidSubscribeToRemoteAudioTrack: @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void
var onDidUnsubscribeFromRemoteAudioTrack: @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void
var onDidSubscribeToRemoteVideoTrack: @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void
var onDidUnsubscribeFromRemoteVideoTrack: @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void
init(
data: UnsafeRawPointer,
onDidDisconnect: @escaping @convention(c) (UnsafeRawPointer) -> Void,
onDidSubscribeToRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void,
onDidUnsubscribeFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void,
onDidSubscribeToRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void,
onDidUnsubscribeFromRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void)
{
self.data = data
self.onDidDisconnect = onDidDisconnect
self.onDidSubscribeToRemoteAudioTrack = onDidSubscribeToRemoteAudioTrack
self.onDidUnsubscribeFromRemoteAudioTrack = onDidUnsubscribeFromRemoteAudioTrack
self.onDidSubscribeToRemoteVideoTrack = onDidSubscribeToRemoteVideoTrack
self.onDidUnsubscribeFromRemoteVideoTrack = onDidUnsubscribeFromRemoteVideoTrack
}
@@ -36,16 +30,12 @@ class LKRoomDelegate: RoomDelegate {
func room(_ room: Room, participant: RemoteParticipant, didSubscribe publication: RemoteTrackPublication, track: Track) {
if track.kind == .video {
self.onDidSubscribeToRemoteVideoTrack(self.data, participant.identity as CFString, track.sid! as CFString, Unmanaged.passUnretained(track).toOpaque())
} else if track.kind == .audio {
self.onDidSubscribeToRemoteAudioTrack(self.data, participant.identity as CFString, track.sid! as CFString, Unmanaged.passUnretained(track).toOpaque())
}
}
func room(_ room: Room, participant: RemoteParticipant, didUnsubscribe publication: RemoteTrackPublication, track: Track) {
if track.kind == .video {
self.onDidUnsubscribeFromRemoteVideoTrack(self.data, participant.identity as CFString, track.sid! as CFString)
} else if track.kind == .audio {
self.onDidUnsubscribeFromRemoteAudioTrack(self.data, participant.identity as CFString, track.sid! as CFString)
}
}
}
@@ -87,16 +77,12 @@ class LKVideoRenderer: NSObject, VideoRenderer {
public func LKRoomDelegateCreate(
data: UnsafeRawPointer,
onDidDisconnect: @escaping @convention(c) (UnsafeRawPointer) -> Void,
onDidSubscribeToRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void,
onDidUnsubscribeFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void,
onDidSubscribeToRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void,
onDidUnsubscribeFromRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void
) -> UnsafeMutableRawPointer {
let delegate = LKRoomDelegate(
data: data,
onDidDisconnect: onDidDisconnect,
onDidSubscribeToRemoteAudioTrack: onDidSubscribeToRemoteAudioTrack,
onDidUnsubscribeFromRemoteAudioTrack: onDidUnsubscribeFromRemoteAudioTrack,
onDidSubscribeToRemoteVideoTrack: onDidSubscribeToRemoteVideoTrack,
onDidUnsubscribeFromRemoteVideoTrack: onDidUnsubscribeFromRemoteVideoTrack
)
@@ -137,18 +123,6 @@ public func LKRoomPublishVideoTrack(room: UnsafeRawPointer, track: UnsafeRawPoin
}
}
@_cdecl("LKRoomPublishAudioTrack")
public func LKRoomPublishAudioTrack(room: UnsafeRawPointer, track: UnsafeRawPointer, callback: @escaping @convention(c) (UnsafeRawPointer, UnsafeMutableRawPointer?, CFString?) -> Void, callback_data: UnsafeRawPointer) {
let room = Unmanaged<Room>.fromOpaque(room).takeUnretainedValue()
let track = Unmanaged<LocalAudioTrack>.fromOpaque(track).takeUnretainedValue()
room.localParticipant?.publishAudioTrack(track: track).then { publication in
callback(callback_data, Unmanaged.passRetained(publication).toOpaque(), nil)
}.catch { error in
callback(callback_data, nil, error.localizedDescription as CFString)
}
}
@_cdecl("LKRoomUnpublishTrack")
public func LKRoomUnpublishTrack(room: UnsafeRawPointer, publication: UnsafeRawPointer) {
let room = Unmanaged<Room>.fromOpaque(room).takeUnretainedValue()
@@ -156,20 +130,6 @@ public func LKRoomUnpublishTrack(room: UnsafeRawPointer, publication: UnsafeRawP
let _ = room.localParticipant?.unpublish(publication: publication)
}
@_cdecl("LKRoomAudioTracksForRemoteParticipant")
public func LKRoomAudioTracksForRemoteParticipant(room: UnsafeRawPointer, participantId: CFString) -> CFArray? {
let room = Unmanaged<Room>.fromOpaque(room).takeUnretainedValue()
for (_, participant) in room.remoteParticipants {
if participant.identity == participantId as String {
return participant.audioTracks.compactMap { $0.track as? RemoteAudioTrack } as CFArray?
}
}
return nil;
}
@_cdecl("LKRoomVideoTracksForRemoteParticipant")
public func LKRoomVideoTracksForRemoteParticipant(room: UnsafeRawPointer, participantId: CFString) -> CFArray? {
let room = Unmanaged<Room>.fromOpaque(room).takeUnretainedValue()
@@ -183,17 +143,6 @@ public func LKRoomVideoTracksForRemoteParticipant(room: UnsafeRawPointer, partic
return nil;
}
@_cdecl("LKLocalAudioTrackCreateTrack")
public func LKLocalAudioTrackCreateTrack() -> UnsafeMutableRawPointer {
let track = LocalAudioTrack.createTrack(options: AudioCaptureOptions(
echoCancellation: true,
noiseSuppression: true
))
return Unmanaged.passRetained(track).toOpaque()
}
@_cdecl("LKCreateScreenShareTrackForDisplay")
public func LKCreateScreenShareTrackForDisplay(display: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer {
let display = Unmanaged<MacOSDisplay>.fromOpaque(display).takeUnretainedValue()
@@ -201,19 +150,6 @@ public func LKCreateScreenShareTrackForDisplay(display: UnsafeMutableRawPointer)
return Unmanaged.passRetained(track).toOpaque()
}
@_cdecl("LKRemoteAudioTrackStart")
public func LKRemoteAudioTrackStart(track: UnsafeRawPointer, onStart: @escaping @convention(c) (UnsafeRawPointer, Bool) -> Void, callbackData: UnsafeRawPointer) {
let track = Unmanaged<Track>.fromOpaque(track).takeUnretainedValue() as! RemoteAudioTrack
track.start().then { success in
onStart(callbackData, success)
}
.catch { _ in
onStart(callbackData, false)
}
}
@_cdecl("LKVideoRendererCreate")
public func LKVideoRendererCreate(data: UnsafeRawPointer, onFrame: @escaping @convention(c) (UnsafeRawPointer, CVPixelBuffer) -> Bool, onDrop: @escaping @convention(c) (UnsafeRawPointer) -> Void) -> UnsafeMutableRawPointer {
Unmanaged.passRetained(LKVideoRenderer(data: data, onFrame: onFrame, onDrop: onDrop)).toOpaque()
@@ -233,12 +169,6 @@ public func LKRemoteVideoTrackGetSid(track: UnsafeRawPointer) -> CFString {
return track.sid! as CFString
}
@_cdecl("LKRemoteAudioTrackGetSid")
public func LKRemoteAudioTrackGetSid(track: UnsafeRawPointer) -> CFString {
let track = Unmanaged<RemoteAudioTrack>.fromOpaque(track).takeUnretainedValue()
return track.sid! as CFString
}
@_cdecl("LKDisplaySources")
public func LKDisplaySources(data: UnsafeRawPointer, callback: @escaping @convention(c) (UnsafeRawPointer, CFArray?, CFString?) -> Void) {
MacOSScreenCapturer.sources(for: .display, includeCurrentApplication: false, preferredMethod: .legacy).then { displaySources in

View File

@@ -1,10 +1,6 @@
use std::time::Duration;
use futures::StreamExt;
use gpui::{actions, keymap_matcher::Binding, Menu, MenuItem};
use live_kit_client::{
LocalAudioTrack, LocalVideoTrack, RemoteAudioTrackUpdate, RemoteVideoTrackUpdate, Room,
};
use live_kit_client::{LocalVideoTrack, RemoteVideoTrackUpdate, Room};
use live_kit_server::token::{self, VideoGrant};
use log::LevelFilter;
use simplelog::SimpleLogger;
@@ -15,12 +11,6 @@ fn main() {
SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
gpui::App::new(()).unwrap().run(|cx| {
#[cfg(any(test, feature = "test-support"))]
println!("USING TEST LIVEKIT");
#[cfg(not(any(test, feature = "test-support")))]
println!("USING REAL LIVEKIT");
cx.platform().activate(true);
cx.add_global_action(quit);
@@ -59,14 +49,16 @@ fn main() {
let room_b = Room::new();
room_b.connect(&live_kit_url, &user2_token).await.unwrap();
let mut audio_track_updates = room_b.remote_audio_track_updates();
let audio_track = LocalAudioTrack::create();
let audio_track_publication = room_a.publish_audio_track(&audio_track).await.unwrap();
let mut track_changes = room_b.remote_video_track_updates();
if let RemoteAudioTrackUpdate::Subscribed(track) =
audio_track_updates.next().await.unwrap()
{
let remote_tracks = room_b.remote_audio_tracks("test-participant-1");
let displays = room_a.display_sources().await.unwrap();
let display = displays.into_iter().next().unwrap();
let track_a = LocalVideoTrack::screen_share_for_display(&display);
let track_a_publication = room_a.publish_video_track(&track_a).await.unwrap();
if let RemoteVideoTrackUpdate::Subscribed(track) = track_changes.next().await.unwrap() {
let remote_tracks = room_b.remote_video_tracks("test-participant-1");
assert_eq!(remote_tracks.len(), 1);
assert_eq!(remote_tracks[0].publisher_id(), "test-participant-1");
assert_eq!(track.publisher_id(), "test-participant-1");
@@ -74,60 +66,18 @@ fn main() {
panic!("unexpected message");
}
println!("Pausing for 5 seconds to test audio, make some noise!");
let timer = cx.background().timer(Duration::from_secs(5));
timer.await;
let remote_audio_track = room_b
.remote_audio_tracks("test-participant-1")
.pop()
.unwrap();
room_a.unpublish_track(audio_track_publication);
if let RemoteAudioTrackUpdate::Unsubscribed {
publisher_id,
track_id,
} = audio_track_updates.next().await.unwrap()
{
assert_eq!(publisher_id, "test-participant-1");
assert_eq!(remote_audio_track.sid(), track_id);
assert_eq!(room_b.remote_audio_tracks("test-participant-1").len(), 0);
} else {
panic!("unexpected message");
}
let mut video_track_updates = room_b.remote_video_track_updates();
let displays = room_a.display_sources().await.unwrap();
let display = displays.into_iter().next().unwrap();
let local_video_track = LocalVideoTrack::screen_share_for_display(&display);
let local_video_track_publication = room_a
.publish_video_track(&local_video_track)
.await
.unwrap();
if let RemoteVideoTrackUpdate::Subscribed(track) =
video_track_updates.next().await.unwrap()
{
let remote_video_tracks = room_b.remote_video_tracks("test-participant-1");
assert_eq!(remote_video_tracks.len(), 1);
assert_eq!(remote_video_tracks[0].publisher_id(), "test-participant-1");
assert_eq!(track.publisher_id(), "test-participant-1");
} else {
panic!("unexpected message");
}
let remote_video_track = room_b
let remote_track = room_b
.remote_video_tracks("test-participant-1")
.pop()
.unwrap();
room_a.unpublish_track(local_video_track_publication);
room_a.unpublish_track(track_a_publication);
if let RemoteVideoTrackUpdate::Unsubscribed {
publisher_id,
track_id,
} = video_track_updates.next().await.unwrap()
} = track_changes.next().await.unwrap()
{
assert_eq!(publisher_id, "test-participant-1");
assert_eq!(remote_video_track.sid(), track_id);
assert_eq!(remote_track.sid(), track_id);
assert_eq!(room_b.remote_video_tracks("test-participant-1").len(), 0);
} else {
panic!("unexpected message");

View File

@@ -4,7 +4,7 @@ pub mod prod;
pub use prod::*;
#[cfg(any(test, feature = "test-support"))]
pub mod test;
mod test;
#[cfg(any(test, feature = "test-support"))]
pub use test::*;

View File

@@ -21,17 +21,6 @@ extern "C" {
fn LKRoomDelegateCreate(
callback_data: *mut c_void,
on_did_disconnect: extern "C" fn(callback_data: *mut c_void),
on_did_subscribe_to_remote_audio_track: extern "C" fn(
callback_data: *mut c_void,
publisher_id: CFStringRef,
track_id: CFStringRef,
remote_track: *const c_void,
),
on_did_unsubscribe_from_remote_audio_track: extern "C" fn(
callback_data: *mut c_void,
publisher_id: CFStringRef,
track_id: CFStringRef,
),
on_did_subscribe_to_remote_video_track: extern "C" fn(
callback_data: *mut c_void,
publisher_id: CFStringRef,
@@ -60,18 +49,7 @@ extern "C" {
callback: extern "C" fn(*mut c_void, *mut c_void, CFStringRef),
callback_data: *mut c_void,
);
fn LKRoomPublishAudioTrack(
room: *const c_void,
track: *const c_void,
callback: extern "C" fn(*mut c_void, *mut c_void, CFStringRef),
callback_data: *mut c_void,
);
fn LKRoomUnpublishTrack(room: *const c_void, publication: *const c_void);
fn LKRoomAudioTracksForRemoteParticipant(
room: *const c_void,
participant_id: CFStringRef,
) -> CFArrayRef;
fn LKRoomVideoTracksForRemoteParticipant(
room: *const c_void,
participant_id: CFStringRef,
@@ -83,13 +61,6 @@ extern "C" {
on_drop: extern "C" fn(callback_data: *mut c_void),
) -> *const c_void;
fn LKRemoteAudioTrackGetSid(track: *const c_void) -> CFStringRef;
// fn LKRemoteAudioTrackStart(
// track: *const c_void,
// callback: extern "C" fn(*mut c_void, bool),
// callback_data: *mut c_void
// );
fn LKVideoTrackAddRenderer(track: *const c_void, renderer: *const c_void);
fn LKRemoteVideoTrackGetSid(track: *const c_void) -> CFStringRef;
@@ -102,7 +73,6 @@ extern "C" {
),
);
fn LKCreateScreenShareTrackForDisplay(display: *const c_void) -> *const c_void;
fn LKLocalAudioTrackCreateTrack() -> *const c_void;
}
pub type Sid = String;
@@ -119,7 +89,6 @@ pub struct Room {
watch::Sender<ConnectionState>,
watch::Receiver<ConnectionState>,
)>,
remote_audio_track_subscribers: Mutex<Vec<mpsc::UnboundedSender<RemoteAudioTrackUpdate>>>,
remote_video_track_subscribers: Mutex<Vec<mpsc::UnboundedSender<RemoteVideoTrackUpdate>>>,
_delegate: RoomDelegate,
}
@@ -131,7 +100,6 @@ impl Room {
Self {
native_room: unsafe { LKRoomCreate(delegate.native_delegate) },
connection: Mutex::new(watch::channel_with(ConnectionState::Disconnected)),
remote_audio_track_subscribers: Default::default(),
remote_video_track_subscribers: Default::default(),
_delegate: delegate,
}
@@ -223,32 +191,6 @@ impl Room {
async { rx.await.unwrap().context("error publishing video track") }
}
pub fn publish_audio_track(
self: &Arc<Self>,
track: &LocalAudioTrack,
) -> impl Future<Output = Result<LocalTrackPublication>> {
let (tx, rx) = oneshot::channel::<Result<LocalTrackPublication>>();
extern "C" fn callback(tx: *mut c_void, publication: *mut c_void, error: CFStringRef) {
let tx =
unsafe { Box::from_raw(tx as *mut oneshot::Sender<Result<LocalTrackPublication>>) };
if error.is_null() {
let _ = tx.send(Ok(LocalTrackPublication(publication)));
} else {
let error = unsafe { CFString::wrap_under_get_rule(error).to_string() };
let _ = tx.send(Err(anyhow!(error)));
}
}
unsafe {
LKRoomPublishAudioTrack(
self.native_room,
track.0,
callback,
Box::into_raw(Box::new(tx)) as *mut c_void,
);
}
async { rx.await.unwrap().context("error publishing video track") }
}
pub fn unpublish_track(&self, publication: LocalTrackPublication) {
unsafe {
LKRoomUnpublishTrack(self.native_room, publication.0);
@@ -284,65 +226,12 @@ impl Room {
}
}
pub fn remote_audio_tracks(&self, participant_id: &str) -> Vec<Arc<RemoteAudioTrack>> {
unsafe {
let tracks = LKRoomAudioTracksForRemoteParticipant(
self.native_room,
CFString::new(participant_id).as_concrete_TypeRef(),
);
if tracks.is_null() {
Vec::new()
} else {
let tracks = CFArray::wrap_under_get_rule(tracks);
tracks
.into_iter()
.map(|native_track| {
let native_track = *native_track;
let id =
CFString::wrap_under_get_rule(LKRemoteAudioTrackGetSid(native_track))
.to_string();
Arc::new(RemoteAudioTrack::new(
native_track,
id,
participant_id.into(),
))
})
.collect()
}
}
}
pub fn remote_audio_track_updates(&self) -> mpsc::UnboundedReceiver<RemoteAudioTrackUpdate> {
let (tx, rx) = mpsc::unbounded();
self.remote_audio_track_subscribers.lock().push(tx);
rx
}
pub fn remote_video_track_updates(&self) -> mpsc::UnboundedReceiver<RemoteVideoTrackUpdate> {
let (tx, rx) = mpsc::unbounded();
self.remote_video_track_subscribers.lock().push(tx);
rx
}
fn did_subscribe_to_remote_audio_track(&self, track: RemoteAudioTrack) {
let track = Arc::new(track);
self.remote_audio_track_subscribers.lock().retain(|tx| {
tx.unbounded_send(RemoteAudioTrackUpdate::Subscribed(track.clone()))
.is_ok()
});
}
fn did_unsubscribe_from_remote_audio_track(&self, publisher_id: String, track_id: String) {
self.remote_audio_track_subscribers.lock().retain(|tx| {
tx.unbounded_send(RemoteAudioTrackUpdate::Unsubscribed {
publisher_id: publisher_id.clone(),
track_id: track_id.clone(),
})
.is_ok()
});
}
fn did_subscribe_to_remote_video_track(&self, track: RemoteVideoTrack) {
let track = Arc::new(track);
self.remote_video_track_subscribers.lock().retain(|tx| {
@@ -405,8 +294,6 @@ impl RoomDelegate {
LKRoomDelegateCreate(
weak_room as *mut c_void,
Self::on_did_disconnect,
Self::on_did_subscribe_to_remote_audio_track,
Self::on_did_unsubscribe_from_remote_audio_track,
Self::on_did_subscribe_to_remote_video_track,
Self::on_did_unsubscribe_from_remote_video_track,
)
@@ -425,36 +312,6 @@ impl RoomDelegate {
let _ = Weak::into_raw(room);
}
extern "C" fn on_did_subscribe_to_remote_audio_track(
room: *mut c_void,
publisher_id: CFStringRef,
track_id: CFStringRef,
track: *const c_void,
) {
let room = unsafe { Weak::from_raw(room as *mut Room) };
let publisher_id = unsafe { CFString::wrap_under_get_rule(publisher_id).to_string() };
let track_id = unsafe { CFString::wrap_under_get_rule(track_id).to_string() };
let track = RemoteAudioTrack::new(track, track_id, publisher_id);
if let Some(room) = room.upgrade() {
room.did_subscribe_to_remote_audio_track(track);
}
let _ = Weak::into_raw(room);
}
extern "C" fn on_did_unsubscribe_from_remote_audio_track(
room: *mut c_void,
publisher_id: CFStringRef,
track_id: CFStringRef,
) {
let room = unsafe { Weak::from_raw(room as *mut Room) };
let publisher_id = unsafe { CFString::wrap_under_get_rule(publisher_id).to_string() };
let track_id = unsafe { CFString::wrap_under_get_rule(track_id).to_string() };
if let Some(room) = room.upgrade() {
room.did_unsubscribe_from_remote_audio_track(publisher_id, track_id);
}
let _ = Weak::into_raw(room);
}
extern "C" fn on_did_subscribe_to_remote_video_track(
room: *mut c_void,
publisher_id: CFStringRef,
@@ -495,20 +352,6 @@ impl Drop for RoomDelegate {
}
}
pub struct LocalAudioTrack(*const c_void);
impl LocalAudioTrack {
pub fn create() -> Self {
Self(unsafe { LKLocalAudioTrackCreateTrack() })
}
}
impl Drop for LocalAudioTrack {
fn drop(&mut self) {
unsafe { CFRelease(self.0) }
}
}
pub struct LocalVideoTrack(*const c_void);
impl LocalVideoTrack {
@@ -531,34 +374,6 @@ impl Drop for LocalTrackPublication {
}
}
#[derive(Debug)]
pub struct RemoteAudioTrack {
_native_track: *const c_void,
sid: Sid,
publisher_id: String,
}
impl RemoteAudioTrack {
fn new(native_track: *const c_void, sid: Sid, publisher_id: String) -> Self {
unsafe {
CFRetain(native_track);
}
Self {
_native_track: native_track,
sid,
publisher_id,
}
}
pub fn sid(&self) -> &str {
&self.sid
}
pub fn publisher_id(&self) -> &str {
&self.publisher_id
}
}
#[derive(Debug)]
pub struct RemoteVideoTrack {
native_track: *const c_void,
@@ -638,11 +453,6 @@ pub enum RemoteVideoTrackUpdate {
Unsubscribed { publisher_id: Sid, track_id: Sid },
}
pub enum RemoteAudioTrackUpdate {
Subscribed(Arc<RemoteAudioTrack>),
Unsubscribed { publisher_id: Sid, track_id: Sid },
}
pub struct MacOSDisplay(*const c_void);
impl MacOSDisplay {

View File

@@ -67,7 +67,7 @@ impl TestServer {
}
}
pub async fn create_room(&self, room: String) -> Result<()> {
async fn create_room(&self, room: String) -> Result<()> {
self.background.simulate_random_delay().await;
let mut server_rooms = self.rooms.lock();
if server_rooms.contains_key(&room) {
@@ -104,7 +104,7 @@ impl TestServer {
room_name
))
} else {
for track in &room.video_tracks {
for track in &room.tracks {
client_room
.0
.lock()
@@ -182,7 +182,7 @@ impl TestServer {
frames_rx: local_track.frames_rx.clone(),
});
room.video_tracks.push(track.clone());
room.tracks.push(track.clone());
for (id, client_room) in &room.client_rooms {
if *id != identity {
@@ -199,43 +199,6 @@ impl TestServer {
Ok(())
}
async fn publish_audio_track(
&self,
token: String,
_local_track: &LocalAudioTrack,
) -> Result<()> {
self.background.simulate_random_delay().await;
let claims = live_kit_server::token::validate(&token, &self.secret_key)?;
let identity = claims.sub.unwrap().to_string();
let room_name = claims.video.room.unwrap();
let mut server_rooms = self.rooms.lock();
let room = server_rooms
.get_mut(&*room_name)
.ok_or_else(|| anyhow!("room {} does not exist", room_name))?;
let track = Arc::new(RemoteAudioTrack {
sid: nanoid::nanoid!(17),
publisher_id: identity.clone(),
});
room.audio_tracks.push(track.clone());
for (id, client_room) in &room.client_rooms {
if *id != identity {
let _ = client_room
.0
.lock()
.audio_track_updates
.0
.try_broadcast(RemoteAudioTrackUpdate::Subscribed(track.clone()))
.unwrap();
}
}
Ok(())
}
fn video_tracks(&self, token: String) -> Result<Vec<Arc<RemoteVideoTrack>>> {
let claims = live_kit_server::token::validate(&token, &self.secret_key)?;
let room_name = claims.video.room.unwrap();
@@ -244,26 +207,14 @@ impl TestServer {
let room = server_rooms
.get_mut(&*room_name)
.ok_or_else(|| anyhow!("room {} does not exist", room_name))?;
Ok(room.video_tracks.clone())
}
fn audio_tracks(&self, token: String) -> Result<Vec<Arc<RemoteAudioTrack>>> {
let claims = live_kit_server::token::validate(&token, &self.secret_key)?;
let room_name = claims.video.room.unwrap();
let mut server_rooms = self.rooms.lock();
let room = server_rooms
.get_mut(&*room_name)
.ok_or_else(|| anyhow!("room {} does not exist", room_name))?;
Ok(room.audio_tracks.clone())
Ok(room.tracks.clone())
}
}
#[derive(Default)]
struct TestServerRoom {
client_rooms: HashMap<Sid, Arc<Room>>,
video_tracks: Vec<Arc<RemoteVideoTrack>>,
audio_tracks: Vec<Arc<RemoteAudioTrack>>,
tracks: Vec<Arc<RemoteVideoTrack>>,
}
impl TestServerRoom {}
@@ -315,10 +266,6 @@ struct RoomState {
watch::Receiver<ConnectionState>,
),
display_sources: Vec<MacOSDisplay>,
audio_track_updates: (
async_broadcast::Sender<RemoteAudioTrackUpdate>,
async_broadcast::Receiver<RemoteAudioTrackUpdate>,
),
video_track_updates: (
async_broadcast::Sender<RemoteVideoTrackUpdate>,
async_broadcast::Receiver<RemoteVideoTrackUpdate>,
@@ -339,7 +286,6 @@ impl Room {
connection: watch::channel_with(ConnectionState::Disconnected),
display_sources: Default::default(),
video_track_updates: async_broadcast::broadcast(128),
audio_track_updates: async_broadcast::broadcast(128),
})))
}
@@ -381,34 +327,8 @@ impl Room {
Ok(LocalTrackPublication)
}
}
pub fn publish_audio_track(
self: &Arc<Self>,
track: &LocalAudioTrack,
) -> impl Future<Output = Result<LocalTrackPublication>> {
let this = self.clone();
let track = track.clone();
async move {
this.test_server()
.publish_audio_track(this.token(), &track)
.await?;
Ok(LocalTrackPublication)
}
}
pub fn unpublish_track(&self, _publication: LocalTrackPublication) {}
pub fn remote_audio_tracks(&self, publisher_id: &str) -> Vec<Arc<RemoteAudioTrack>> {
if !self.is_connected() {
return Vec::new();
}
self.test_server()
.audio_tracks(self.token())
.unwrap()
.into_iter()
.filter(|track| track.publisher_id() == publisher_id)
.collect()
}
pub fn unpublish_track(&self, _: LocalTrackPublication) {}
pub fn remote_video_tracks(&self, publisher_id: &str) -> Vec<Arc<RemoteVideoTrack>> {
if !self.is_connected() {
@@ -423,10 +343,6 @@ impl Room {
.collect()
}
pub fn remote_audio_track_updates(&self) -> impl Stream<Item = RemoteAudioTrackUpdate> {
self.0.lock().audio_track_updates.1.clone()
}
pub fn remote_video_track_updates(&self) -> impl Stream<Item = RemoteVideoTrackUpdate> {
self.0.lock().video_track_updates.1.clone()
}
@@ -488,15 +404,6 @@ impl LocalVideoTrack {
}
}
#[derive(Clone)]
pub struct LocalAudioTrack;
impl LocalAudioTrack {
pub fn create() -> Self {
Self
}
}
pub struct RemoteVideoTrack {
sid: Sid,
publisher_id: Sid,
@@ -517,33 +424,12 @@ impl RemoteVideoTrack {
}
}
pub struct RemoteAudioTrack {
sid: Sid,
publisher_id: Sid,
}
impl RemoteAudioTrack {
pub fn sid(&self) -> &str {
&self.sid
}
pub fn publisher_id(&self) -> &str {
&self.publisher_id
}
}
#[derive(Clone)]
pub enum RemoteVideoTrackUpdate {
Subscribed(Arc<RemoteVideoTrack>),
Unsubscribed { publisher_id: Sid, track_id: Sid },
}
#[derive(Clone)]
pub enum RemoteAudioTrackUpdate {
Subscribed(Arc<RemoteAudioTrack>),
Unsubscribed { publisher_id: Sid, track_id: Sid },
}
#[derive(Clone)]
pub struct MacOSDisplay {
frames: (

View File

@@ -103,14 +103,14 @@ struct Notification<'a, T> {
params: T,
}
#[derive(Debug, Clone, Deserialize)]
#[derive(Deserialize)]
struct AnyNotification<'a> {
#[serde(default)]
id: Option<usize>,
#[serde(borrow)]
method: &'a str,
#[serde(borrow, default)]
params: Option<&'a RawValue>,
#[serde(borrow)]
params: &'a RawValue,
}
#[derive(Debug, Serialize, Deserialize)]
@@ -157,12 +157,9 @@ impl LanguageServer {
"unhandled notification {}:\n{}",
notification.method,
serde_json::to_string_pretty(
&notification
.params
.and_then(|params| Value::from_str(params.get()).ok())
.unwrap_or(Value::Null)
&Value::from_str(notification.params.get()).unwrap()
)
.unwrap(),
.unwrap()
);
},
);
@@ -282,11 +279,7 @@ impl LanguageServer {
if let Ok(msg) = serde_json::from_slice::<AnyNotification>(&buffer) {
if let Some(handler) = notification_handlers.lock().get_mut(msg.method) {
handler(
msg.id,
&msg.params.map(|params| params.get()).unwrap_or("null"),
cx.clone(),
);
handler(msg.id, msg.params.get(), cx.clone());
} else {
on_unhandled_notification(msg);
}
@@ -835,13 +828,7 @@ impl LanguageServer {
cx,
move |msg| {
notifications_tx
.try_send((
msg.method.to_string(),
msg.params
.map(|raw_value| raw_value.get())
.unwrap_or("null")
.to_string(),
))
.try_send((msg.method.to_string(), msg.params.get().to_string()))
.ok();
},
)),

View File

@@ -204,7 +204,7 @@ impl PickerDelegate for OutlineViewDelegate {
cx: &AppContext,
) -> AnyElement<Picker<Self>> {
let theme = theme::current(cx);
let style = theme.picker.item.in_state(selected).style_for(mouse_state);
let style = theme.picker.item.style_for(mouse_state, selected);
let string_match = &self.matches[ix];
let outline_item = &self.outline.items[string_match.candidate_id];

View File

@@ -38,9 +38,9 @@ use language::{
},
range_from_lsp, range_to_lsp, Anchor, Bias, Buffer, CachedLspAdapter, CodeAction, CodeLabel,
Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Diff, Event as BufferEvent, File as _,
Language, LanguageRegistry, LanguageServerName, LocalFile, LspAdapterDelegate, OffsetRangeExt,
Operation, Patch, PendingLanguageServer, PointUtf16, TextBufferSnapshot, ToOffset,
ToPointUtf16, Transaction, Unclipped,
Language, LanguageRegistry, LanguageServerName, LocalFile, OffsetRangeExt, Operation, Patch,
PendingLanguageServer, PointUtf16, TextBufferSnapshot, ToOffset, ToPointUtf16, Transaction,
Unclipped,
};
use log::error;
use lsp::{
@@ -75,8 +75,8 @@ use std::{
};
use terminals::Terminals;
use util::{
debug_panic, defer, http::HttpClient, merge_json_value_into,
paths::LOCAL_SETTINGS_RELATIVE_PATH, post_inc, ResultExt, TryFutureExt as _,
debug_panic, defer, merge_json_value_into, paths::LOCAL_SETTINGS_RELATIVE_PATH, post_inc,
ResultExt, TryFutureExt as _,
};
pub use fs::*;
@@ -252,7 +252,6 @@ pub enum Event {
LanguageServerAdded(LanguageServerId),
LanguageServerRemoved(LanguageServerId),
LanguageServerLog(LanguageServerId, String),
Notification(String),
ActiveEntryChanged(Option<ProjectEntryId>),
WorktreeAdded,
WorktreeRemoved(WorktreeId),
@@ -436,11 +435,6 @@ pub enum FormatTrigger {
Manual,
}
struct ProjectLspAdapterDelegate {
project: ModelHandle<Project>,
http_client: Arc<dyn HttpClient>,
}
impl FormatTrigger {
fn from_proto(value: i32) -> FormatTrigger {
match value {
@@ -2413,7 +2407,7 @@ impl Project {
language.clone(),
adapter.clone(),
worktree_path.clone(),
ProjectLspAdapterDelegate::new(self, cx),
self.client.http_client(),
cx,
) {
Some(pending_server) => pending_server,
@@ -7194,26 +7188,6 @@ impl<P: AsRef<Path>> From<(WorktreeId, P)> for ProjectPath {
}
}
impl ProjectLspAdapterDelegate {
fn new(project: &Project, cx: &ModelContext<Project>) -> Arc<Self> {
Arc::new(Self {
project: cx.handle(),
http_client: project.client.http_client(),
})
}
}
impl LspAdapterDelegate for ProjectLspAdapterDelegate {
fn show_notification(&self, message: &str, cx: &mut AppContext) {
self.project
.update(cx, |_, cx| cx.emit(Event::Notification(message.to_owned())));
}
fn http_client(&self) -> Arc<dyn HttpClient> {
self.http_client.clone()
}
}
fn split_operations(
mut operations: Vec<proto::Operation>,
) -> impl Iterator<Item = Vec<proto::Operation>> {

View File

@@ -1470,7 +1470,7 @@ impl Snapshot {
break;
}
}
new_entries_by_path.append(cursor.suffix(&()), &());
new_entries_by_path.push_tree(cursor.suffix(&()), &());
new_entries_by_path
};
@@ -2259,7 +2259,7 @@ impl BackgroundScannerState {
let mut cursor = self.snapshot.entries_by_path.cursor::<TraversalProgress>();
new_entries = cursor.slice(&TraversalTarget::Path(path), Bias::Left, &());
removed_entries = cursor.slice(&TraversalTarget::PathSuccessor(path), Bias::Left, &());
new_entries.append(cursor.suffix(&()), &());
new_entries.push_tree(cursor.suffix(&()), &());
}
self.snapshot.entries_by_path = new_entries;

View File

@@ -153,7 +153,6 @@ pub fn init(cx: &mut AppContext) {
);
}
#[derive(Debug)]
pub enum Event {
OpenedEntry {
entry_id: ProjectEntryId,
@@ -1254,10 +1253,7 @@ impl ProjectPanel {
let show_editor = details.is_editing && !details.is_processing;
MouseEventHandler::<Self, _>::new(entry_id.to_usize(), cx, |state, cx| {
let mut style = entry_style
.in_state(details.is_selected)
.style_for(state)
.clone();
let mut style = entry_style.style_for(state, details.is_selected).clone();
if cx
.global::<DragAndDrop<Workspace>>()
@@ -1268,7 +1264,7 @@ impl ProjectPanel {
.filter(|destination| details.path.starts_with(destination))
.is_some()
{
style = entry_style.active_state().default.clone();
style = entry_style.active.clone().unwrap();
}
let row_container_style = if show_editor {
@@ -1409,11 +1405,9 @@ impl View for ProjectPanel {
let button_style = theme.open_project_button.clone();
let context_menu_item_style = theme::current(cx).context_menu.item.clone();
move |state, cx| {
let button_style = button_style.style_for(state).clone();
let context_menu_item = context_menu_item_style
.active_state()
.style_for(state)
.clone();
let button_style = button_style.style_for(state, false).clone();
let context_menu_item =
context_menu_item_style.style_for(state, true).clone();
theme::ui::keystroke_label(
"Open a project",

View File

@@ -196,7 +196,7 @@ impl PickerDelegate for ProjectSymbolsDelegate {
) -> AnyElement<Picker<Self>> {
let theme = theme::current(cx);
let style = &theme.picker.item;
let current_style = style.in_state(selected).style_for(mouse_state);
let current_style = style.style_for(mouse_state, selected);
let string_match = &self.matches[ix];
let symbol = &self.symbols[string_match.candidate_id];
@@ -229,10 +229,7 @@ impl PickerDelegate for ProjectSymbolsDelegate {
.with_child(
// Avoid styling the path differently when it is selected, since
// the symbol's syntax highlighting doesn't change when selected.
Label::new(
path.to_string(),
style.inactive_state().default.label.clone(),
),
Label::new(path.to_string(), style.default.label.clone()),
)
.contained()
.with_style(current_style.container)

View File

@@ -173,7 +173,7 @@ impl PickerDelegate for RecentProjectsDelegate {
cx: &gpui::AppContext,
) -> AnyElement<Picker<Self>> {
let theme = theme::current(cx);
let style = theme.picker.item.in_state(selected).style_for(mouse_state);
let style = theme.picker.item.style_for(mouse_state, selected);
let string_match = &self.matches[ix];

View File

@@ -53,7 +53,7 @@ impl Rope {
}
}
self.chunks.append(chunks.suffix(&()), &());
self.chunks.push_tree(chunks.suffix(&()), &());
self.check_invariants();
}

View File

@@ -259,11 +259,7 @@ impl BufferSearchBar {
}
}
pub fn is_dismissed(&self) -> bool {
self.dismissed
}
pub fn dismiss(&mut self, _: &Dismiss, cx: &mut ViewContext<Self>) {
fn dismiss(&mut self, _: &Dismiss, cx: &mut ViewContext<Self>) {
self.dismissed = true;
for searchable_item in self.seachable_items_with_matches.keys() {
if let Some(searchable_item) =
@@ -279,7 +275,7 @@ impl BufferSearchBar {
cx.notify();
}
pub fn show(&mut self, focus: bool, suggest_query: bool, cx: &mut ViewContext<Self>) -> bool {
fn show(&mut self, focus: bool, suggest_query: bool, cx: &mut ViewContext<Self>) -> bool {
let searchable_item = if let Some(searchable_item) = &self.active_searchable_item {
SearchableItemHandle::boxed_clone(searchable_item.as_ref())
} else {
@@ -332,11 +328,7 @@ impl BufferSearchBar {
Some(
MouseEventHandler::<Self, _>::new(option as usize, cx, |state, cx| {
let theme = theme::current(cx);
let style = theme
.search
.option_button
.in_state(is_active)
.style_for(state);
let style = theme.search.option_button.style_for(state, is_active);
Label::new(icon, style.text.clone())
.contained()
.with_style(style.container)
@@ -379,7 +371,7 @@ impl BufferSearchBar {
enum NavButton {}
MouseEventHandler::<NavButton, _>::new(direction as usize, cx, |state, cx| {
let theme = theme::current(cx);
let style = theme.search.option_button.inactive_state().style_for(state);
let style = theme.search.option_button.style_for(state, false);
Label::new(icon, style.text.clone())
.contained()
.with_style(style.container)
@@ -411,7 +403,7 @@ impl BufferSearchBar {
enum CloseButton {}
MouseEventHandler::<CloseButton, _>::new(0, cx, |state, _| {
let style = theme.dismiss_button.style_for(state);
let style = theme.dismiss_button.style_for(state, false);
Svg::new("icons/x_mark_8.svg")
.with_color(style.color)
.constrained()
@@ -488,7 +480,7 @@ impl BufferSearchBar {
self.select_match(Direction::Prev, cx);
}
pub fn select_match(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
fn select_match(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
if let Some(index) = self.active_match_index {
if let Some(searchable_item) = self.active_searchable_item.as_ref() {
if let Some(matches) = self

View File

@@ -896,7 +896,7 @@ impl ProjectSearchBar {
enum NavButton {}
MouseEventHandler::<NavButton, _>::new(direction as usize, cx, |state, cx| {
let theme = theme::current(cx);
let style = theme.search.option_button.inactive_state().style_for(state);
let style = theme.search.option_button.style_for(state, false);
Label::new(icon, style.text.clone())
.contained()
.with_style(style.container)
@@ -927,11 +927,7 @@ impl ProjectSearchBar {
let is_active = self.is_option_enabled(option, cx);
MouseEventHandler::<Self, _>::new(option as usize, cx, |state, cx| {
let theme = theme::current(cx);
let style = theme
.search
.option_button
.in_state(is_active)
.style_for(state);
let style = theme.search.option_button.style_for(state, is_active);
Label::new(icon, style.text.clone())
.contained()
.with_style(style.container)

View File

@@ -21,7 +21,7 @@ util = { path = "../util" }
anyhow.workspace = true
futures.workspace = true
serde_json_lenient = {version = "0.1", features = ["preserve_order", "raw_value"]}
json_comments = "0.2"
lazy_static.workspace = true
postage.workspace = true
rust-embed.workspace = true
@@ -37,6 +37,6 @@ tree-sitter-json = "*"
[dev-dependencies]
gpui = { path = "../gpui", features = ["test-support"] }
fs = { path = "../fs", features = ["test-support"] }
indoc.workspace = true
pretty_assertions = "1.3.0"
unindent.workspace = true

View File

@@ -1,5 +1,5 @@
use crate::{settings_store::parse_json_with_comments, SettingsAssets};
use anyhow::{anyhow, Context, Result};
use anyhow::{Context, Result};
use collections::BTreeMap;
use gpui::{keymap_matcher::Binding, AppContext};
use schemars::{
@@ -8,7 +8,7 @@ use schemars::{
JsonSchema,
};
use serde::Deserialize;
use serde_json::Value;
use serde_json::{value::RawValue, Value};
use util::{asset_str, ResultExt};
#[derive(Deserialize, Default, Clone, JsonSchema)]
@@ -24,7 +24,7 @@ pub struct KeymapBlock {
#[derive(Deserialize, Default, Clone)]
#[serde(transparent)]
pub struct KeymapAction(Value);
pub struct KeymapAction(Box<RawValue>);
impl JsonSchema for KeymapAction {
fn schema_name() -> String {
@@ -37,12 +37,11 @@ impl JsonSchema for KeymapAction {
}
#[derive(Deserialize)]
struct ActionWithData(Box<str>, Value);
struct ActionWithData(Box<str>, Box<RawValue>);
impl KeymapFile {
pub fn load_asset(asset_path: &str, cx: &mut AppContext) -> Result<()> {
let content = asset_str::<SettingsAssets>(asset_path);
Self::parse(content.as_ref())?.add_to_cx(cx)
}
@@ -55,27 +54,18 @@ impl KeymapFile {
let bindings = bindings
.into_iter()
.filter_map(|(keystroke, action)| {
let action = action.0;
let action = action.0.get();
// This is a workaround for a limitation in serde: serde-rs/json#497
// We want to deserialize the action data as a `RawValue` so that we can
// deserialize the action itself dynamically directly from the JSON
// string. But `RawValue` currently does not work inside of an untagged enum.
if let Value::Array(items) = action {
let Ok([name, data]): Result<[serde_json::Value; 2], _> = items.try_into() else {
return Some(Err(anyhow!("Expected array of length 2")));
};
let serde_json::Value::String(name) = name else {
return Some(Err(anyhow!("Expected first item in array to be a string.")))
};
cx.deserialize_action(
&name,
Some(data),
)
} else if let Value::String(name) = action {
cx.deserialize_action(&name, None)
if action.starts_with('[') {
let ActionWithData(name, data) = serde_json::from_str(action).log_err()?;
cx.deserialize_action(&name, Some(data.get()))
} else {
return Some(Err(anyhow!("Expected two-element array, got {:?}", action)));
let name = serde_json::from_str(action).log_err()?;
cx.deserialize_action(name, None)
}
.with_context(|| {
format!(
@@ -128,24 +118,3 @@ impl KeymapFile {
serde_json::to_value(root_schema).unwrap()
}
}
#[cfg(test)]
mod tests {
use crate::KeymapFile;
#[test]
fn can_deserialize_keymap_with_trailing_comma() {
let json = indoc::indoc! {"[
// Standard macOS bindings
{
\"bindings\": {
\"up\": \"menu::SelectPrev\",
},
},
]
"
};
KeymapFile::parse(json).unwrap();
}
}

View File

@@ -834,8 +834,11 @@ fn to_pretty_json(value: &impl Serialize, indent_size: usize, indent_prefix_len:
}
pub fn parse_json_with_comments<T: DeserializeOwned>(content: &str) -> Result<T> {
Ok(serde_json_lenient::from_str(content)?)
Ok(serde_json::from_reader(
json_comments::CommentSettings::c_style().strip_comments(content.as_bytes()),
)?)
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -669,7 +669,7 @@ impl<'a, T: Item> SeekAggregate<'a, T> for () {
impl<'a, T: Item> SeekAggregate<'a, T> for SliceSeekAggregate<T> {
fn begin_leaf(&mut self) {}
fn end_leaf(&mut self, cx: &<T::Summary as Summary>::Context) {
self.tree.append(
self.tree.push_tree(
SumTree(Arc::new(Node::Leaf {
summary: mem::take(&mut self.leaf_summary),
items: mem::take(&mut self.leaf_items),
@@ -689,7 +689,7 @@ impl<'a, T: Item> SeekAggregate<'a, T> for SliceSeekAggregate<T> {
_: &T::Summary,
cx: &<T::Summary as Summary>::Context,
) {
self.tree.append(tree.clone(), cx);
self.tree.push_tree(tree.clone(), cx);
}
}

View File

@@ -268,7 +268,7 @@ impl<T: Item> SumTree<T> {
for item in iter {
if leaf.is_some() && leaf.as_ref().unwrap().items().len() == 2 * TREE_BASE {
self.append(SumTree(Arc::new(leaf.take().unwrap())), cx);
self.push_tree(SumTree(Arc::new(leaf.take().unwrap())), cx);
}
if leaf.is_none() {
@@ -295,13 +295,13 @@ impl<T: Item> SumTree<T> {
}
if leaf.is_some() {
self.append(SumTree(Arc::new(leaf.take().unwrap())), cx);
self.push_tree(SumTree(Arc::new(leaf.take().unwrap())), cx);
}
}
pub fn push(&mut self, item: T, cx: &<T::Summary as Summary>::Context) {
let summary = item.summary();
self.append(
self.push_tree(
SumTree(Arc::new(Node::Leaf {
summary: summary.clone(),
items: ArrayVec::from_iter(Some(item)),
@@ -311,11 +311,11 @@ impl<T: Item> SumTree<T> {
);
}
pub fn append(&mut self, other: Self, cx: &<T::Summary as Summary>::Context) {
pub fn push_tree(&mut self, other: Self, cx: &<T::Summary as Summary>::Context) {
if !other.0.is_leaf() || !other.0.items().is_empty() {
if self.0.height() < other.0.height() {
for tree in other.0.child_trees() {
self.append(tree.clone(), cx);
self.push_tree(tree.clone(), cx);
}
} else if let Some(split_tree) = self.push_tree_recursive(other, cx) {
*self = Self::from_child_trees(self.clone(), split_tree, cx);
@@ -512,7 +512,7 @@ impl<T: KeyedItem> SumTree<T> {
}
}
new_tree.push(item, cx);
new_tree.append(cursor.suffix(cx), cx);
new_tree.push_tree(cursor.suffix(cx), cx);
new_tree
};
replaced
@@ -529,7 +529,7 @@ impl<T: KeyedItem> SumTree<T> {
cursor.next(cx);
}
}
new_tree.append(cursor.suffix(cx), cx);
new_tree.push_tree(cursor.suffix(cx), cx);
new_tree
};
removed
@@ -563,7 +563,7 @@ impl<T: KeyedItem> SumTree<T> {
{
new_tree.extend(buffered_items.drain(..), cx);
let slice = cursor.slice(&new_key, Bias::Left, cx);
new_tree.append(slice, cx);
new_tree.push_tree(slice, cx);
old_item = cursor.item();
}
@@ -583,7 +583,7 @@ impl<T: KeyedItem> SumTree<T> {
}
new_tree.extend(buffered_items, cx);
new_tree.append(cursor.suffix(cx), cx);
new_tree.push_tree(cursor.suffix(cx), cx);
new_tree
};
@@ -719,7 +719,7 @@ mod tests {
let mut tree2 = SumTree::new();
tree2.extend(50..100, &());
tree1.append(tree2, &());
tree1.push_tree(tree2, &());
assert_eq!(
tree1.items(&()),
(0..20).chain(50..100).collect::<Vec<u8>>()
@@ -766,7 +766,7 @@ mod tests {
let mut new_tree = cursor.slice(&Count(splice_start), Bias::Right, &());
new_tree.extend(new_items, &());
cursor.seek(&Count(splice_end), Bias::Right, &());
new_tree.append(cursor.slice(&tree_end, Bias::Right, &()), &());
new_tree.push_tree(cursor.slice(&tree_end, Bias::Right, &()), &());
new_tree
};

View File

@@ -67,7 +67,7 @@ impl<K: Clone + Debug + Default + Ord, V: Clone + Debug> TreeMap<K, V> {
removed = Some(cursor.item().unwrap().value.clone());
cursor.next(&());
}
new_tree.append(cursor.suffix(&()), &());
new_tree.push_tree(cursor.suffix(&()), &());
drop(cursor);
self.0 = new_tree;
removed
@@ -79,7 +79,7 @@ impl<K: Clone + Debug + Default + Ord, V: Clone + Debug> TreeMap<K, V> {
let mut cursor = self.0.cursor::<MapKeyRef<'_, K>>();
let mut new_tree = cursor.slice(&start, Bias::Left, &());
cursor.seek(&end, Bias::Left, &());
new_tree.append(cursor.suffix(&()), &());
new_tree.push_tree(cursor.suffix(&()), &());
drop(cursor);
self.0 = new_tree;
}
@@ -117,7 +117,7 @@ impl<K: Clone + Debug + Default + Ord, V: Clone + Debug> TreeMap<K, V> {
new_tree.push(updated, &());
cursor.next(&());
}
new_tree.append(cursor.suffix(&()), &());
new_tree.push_tree(cursor.suffix(&()), &());
drop(cursor);
self.0 = new_tree;
result

View File

@@ -25,7 +25,6 @@ pub fn init(cx: &mut AppContext) {
cx.add_action(TerminalPanel::new_terminal);
}
#[derive(Debug)]
pub enum Event {
Close,
DockPositionChanged,

View File

@@ -600,7 +600,7 @@ impl Buffer {
let mut old_fragments = self.fragments.cursor::<FragmentTextSummary>();
let mut new_fragments =
old_fragments.slice(&edits.peek().unwrap().0.start, Bias::Right, &None);
new_ropes.append(new_fragments.summary().text);
new_ropes.push_tree(new_fragments.summary().text);
let mut fragment_start = old_fragments.start().visible;
for (range, new_text) in edits {
@@ -625,8 +625,8 @@ impl Buffer {
}
let slice = old_fragments.slice(&range.start, Bias::Right, &None);
new_ropes.append(slice.summary().text);
new_fragments.append(slice, &None);
new_ropes.push_tree(slice.summary().text);
new_fragments.push_tree(slice, &None);
fragment_start = old_fragments.start().visible;
}
@@ -728,8 +728,8 @@ impl Buffer {
}
let suffix = old_fragments.suffix(&None);
new_ropes.append(suffix.summary().text);
new_fragments.append(suffix, &None);
new_ropes.push_tree(suffix.summary().text);
new_fragments.push_tree(suffix, &None);
let (visible_text, deleted_text) = new_ropes.finish();
drop(old_fragments);
@@ -828,7 +828,7 @@ impl Buffer {
Bias::Left,
&cx,
);
new_ropes.append(new_fragments.summary().text);
new_ropes.push_tree(new_fragments.summary().text);
let mut fragment_start = old_fragments.start().0.full_offset();
for (range, new_text) in edits {
@@ -854,8 +854,8 @@ impl Buffer {
let slice =
old_fragments.slice(&VersionedFullOffset::Offset(range.start), Bias::Left, &cx);
new_ropes.append(slice.summary().text);
new_fragments.append(slice, &None);
new_ropes.push_tree(slice.summary().text);
new_fragments.push_tree(slice, &None);
fragment_start = old_fragments.start().0.full_offset();
}
@@ -986,8 +986,8 @@ impl Buffer {
}
let suffix = old_fragments.suffix(&cx);
new_ropes.append(suffix.summary().text);
new_fragments.append(suffix, &None);
new_ropes.push_tree(suffix.summary().text);
new_fragments.push_tree(suffix, &None);
let (visible_text, deleted_text) = new_ropes.finish();
drop(old_fragments);
@@ -1056,8 +1056,8 @@ impl Buffer {
for fragment_id in self.fragment_ids_for_edits(undo.counts.keys()) {
let preceding_fragments = old_fragments.slice(&Some(fragment_id), Bias::Left, &None);
new_ropes.append(preceding_fragments.summary().text);
new_fragments.append(preceding_fragments, &None);
new_ropes.push_tree(preceding_fragments.summary().text);
new_fragments.push_tree(preceding_fragments, &None);
if let Some(fragment) = old_fragments.item() {
let mut fragment = fragment.clone();
@@ -1087,8 +1087,8 @@ impl Buffer {
}
let suffix = old_fragments.suffix(&None);
new_ropes.append(suffix.summary().text);
new_fragments.append(suffix, &None);
new_ropes.push_tree(suffix.summary().text);
new_fragments.push_tree(suffix, &None);
drop(old_fragments);
let (visible_text, deleted_text) = new_ropes.finish();
@@ -2070,7 +2070,7 @@ impl<'a> RopeBuilder<'a> {
}
}
fn append(&mut self, len: FragmentTextSummary) {
fn push_tree(&mut self, len: FragmentTextSummary) {
self.push(len.visible, true, true);
self.push(len.deleted, false, false);
}

View File

@@ -4,7 +4,7 @@ pub mod ui;
use gpui::{
color::Color,
elements::{ContainerStyle, ImageStyle, LabelStyle, Shadow, SvgStyle, TooltipStyle},
elements::{ContainerStyle, ImageStyle, LabelStyle, Shadow, TooltipStyle},
fonts::{HighlightStyle, TextStyle},
platform, AppContext, AssetSource, Border, MouseState,
};
@@ -12,7 +12,7 @@ use serde::{de::DeserializeOwned, Deserialize};
use serde_json::Value;
use settings::SettingsStore;
use std::{collections::HashMap, sync::Arc};
use ui::{ButtonStyle, CheckboxStyle, IconStyle, ModalStyle};
use ui::{ButtonStyle, CheckboxStyle, IconStyle, ModalStyle, SvgStyle};
pub use theme_registry::*;
pub use theme_settings::*;
@@ -128,12 +128,12 @@ pub struct Titlebar {
pub leader_avatar: AvatarStyle,
pub follower_avatar: AvatarStyle,
pub inactive_avatar_grayscale: bool,
pub sign_in_prompt: Toggleable<Interactive<ContainedText>>,
pub sign_in_prompt: Interactive<ContainedText>,
pub outdated_warning: ContainedText,
pub share_button: Toggleable<Interactive<ContainedText>>,
pub share_button: Interactive<ContainedText>,
pub call_control: Interactive<IconButton>,
pub toggle_contacts_button: Toggleable<Interactive<IconButton>>,
pub user_menu_button: Toggleable<Interactive<IconButton>>,
pub toggle_contacts_button: Interactive<IconButton>,
pub user_menu_button: Interactive<IconButton>,
pub toggle_contacts_badge: ContainerStyle,
}
@@ -204,12 +204,12 @@ pub struct ContactList {
pub user_query_editor: FieldEditor,
pub user_query_editor_height: f32,
pub add_contact_button: IconButton,
pub header_row: Toggleable<Interactive<ContainedText>>,
pub header_row: Interactive<ContainedText>,
pub leave_call: Interactive<ContainedText>,
pub contact_row: Toggleable<Interactive<ContainerStyle>>,
pub contact_row: Interactive<ContainerStyle>,
pub row_height: f32,
pub project_row: Toggleable<Interactive<ProjectRow>>,
pub tree_branch: Toggleable<Interactive<TreeBranch>>,
pub project_row: Interactive<ProjectRow>,
pub tree_branch: Interactive<TreeBranch>,
pub contact_avatar: ImageStyle,
pub contact_status_free: ContainerStyle,
pub contact_status_busy: ContainerStyle,
@@ -251,7 +251,7 @@ pub struct DropdownMenu {
pub container: ContainerStyle,
pub header: Interactive<DropdownMenuItem>,
pub section_header: ContainedText,
pub item: Toggleable<Interactive<DropdownMenuItem>>,
pub item: Interactive<DropdownMenuItem>,
pub row_height: f32,
}
@@ -270,7 +270,7 @@ pub struct DropdownMenuItem {
pub struct TabBar {
#[serde(flatten)]
pub container: ContainerStyle,
pub pane_button: Toggleable<Interactive<IconButton>>,
pub pane_button: Interactive<IconButton>,
pub pane_button_container: ContainerStyle,
pub active_pane: TabStyles,
pub inactive_pane: TabStyles,
@@ -359,7 +359,7 @@ pub struct Search {
pub include_exclude_editor: FindEditor,
pub invalid_include_exclude_editor: ContainerStyle,
pub include_exclude_inputs: ContainedText,
pub option_button: Toggleable<Interactive<ContainedText>>,
pub option_button: Interactive<ContainedText>,
pub match_background: Color,
pub match_index: ContainedText,
pub results_status: TextStyle,
@@ -395,7 +395,7 @@ pub struct StatusBarPanelButtons {
pub group_left: ContainerStyle,
pub group_bottom: ContainerStyle,
pub group_right: ContainerStyle,
pub button: Toggleable<Interactive<PanelButton>>,
pub button: Interactive<PanelButton>,
}
#[derive(Deserialize, Default)]
@@ -444,10 +444,10 @@ pub struct PanelButton {
pub struct ProjectPanel {
#[serde(flatten)]
pub container: ContainerStyle,
pub entry: Toggleable<Interactive<ProjectPanelEntry>>,
pub entry: Interactive<ProjectPanelEntry>,
pub dragged_entry: ProjectPanelEntry,
pub ignored_entry: Toggleable<Interactive<ProjectPanelEntry>>,
pub cut_entry: Toggleable<Interactive<ProjectPanelEntry>>,
pub ignored_entry: Interactive<ProjectPanelEntry>,
pub cut_entry: Interactive<ProjectPanelEntry>,
pub filename_editor: FieldEditor,
pub indent_width: f32,
pub open_project_button: Interactive<ContainedText>,
@@ -481,7 +481,7 @@ pub struct GitProjectStatus {
pub struct ContextMenu {
#[serde(flatten)]
pub container: ContainerStyle,
pub item: Toggleable<Interactive<ContextMenuItem>>,
pub item: Interactive<ContextMenuItem>,
pub keystroke_margin: f32,
pub separator: ContainerStyle,
}
@@ -498,7 +498,7 @@ pub struct ContextMenuItem {
#[derive(Debug, Deserialize, Default)]
pub struct CommandPalette {
pub key: Toggleable<ContainedLabel>,
pub key: Interactive<ContainedLabel>,
pub keystroke_spacing: f32,
}
@@ -565,7 +565,7 @@ pub struct Picker {
pub input_editor: FieldEditor,
pub empty_input_editor: FieldEditor,
pub no_matches: ContainedLabel,
pub item: Toggleable<Interactive<ContainedLabel>>,
pub item: Interactive<ContainedLabel>,
}
#[derive(Clone, Debug, Deserialize, Default)]
@@ -771,13 +771,13 @@ pub struct InteractiveColor {
#[derive(Clone, Deserialize, Default)]
pub struct CodeActions {
#[serde(default)]
pub indicator: Toggleable<Interactive<InteractiveColor>>,
pub indicator: Interactive<InteractiveColor>,
pub vertical_scale: f32,
}
#[derive(Clone, Deserialize, Default)]
pub struct Folds {
pub indicator: Toggleable<Interactive<InteractiveColor>>,
pub indicator: Interactive<InteractiveColor>,
pub ellipses: FoldEllipses,
pub fold_background: Color,
pub icon_margin_scale: f32,
@@ -805,46 +805,38 @@ pub struct DiffStyle {
#[derive(Debug, Default, Clone, Copy)]
pub struct Interactive<T> {
pub default: T,
pub hovered: Option<T>,
pub hover: Option<T>,
pub hover_and_active: Option<T>,
pub clicked: Option<T>,
pub click_and_active: Option<T>,
pub active: Option<T>,
pub disabled: Option<T>,
}
#[derive(Clone, Copy, Debug, Default, Deserialize)]
pub struct Toggleable<T> {
active: T,
inactive: T,
}
impl<T> Toggleable<T> {
pub fn new(active: T, inactive: T) -> Self {
Self { active, inactive }
}
pub fn in_state(&self, active: bool) -> &T {
if active {
&self.active
} else {
&self.inactive
}
}
pub fn active_state(&self) -> &T {
self.in_state(true)
}
pub fn inactive_state(&self) -> &T {
self.in_state(false)
}
}
impl<T> Interactive<T> {
pub fn style_for(&self, state: &mut MouseState) -> &T {
if state.clicked() == Some(platform::MouseButton::Left) && self.clicked.is_some() {
pub fn style_for(&self, state: &mut MouseState, active: bool) -> &T {
if active {
if state.hovered() {
self.hover_and_active
.as_ref()
.unwrap_or(self.active.as_ref().unwrap_or(&self.default))
} else if state.clicked() == Some(platform::MouseButton::Left) && self.clicked.is_some()
{
self.click_and_active
.as_ref()
.unwrap_or(self.active.as_ref().unwrap_or(&self.default))
} else {
self.active.as_ref().unwrap_or(&self.default)
}
} else if state.clicked() == Some(platform::MouseButton::Left) && self.clicked.is_some() {
self.clicked.as_ref().unwrap()
} else if state.hovered() {
self.hovered.as_ref().unwrap_or(&self.default)
self.hover.as_ref().unwrap_or(&self.default)
} else {
&self.default
}
}
pub fn disabled_style(&self) -> &T {
self.disabled.as_ref().unwrap_or(&self.default)
}
@@ -857,9 +849,13 @@ impl<'de, T: DeserializeOwned> Deserialize<'de> for Interactive<T> {
{
#[derive(Deserialize)]
struct Helper {
#[serde(flatten)]
default: Value,
hovered: Option<Value>,
hover: Option<Value>,
hover_and_active: Option<Value>,
clicked: Option<Value>,
click_and_active: Option<Value>,
active: Option<Value>,
disabled: Option<Value>,
}
@@ -884,15 +880,21 @@ impl<'de, T: DeserializeOwned> Deserialize<'de> for Interactive<T> {
}
};
let hovered = deserialize_state(json.hovered)?;
let hover = deserialize_state(json.hover)?;
let hover_and_active = deserialize_state(json.hover_and_active)?;
let clicked = deserialize_state(json.clicked)?;
let click_and_active = deserialize_state(json.click_and_active)?;
let active = deserialize_state(json.active)?;
let disabled = deserialize_state(json.disabled)?;
let default = serde_json::from_value(json.default).map_err(serde::de::Error::custom)?;
Ok(Interactive {
default,
hovered,
hover,
hover_and_active,
clicked,
click_and_active,
active,
disabled,
})
}
@@ -992,33 +994,18 @@ pub struct TerminalStyle {
#[derive(Clone, Deserialize, Default)]
pub struct AssistantStyle {
pub container: ContainerStyle,
pub hamburger_button: Interactive<IconStyle>,
pub split_button: Interactive<IconStyle>,
pub assist_button: Interactive<IconStyle>,
pub quote_button: Interactive<IconStyle>,
pub zoom_in_button: Interactive<IconStyle>,
pub zoom_out_button: Interactive<IconStyle>,
pub plus_button: Interactive<IconStyle>,
pub title: ContainedText,
pub message_header: ContainerStyle,
pub header: ContainerStyle,
pub sent_at: ContainedText,
pub user_sender: Interactive<ContainedText>,
pub assistant_sender: Interactive<ContainedText>,
pub system_sender: Interactive<ContainedText>,
pub model_info_container: ContainerStyle,
pub model: Interactive<ContainedText>,
pub remaining_tokens: ContainedText,
pub no_remaining_tokens: ContainedText,
pub error_icon: Icon,
pub api_key_editor: FieldEditor,
pub api_key_prompt: ContainedText,
pub saved_conversation: SavedConversation,
}
#[derive(Clone, Deserialize, Default)]
pub struct SavedConversation {
pub container: Interactive<ContainerStyle>,
pub saved_at: ContainedText,
pub title: ContainedText,
}
#[derive(Clone, Deserialize, Default)]

View File

@@ -1,12 +1,13 @@
use std::borrow::Cow;
use gpui::{
color::Color,
elements::{
ConstrainedBox, Container, ContainerStyle, Dimensions, Empty, Flex, KeystrokeLabel, Label,
MouseEventHandler, ParentElement, Stack, Svg, SvgStyle,
ConstrainedBox, Container, ContainerStyle, Empty, Flex, KeystrokeLabel, Label,
MouseEventHandler, ParentElement, Stack, Svg,
},
fonts::TextStyle,
geometry::vector::Vector2F,
geometry::vector::{vec2f, Vector2F},
platform,
platform::MouseButton,
scene::MouseClick,
@@ -92,6 +93,25 @@ where
.with_cursor_style(platform::CursorStyle::PointingHand)
}
#[derive(Clone, Deserialize, Default)]
pub struct SvgStyle {
pub color: Color,
pub asset: String,
pub dimensions: Dimensions,
}
#[derive(Clone, Deserialize, Default)]
pub struct Dimensions {
pub width: f32,
pub height: f32,
}
impl Dimensions {
pub fn to_vec(&self) -> Vector2F {
vec2f(self.width, self.height)
}
}
pub fn svg<V: View>(style: &SvgStyle) -> ConstrainedBox<V> {
Svg::new(style.asset.clone())
.with_color(style.color)
@@ -102,8 +122,8 @@ pub fn svg<V: View>(style: &SvgStyle) -> ConstrainedBox<V> {
#[derive(Clone, Deserialize, Default)]
pub struct IconStyle {
pub icon: SvgStyle,
pub container: ContainerStyle,
icon: SvgStyle,
container: ContainerStyle,
}
pub fn icon<V: View>(style: &IconStyle) -> Container<V> {
@@ -150,7 +170,7 @@ where
F: Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
{
MouseEventHandler::<Tag, V>::new(0, cx, |state, _| {
let style = style.style_for(state);
let style = style.style_for(state, false);
Label::new(label, style.text.to_owned())
.aligned()
.contained()
@@ -200,13 +220,13 @@ where
title,
style
.title_text
.style_for(&mut MouseState::default())
.style_for(&mut MouseState::default(), false)
.clone(),
))
.with_child(
// FIXME: Get a better tag type
MouseEventHandler::<Tag, V>::new(999999, cx, |state, _cx| {
let style = style.close_icon.style_for(state);
let style = style.close_icon.style_for(state, false);
icon(style)
})
.on_click(platform::MouseButton::Left, move |_, _, cx| {

View File

@@ -208,7 +208,7 @@ impl PickerDelegate for ThemeSelectorDelegate {
cx: &AppContext,
) -> AnyElement<Picker<Self>> {
let theme = theme::current(cx);
let style = theme.picker.item.in_state(selected).style_for(mouse_state);
let style = theme.picker.item.style_for(mouse_state, selected);
let theme_match = &self.matches[ix];
Label::new(theme_match.string.clone(), style.label.clone())

View File

@@ -0,0 +1,19 @@
[package]
name = "theme_testbench"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
path = "src/theme_testbench.rs"
doctest = false
[dependencies]
gpui = { path = "../gpui" }
theme = { path = "../theme" }
settings = { path = "../settings" }
workspace = { path = "../workspace" }
project = { path = "../project" }
smallvec.workspace = true

View File

@@ -0,0 +1,300 @@
use gpui::{
actions,
color::Color,
elements::{
AnyElement, Canvas, Container, ContainerStyle, Flex, Label, Margin, MouseEventHandler,
Padding, ParentElement,
},
fonts::TextStyle,
AppContext, Border, Element, Entity, ModelHandle, Quad, Task, View, ViewContext, ViewHandle,
WeakViewHandle,
};
use project::Project;
use theme::{ColorScheme, Layer, Style, StyleSet, ThemeSettings};
use workspace::{item::Item, register_deserializable_item, Pane, Workspace};
actions!(theme, [DeployThemeTestbench]);
pub fn init(cx: &mut AppContext) {
cx.add_action(ThemeTestbench::deploy);
register_deserializable_item::<ThemeTestbench>(cx)
}
pub struct ThemeTestbench {}
impl ThemeTestbench {
pub fn deploy(
workspace: &mut Workspace,
_: &DeployThemeTestbench,
cx: &mut ViewContext<Workspace>,
) {
let view = cx.add_view(|_| ThemeTestbench {});
workspace.add_item(Box::new(view), cx);
}
fn render_ramps(color_scheme: &ColorScheme) -> Flex<Self> {
fn display_ramp(ramp: &Vec<Color>) -> AnyElement<ThemeTestbench> {
Flex::row()
.with_children(ramp.iter().cloned().map(|color| {
Canvas::new(move |scene, bounds, _, _, _| {
scene.push_quad(Quad {
bounds,
background: Some(color),
..Default::default()
});
})
.flex(1.0, false)
}))
.flex(1.0, false)
.into_any()
}
Flex::column()
.with_child(display_ramp(&color_scheme.ramps.neutral))
.with_child(display_ramp(&color_scheme.ramps.red))
.with_child(display_ramp(&color_scheme.ramps.orange))
.with_child(display_ramp(&color_scheme.ramps.yellow))
.with_child(display_ramp(&color_scheme.ramps.green))
.with_child(display_ramp(&color_scheme.ramps.cyan))
.with_child(display_ramp(&color_scheme.ramps.blue))
.with_child(display_ramp(&color_scheme.ramps.violet))
.with_child(display_ramp(&color_scheme.ramps.magenta))
}
fn render_layer(
layer_index: usize,
layer: &Layer,
cx: &mut ViewContext<Self>,
) -> Container<Self> {
Flex::column()
.with_child(
Self::render_button_set(0, layer_index, "base", &layer.base, cx).flex(1., false),
)
.with_child(
Self::render_button_set(1, layer_index, "variant", &layer.variant, cx)
.flex(1., false),
)
.with_child(
Self::render_button_set(2, layer_index, "on", &layer.on, cx).flex(1., false),
)
.with_child(
Self::render_button_set(3, layer_index, "accent", &layer.accent, cx)
.flex(1., false),
)
.with_child(
Self::render_button_set(4, layer_index, "positive", &layer.positive, cx)
.flex(1., false),
)
.with_child(
Self::render_button_set(5, layer_index, "warning", &layer.warning, cx)
.flex(1., false),
)
.with_child(
Self::render_button_set(6, layer_index, "negative", &layer.negative, cx)
.flex(1., false),
)
.contained()
.with_style(ContainerStyle {
margin: Margin {
top: 10.,
bottom: 10.,
left: 10.,
right: 10.,
},
background_color: Some(layer.base.default.background),
..Default::default()
})
}
fn render_button_set(
set_index: usize,
layer_index: usize,
set_name: &'static str,
style_set: &StyleSet,
cx: &mut ViewContext<Self>,
) -> Flex<Self> {
Flex::row()
.with_child(Self::render_button(
set_index * 6,
layer_index,
set_name,
&style_set,
None,
cx,
))
.with_child(Self::render_button(
set_index * 6 + 1,
layer_index,
"hovered",
&style_set,
Some(|style_set| &style_set.hovered),
cx,
))
.with_child(Self::render_button(
set_index * 6 + 2,
layer_index,
"pressed",
&style_set,
Some(|style_set| &style_set.pressed),
cx,
))
.with_child(Self::render_button(
set_index * 6 + 3,
layer_index,
"active",
&style_set,
Some(|style_set| &style_set.active),
cx,
))
.with_child(Self::render_button(
set_index * 6 + 4,
layer_index,
"disabled",
&style_set,
Some(|style_set| &style_set.disabled),
cx,
))
.with_child(Self::render_button(
set_index * 6 + 5,
layer_index,
"inverted",
&style_set,
Some(|style_set| &style_set.inverted),
cx,
))
}
fn render_button(
button_index: usize,
layer_index: usize,
text: &'static str,
style_set: &StyleSet,
style_override: Option<fn(&StyleSet) -> &Style>,
cx: &mut ViewContext<Self>,
) -> AnyElement<Self> {
enum TestBenchButton {}
MouseEventHandler::<TestBenchButton, _>::new(layer_index + button_index, cx, |state, cx| {
let style = if let Some(style_override) = style_override {
style_override(&style_set)
} else if state.clicked().is_some() {
&style_set.pressed
} else if state.hovered() {
&style_set.hovered
} else {
&style_set.default
};
Self::render_label(text.to_string(), style, cx)
.contained()
.with_style(ContainerStyle {
margin: Margin {
top: 4.,
bottom: 4.,
left: 4.,
right: 4.,
},
padding: Padding {
top: 4.,
bottom: 4.,
left: 4.,
right: 4.,
},
background_color: Some(style.background),
border: Border {
width: 1.,
color: style.border,
overlay: false,
top: true,
bottom: true,
left: true,
right: true,
},
corner_radius: 2.,
..Default::default()
})
})
.flex(1., true)
.into_any()
}
fn render_label(text: String, style: &Style, cx: &mut ViewContext<Self>) -> Label {
let settings = settings::get::<ThemeSettings>(cx);
let font_cache = cx.font_cache();
let family_id = settings.buffer_font_family;
let font_size = settings.buffer_font_size(cx);
let font_id = font_cache
.select_font(family_id, &Default::default())
.unwrap();
let text_style = TextStyle {
color: style.foreground,
font_family_id: family_id,
font_family_name: font_cache.family_name(family_id).unwrap(),
font_id,
font_size,
font_properties: Default::default(),
underline: Default::default(),
};
Label::new(text, text_style)
}
}
impl Entity for ThemeTestbench {
type Event = ();
}
impl View for ThemeTestbench {
fn ui_name() -> &'static str {
"ThemeTestbench"
}
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> AnyElement<Self> {
let color_scheme = &theme::current(cx).clone().color_scheme;
Flex::row()
.with_child(
Self::render_ramps(color_scheme)
.contained()
.with_margin_right(10.)
.flex(0.1, false),
)
.with_child(
Flex::column()
.with_child(Self::render_layer(100, &color_scheme.lowest, cx).flex(1., true))
.with_child(Self::render_layer(200, &color_scheme.middle, cx).flex(1., true))
.with_child(Self::render_layer(300, &color_scheme.highest, cx).flex(1., true))
.flex(1., false),
)
.into_any()
}
}
impl Item for ThemeTestbench {
fn tab_content<T: View>(
&self,
_: Option<usize>,
style: &theme::Tab,
_: &AppContext,
) -> AnyElement<T> {
Label::new("Theme Testbench", style.label.clone())
.aligned()
.contained()
.into_any()
}
fn serialized_item_kind() -> Option<&'static str> {
Some("ThemeTestBench")
}
fn deserialize(
_project: ModelHandle<Project>,
_workspace: WeakViewHandle<Workspace>,
_workspace_id: workspace::WorkspaceId,
_item_id: workspace::ItemId,
cx: &mut ViewContext<Pane>,
) -> Task<gpui::anyhow::Result<ViewHandle<Self>>> {
Task::ready(Ok(cx.add_view(|_| Self {})))
}
}

View File

@@ -5,7 +5,6 @@ use serde::{Deserialize, Serialize};
lazy_static::lazy_static! {
pub static ref HOME: PathBuf = dirs::home_dir().expect("failed to determine home directory");
pub static ref CONFIG_DIR: PathBuf = HOME.join(".config").join("zed");
pub static ref CONVERSATIONS_DIR: PathBuf = HOME.join(".config/zed/conversations");
pub static ref LOGS_DIR: PathBuf = HOME.join("Library/Logs/Zed");
pub static ref SUPPORT_DIR: PathBuf = HOME.join("Library/Application Support/Zed");
pub static ref LANGUAGES_DIR: PathBuf = HOME.join("Library/Application Support/Zed/languages");

View File

@@ -141,7 +141,7 @@ impl PickerDelegate for BaseKeymapSelectorDelegate {
) -> gpui::AnyElement<Picker<Self>> {
let theme = &theme::current(cx);
let keymap_match = &self.matches[ix];
let style = theme.picker.item.in_state(selected).style_for(mouse_state);
let style = theme.picker.item.style_for(mouse_state, selected);
Label::new(keymap_match.string.clone(), style.label.clone())
.with_highlights(keymap_match.positions.clone())

View File

@@ -249,7 +249,7 @@ impl Dock {
}
}
pub(crate) fn add_panel<T: Panel>(&mut self, panel: ViewHandle<T>, cx: &mut ViewContext<Self>) {
pub fn add_panel<T: Panel>(&mut self, panel: ViewHandle<T>, cx: &mut ViewContext<Self>) {
let subscriptions = [
cx.observe(&panel, |_, _, cx| cx.notify()),
cx.subscribe(&panel, |this, panel, event, cx| {
@@ -498,9 +498,7 @@ impl View for PanelButtons {
Stack::new()
.with_child(
MouseEventHandler::<Self, _>::new(panel_ix, cx, |state, cx| {
let style = button_style.in_state(is_active);
let style = style.style_for(state);
let style = button_style.style_for(state, is_active);
Flex::row()
.with_child(
Svg::new(view.icon_path(cx))
@@ -605,7 +603,6 @@ pub mod test {
use super::*;
use gpui::{ViewContext, WindowContext};
#[derive(Debug)]
pub enum TestPanelEvent {
PositionChanged,
Activated,

View File

@@ -291,7 +291,7 @@ pub mod simple_message_notification {
)
.with_child(
MouseEventHandler::<Cancel, _>::new(0, cx, |state, _| {
let style = theme.dismiss_button.style_for(state);
let style = theme.dismiss_button.style_for(state, false);
Svg::new("icons/x_mark_8.svg")
.with_color(style.color)
.constrained()
@@ -323,7 +323,7 @@ pub mod simple_message_notification {
0,
cx,
|state, _| {
let style = theme.action_message.style_for(state);
let style = theme.action_message.style_for(state, false);
Flex::row()
.with_child(

View File

@@ -1,10 +1,9 @@
mod dragged_item_receiver;
use super::{ItemHandle, SplitDirection};
pub use crate::toolbar::Toolbar;
use crate::{
item::WeakItemHandle, notify_of_new_dock, AutosaveSetting, Item, NewCenterTerminal, NewFile,
NewSearch, ToggleZoom, Workspace, WorkspaceSettings,
item::WeakItemHandle, notify_of_new_dock, toolbar::Toolbar, AutosaveSetting, Item,
NewCenterTerminal, NewFile, NewSearch, ToggleZoom, Workspace, WorkspaceSettings,
};
use anyhow::Result;
use collections::{HashMap, HashSet, VecDeque};
@@ -251,7 +250,7 @@ impl Pane {
pane: handle.clone(),
next_timestamp,
}))),
toolbar: cx.add_view(|_| Toolbar::new(Some(handle))),
toolbar: cx.add_view(|_| Toolbar::new(handle)),
tab_bar_context_menu: TabBarContextMenu {
kind: TabBarContextMenuKind::New,
handle: context_menu,
@@ -286,27 +285,19 @@ impl Pane {
pane.tab_bar_context_menu
.handle_if_kind(TabBarContextMenuKind::Split),
))
.with_child({
let icon_path;
let tooltip_label;
.with_child(Pane::render_tab_bar_button(
2,
if pane.is_zoomed() {
icon_path = "icons/minimize_8.svg";
tooltip_label = "Zoom In".into();
"icons/minimize_8.svg"
} else {
icon_path = "icons/maximize_8.svg";
tooltip_label = "Zoom In".into();
}
Pane::render_tab_bar_button(
2,
icon_path,
pane.is_zoomed(),
Some((tooltip_label, Some(Box::new(ToggleZoom)))),
cx,
move |pane, cx| pane.toggle_zoom(&Default::default(), cx),
None,
)
})
"icons/maximize_8.svg"
},
pane.is_zoomed(),
Some(("Toggle Zoom".into(), Some(Box::new(ToggleZoom)))),
cx,
move |pane, cx| pane.toggle_zoom(&Default::default(), cx),
None,
))
.into_any()
}),
}
@@ -1121,7 +1112,7 @@ impl Pane {
.get(self.active_item_index)
.map(|item| item.as_ref());
self.toolbar.update(cx, |toolbar, cx| {
toolbar.set_active_item(active_item, cx);
toolbar.set_active_pane_item(active_item, cx);
});
}
@@ -1419,7 +1410,7 @@ impl Pane {
pub fn render_tab_bar_button<F: 'static + Fn(&mut Pane, &mut EventContext<Pane>)>(
index: usize,
icon: &'static str,
is_active: bool,
active: bool,
tooltip: Option<(String, Option<Box<dyn Action>>)>,
cx: &mut ViewContext<Pane>,
on_click: F,
@@ -1429,7 +1420,7 @@ impl Pane {
let mut button = MouseEventHandler::<TabBarButton, _>::new(index, cx, |mouse_state, cx| {
let theme = &settings::get::<ThemeSettings>(cx).theme.workspace.tab_bar;
let style = theme.pane_button.in_state(is_active).style_for(mouse_state);
let style = theme.pane_button.style_for(mouse_state, active);
Svg::new(icon)
.with_color(style.color)
.constrained()
@@ -1611,7 +1602,7 @@ impl View for Pane {
}
self.toolbar.update(cx, |toolbar, cx| {
toolbar.focus_changed(true, cx);
toolbar.pane_focus_update(true, cx);
});
if let Some(active_item) = self.active_item() {
@@ -1640,7 +1631,7 @@ impl View for Pane {
fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
self.has_focus = false;
self.toolbar.update(cx, |toolbar, cx| {
toolbar.focus_changed(false, cx);
toolbar.pane_focus_update(false, cx);
});
cx.notify();
}

View File

@@ -162,12 +162,6 @@ define_connection! {
ALTER TABLE workspaces ADD COLUMN right_dock_active_panel TEXT;
ALTER TABLE workspaces ADD COLUMN bottom_dock_visible INTEGER; //bool
ALTER TABLE workspaces ADD COLUMN bottom_dock_active_panel TEXT;
),
// Add panel zoom persistence
sql!(
ALTER TABLE workspaces ADD COLUMN left_dock_zoom INTEGER; //bool
ALTER TABLE workspaces ADD COLUMN right_dock_zoom INTEGER; //bool
ALTER TABLE workspaces ADD COLUMN bottom_dock_zoom INTEGER; //bool
)];
}
@@ -202,13 +196,10 @@ impl WorkspaceDb {
display,
left_dock_visible,
left_dock_active_panel,
left_dock_zoom,
right_dock_visible,
right_dock_active_panel,
right_dock_zoom,
bottom_dock_visible,
bottom_dock_active_panel,
bottom_dock_zoom
bottom_dock_active_panel
FROM workspaces
WHERE workspace_location = ?
})
@@ -253,28 +244,22 @@ impl WorkspaceDb {
workspace_location,
left_dock_visible,
left_dock_active_panel,
left_dock_zoom,
right_dock_visible,
right_dock_active_panel,
right_dock_zoom,
bottom_dock_visible,
bottom_dock_active_panel,
bottom_dock_zoom,
timestamp
)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, CURRENT_TIMESTAMP)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, CURRENT_TIMESTAMP)
ON CONFLICT DO
UPDATE SET
workspace_location = ?2,
left_dock_visible = ?3,
left_dock_active_panel = ?4,
left_dock_zoom = ?5,
right_dock_visible = ?6,
right_dock_active_panel = ?7,
right_dock_zoom = ?8,
bottom_dock_visible = ?9,
bottom_dock_active_panel = ?10,
bottom_dock_zoom = ?11,
right_dock_visible = ?5,
right_dock_active_panel = ?6,
bottom_dock_visible = ?7,
bottom_dock_active_panel = ?8,
timestamp = CURRENT_TIMESTAMP
))?((workspace.id, &workspace.location, workspace.docks))
.context("Updating workspace")?;

View File

@@ -100,19 +100,16 @@ impl Bind for DockStructure {
pub struct DockData {
pub(crate) visible: bool,
pub(crate) active_panel: Option<String>,
pub(crate) zoom: bool,
}
impl Column for DockData {
fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
let (visible, next_index) = Option::<bool>::column(statement, start_index)?;
let (active_panel, next_index) = Option::<String>::column(statement, next_index)?;
let (zoom, next_index) = Option::<bool>::column(statement, next_index)?;
Ok((
DockData {
visible: visible.unwrap_or(false),
active_panel,
zoom: zoom.unwrap_or(false),
},
next_index,
))
@@ -122,8 +119,7 @@ impl Column for DockData {
impl Bind for DockData {
fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
let next_index = statement.bind(&self.visible, start_index)?;
let next_index = statement.bind(&self.active_panel, next_index)?;
statement.bind(&self.zoom, next_index)
statement.bind(&self.active_panel, next_index)
}
}

View File

@@ -38,7 +38,7 @@ trait ToolbarItemViewHandle {
active_pane_item: Option<&dyn ItemHandle>,
cx: &mut WindowContext,
) -> ToolbarItemLocation;
fn focus_changed(&mut self, pane_focused: bool, cx: &mut WindowContext);
fn pane_focus_update(&mut self, pane_focused: bool, cx: &mut WindowContext);
fn row_count(&self, cx: &WindowContext) -> usize;
}
@@ -51,10 +51,10 @@ pub enum ToolbarItemLocation {
}
pub struct Toolbar {
active_item: Option<Box<dyn ItemHandle>>,
active_pane_item: Option<Box<dyn ItemHandle>>,
hidden: bool,
can_navigate: bool,
pane: Option<WeakViewHandle<Pane>>,
pane: WeakViewHandle<Pane>,
items: Vec<(Box<dyn ToolbarItemViewHandle>, ToolbarItemLocation)>,
}
@@ -121,7 +121,7 @@ impl View for Toolbar {
let pane = self.pane.clone();
let mut enable_go_backward = false;
let mut enable_go_forward = false;
if let Some(pane) = pane.and_then(|pane| pane.upgrade(cx)) {
if let Some(pane) = pane.upgrade(cx) {
let pane = pane.read(cx);
enable_go_backward = pane.can_navigate_backward();
enable_go_forward = pane.can_navigate_forward();
@@ -143,17 +143,19 @@ impl View for Toolbar {
enable_go_backward,
spacing,
{
let pane = pane.clone();
move |toolbar, cx| {
if let Some(pane) = toolbar.pane.as_ref().and_then(|pane| pane.upgrade(cx))
if let Some(workspace) = toolbar
.pane
.upgrade(cx)
.and_then(|pane| pane.read(cx).workspace().upgrade(cx))
{
if let Some(workspace) = pane.read(cx).workspace().upgrade(cx) {
let pane = pane.downgrade();
cx.window_context().defer(move |cx| {
workspace.update(cx, |workspace, cx| {
workspace.go_back(pane, cx).detach_and_log_err(cx);
});
})
}
let pane = pane.clone();
cx.window_context().defer(move |cx| {
workspace.update(cx, |workspace, cx| {
workspace.go_back(pane.clone(), cx).detach_and_log_err(cx);
});
})
}
}
},
@@ -169,17 +171,21 @@ impl View for Toolbar {
enable_go_forward,
spacing,
{
let pane = pane.clone();
move |toolbar, cx| {
if let Some(pane) = toolbar.pane.as_ref().and_then(|pane| pane.upgrade(cx))
if let Some(workspace) = toolbar
.pane
.upgrade(cx)
.and_then(|pane| pane.read(cx).workspace().upgrade(cx))
{
if let Some(workspace) = pane.read(cx).workspace().upgrade(cx) {
let pane = pane.downgrade();
cx.window_context().defer(move |cx| {
workspace.update(cx, |workspace, cx| {
workspace.go_forward(pane, cx).detach_and_log_err(cx);
});
})
}
let pane = pane.clone();
cx.window_context().defer(move |cx| {
workspace.update(cx, |workspace, cx| {
workspace
.go_forward(pane.clone(), cx)
.detach_and_log_err(cx);
});
});
}
}
},
@@ -225,7 +231,7 @@ fn nav_button<A: Action, F: 'static + Fn(&mut Toolbar, &mut ViewContext<Toolbar>
) -> AnyElement<Toolbar> {
MouseEventHandler::<A, _>::new(0, cx, |state, _| {
let style = if enabled {
style.style_for(state)
style.style_for(state, false)
} else {
style.disabled_style()
};
@@ -263,9 +269,9 @@ fn nav_button<A: Action, F: 'static + Fn(&mut Toolbar, &mut ViewContext<Toolbar>
}
impl Toolbar {
pub fn new(pane: Option<WeakViewHandle<Pane>>) -> Self {
pub fn new(pane: WeakViewHandle<Pane>) -> Self {
Self {
active_item: None,
active_pane_item: None,
pane,
items: Default::default(),
hidden: false,
@@ -282,7 +288,7 @@ impl Toolbar {
where
T: 'static + ToolbarItemView,
{
let location = item.set_active_pane_item(self.active_item.as_deref(), cx);
let location = item.set_active_pane_item(self.active_pane_item.as_deref(), cx);
cx.subscribe(&item, |this, item, event, cx| {
if let Some((_, current_location)) =
this.items.iter_mut().find(|(i, _)| i.id() == item.id())
@@ -301,16 +307,20 @@ impl Toolbar {
cx.notify();
}
pub fn set_active_item(&mut self, item: Option<&dyn ItemHandle>, cx: &mut ViewContext<Self>) {
self.active_item = item.map(|item| item.boxed_clone());
pub fn set_active_pane_item(
&mut self,
pane_item: Option<&dyn ItemHandle>,
cx: &mut ViewContext<Self>,
) {
self.active_pane_item = pane_item.map(|item| item.boxed_clone());
self.hidden = self
.active_item
.active_pane_item
.as_ref()
.map(|item| !item.show_toolbar(cx))
.unwrap_or(false);
for (toolbar_item, current_location) in self.items.iter_mut() {
let new_location = toolbar_item.set_active_pane_item(item, cx);
let new_location = toolbar_item.set_active_pane_item(pane_item, cx);
if new_location != *current_location {
*current_location = new_location;
cx.notify();
@@ -318,9 +328,9 @@ impl Toolbar {
}
}
pub fn focus_changed(&mut self, focused: bool, cx: &mut ViewContext<Self>) {
pub fn pane_focus_update(&mut self, pane_focused: bool, cx: &mut ViewContext<Self>) {
for (toolbar_item, _) in self.items.iter_mut() {
toolbar_item.focus_changed(focused, cx);
toolbar_item.pane_focus_update(pane_focused, cx);
}
}
@@ -354,7 +364,7 @@ impl<T: ToolbarItemView> ToolbarItemViewHandle for ViewHandle<T> {
})
}
fn focus_changed(&mut self, pane_focused: bool, cx: &mut WindowContext) {
fn pane_focus_update(&mut self, pane_focused: bool, cx: &mut WindowContext) {
self.update(cx, |this, cx| {
this.pane_focus_update(pane_focused, cx);
cx.notify();

View File

@@ -140,11 +140,9 @@ pub struct OpenPaths {
#[derive(Clone, Deserialize, PartialEq)]
pub struct ActivatePane(pub usize);
#[derive(Deserialize)]
pub struct Toast {
id: usize,
msg: Cow<'static, str>,
#[serde(skip)]
on_click: Option<(Cow<'static, str>, Arc<dyn Fn(&mut WindowContext)>)>,
}
@@ -185,10 +183,10 @@ impl Clone for Toast {
}
}
impl_actions!(workspace, [ActivatePane, Toast]);
pub type WorkspaceId = i64;
impl_actions!(workspace, [ActivatePane]);
pub fn init_settings(cx: &mut AppContext) {
settings::register::<WorkspaceSettings>(cx);
}
@@ -555,10 +553,6 @@ impl Workspace {
}
}
project::Event::Notification(message) => this.show_notification(0, cx, |cx| {
cx.add_view(|_| MessageNotification::new(message.clone()))
}),
_ => {}
}
cx.notify()
@@ -861,10 +855,7 @@ impl Workspace {
&self.right_dock
}
pub fn add_panel<T: Panel>(&mut self, panel: ViewHandle<T>, cx: &mut ViewContext<Self>)
where
T::Event: std::fmt::Debug,
{
pub fn add_panel<T: Panel>(&mut self, panel: ViewHandle<T>, cx: &mut ViewContext<Self>) {
let dock = match panel.position(cx) {
DockPosition::Left => &self.left_dock,
DockPosition::Bottom => &self.bottom_dock,
@@ -907,11 +898,10 @@ impl Workspace {
});
} else if T::should_zoom_in_on_event(event) {
dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, true, cx));
if !panel.has_focus(cx) {
cx.focus(&panel);
if panel.has_focus(cx) {
this.zoomed = Some(panel.downgrade().into_any());
this.zoomed_position = Some(panel.read(cx).position(cx));
}
this.zoomed = Some(panel.downgrade().into_any());
this.zoomed_position = Some(panel.read(cx).position(cx));
} else if T::should_zoom_out_on_event(event) {
dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, false, cx));
if this.zoomed_position == Some(prev_position) {
@@ -1609,7 +1599,9 @@ impl Workspace {
focus_center = true;
}
} else {
cx.focus(active_panel.as_any());
if active_panel.is_zoomed(cx) {
cx.focus(active_panel.as_any());
}
reveal_dock = true;
}
}
@@ -1706,11 +1698,6 @@ impl Workspace {
cx.notify();
}
#[cfg(any(test, feature = "test-support"))]
pub fn zoomed_view(&self, cx: &AppContext) -> Option<AnyViewHandle> {
self.zoomed.and_then(|view| view.upgrade(cx))
}
fn dismiss_zoomed_items_to_reveal(
&mut self,
dock_to_reveal: Option<DockPosition>,
@@ -2863,7 +2850,7 @@ impl Workspace {
cx.notify();
}
fn serialize_workspace(&self, cx: &ViewContext<Self>) {
fn serialize_workspace(&self, cx: &AppContext) {
fn serialize_pane_handle(
pane_handle: &ViewHandle<Pane>,
cx: &AppContext,
@@ -2906,7 +2893,7 @@ impl Workspace {
}
}
fn build_serialized_docks(this: &Workspace, cx: &ViewContext<Workspace>) -> DockStructure {
fn build_serialized_docks(this: &Workspace, cx: &AppContext) -> DockStructure {
let left_dock = this.left_dock.read(cx);
let left_visible = left_dock.is_open();
let left_active_panel = left_dock.visible_panel().and_then(|panel| {
@@ -2915,10 +2902,6 @@ impl Workspace {
.to_string(),
)
});
let left_dock_zoom = left_dock
.visible_panel()
.map(|panel| panel.is_zoomed(cx))
.unwrap_or(false);
let right_dock = this.right_dock.read(cx);
let right_visible = right_dock.is_open();
@@ -2928,10 +2911,6 @@ impl Workspace {
.to_string(),
)
});
let right_dock_zoom = right_dock
.visible_panel()
.map(|panel| panel.is_zoomed(cx))
.unwrap_or(false);
let bottom_dock = this.bottom_dock.read(cx);
let bottom_visible = bottom_dock.is_open();
@@ -2941,26 +2920,19 @@ impl Workspace {
.to_string(),
)
});
let bottom_dock_zoom = bottom_dock
.visible_panel()
.map(|panel| panel.is_zoomed(cx))
.unwrap_or(false);
DockStructure {
left: DockData {
visible: left_visible,
active_panel: left_active_panel,
zoom: left_dock_zoom,
},
right: DockData {
visible: right_visible,
active_panel: right_active_panel,
zoom: right_dock_zoom,
},
bottom: DockData {
visible: bottom_visible,
active_panel: bottom_active_panel,
zoom: bottom_dock_zoom,
},
}
}
@@ -3073,31 +3045,14 @@ impl Workspace {
dock.activate_panel(ix, cx);
}
}
dock.active_panel()
.map(|panel| {
panel.set_zoomed(docks.left.zoom, cx)
});
if docks.left.visible && docks.left.zoom {
cx.focus_self()
}
});
// TODO: I think the bug is that setting zoom or active undoes the bottom zoom or something
workspace.right_dock.update(cx, |dock, cx| {
dock.set_open(docks.right.visible, cx);
if let Some(active_panel) = docks.right.active_panel {
if let Some(ix) = dock.panel_index_for_ui_name(&active_panel, cx) {
dock.activate_panel(ix, cx);
}
}
dock.active_panel()
.map(|panel| {
panel.set_zoomed(docks.right.zoom, cx)
});
if docks.right.visible && docks.right.zoom {
cx.focus_self()
}
});
workspace.bottom_dock.update(cx, |dock, cx| {
dock.set_open(docks.bottom.visible, cx);
@@ -3106,18 +3061,8 @@ impl Workspace {
dock.activate_panel(ix, cx);
}
}
dock.active_panel()
.map(|panel| {
panel.set_zoomed(docks.bottom.zoom, cx)
});
if docks.bottom.visible && docks.bottom.zoom {
cx.focus_self()
}
});
cx.notify();
})?;
@@ -4480,7 +4425,7 @@ mod tests {
workspace.read_with(cx, |workspace, cx| {
assert!(workspace.right_dock().read(cx).is_open());
assert!(!panel.is_zoomed(cx));
assert!(panel.has_focus(cx));
assert!(!panel.has_focus(cx));
});
// Focus and zoom panel
@@ -4555,7 +4500,7 @@ mod tests {
workspace.read_with(cx, |workspace, cx| {
let pane = pane.read(cx);
assert!(!pane.is_zoomed());
assert!(!pane.has_focus());
assert!(pane.has_focus());
assert!(workspace.right_dock().read(cx).is_open());
assert!(workspace.zoomed.is_none());
});

View File

@@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathansobo@gmail.com>"]
description = "The fast, collaborative code editor."
edition = "2021"
name = "zed"
version = "0.92.2"
version = "0.91.4"
publish = false
[lib]
@@ -62,6 +62,7 @@ text = { path = "../text" }
terminal_view = { path = "../terminal_view" }
theme = { path = "../theme" }
theme_selector = { path = "../theme_selector" }
theme_testbench = { path = "../theme_testbench" }
util = { path = "../util" }
vim = { path = "../vim" }
workspace = { path = "../workspace" }

View File

@@ -4,11 +4,12 @@ use futures::StreamExt;
pub use language::*;
use smol::fs::{self, File};
use std::{any::Any, path::PathBuf, sync::Arc};
use util::{
fs::remove_matching,
github::{latest_github_release, GitHubLspBinaryVersion},
ResultExt,
};
use util::fs::remove_matching;
use util::github::latest_github_release;
use util::http::HttpClient;
use util::ResultExt;
use util::github::GitHubLspBinaryVersion;
pub struct CLspAdapter;
@@ -20,9 +21,9 @@ impl super::LspAdapter for CLspAdapter {
async fn fetch_latest_server_version(
&self,
delegate: &dyn LspAdapterDelegate,
http: Arc<dyn HttpClient>,
) -> Result<Box<dyn 'static + Send + Any>> {
let release = latest_github_release("clangd/clangd", false, delegate.http_client()).await?;
let release = latest_github_release("clangd/clangd", false, http).await?;
let asset_name = format!("clangd-mac-{}.zip", release.name);
let asset = release
.assets
@@ -39,8 +40,8 @@ impl super::LspAdapter for CLspAdapter {
async fn fetch_server_binary(
&self,
version: Box<dyn 'static + Send + Any>,
http: Arc<dyn HttpClient>,
container_dir: PathBuf,
delegate: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary> {
let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
let zip_path = container_dir.join(format!("clangd_{}.zip", version.name));
@@ -48,8 +49,7 @@ impl super::LspAdapter for CLspAdapter {
let binary_path = version_dir.join("bin/clangd");
if fs::metadata(&binary_path).await.is_err() {
let mut response = delegate
.http_client()
let mut response = http
.get(&version.url, Default::default(), true)
.await
.context("error downloading release")?;
@@ -81,11 +81,7 @@ impl super::LspAdapter for CLspAdapter {
})
}
async fn cached_server_binary(
&self,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<LanguageServerBinary> {
(|| async move {
let mut last_clangd_dir = None;
let mut entries = fs::read_dir(&container_dir).await?;

View File

@@ -1,23 +1,16 @@
use anyhow::{anyhow, Context, Result};
use async_trait::async_trait;
use futures::StreamExt;
use gpui::{AsyncAppContext, Task};
pub use language::*;
use lsp::{CompletionItemKind, SymbolKind};
use smol::fs::{self, File};
use std::{
any::Any,
path::PathBuf,
sync::{
atomic::{AtomicBool, Ordering::SeqCst},
Arc,
},
};
use util::{
fs::remove_matching,
github::{latest_github_release, GitHubLspBinaryVersion},
ResultExt,
};
use std::{any::Any, path::PathBuf, sync::Arc};
use util::fs::remove_matching;
use util::github::latest_github_release;
use util::http::HttpClient;
use util::ResultExt;
use util::github::GitHubLspBinaryVersion;
pub struct ElixirLspAdapter;
@@ -27,58 +20,19 @@ impl LspAdapter for ElixirLspAdapter {
LanguageServerName("elixir-ls".into())
}
fn will_start_server(
&self,
delegate: &Arc<dyn LspAdapterDelegate>,
cx: &mut AsyncAppContext,
) -> Option<Task<Result<()>>> {
static DID_SHOW_NOTIFICATION: AtomicBool = AtomicBool::new(false);
const NOTIFICATION_MESSAGE: &str = "Could not run the elixir language server, `elixir-ls`, because `elixir` was not found.";
let delegate = delegate.clone();
Some(cx.spawn(|mut cx| async move {
let elixir_output = smol::process::Command::new("elixir")
.args(["--version"])
.output()
.await;
if elixir_output.is_err() {
if DID_SHOW_NOTIFICATION
.compare_exchange(false, true, SeqCst, SeqCst)
.is_ok()
{
cx.update(|cx| {
delegate.show_notification(NOTIFICATION_MESSAGE, cx);
})
}
return Err(anyhow!("cannot run elixir-ls"));
}
Ok(())
}))
}
async fn fetch_latest_server_version(
&self,
delegate: &dyn LspAdapterDelegate,
http: Arc<dyn HttpClient>,
) -> Result<Box<dyn 'static + Send + Any>> {
let http = delegate.http_client();
let release = latest_github_release("elixir-lsp/elixir-ls", false, http).await?;
let version_name = release
.name
.strip_prefix("Release ")
.context("Elixir-ls release name does not start with prefix")?
.to_owned();
let asset_name = format!("elixir-ls-{}.zip", &version_name);
let asset_name = "elixir-ls.zip";
let asset = release
.assets
.iter()
.find(|asset| asset.name == asset_name)
.ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?;
let version = GitHubLspBinaryVersion {
name: version_name,
name: release.name,
url: asset.browser_download_url.clone(),
};
Ok(Box::new(version) as Box<_>)
@@ -87,8 +41,8 @@ impl LspAdapter for ElixirLspAdapter {
async fn fetch_server_binary(
&self,
version: Box<dyn 'static + Send + Any>,
http: Arc<dyn HttpClient>,
container_dir: PathBuf,
delegate: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary> {
let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
let zip_path = container_dir.join(format!("elixir-ls_{}.zip", version.name));
@@ -96,8 +50,7 @@ impl LspAdapter for ElixirLspAdapter {
let binary_path = version_dir.join("language_server.sh");
if fs::metadata(&binary_path).await.is_err() {
let mut response = delegate
.http_client()
let mut response = http
.get(&version.url, Default::default(), true)
.await
.context("error downloading release")?;
@@ -123,7 +76,7 @@ impl LspAdapter for ElixirLspAdapter {
.await?
.status;
if !unzip_status.success() {
Err(anyhow!("failed to unzip elixir-ls archive"))?;
Err(anyhow!("failed to unzip clangd archive"))?;
}
remove_matching(&container_dir, |entry| entry != version_dir).await;
@@ -135,11 +88,7 @@ impl LspAdapter for ElixirLspAdapter {
})
}
async fn cached_server_binary(
&self,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<LanguageServerBinary> {
(|| async move {
let mut last = None;
let mut entries = fs::read_dir(&container_dir).await?;

View File

@@ -36,6 +36,8 @@
(char) @constant
(interpolation "#{" @punctuation.special "}" @punctuation.special) @embedded
(escape_sequence) @string.escape
[
@@ -144,10 +146,3 @@
"<<"
">>"
] @punctuation.bracket
(interpolation "#{" @punctuation.special "}" @punctuation.special) @embedded
((sigil
(sigil_name) @_sigil_name
(quoted_content) @embedded)
(#eq? @_sigil_name "H"))

View File

@@ -1,23 +1,16 @@
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use futures::StreamExt;
use gpui::{AsyncAppContext, Task};
pub use language::*;
use lazy_static::lazy_static;
use regex::Regex;
use smol::{fs, process};
use std::{
any::Any,
ffi::{OsStr, OsString},
ops::Range,
path::PathBuf,
str,
sync::{
atomic::{AtomicBool, Ordering::SeqCst},
Arc,
},
};
use util::{fs::remove_matching, github::latest_github_release, ResultExt};
use std::ffi::{OsStr, OsString};
use std::{any::Any, ops::Range, path::PathBuf, str, sync::Arc};
use util::fs::remove_matching;
use util::github::latest_github_release;
use util::http::HttpClient;
use util::ResultExt;
fn server_binary_arguments() -> Vec<OsString> {
vec!["-mode=stdio".into()]
@@ -38,9 +31,9 @@ impl super::LspAdapter for GoLspAdapter {
async fn fetch_latest_server_version(
&self,
delegate: &dyn LspAdapterDelegate,
http: Arc<dyn HttpClient>,
) -> Result<Box<dyn 'static + Send + Any>> {
let release = latest_github_release("golang/tools", false, delegate.http_client()).await?;
let release = latest_github_release("golang/tools", false, http).await?;
let version: Option<String> = release.name.strip_prefix("gopls/v").map(str::to_string);
if version.is_none() {
log::warn!(
@@ -51,39 +44,11 @@ impl super::LspAdapter for GoLspAdapter {
Ok(Box::new(version) as Box<_>)
}
fn will_fetch_server(
&self,
delegate: &Arc<dyn LspAdapterDelegate>,
cx: &mut AsyncAppContext,
) -> Option<Task<Result<()>>> {
static DID_SHOW_NOTIFICATION: AtomicBool = AtomicBool::new(false);
const NOTIFICATION_MESSAGE: &str =
"Could not install the Go language server `gopls`, because `go` was not found.";
let delegate = delegate.clone();
Some(cx.spawn(|mut cx| async move {
let install_output = process::Command::new("go").args(["version"]).output().await;
if install_output.is_err() {
if DID_SHOW_NOTIFICATION
.compare_exchange(false, true, SeqCst, SeqCst)
.is_ok()
{
cx.update(|cx| {
delegate.show_notification(NOTIFICATION_MESSAGE, cx);
})
}
return Err(anyhow!("cannot install gopls"));
}
Ok(())
}))
}
async fn fetch_server_binary(
&self,
version: Box<dyn 'static + Send + Any>,
_: Arc<dyn HttpClient>,
container_dir: PathBuf,
delegate: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary> {
let version = version.downcast::<Option<String>>().unwrap();
let this = *self;
@@ -103,10 +68,7 @@ impl super::LspAdapter for GoLspAdapter {
});
}
}
} else if let Some(path) = this
.cached_server_binary(container_dir.clone(), delegate)
.await
{
} else if let Some(path) = this.cached_server_binary(container_dir.clone()).await {
return Ok(path);
}
@@ -143,11 +105,7 @@ impl super::LspAdapter for GoLspAdapter {
})
}
async fn cached_server_binary(
&self,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<LanguageServerBinary> {
(|| async move {
let mut last_binary_path = None;
let mut entries = fs::read_dir(&container_dir).await?;

View File

@@ -1,11 +1,17 @@
; HEEx delimiters
[
"%>"
"--%>"
"-->"
"/>"
"<!"
"<!--"
"<"
"<%!--"
"<%"
"<%#"
"<%%="
"<%="
"</"
"</:"
"<:"
@@ -14,15 +20,6 @@
"}"
] @punctuation.bracket
[
"<%!--"
"<%"
"<%#"
"<%%="
"<%="
"%>"
] @keyword
; HEEx operators are highlighted as such
"=" @operator

View File

@@ -1,13 +1,11 @@
(
(directive
[
(partial_expression_value)
(expression_value)
(ending_expression_value)
] @content)
(#set! language "elixir")
(#set! combined)
)
((directive (partial_expression_value) @content)
(#set! language "elixir")
(#set! include-children)
(#set! combined))
; Regular expression_values do not need to be combined
((directive (expression_value) @content)
(#set! language "elixir"))
((expression (expression_value) @content)
(#set! language "elixir"))

View File

@@ -1,16 +1,14 @@
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use futures::StreamExt;
use language::{LanguageServerBinary, LanguageServerName, LspAdapter, LspAdapterDelegate};
use language::{LanguageServerBinary, LanguageServerName, LspAdapter};
use node_runtime::NodeRuntime;
use serde_json::json;
use smol::fs;
use std::{
any::Any,
ffi::OsString,
path::{Path, PathBuf},
sync::Arc,
};
use std::ffi::OsString;
use std::path::Path;
use std::{any::Any, path::PathBuf, sync::Arc};
use util::http::HttpClient;
use util::ResultExt;
fn server_binary_arguments(server_path: &Path) -> Vec<OsString> {
@@ -38,7 +36,7 @@ impl LspAdapter for HtmlLspAdapter {
async fn fetch_latest_server_version(
&self,
_: &dyn LspAdapterDelegate,
_: Arc<dyn HttpClient>,
) -> Result<Box<dyn 'static + Any + Send>> {
Ok(Box::new(
self.node
@@ -50,8 +48,8 @@ impl LspAdapter for HtmlLspAdapter {
async fn fetch_server_binary(
&self,
version: Box<dyn 'static + Send + Any>,
_: Arc<dyn HttpClient>,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary> {
let version = version.downcast::<String>().unwrap();
let server_path = container_dir.join(Self::SERVER_PATH);
@@ -71,11 +69,7 @@ impl LspAdapter for HtmlLspAdapter {
})
}
async fn cached_server_binary(
&self,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<LanguageServerBinary> {
(|| async move {
let mut last_version_dir = None;
let mut entries = fs::read_dir(&container_dir).await?;

View File

@@ -3,9 +3,7 @@ use async_trait::async_trait;
use collections::HashMap;
use futures::{future::BoxFuture, FutureExt, StreamExt};
use gpui::AppContext;
use language::{
LanguageRegistry, LanguageServerBinary, LanguageServerName, LspAdapter, LspAdapterDelegate,
};
use language::{LanguageRegistry, LanguageServerBinary, LanguageServerName, LspAdapter};
use node_runtime::NodeRuntime;
use serde_json::json;
use settings::{KeymapFile, SettingsJsonSchemaParams, SettingsStore};
@@ -18,6 +16,7 @@ use std::{
path::{Path, PathBuf},
sync::Arc,
};
use util::http::HttpClient;
use util::{paths, ResultExt};
const SERVER_PATH: &'static str =
@@ -46,7 +45,7 @@ impl LspAdapter for JsonLspAdapter {
async fn fetch_latest_server_version(
&self,
_: &dyn LspAdapterDelegate,
_: Arc<dyn HttpClient>,
) -> Result<Box<dyn 'static + Send + Any>> {
Ok(Box::new(
self.node
@@ -58,8 +57,8 @@ impl LspAdapter for JsonLspAdapter {
async fn fetch_server_binary(
&self,
version: Box<dyn 'static + Send + Any>,
_: Arc<dyn HttpClient>,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary> {
let version = version.downcast::<String>().unwrap();
let server_path = container_dir.join(SERVER_PATH);
@@ -79,11 +78,7 @@ impl LspAdapter for JsonLspAdapter {
})
}
async fn cached_server_binary(
&self,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<LanguageServerBinary> {
(|| async move {
let mut last_version_dir = None;
let mut entries = fs::read_dir(&container_dir).await?;

View File

@@ -3,9 +3,10 @@ use async_trait::async_trait;
use collections::HashMap;
use futures::lock::Mutex;
use gpui::executor::Background;
use language::{LanguageServerBinary, LanguageServerName, LspAdapter, LspAdapterDelegate};
use language::{LanguageServerBinary, LanguageServerName, LspAdapter};
use plugin_runtime::{Plugin, PluginBinary, PluginBuilder, WasiFn};
use std::{any::Any, path::PathBuf, sync::Arc};
use util::http::HttpClient;
use util::ResultExt;
#[allow(dead_code)]
@@ -71,7 +72,7 @@ impl LspAdapter for PluginLspAdapter {
async fn fetch_latest_server_version(
&self,
_: &dyn LspAdapterDelegate,
_: Arc<dyn HttpClient>,
) -> Result<Box<dyn 'static + Send + Any>> {
let runtime = self.runtime.clone();
let function = self.fetch_latest_server_version;
@@ -91,8 +92,8 @@ impl LspAdapter for PluginLspAdapter {
async fn fetch_server_binary(
&self,
version: Box<dyn 'static + Send + Any>,
_: Arc<dyn HttpClient>,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary> {
let version = *version.downcast::<String>().unwrap();
let runtime = self.runtime.clone();
@@ -109,11 +110,7 @@ impl LspAdapter for PluginLspAdapter {
.await
}
async fn cached_server_binary(
&self,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<LanguageServerBinary> {
let runtime = self.runtime.clone();
let function = self.cached_server_binary;

View File

@@ -3,14 +3,12 @@ use async_compression::futures::bufread::GzipDecoder;
use async_tar::Archive;
use async_trait::async_trait;
use futures::{io::BufReader, StreamExt};
use language::{LanguageServerBinary, LanguageServerName, LspAdapterDelegate};
use language::{LanguageServerBinary, LanguageServerName};
use smol::fs;
use std::{any::Any, env::consts, ffi::OsString, path::PathBuf};
use util::{
async_iife,
github::{latest_github_release, GitHubLspBinaryVersion},
ResultExt,
};
use std::{any::Any, env::consts, ffi::OsString, path::PathBuf, sync::Arc};
use util::{async_iife, github::latest_github_release, http::HttpClient, ResultExt};
use util::github::GitHubLspBinaryVersion;
#[derive(Copy, Clone)]
pub struct LuaLspAdapter;
@@ -30,11 +28,9 @@ impl super::LspAdapter for LuaLspAdapter {
async fn fetch_latest_server_version(
&self,
delegate: &dyn LspAdapterDelegate,
http: Arc<dyn HttpClient>,
) -> Result<Box<dyn 'static + Send + Any>> {
let release =
latest_github_release("LuaLS/lua-language-server", false, delegate.http_client())
.await?;
let release = latest_github_release("LuaLS/lua-language-server", false, http).await?;
let version = release.name.clone();
let platform = match consts::ARCH {
"x86_64" => "x64",
@@ -57,16 +53,15 @@ impl super::LspAdapter for LuaLspAdapter {
async fn fetch_server_binary(
&self,
version: Box<dyn 'static + Send + Any>,
http: Arc<dyn HttpClient>,
container_dir: PathBuf,
delegate: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary> {
let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
let binary_path = container_dir.join("bin/lua-language-server");
if fs::metadata(&binary_path).await.is_err() {
let mut response = delegate
.http_client()
let mut response = http
.get(&version.url, Default::default(), true)
.await
.map_err(|err| anyhow!("error downloading release: {}", err))?;
@@ -86,11 +81,7 @@ impl super::LspAdapter for LuaLspAdapter {
})
}
async fn cached_server_binary(
&self,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<LanguageServerBinary> {
async_iife!({
let mut last_binary_path = None;
let mut entries = fs::read_dir(&container_dir).await?;

View File

@@ -1,7 +1,7 @@
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use futures::StreamExt;
use language::{LanguageServerBinary, LanguageServerName, LspAdapter, LspAdapterDelegate};
use language::{LanguageServerBinary, LanguageServerName, LspAdapter};
use node_runtime::NodeRuntime;
use smol::fs;
use std::{
@@ -10,6 +10,7 @@ use std::{
path::{Path, PathBuf},
sync::Arc,
};
use util::http::HttpClient;
use util::ResultExt;
fn server_binary_arguments(server_path: &Path) -> Vec<OsString> {
@@ -36,7 +37,7 @@ impl LspAdapter for PythonLspAdapter {
async fn fetch_latest_server_version(
&self,
_: &dyn LspAdapterDelegate,
_: Arc<dyn HttpClient>,
) -> Result<Box<dyn 'static + Any + Send>> {
Ok(Box::new(self.node.npm_package_latest_version("pyright").await?) as Box<_>)
}
@@ -44,8 +45,8 @@ impl LspAdapter for PythonLspAdapter {
async fn fetch_server_binary(
&self,
version: Box<dyn 'static + Send + Any>,
_: Arc<dyn HttpClient>,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary> {
let version = version.downcast::<String>().unwrap();
let server_path = container_dir.join(Self::SERVER_PATH);
@@ -62,11 +63,7 @@ impl LspAdapter for PythonLspAdapter {
})
}
async fn cached_server_binary(
&self,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<LanguageServerBinary> {
(|| async move {
let mut last_version_dir = None;
let mut entries = fs::read_dir(&container_dir).await?;

View File

@@ -1,7 +1,8 @@
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use language::{LanguageServerBinary, LanguageServerName, LspAdapter, LspAdapterDelegate};
use language::{LanguageServerBinary, LanguageServerName, LspAdapter};
use std::{any::Any, path::PathBuf, sync::Arc};
use util::http::HttpClient;
pub struct RubyLanguageServer;
@@ -13,7 +14,7 @@ impl LspAdapter for RubyLanguageServer {
async fn fetch_latest_server_version(
&self,
_: &dyn LspAdapterDelegate,
_: Arc<dyn HttpClient>,
) -> Result<Box<dyn 'static + Any + Send>> {
Ok(Box::new(()))
}
@@ -21,17 +22,13 @@ impl LspAdapter for RubyLanguageServer {
async fn fetch_server_binary(
&self,
_version: Box<dyn 'static + Send + Any>,
_: Arc<dyn HttpClient>,
_container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary> {
Err(anyhow!("solargraph must be installed manually"))
}
async fn cached_server_binary(
&self,
_: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
async fn cached_server_binary(&self, _container_dir: PathBuf) -> Option<LanguageServerBinary> {
Some(LanguageServerBinary {
path: "solargraph".into(),
arguments: vec!["stdio".into()],

View File

@@ -7,11 +7,10 @@ use lazy_static::lazy_static;
use regex::Regex;
use smol::fs::{self, File};
use std::{any::Any, borrow::Cow, env::consts, path::PathBuf, str, sync::Arc};
use util::{
fs::remove_matching,
github::{latest_github_release, GitHubLspBinaryVersion},
ResultExt,
};
use util::fs::remove_matching;
use util::github::{latest_github_release, GitHubLspBinaryVersion};
use util::http::HttpClient;
use util::ResultExt;
pub struct RustLspAdapter;
@@ -23,11 +22,9 @@ impl LspAdapter for RustLspAdapter {
async fn fetch_latest_server_version(
&self,
delegate: &dyn LspAdapterDelegate,
http: Arc<dyn HttpClient>,
) -> Result<Box<dyn 'static + Send + Any>> {
let release =
latest_github_release("rust-analyzer/rust-analyzer", false, delegate.http_client())
.await?;
let release = latest_github_release("rust-analyzer/rust-analyzer", false, http).await?;
let asset_name = format!("rust-analyzer-{}-apple-darwin.gz", consts::ARCH);
let asset = release
.assets
@@ -43,15 +40,14 @@ impl LspAdapter for RustLspAdapter {
async fn fetch_server_binary(
&self,
version: Box<dyn 'static + Send + Any>,
http: Arc<dyn HttpClient>,
container_dir: PathBuf,
delegate: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary> {
let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
let destination_path = container_dir.join(format!("rust-analyzer-{}", version.name));
if fs::metadata(&destination_path).await.is_err() {
let mut response = delegate
.http_client()
let mut response = http
.get(&version.url, Default::default(), true)
.await
.map_err(|err| anyhow!("error downloading release: {}", err))?;
@@ -73,11 +69,7 @@ impl LspAdapter for RustLspAdapter {
})
}
async fn cached_server_binary(
&self,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<LanguageServerBinary> {
(|| async move {
let mut last = None;
let mut entries = fs::read_dir(&container_dir).await?;

View File

@@ -4,7 +4,7 @@ use async_tar::Archive;
use async_trait::async_trait;
use futures::{future::BoxFuture, FutureExt};
use gpui::AppContext;
use language::{LanguageServerBinary, LanguageServerName, LspAdapter, LspAdapterDelegate};
use language::{LanguageServerBinary, LanguageServerName, LspAdapter};
use lsp::CodeActionKind;
use node_runtime::NodeRuntime;
use serde_json::{json, Value};
@@ -16,7 +16,7 @@ use std::{
path::{Path, PathBuf},
sync::Arc,
};
use util::{fs::remove_matching, github::latest_github_release};
use util::{fs::remove_matching, github::latest_github_release, http::HttpClient};
use util::{github::GitHubLspBinaryVersion, ResultExt};
fn typescript_server_binary_arguments(server_path: &Path) -> Vec<OsString> {
@@ -58,7 +58,7 @@ impl LspAdapter for TypeScriptLspAdapter {
async fn fetch_latest_server_version(
&self,
_: &dyn LspAdapterDelegate,
_: Arc<dyn HttpClient>,
) -> Result<Box<dyn 'static + Send + Any>> {
Ok(Box::new(TypeScriptVersions {
typescript_version: self.node.npm_package_latest_version("typescript").await?,
@@ -72,8 +72,8 @@ impl LspAdapter for TypeScriptLspAdapter {
async fn fetch_server_binary(
&self,
version: Box<dyn 'static + Send + Any>,
_: Arc<dyn HttpClient>,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary> {
let version = version.downcast::<TypeScriptVersions>().unwrap();
let server_path = container_dir.join(Self::NEW_SERVER_PATH);
@@ -99,11 +99,7 @@ impl LspAdapter for TypeScriptLspAdapter {
})
}
async fn cached_server_binary(
&self,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<LanguageServerBinary> {
(|| async move {
let old_server_path = container_dir.join(Self::OLD_SERVER_PATH);
let new_server_path = container_dir.join(Self::NEW_SERVER_PATH);
@@ -208,13 +204,12 @@ impl LspAdapter for EsLintLspAdapter {
async fn fetch_latest_server_version(
&self,
delegate: &dyn LspAdapterDelegate,
http: Arc<dyn HttpClient>,
) -> Result<Box<dyn 'static + Send + Any>> {
// At the time of writing the latest vscode-eslint release was released in 2020 and requires
// special custom LSP protocol extensions be handled to fully initialize. Download the latest
// prerelease instead to sidestep this issue
let release =
latest_github_release("microsoft/vscode-eslint", true, delegate.http_client()).await?;
let release = latest_github_release("microsoft/vscode-eslint", true, http).await?;
Ok(Box::new(GitHubLspBinaryVersion {
name: release.name,
url: release.tarball_url,
@@ -224,8 +219,8 @@ impl LspAdapter for EsLintLspAdapter {
async fn fetch_server_binary(
&self,
version: Box<dyn 'static + Send + Any>,
http: Arc<dyn HttpClient>,
container_dir: PathBuf,
delegate: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary> {
let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
let destination_path = container_dir.join(format!("vscode-eslint-{}", version.name));
@@ -234,8 +229,7 @@ impl LspAdapter for EsLintLspAdapter {
if fs::metadata(&server_path).await.is_err() {
remove_matching(&container_dir, |entry| entry != destination_path).await;
let mut response = delegate
.http_client()
let mut response = http
.get(&version.url, Default::default(), true)
.await
.map_err(|err| anyhow!("error downloading release: {}", err))?;
@@ -263,11 +257,7 @@ impl LspAdapter for EsLintLspAdapter {
})
}
async fn cached_server_binary(
&self,
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<LanguageServerBinary> {
(|| async move {
// This is unfortunate but we don't know what the version is to build a path directly
let mut dir = fs::read_dir(&container_dir).await?;

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