Compare commits

..

19 Commits

Author SHA1 Message Date
Nate Butler
f8103b9ffa Checkpoint 2024-12-10 23:39:34 -05:00
Nate Butler
7719afeafa Checkpoint 2024-12-10 23:31:07 -05:00
Nate Butler
27b25100c9 Checkpoint 2024-12-10 23:18:13 -05:00
Nate Butler
4d16a3a2a8 Checkpoint 2024-12-10 23:02:47 -05:00
Nate Butler
d36230e46e wip 2024-12-10 19:31:37 -05:00
Nate Butler
8dddbf31bf remove unused 2024-12-10 19:12:30 -05:00
Nate Butler
0c8bb20424 Remove unused ui::Component trait 2024-12-09 16:08:48 -05:00
Nate Butler
5edb86511b component_system & component preview 2024-12-09 15:55:26 -05:00
Nate Butler
7e75244620 wip 2024-12-09 15:02:41 -05:00
Nate Butler
175290c43a wip 2024-12-09 14:53:44 -05:00
Nate Butler
27c7541e5b Clean up unused 2024-12-09 13:58:41 -05:00
Nate Butler
ae9f51d0e0 update checkbox preview 2024-12-09 13:57:26 -05:00
Nate Butler
f609429ac4 Update component preview 2024-12-09 13:37:03 -05:00
Nate Butler
7cbf651cf9 Add component preview util 2024-12-09 12:50:25 -05:00
Nate Butler
81475ef182 wip 2024-12-09 12:23:17 -05:00
Nate Butler
9ce110f9ad Checkpoint 2024-12-06 13:11:09 -05:00
Nate Butler
f500de9c3b Checkpoint 2024-12-06 12:33:44 -05:00
Nate Butler
9d810a44fd Checkpoint 2024-12-06 10:42:40 -05:00
Nate Butler
3248bb9172 Checkpoint 2024-12-06 07:54:26 -05:00
88 changed files with 1613 additions and 2422 deletions

View File

@@ -1,6 +1,3 @@
[env]
LK_CUSTOM_WEBRTC = { value = "../livekit-rust-sdks/webrtc-sys/libwebrtc/linux-x64-release", relative = true }
[build]
# v0 mangling scheme provides more detailed backtraces around closures
rustflags = ["-C", "symbol-mangling-version=v0", "--cfg", "tokio_unstable"]

View File

@@ -129,9 +129,8 @@ jobs:
run: |
cargo build --workspace --bins --all-features
cargo check -p gpui --features "macos-blade"
cargo check -p workspace
cargo check -p workspace --features "livekit-cross-platform"
cargo build -p remote_server
script/check-rust-livekit-macos
linux_tests:
timeout-minutes: 60
@@ -163,10 +162,8 @@ jobs:
- name: Run tests
uses: ./.github/actions/run_tests
- name: Build other binaries and features
run: |
cargo build -p zed
cargo check -p workspace
- name: Build Zed
run: cargo build -p zed
build_remote_server:
timeout-minutes: 60

View File

@@ -37,28 +37,28 @@ jobs:
mdbook build ./docs --dest-dir=../target/deploy/docs/
- name: Deploy Docs
uses: cloudflare/wrangler-action@6d58852c35a27e6034745c5d0bc373d739014f7f # v3
uses: cloudflare/wrangler-action@05f17c4a695b4d94b57b59997562c6a4624c64e4 # v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy target/deploy --project-name=docs
- name: Deploy Install
uses: cloudflare/wrangler-action@6d58852c35a27e6034745c5d0bc373d739014f7f # v3
uses: cloudflare/wrangler-action@05f17c4a695b4d94b57b59997562c6a4624c64e4 # v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: r2 object put -f script/install.sh zed-open-source-website-assets/install.sh
- name: Deploy Docs Workers
uses: cloudflare/wrangler-action@6d58852c35a27e6034745c5d0bc373d739014f7f # v3
uses: cloudflare/wrangler-action@05f17c4a695b4d94b57b59997562c6a4624c64e4 # v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy .cloudflare/docs-proxy/src/worker.js
- name: Deploy Install Workers
uses: cloudflare/wrangler-action@6d58852c35a27e6034745c5d0bc373d739014f7f # v3
uses: cloudflare/wrangler-action@05f17c4a695b4d94b57b59997562c6a4624c64e4 # v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}

46
Cargo.lock generated
View File

@@ -2862,6 +2862,29 @@ dependencies = [
"gpui",
]
[[package]]
name = "component_preview"
version = "0.1.0"
dependencies = [
"component_system",
"gpui",
"strum 0.25.0",
"ui",
"workspace",
]
[[package]]
name = "component_system"
version = "0.1.0"
dependencies = [
"collections",
"gpui",
"lazy_static",
"linkme",
"once_cell",
"parking_lot",
]
[[package]]
name = "concurrent-queue"
version = "2.5.0"
@@ -3480,9 +3503,9 @@ dependencies = [
[[package]]
name = "ctor"
version = "0.2.9"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501"
checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f"
dependencies = [
"quote",
"syn 2.0.87",
@@ -3957,7 +3980,6 @@ dependencies = [
"unindent",
"url",
"util",
"uuid",
"workspace",
]
@@ -7422,7 +7444,6 @@ dependencies = [
"settings",
"theme",
"ui",
"util",
"workspace",
]
@@ -8579,9 +8600,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "pathdiff"
version = "0.2.3"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
checksum = "d61c5ce1153ab5b689d0c074c4e7fc613e942dfb7dd9eea5ab202d2ad91fe361"
[[package]]
name = "pathfinder_geometry"
@@ -10695,9 +10716,9 @@ dependencies = [
[[package]]
name = "rsa"
version = "0.9.7"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519"
checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc"
dependencies = [
"const-oid",
"digest",
@@ -13811,9 +13832,14 @@ name = "ui"
version = "0.1.0"
dependencies = [
"chrono",
"collections",
"component_system",
"gpui",
"itertools 0.13.0",
"lazy_static",
"linkme",
"menu",
"once_cell",
"serde",
"settings",
"smallvec",
@@ -13839,6 +13865,7 @@ dependencies = [
name = "ui_macros"
version = "0.1.0"
dependencies = [
"component_system",
"convert_case 0.6.0",
"proc-macro2",
"quote",
@@ -15622,6 +15649,7 @@ dependencies = [
"client",
"clock",
"collections",
"component_system",
"db",
"derive_more",
"env_logger 0.11.5",
@@ -16053,6 +16081,7 @@ dependencies = [
"collections",
"command_palette",
"command_palette_hooks",
"component_preview",
"copilot",
"db",
"diagnostics",
@@ -16085,7 +16114,6 @@ dependencies = [
"languages",
"libc",
"log",
"markdown",
"markdown_preview",
"menu",
"mimalloc",

View File

@@ -21,6 +21,8 @@ members = [
"crates/collab",
"crates/collab_ui",
"crates/collections",
"crates/component_preview",
"crates/component_system",
"crates/command_palette",
"crates/command_palette_hooks",
"crates/context_server",
@@ -142,15 +144,6 @@ members = [
"crates/zed",
"crates/zed_actions",
"crates/livekit",
"crates/livekit-api",
"crates/livekit-protocol",
"crates/livekit-ffi",
"crates/libwebrtc",
"crates/soxr-sys",
"crates/webrtc-sys",
"crates/webrtc-sys/build",
#
# Extensions
#
@@ -216,6 +209,8 @@ clock = { path = "crates/clock" }
collab = { path = "crates/collab" }
collab_ui = { path = "crates/collab_ui" }
collections = { path = "crates/collections" }
component_system = { path = "crates/component_system" }
component_preview = { path = "crates/component_preview" }
command_palette = { path = "crates/command_palette" }
command_palette_hooks = { path = "crates/command_palette_hooks" }
context_server = { path = "crates/context_server" }
@@ -335,8 +330,6 @@ worktree = { path = "crates/worktree" }
zed = { path = "crates/zed" }
zed_actions = { path = "crates/zed_actions" }
livekit = { path = "crates/livekit", features = ["dispatcher", "services-dispatcher", "rustls-tls-native-roots" ], default-features = false }
#
# External crates
#
@@ -404,10 +397,12 @@ itertools = "0.13.0"
jsonwebtoken = "9.3"
jupyter-protocol = { version = "0.5.0" }
jupyter-websocket-client = { version = "0.8.0" }
lazy_static = "1.5.0"
libc = "0.2"
libsqlite3-sys = { version = "0.30.1", features = ["bundled"] }
linkify = "0.10.0"
# livekit = { git = "https://github.com/zed-industries/rust-sdks", rev="799f10133d93ba2a88642cd480d01ec4da53408c", features = ["dispatcher", "services-dispatcher", "rustls-tls-native-roots"], default-features = false }
linkme = "0.3"
livekit = { git = "https://github.com/zed-industries/rust-sdks", rev="799f10133d93ba2a88642cd480d01ec4da53408c", features = ["dispatcher", "services-dispatcher", "rustls-tls-native-roots"], default-features = false }
log = { version = "0.4.16", features = ["kv_unstable_serde", "serde"] }
markup5ever_rcdom = "0.3.0"
nanoid = "0.4"
@@ -517,7 +512,7 @@ unindent = "0.1.7"
unicode-segmentation = "1.10"
unicode-script = "0.5.7"
url = "2.2"
uuid = { version = "1.1.2", features = ["v4", "v5", "v7", "serde"] }
uuid = { version = "1.1.2", features = ["v4", "v5", "serde"] }
wasmparser = "0.215"
wasm-encoder = "0.215"
wasmtime = { version = "24", default-features = false, features = [

View File

@@ -1,7 +1,6 @@
[
// Standard macOS bindings
{
"use_key_equivalents": true,
"bindings": {
"up": "menu::SelectPrev",
"shift-tab": "menu::SelectPrev",
@@ -41,7 +40,6 @@
},
{
"context": "Editor",
"use_key_equivalents": true,
"bindings": {
"escape": "editor::Cancel",
"backspace": "editor::Backspace",
@@ -133,7 +131,6 @@
},
{
"context": "Editor && mode == full",
"use_key_equivalents": true,
"bindings": {
"enter": "editor::Newline",
"shift-enter": "editor::Newline",
@@ -151,23 +148,20 @@
},
{
"context": "Editor && mode == full && inline_completion",
"use_key_equivalents": true,
"bindings": {
"alt-tab": "editor::NextInlineCompletion",
"alt-shift-tab": "editor::PreviousInlineCompletion",
"alt-]": "editor::NextInlineCompletion",
"alt-[": "editor::PreviousInlineCompletion",
"ctrl-right": "editor::AcceptPartialInlineCompletion"
}
},
{
"context": "Editor && !inline_completion",
"use_key_equivalents": true,
"bindings": {
"alt-tab": "editor::ShowInlineCompletion"
"alt-\\": "editor::ShowInlineCompletion"
}
},
{
"context": "Editor && mode == auto_height",
"use_key_equivalents": true,
"bindings": {
"ctrl-enter": "editor::Newline",
"shift-enter": "editor::Newline",
@@ -176,14 +170,12 @@
},
{
"context": "Markdown",
"use_key_equivalents": true,
"bindings": {
"cmd-c": "markdown::Copy"
}
},
{
"context": "Editor && jupyter && !ContextEditor",
"use_key_equivalents": true,
"bindings": {
"ctrl-shift-enter": "repl::Run",
"ctrl-alt-enter": "repl::RunInPlace"
@@ -191,7 +183,6 @@
},
{
"context": "AssistantPanel",
"use_key_equivalents": true,
"bindings": {
"cmd-k c": "assistant::CopyCode",
"cmd-g": "search::SelectNextMatch",
@@ -204,7 +195,6 @@
},
{
"context": "ContextEditor > Editor",
"use_key_equivalents": true,
"bindings": {
"cmd-enter": "assistant::Assist",
"cmd-shift-enter": "assistant::Edit",
@@ -219,7 +209,6 @@
},
{
"context": "AssistantPanel2",
"use_key_equivalents": true,
"bindings": {
"cmd-n": "assistant2::NewThread",
"cmd-shift-h": "assistant2::OpenHistory"
@@ -227,14 +216,12 @@
},
{
"context": "MessageEditor > Editor",
"use_key_equivalents": true,
"bindings": {
"cmd-enter": "assistant2::Chat"
}
},
{
"context": "PromptLibrary",
"use_key_equivalents": true,
"bindings": {
"cmd-n": "prompt_library::NewPrompt",
"cmd-shift-s": "prompt_library::ToggleDefaultPrompt",
@@ -243,7 +230,6 @@
},
{
"context": "BufferSearchBar",
"use_key_equivalents": true,
"bindings": {
"escape": "buffer_search::Dismiss",
"tab": "buffer_search::FocusEditor",
@@ -257,7 +243,6 @@
},
{
"context": "BufferSearchBar && in_replace > Editor",
"use_key_equivalents": true,
"bindings": {
"enter": "search::ReplaceNext",
"cmd-enter": "search::ReplaceAll"
@@ -265,7 +250,6 @@
},
{
"context": "BufferSearchBar && !in_replace > Editor",
"use_key_equivalents": true,
"bindings": {
"up": "search::PreviousHistoryQuery",
"down": "search::NextHistoryQuery"
@@ -273,7 +257,6 @@
},
{
"context": "ProjectSearchBar",
"use_key_equivalents": true,
"bindings": {
"escape": "project_search::ToggleFocus",
"cmd-shift-j": "project_search::ToggleFilters",
@@ -285,7 +268,6 @@
},
{
"context": "ProjectSearchBar > Editor",
"use_key_equivalents": true,
"bindings": {
"up": "search::PreviousHistoryQuery",
"down": "search::NextHistoryQuery"
@@ -293,7 +275,6 @@
},
{
"context": "ProjectSearchBar && in_replace > Editor",
"use_key_equivalents": true,
"bindings": {
"enter": "search::ReplaceNext",
"cmd-enter": "search::ReplaceAll"
@@ -301,7 +282,6 @@
},
{
"context": "ProjectSearchView",
"use_key_equivalents": true,
"bindings": {
"escape": "project_search::ToggleFocus",
"cmd-shift-j": "project_search::ToggleFilters",
@@ -312,7 +292,6 @@
},
{
"context": "Pane",
"use_key_equivalents": true,
"bindings": {
"cmd-{": "pane::ActivatePrevItem",
"cmd-}": "pane::ActivateNextItem",
@@ -341,7 +320,6 @@
// Bindings from VS Code
{
"context": "Editor",
"use_key_equivalents": true,
"bindings": {
"cmd-[": "editor::Outdent",
"cmd-]": "editor::Indent",
@@ -405,7 +383,6 @@
},
{
"context": "Editor && mode == full",
"use_key_equivalents": true,
"bindings": {
"cmd-shift-o": "outline::Toggle",
"ctrl-g": "go_to_line::Toggle"
@@ -413,7 +390,6 @@
},
{
"context": "Pane",
"use_key_equivalents": true,
"bindings": {
"ctrl-1": ["pane::ActivateItem", 0],
"ctrl-2": ["pane::ActivateItem", 1],
@@ -433,7 +409,6 @@
},
{
"context": "Workspace",
"use_key_equivalents": true,
"bindings": {
// Change the default action on `menu::Confirm` by setting the parameter
// "alt-cmd-o": ["projects::OpenRecent", {"create_new_window": true }],
@@ -489,7 +464,6 @@
},
{
"context": "Workspace && !Terminal",
"use_key_equivalents": true,
"bindings": {
"cmd-shift-r": "task::Spawn",
"cmd-alt-r": "task::Rerun",
@@ -500,7 +474,6 @@
// Bindings from Sublime Text
{
"context": "Editor",
"use_key_equivalents": true,
"bindings": {
"ctrl-j": "editor::JoinLines",
"ctrl-alt-backspace": "editor::DeleteToPreviousSubwordStart",
@@ -520,7 +493,6 @@
// Bindings from Atom
{
"context": "Pane",
"use_key_equivalents": true,
"bindings": {
"cmd-k up": "pane::SplitUp",
"cmd-k down": "pane::SplitDown",
@@ -531,14 +503,12 @@
// Bindings that should be unified with bindings for more general actions
{
"context": "Editor && renaming",
"use_key_equivalents": true,
"bindings": {
"enter": "editor::ConfirmRename"
}
},
{
"context": "Editor && showing_completions",
"use_key_equivalents": true,
"bindings": {
"enter": "editor::ConfirmCompletion",
"tab": "editor::ComposeCompletion"
@@ -546,21 +516,18 @@
},
{
"context": "Editor && inline_completion && !showing_completions",
"use_key_equivalents": true,
"bindings": {
"tab": "editor::AcceptInlineCompletion"
}
},
{
"context": "Editor && showing_code_actions",
"use_key_equivalents": true,
"bindings": {
"enter": "editor::ConfirmCodeAction"
}
},
{
"context": "Editor && (showing_code_actions || showing_completions)",
"use_key_equivalents": true,
"bindings": {
"up": "editor::ContextMenuPrev",
"ctrl-p": "editor::ContextMenuPrev",
@@ -572,7 +539,6 @@
},
// Custom bindings
{
"use_key_equivalents": true,
"bindings": {
"ctrl-alt-cmd-f": "workspace::FollowNextCollaborator",
// TODO: Move this to a dock open action
@@ -583,7 +549,6 @@
},
{
"context": "Editor && mode == full",
"use_key_equivalents": true,
"bindings": {
"alt-enter": "editor::OpenExcerpts",
"shift-enter": "editor::ExpandExcerpts",
@@ -595,7 +560,6 @@
},
{
"context": "ProposedChangesEditor",
"use_key_equivalents": true,
"bindings": {
"cmd-shift-y": "editor::ApplyDiffHunk",
"cmd-shift-a": "editor::ApplyAllDiffHunks"
@@ -603,7 +567,6 @@
},
{
"context": "PromptEditor",
"use_key_equivalents": true,
"bindings": {
"ctrl-[": "assistant::CyclePreviousInlineAssist",
"ctrl-]": "assistant::CycleNextInlineAssist"
@@ -611,14 +574,12 @@
},
{
"context": "ProjectSearchBar && !in_replace",
"use_key_equivalents": true,
"bindings": {
"cmd-enter": "project_search::SearchInNew"
}
},
{
"context": "OutlinePanel && not_editing",
"use_key_equivalents": true,
"bindings": {
"escape": "menu::Cancel",
"left": "outline_panel::CollapseSelectedEntry",
@@ -635,7 +596,6 @@
},
{
"context": "ProjectPanel",
"use_key_equivalents": true,
"bindings": {
"left": "project_panel::CollapseSelectedEntry",
"right": "project_panel::ExpandSelectedEntry",
@@ -665,14 +625,12 @@
},
{
"context": "ProjectPanel && not_editing",
"use_key_equivalents": true,
"bindings": {
"space": "project_panel::Open"
}
},
{
"context": "CollabPanel && not_editing",
"use_key_equivalents": true,
"bindings": {
"ctrl-backspace": "collab_panel::Remove",
"space": "menu::Confirm"
@@ -680,21 +638,18 @@
},
{
"context": "(CollabPanel && editing) > Editor",
"use_key_equivalents": true,
"bindings": {
"space": "collab_panel::InsertSpace"
}
},
{
"context": "ChannelModal",
"use_key_equivalents": true,
"bindings": {
"tab": "channel_modal::ToggleMode"
}
},
{
"context": "Picker > Editor",
"use_key_equivalents": true,
"bindings": {
"tab": "picker::ConfirmCompletion",
"alt-enter": ["picker::ConfirmInput", { "secondary": false }],
@@ -703,21 +658,18 @@
},
{
"context": "ChannelModal > Picker > Editor",
"use_key_equivalents": true,
"bindings": {
"tab": "channel_modal::ToggleMode"
}
},
{
"context": "FileFinder",
"use_key_equivalents": true,
"bindings": {
"cmd": "file_finder::ToggleMenu"
}
},
{
"context": "FileFinder && !menu_open",
"use_key_equivalents": true,
"bindings": {
"cmd-shift-p": "file_finder::SelectPrev",
"cmd-j": "pane::SplitDown",
@@ -728,7 +680,6 @@
},
{
"context": "FileFinder && menu_open",
"use_key_equivalents": true,
"bindings": {
"j": "pane::SplitDown",
"k": "pane::SplitUp",
@@ -738,7 +689,6 @@
},
{
"context": "TabSwitcher",
"use_key_equivalents": true,
"bindings": {
"ctrl-up": "menu::SelectPrev",
"ctrl-down": "menu::SelectNext",
@@ -748,7 +698,6 @@
},
{
"context": "Terminal",
"use_key_equivalents": true,
"bindings": {
"ctrl-cmd-space": "terminal::ShowCharacterPalette",
"cmd-c": "terminal::Copy",

View File

@@ -1,6 +1,7 @@
[
{
"context": "VimControl && !menu",
"use_layout_keys": true,
"bindings": {
"i": ["vim::PushOperator", { "Object": { "around": false } }],
"a": ["vim::PushOperator", { "Object": { "around": true } }],
@@ -187,6 +188,7 @@
},
{
"context": "vim_mode == normal",
"use_layout_keys": true,
"bindings": {
"escape": "editor::Cancel",
"ctrl-[": "editor::Cancel",
@@ -241,6 +243,7 @@
},
{
"context": "VimControl && VimCount",
"use_layout_keys": true,
"bindings": {
"0": ["vim::Number", 0],
":": "vim::CountCommand"
@@ -248,6 +251,7 @@
},
{
"context": "vim_mode == visual",
"use_layout_keys": true,
"bindings": {
":": "vim::VisualCommand",
"u": "vim::ConvertToLowerCase",
@@ -297,6 +301,7 @@
},
{
"context": "vim_mode == insert",
"use_layout_keys": true,
"bindings": {
"escape": "vim::NormalBefore",
"ctrl-c": "vim::NormalBefore",
@@ -326,7 +331,6 @@
"bindings": {
"i": "vim::InsertBefore",
"a": "vim::InsertAfter",
"d": "vim::HelixDelete",
"w": "vim::NextWordStart",
"e": "vim::NextWordEnd",
"b": "vim::PreviousWordStart",
@@ -340,6 +344,7 @@
{
"context": "vim_mode == insert && !(showing_code_actions || showing_completions)",
"use_layout_keys": true,
"bindings": {
"ctrl-p": "editor::ShowCompletions",
"ctrl-n": "editor::ShowCompletions"
@@ -347,6 +352,7 @@
},
{
"context": "vim_mode == replace",
"use_layout_keys": true,
"bindings": {
"escape": "vim::NormalBefore",
"ctrl-c": "vim::NormalBefore",
@@ -364,6 +370,7 @@
},
{
"context": "vim_mode == waiting",
"use_layout_keys": true,
"bindings": {
"tab": "vim::Tab",
"enter": "vim::Enter",
@@ -377,6 +384,7 @@
},
{
"context": "vim_mode == operator",
"use_layout_keys": true,
"bindings": {
"escape": "vim::ClearOperators",
"ctrl-c": "vim::ClearOperators",
@@ -386,6 +394,7 @@
},
{
"context": "vim_operator == a || vim_operator == i || vim_operator == cs",
"use_layout_keys": true,
"bindings": {
"w": "vim::Word",
"shift-w": ["vim::Word", { "ignorePunctuation": true }],
@@ -416,6 +425,7 @@
},
{
"context": "vim_operator == c",
"use_layout_keys": true,
"bindings": {
"c": "vim::CurrentLine",
"d": "editor::Rename", // zed specific
@@ -424,6 +434,7 @@
},
{
"context": "vim_operator == d",
"use_layout_keys": true,
"bindings": {
"d": "vim::CurrentLine",
"s": ["vim::PushOperator", "DeleteSurrounds"],
@@ -433,6 +444,7 @@
},
{
"context": "vim_operator == gu",
"use_layout_keys": true,
"bindings": {
"g u": "vim::CurrentLine",
"u": "vim::CurrentLine"
@@ -440,6 +452,7 @@
},
{
"context": "vim_operator == gU",
"use_layout_keys": true,
"bindings": {
"g shift-u": "vim::CurrentLine",
"shift-u": "vim::CurrentLine"
@@ -447,6 +460,7 @@
},
{
"context": "vim_operator == g~",
"use_layout_keys": true,
"bindings": {
"g ~": "vim::CurrentLine",
"~": "vim::CurrentLine"
@@ -454,6 +468,7 @@
},
{
"context": "vim_operator == gq",
"use_layout_keys": true,
"bindings": {
"g q": "vim::CurrentLine",
"q": "vim::CurrentLine",
@@ -463,6 +478,7 @@
},
{
"context": "vim_operator == y",
"use_layout_keys": true,
"bindings": {
"y": "vim::CurrentLine",
"s": ["vim::PushOperator", { "AddSurrounds": {} }]
@@ -470,36 +486,42 @@
},
{
"context": "vim_operator == ys",
"use_layout_keys": true,
"bindings": {
"s": "vim::CurrentLine"
}
},
{
"context": "vim_operator == >",
"use_layout_keys": true,
"bindings": {
">": "vim::CurrentLine"
}
},
{
"context": "vim_operator == <",
"use_layout_keys": true,
"bindings": {
"<": "vim::CurrentLine"
}
},
{
"context": "vim_operator == eq",
"use_layout_keys": true,
"bindings": {
"=": "vim::CurrentLine"
}
},
{
"context": "vim_operator == gc",
"use_layout_keys": true,
"bindings": {
"c": "vim::CurrentLine"
}
},
{
"context": "vim_mode == literal",
"use_layout_keys": true,
"bindings": {
"ctrl-@": ["vim::Literal", ["ctrl-@", "\u0000"]],
"ctrl-a": ["vim::Literal", ["ctrl-a", "\u0001"]],
@@ -543,6 +565,7 @@
},
{
"context": "BufferSearchBar && !in_replace",
"use_layout_keys": true,
"bindings": {
"enter": "vim::SearchSubmit",
"escape": "buffer_search::Dismiss"
@@ -550,6 +573,7 @@
},
{
"context": "ProjectPanel || CollabPanel || OutlinePanel || ChatPanel || VimControl || EmptyPane || SharedScreen || MarkdownPreview || KeyContextView",
"use_layout_keys": true,
"bindings": {
// window related commands (ctrl-w X)
"ctrl-w": null,
@@ -606,6 +630,7 @@
},
{
"context": "EmptyPane || SharedScreen || MarkdownPreview || KeyContextView || Welcome",
"use_layout_keys": true,
"bindings": {
":": "command_palette::Toggle",
"g /": "pane::DeploySearch"
@@ -614,6 +639,7 @@
{
// netrw compatibility
"context": "ProjectPanel && not_editing",
"use_layout_keys": true,
"bindings": {
":": "command_palette::Toggle",
"%": "project_panel::NewFile",
@@ -647,6 +673,7 @@
},
{
"context": "OutlinePanel && not_editing",
"use_layout_keys": true,
"bindings": {
"j": "menu::SelectNext",
"k": "menu::SelectPrev",

View File

@@ -569,8 +569,7 @@
// "Neighbour"
"activate_on_close": "history",
/// Which files containing diagnostic errors/warnings to mark in the tabs.
/// Diagnostics are only shown when file icons are also active.
/// This setting only works when can take the following three values:
/// This setting can take the following three values:
///
/// 1. Do not mark any files:
/// "off"
@@ -578,7 +577,7 @@
/// "errors"
/// 3. Mark files with errors and warnings:
/// "all"
"show_diagnostics": "off"
"show_diagnostics": "all"
},
// Settings related to preview tabs.
"preview_tabs": {

View File

@@ -21,6 +21,8 @@ test-support = [
"project/test-support",
"util/test-support"
]
livekit-macos = ["livekit_client_macos"]
livekit-cross-platform = ["livekit_client"]
[dependencies]
anyhow.workspace = true
@@ -40,12 +42,8 @@ serde.workspace = true
serde_derive.workspace = true
settings.workspace = true
util.workspace = true
[target.'cfg(target_os = "macos")'.dependencies]
livekit_client_macos = { workspace = true }
[target.'cfg(not(target_os = "macos"))'.dependencies]
livekit_client = { workspace = true }
livekit_client_macos = { workspace = true, optional = true }
livekit_client = { workspace = true, optional = true }
[dev-dependencies]
client = { workspace = true, features = ["test-support"] }

View File

@@ -1,13 +1,41 @@
pub mod call_settings;
#[cfg(target_os = "macos")]
#[cfg(any(
all(target_os = "macos", feature = "livekit-macos"),
all(
not(target_os = "macos"),
feature = "livekit-macos",
not(feature = "livekit-cross-platform")
)
))]
mod macos;
#[cfg(target_os = "macos")]
#[cfg(any(
all(target_os = "macos", feature = "livekit-macos"),
all(
not(target_os = "macos"),
feature = "livekit-macos",
not(feature = "livekit-cross-platform")
)
))]
pub use macos::*;
#[cfg(not(target_os = "macos"))]
#[cfg(any(
all(
target_os = "macos",
feature = "livekit-cross-platform",
not(feature = "livekit-macos"),
),
all(not(target_os = "macos"), feature = "livekit-cross-platform"),
))]
mod cross_platform;
#[cfg(not(target_os = "macos"))]
#[cfg(any(
all(
target_os = "macos",
feature = "livekit-cross-platform",
not(feature = "livekit-macos"),
),
all(not(target_os = "macos"), feature = "livekit-cross-platform"),
))]
pub use cross_platform::*;

View File

@@ -9,7 +9,6 @@ use collections::HashSet;
use reqwest::StatusCode;
use sea_orm::ActiveValue;
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::{str::FromStr, sync::Arc, time::Duration};
use stripe::{
BillingPortalSession, CreateBillingPortalSession, CreateBillingPortalSessionFlowData,
@@ -20,7 +19,6 @@ use stripe::{
};
use util::ResultExt;
use crate::api::events::SnowflakeRow;
use crate::llm::{DEFAULT_MAX_MONTHLY_SPEND, FREE_TIER_MONTHLY_SPENDING_LIMIT};
use crate::rpc::{ResultExt as _, Server};
use crate::{
@@ -126,20 +124,6 @@ async fn update_billing_preferences(
.await?
};
SnowflakeRow::new(
"Spend Limit Updated",
Some(user.metrics_id),
user.admin,
None,
json!({
"user_id": user.id,
"max_monthly_llm_usage_spending_in_cents": billing_preferences.max_monthly_llm_usage_spending_in_cents,
}),
)
.write(&app.kinesis_client, &app.config.kinesis_stream)
.await
.log_err();
rpc_server.refresh_llm_tokens_for_user(user.id).await;
Ok(Json(BillingPreferencesResponse {

View File

@@ -0,0 +1,22 @@
[package]
name = "component_preview"
version = "0.1.0"
edition = "2021"
publish = false
license = "GPL-3.0-or-later"
[lints]
workspace = true
[lib]
path = "src/component_preview.rs"
[dependencies]
gpui.workspace = true
ui.workspace = true
component_system.workspace = true
strum.workspace = true
workspace.workspace = true
[features]
default = []

View File

@@ -0,0 +1,110 @@
//! # Component Preview
//!
//! A view for exploring Zed components.
use component_system::components;
use gpui::{prelude::*, AppContext, EventEmitter, FocusHandle, FocusableView};
use strum::{EnumIter, IntoEnumIterator};
use ui::{prelude::*, TintColor};
use workspace::{item::ItemEvent, Item, Workspace, WorkspaceId};
pub fn init(cx: &mut AppContext) {
cx.observe_new_views(|workspace: &mut Workspace, _cx| {
workspace.register_action(|workspace, _: &workspace::ComponentPreview, cx| {
let component_preview = cx.new_view(ComponentPreview::new);
workspace.add_item_to_active_pane(Box::new(component_preview), None, true, cx)
});
})
.detach();
}
struct ComponentPreview {
focus_handle: FocusHandle,
}
impl ComponentPreview {
pub fn new(cx: &mut ViewContext<Self>) -> Self {
Self {
focus_handle: cx.focus_handle(),
}
}
fn render_sidebar(&self, cx: &ViewContext<Self>) -> impl IntoElement {
h_flex().children(components().all().iter().map(|component| {
Button::new(component.name().clone(), component.name()).on_click(cx.listener(
move |_this, _, _cx| {
// Handle button click
},
))
}))
}
fn render_preview(&self, cx: &ViewContext<Self>) -> impl IntoElement {
v_flex()
.size_full()
.children(components().all_previews().iter().map(|component| {
if let Some(preview) = component.preview() {
preview(cx)
} else {
div().into_any_element()
}
}))
}
}
impl Render for ComponentPreview {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
h_flex()
.id("component-preview")
.key_context("ComponentPreview")
.items_start()
.overflow_hidden()
.size_full()
.max_h_full()
.track_focus(&self.focus_handle)
.px_2()
.bg(cx.theme().colors().editor_background)
.child(self.render_sidebar(cx))
.child(self.render_preview(cx))
}
}
impl EventEmitter<ItemEvent> for ComponentPreview {}
impl FocusableView for ComponentPreview {
fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle {
self.focus_handle.clone()
}
}
impl Item for ComponentPreview {
type Event = ItemEvent;
fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
Some("Component Preview".into())
}
fn telemetry_event_text(&self) -> Option<&'static str> {
None
}
fn show_toolbar(&self) -> bool {
false
}
fn clone_on_split(
&self,
_workspace_id: Option<WorkspaceId>,
cx: &mut ViewContext<Self>,
) -> Option<gpui::View<Self>>
where
Self: Sized,
{
Some(cx.new_view(Self::new))
}
fn to_item_events(event: &Self::Event, mut f: impl FnMut(workspace::item::ItemEvent)) {
f(*event)
}
}

View File

@@ -0,0 +1,23 @@
[package]
name = "component_system"
version = "0.1.0"
edition = "2021"
publish = false
license = "GPL-3.0-or-later"
[lints]
workspace = true
[lib]
path = "src/component_system.rs"
[dependencies]
gpui.workspace = true
lazy_static.workspace = true
linkme.workspace = true
once_cell.workspace = true
collections.workspace = true
parking_lot.workspace = true
[features]
default = []

View File

@@ -0,0 +1,142 @@
use collections::HashMap;
use gpui::{AnyElement, SharedString, WindowContext};
use linkme::distributed_slice;
use once_cell::sync::Lazy;
use parking_lot::RwLock;
pub trait Component {
fn scope() -> Option<&'static str>;
fn name() -> &'static str {
std::any::type_name::<Self>()
}
fn description() -> Option<&'static str> {
None
}
}
pub trait ComponentPreview: Component {
fn preview(_cx: &WindowContext) -> AnyElement;
}
#[distributed_slice]
pub static __ALL_COMPONENTS: [fn()] = [..];
#[distributed_slice]
pub static __ALL_PREVIEWS: [fn()] = [..];
pub static COMPONENT_DATA: Lazy<RwLock<ComponentData>> =
Lazy::new(|| RwLock::new(ComponentData::new()));
pub struct ComponentData {
components: Vec<(Option<&'static str>, &'static str, Option<&'static str>)>,
previews: HashMap<&'static str, fn(&WindowContext) -> AnyElement>,
}
impl ComponentData {
fn new() -> Self {
ComponentData {
components: Vec::new(),
previews: HashMap::default(),
}
}
}
pub fn init() {
let component_fns: Vec<_> = __ALL_COMPONENTS.iter().cloned().collect();
let preview_fns: Vec<_> = __ALL_PREVIEWS.iter().cloned().collect();
for f in component_fns {
f();
}
for f in preview_fns {
f();
}
}
pub fn register_component<T: Component>() {
let component_data = (T::scope(), T::name(), T::description());
COMPONENT_DATA.write().components.push(component_data);
}
pub fn register_preview<T: ComponentPreview>() {
let preview_data = (T::name(), T::preview as fn(&WindowContext) -> AnyElement);
COMPONENT_DATA
.write()
.previews
.insert(preview_data.0, preview_data.1);
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ComponentId(pub &'static str);
pub struct ComponentMetadata {
name: SharedString,
scope: Option<SharedString>,
description: Option<SharedString>,
preview: Option<fn(&WindowContext) -> AnyElement>,
}
impl ComponentMetadata {
pub fn name(&self) -> SharedString {
self.name.clone()
}
pub fn scope(&self) -> Option<SharedString> {
self.scope.clone()
}
pub fn description(&self) -> Option<SharedString> {
self.description.clone()
}
pub fn preview(&self) -> Option<fn(&WindowContext) -> AnyElement> {
self.preview
}
}
pub struct AllComponents(pub HashMap<ComponentId, ComponentMetadata>);
impl AllComponents {
pub fn new() -> Self {
AllComponents(HashMap::default())
}
pub fn add(&mut self, id: ComponentId, metadata: ComponentMetadata) {
self.0.insert(id, metadata);
}
pub fn get(&self, id: &ComponentId) -> Option<&ComponentMetadata> {
self.0.get(id)
}
/// Returns all components with previews
pub fn all_previews(&self) -> Vec<&ComponentMetadata> {
self.0.values().filter(|c| c.preview.is_some()).collect()
}
/// Returns all components
pub fn all(&self) -> Vec<&ComponentMetadata> {
self.0.values().collect()
}
}
pub fn components() -> AllComponents {
let data = COMPONENT_DATA.read();
let mut all_components = AllComponents::new();
for &(scope, name, description) in &data.components {
let scope = scope.map(Into::into);
let preview = data.previews.get(name).cloned();
all_components.add(
ComponentId(name),
ComponentMetadata {
name: name.into(),
scope,
description: description.map(Into::into),
preview,
},
);
}
all_components
}

View File

@@ -85,7 +85,6 @@ unindent = { workspace = true, optional = true }
ui.workspace = true
url.workspace = true
util.workspace = true
uuid.workspace = true
workspace.workspace = true
[dev-dependencies]

View File

@@ -105,7 +105,6 @@ pub struct MoveDownByLines {
#[serde(default)]
pub(super) lines: u32,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
pub struct SelectUpByLines {
#[serde(default)]
@@ -167,13 +166,6 @@ pub struct SpawnNearestTask {
pub reveal: task::RevealStrategy,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Deserialize, Default)]
pub enum UuidVersion {
#[default]
V4,
V7,
}
impl_actions!(
editor,
[
@@ -279,8 +271,6 @@ gpui::actions!(
HalfPageUp,
Hover,
Indent,
InsertUuidV4,
InsertUuidV7,
JoinLines,
KillRingCut,
KillRingYank,

View File

@@ -12004,33 +12004,6 @@ impl Editor {
.detach();
}
pub fn insert_uuid_v4(&mut self, _: &InsertUuidV4, cx: &mut ViewContext<Self>) {
self.insert_uuid(UuidVersion::V4, cx);
}
pub fn insert_uuid_v7(&mut self, _: &InsertUuidV7, cx: &mut ViewContext<Self>) {
self.insert_uuid(UuidVersion::V7, cx);
}
fn insert_uuid(&mut self, version: UuidVersion, cx: &mut ViewContext<Self>) {
self.transact(cx, |this, cx| {
let edits = this
.selections
.all::<Point>(cx)
.into_iter()
.map(|selection| {
let uuid = match version {
UuidVersion::V4 => uuid::Uuid::new_v4(),
UuidVersion::V7 => uuid::Uuid::now_v7(),
};
(selection.range(), uuid.to_string())
});
this.edit(edits, cx);
this.refresh_inline_completion(true, false, cx);
});
}
/// Adds a row highlight for the given range. If a row has multiple highlights, the
/// last highlight added will be used.
///

View File

@@ -456,8 +456,6 @@ impl EditorElement {
register_action(view, cx, Editor::open_active_item_in_terminal);
register_action(view, cx, Editor::reload_file);
register_action(view, cx, Editor::spawn_nearest_task);
register_action(view, cx, Editor::insert_uuid_v4);
register_action(view, cx, Editor::insert_uuid_v7);
}
fn register_key_listeners(&self, cx: &mut WindowContext, layout: &EditorLayout) {

View File

@@ -6,7 +6,7 @@ use std::{
time::Duration,
};
use anyhow::{anyhow, Context as _};
use anyhow::Context as _;
use collections::{BTreeMap, HashMap};
use feature_flags::FeatureFlagAppExt;
use git::{
@@ -235,30 +235,22 @@ impl ProjectDiffEditor {
>::default();
let mut change_sets = Vec::new();
for (status, entry_id, entry_path, open_task) in open_tasks {
let Some(buffer) = open_task
.await
.and_then(|(_, opened_model)| {
opened_model
.downcast::<Buffer>()
.map_err(|_| anyhow!("Unexpected non-buffer"))
})
.with_context(|| {
format!("loading {} for git diff", entry_path.path.display())
})
.log_err()
else {
continue;
let (_, opened_model) = open_task.await.with_context(|| {
format!("loading buffer {:?} for git diff", entry_path.path)
})?;
let buffer = match opened_model.downcast::<Buffer>() {
Ok(buffer) => buffer,
Err(_model) => anyhow::bail!(
"Could not load {:?} as a buffer for git diff",
entry_path.path
),
};
let Some(change_set) = project
let change_set = project
.update(&mut cx, |project, cx| {
project.open_unstaged_changes(buffer.clone(), cx)
})?
.await
.log_err()
else {
continue;
};
.await?;
cx.update(|cx| {
buffers.insert(
@@ -275,7 +267,7 @@ impl ProjectDiffEditor {
new_entries.push((entry_path, entry_id));
}
anyhow::Ok((buffers, new_entries, change_sets))
Ok((buffers, new_entries, change_sets))
})
.await
.log_err()

View File

@@ -80,7 +80,7 @@ gpui_macros.workspace = true
http_client = { optional = true, workspace = true }
image = "0.25.1"
itertools.workspace = true
linkme = "0.3"
linkme.workspace = true
log.workspace = true
num_cpus = "1.13"
parking = "2.0.0"

View File

@@ -8,8 +8,7 @@ use anyhow::{anyhow, Result};
use futures::{AsyncReadExt, Future};
use image::{
codecs::{gif::GifDecoder, webp::WebPDecoder},
AnimationDecoder, DynamicImage, Frame, ImageBuffer, ImageError, ImageFormat, Rgba,
codecs::gif::GifDecoder, AnimationDecoder, Frame, ImageBuffer, ImageError, ImageFormat,
};
use smallvec::SmallVec;
use std::{
@@ -543,34 +542,6 @@ impl Asset for ImageAssetLoader {
frames
}
ImageFormat::WebP => {
let mut decoder = WebPDecoder::new(Cursor::new(&bytes))?;
if decoder.has_animation() {
let _ = decoder.set_background_color(Rgba([0, 0, 0, 0]));
let mut frames = SmallVec::new();
for frame in decoder.into_frames() {
let mut frame = frame?;
// Convert from RGBA to BGRA.
for pixel in frame.buffer_mut().chunks_exact_mut(4) {
pixel.swap(0, 2);
}
frames.push(frame);
}
frames
} else {
let mut data = DynamicImage::from_decoder(decoder)?.into_rgba8();
// Convert from RGBA to BGRA.
for pixel in data.chunks_exact_mut(4) {
pixel.swap(0, 2);
}
SmallVec::from_elem(Frame::new(data), 1)
}
}
_ => {
let mut data =
image::load_from_memory_with_format(&bytes, format)?.into_rgba8();

View File

@@ -8,7 +8,7 @@ use std::fs::File;
use std::io::Read;
use std::ops::{Deref, DerefMut};
use std::os::fd::{AsFd, AsRawFd, FromRawFd};
use std::panic::{AssertUnwindSafe, Location};
use std::panic::Location;
use std::rc::Weak;
use std::{
path::{Path, PathBuf},
@@ -18,12 +18,12 @@ use std::{
time::Duration,
};
use anyhow::{anyhow, Context as _};
use anyhow::anyhow;
use async_task::Runnable;
use calloop::channel::Channel;
use calloop::{EventLoop, LoopHandle, LoopSignal};
use flume::{Receiver, Sender};
use futures::{channel::oneshot, future::FutureExt};
use futures::channel::oneshot;
use parking_lot::Mutex;
use util::ResultExt;
@@ -382,14 +382,14 @@ impl<P: LinuxClient + 'static> Platform for P {
}
fn open_with_system(&self, path: &Path) {
let executor = self.background_executor().clone();
let path = path.to_owned();
self.background_executor()
executor
.spawn(async move {
let _ = std::process::Command::new("xdg-open")
.arg(path)
.spawn()
.context("invoking xdg-open")
.log_err();
.expect("Failed to open file with xdg-open");
})
.detach();
}
@@ -489,12 +489,7 @@ impl<P: LinuxClient + 'static> Platform for P {
let username = attributes
.get("username")
.ok_or_else(|| anyhow!("Cannot find username in stored credentials"))?;
// oo7 panics if the retrieved secret can't be decrypted due to
// unexpected padding.
let secret = AssertUnwindSafe(item.secret())
.catch_unwind()
.await
.map_err(|_| anyhow!("oo7 panicked while trying to read credentials"))??;
let secret = item.secret().await?;
// we lose the zeroizing capabilities at this boundary,
// a current limitation GPUI's credentials api

View File

@@ -10,7 +10,7 @@ use crate::{
PlatformTextSystem, PlatformWindow, Result, ScreenCaptureSource, SemanticVersion, Task,
WindowAppearance, WindowParams,
};
use anyhow::{anyhow, Context as _};
use anyhow::anyhow;
use block::ConcreteBlock;
use cocoa::{
appkit::{
@@ -57,7 +57,6 @@ use std::{
sync::Arc,
};
use strum::IntoEnumIterator;
use util::ResultExt;
#[allow(non_upper_case_globals)]
const NSUTF8StringEncoding: NSUInteger = 4;
@@ -780,16 +779,15 @@ impl Platform for MacPlatform {
}
fn open_with_system(&self, path: &Path) {
let path = path.to_owned();
let path = path.to_path_buf();
self.0
.lock()
.background_executor
.spawn(async move {
let _ = std::process::Command::new("open")
std::process::Command::new("open")
.arg(path)
.spawn()
.context("invoking open command")
.log_err();
.expect("Failed to open file");
})
.detach();
}

View File

@@ -152,6 +152,10 @@ unsafe fn build_classes() {
sel!(flagsChanged:),
handle_view_event as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(cancelOperation:),
cancel_operation as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(makeBackingLayer),
@@ -1451,6 +1455,29 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
}
}
// Allows us to receive `cmd-.` (the shortcut for closing a dialog)
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=300620#c6
extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
let window_state = unsafe { get_window_state(this) };
let mut lock = window_state.as_ref().lock();
let keystroke = Keystroke {
modifiers: Default::default(),
key: ".".into(),
key_char: None,
};
let event = PlatformInput::KeyDown(KeyDownEvent {
keystroke: keystroke.clone(),
is_held: false,
});
if let Some(mut callback) = lock.event_callback.take() {
drop(lock);
callback(event);
window_state.lock().event_callback = Some(callback);
}
}
extern "C" fn window_did_change_occlusion_state(this: &Object, _: Sel, _: id) {
let window_state = unsafe { get_window_state(this) };
let lock = &mut *window_state.lock();

View File

@@ -33,7 +33,7 @@ pub(crate) fn handle_msg(
WM_ACTIVATE => handle_activate_msg(handle, wparam, state_ptr),
WM_CREATE => handle_create_msg(handle, state_ptr),
WM_MOVE => handle_move_msg(handle, lparam, state_ptr),
WM_SIZE => handle_size_msg(wparam, lparam, state_ptr),
WM_SIZE => handle_size_msg(lparam, state_ptr),
WM_ENTERSIZEMOVE | WM_ENTERMENULOOP => handle_size_move_loop(handle),
WM_EXITSIZEMOVE | WM_EXITMENULOOP => handle_size_move_loop_exit(handle),
WM_TIMER => handle_timer_msg(handle, wparam, state_ptr),
@@ -136,15 +136,7 @@ fn handle_move_msg(
Some(0)
}
fn handle_size_msg(
wparam: WPARAM,
lparam: LPARAM,
state_ptr: Rc<WindowsWindowStatePtr>,
) -> Option<isize> {
if wparam.0 == SIZE_MINIMIZED as usize {
return Some(0);
}
fn handle_size_msg(lparam: LPARAM, state_ptr: Rc<WindowsWindowStatePtr>) -> Option<isize> {
let width = lparam.loword().max(1) as i32;
let height = lparam.hiword().max(1) as i32;
let mut lock = state_ptr.state.borrow_mut();

View File

@@ -14,7 +14,7 @@ use settings::WorktreeId;
use crate::LanguageName;
/// Represents a single toolchain.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
pub struct Toolchain {
/// User-facing label
pub name: SharedString,
@@ -24,18 +24,6 @@ pub struct Toolchain {
pub as_json: serde_json::Value,
}
impl PartialEq for Toolchain {
fn eq(&self, other: &Self) -> bool {
// Do not use as_json for comparisons; it shouldn't impact equality, as it's not user-surfaced.
// Thus, there could be multiple entries that look the same in the UI.
(&self.name, &self.path, &self.language_name).eq(&(
&other.name,
&other.path,
&other.language_name,
))
}
}
#[async_trait]
pub trait ToolchainLister: Send + Sync {
async fn list(

View File

@@ -104,15 +104,14 @@ impl LanguageSelectorDelegate {
let candidates = language_registry
.language_names()
.into_iter()
.filter_map(|name| {
.enumerate()
.filter_map(|(candidate_id, name)| {
language_registry
.available_language_for_name(&name)?
.hidden()
.not()
.then_some(name)
.then(|| StringMatchCandidate::new(candidate_id, name))
})
.enumerate()
.map(|(candidate_id, name)| StringMatchCandidate::new(candidate_id, name))
.collect::<Vec<_>>();
Self {

View File

@@ -8,7 +8,3 @@
((html_block) @content
(#set! "language" "html"))
((minus_metadata) @content (#set! "language" "yaml"))
((plus_metadata) @content (#set! "language" "toml"))

View File

@@ -1 +0,0 @@
../../livekit-rust-sdks/libwebrtc

View File

@@ -1 +0,0 @@
../../livekit-rust-sdks/livekit/

View File

@@ -1 +0,0 @@
../../livekit-rust-sdks/livekit-api/

View File

@@ -1 +0,0 @@
../../livekit-rust-sdks/livekit-ffi

View File

@@ -1 +0,0 @@
../../livekit-rust-sdks/livekit-protocol

View File

@@ -1 +0,0 @@
../../livekit-rust-sdks/livekit-runtime

View File

@@ -6,8 +6,8 @@
"repositoryURL": "https://github.com/livekit/client-sdk-swift.git",
"state": {
"branch": null,
"revision": "8cde9e66ce9b470c3a743f5c72784f57c5a6d0c3",
"version": "1.1.6"
"revision": "7331b813a5ab8a95cfb81fb2b4ed10519428b9ff",
"version": "1.0.12"
}
},
{
@@ -24,8 +24,8 @@
"repositoryURL": "https://github.com/webrtc-sdk/Specs.git",
"state": {
"branch": null,
"revision": "4fa8d6d647fc759cdd0265fd413d2f28ea2e0e08",
"version": "114.5735.8"
"revision": "2f6bab30c8df0fe59ab3e58bc99097f757f85f65",
"version": "104.5112.17"
}
},
{

View File

@@ -12,16 +12,16 @@ let package = Package(
.library(
name: "LiveKitBridge",
type: .static,
targets: ["LiveKitBridge"])
targets: ["LiveKitBridge"]),
],
dependencies: [
.package(url: "https://github.com/livekit/client-sdk-swift.git", .exact("1.1.6"))
.package(url: "https://github.com/livekit/client-sdk-swift.git", .exact("1.0.12")),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "LiveKitBridge",
dependencies: [.product(name: "LiveKit", package: "client-sdk-swift")])
dependencies: [.product(name: "LiveKit", package: "client-sdk-swift")]),
]
)

View File

@@ -638,12 +638,6 @@ impl LanguageServer {
snippet_edit_support: Some(true),
..WorkspaceEditClientCapabilities::default()
}),
file_operations: Some(WorkspaceFileOperationsClientCapabilities {
dynamic_registration: Some(false),
did_rename: Some(true),
will_rename: Some(true),
..Default::default()
}),
..Default::default()
}),
text_document: Some(TextDocumentClientCapabilities {

View File

@@ -28,7 +28,6 @@ pulldown-cmark.workspace = true
settings.workspace = true
theme.workspace = true
ui.workspace = true
util.workspace = true
workspace.workspace = true
[dev-dependencies]

View File

@@ -7,8 +7,8 @@ use crate::markdown_elements::{
use gpui::{
div, img, px, rems, AbsoluteLength, AnyElement, ClipboardItem, DefiniteLength, Div, Element,
ElementId, HighlightStyle, Hsla, ImageSource, InteractiveText, IntoElement, Keystroke, Length,
Modifiers, ParentElement, Render, Resource, SharedString, Styled, StyledText, TextStyle, View,
WeakView, WindowContext,
Modifiers, ParentElement, Resource, SharedString, Styled, StyledText, TextStyle, WeakView,
WindowContext,
};
use settings::Settings;
use std::{
@@ -18,10 +18,9 @@ use std::{
};
use theme::{ActiveTheme, SyntaxTheme, ThemeSettings};
use ui::{
h_flex, relative, tooltip_container, v_flex, Checkbox, Clickable, Color, FluentBuilder,
IconButton, IconName, IconSize, InteractiveElement, Label, LabelCommon, LabelSize, LinkPreview,
Selection, StatefulInteractiveElement, StyledExt, StyledImage, ViewContext, VisibleOnHover,
VisualContext as _,
h_flex, relative, v_flex, Checkbox, Clickable, FluentBuilder, IconButton, IconName, IconSize,
InteractiveElement, LinkPreview, Selection, StatefulInteractiveElement, StyledExt, StyledImage,
Tooltip, VisibleOnHover,
};
use workspace::Workspace;
@@ -207,7 +206,15 @@ fn render_markdown_list_item(
)
.hover(|s| s.cursor_pointer())
.tooltip(|cx| {
InteractiveMarkdownElementTooltip::new(None, "toggle checkbox", cx).into()
let secondary_modifier = Keystroke {
key: "".to_string(),
modifiers: Modifiers::secondary_key(),
key_char: None,
};
Tooltip::text(
format!("{}-click to toggle the checkbox", secondary_modifier),
cx,
)
})
.into_any_element(),
};
@@ -506,7 +513,6 @@ fn render_markdown_text(parsed_new: &MarkdownParagraph, cx: &mut RenderContext)
let image_element = div()
.id(element_id)
.cursor_pointer()
.child(img(ImageSource::Resource(image_resource)).with_fallback({
let alt_text = image.alt_text.clone();
{
@@ -515,31 +521,18 @@ fn render_markdown_text(parsed_new: &MarkdownParagraph, cx: &mut RenderContext)
}))
.tooltip({
let link = image.link.clone();
move |cx| {
InteractiveMarkdownElementTooltip::new(
Some(link.to_string()),
"open image",
cx,
)
.into()
}
move |cx| LinkPreview::new(&link.to_string(), cx)
})
.on_click({
let workspace = workspace_clone.clone();
let link = image.link.clone();
move |_, cx| {
if cx.modifiers().secondary() {
match &link {
Link::Web { url } => cx.open_url(url),
Link::Path { path, .. } => {
if let Some(workspace) = &workspace {
_ = workspace.update(cx, |workspace, cx| {
workspace
.open_abs_path(path.clone(), false, cx)
.detach();
});
}
}
move |_event, window_cx| match &link {
Link::Web { url } => window_cx.open_url(url),
Link::Path { path, .. } => {
if let Some(workspace) = &workspace {
_ = workspace.update(window_cx, |workspace, cx| {
workspace.open_abs_path(path.clone(), false, cx).detach();
});
}
}
}
@@ -557,50 +550,3 @@ fn render_markdown_rule(cx: &mut RenderContext) -> AnyElement {
let rule = div().w_full().h(px(2.)).bg(cx.border_color);
div().pt_3().pb_3().child(rule).into_any()
}
struct InteractiveMarkdownElementTooltip {
tooltip_text: Option<SharedString>,
action_text: String,
}
impl InteractiveMarkdownElementTooltip {
pub fn new(
tooltip_text: Option<String>,
action_text: &str,
cx: &mut WindowContext,
) -> View<Self> {
let tooltip_text = tooltip_text.map(|t| util::truncate_and_trailoff(&t, 50).into());
cx.new_view(|_| Self {
tooltip_text,
action_text: action_text.to_string(),
})
}
}
impl Render for InteractiveMarkdownElementTooltip {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
tooltip_container(cx, |el, _| {
let secondary_modifier = Keystroke {
modifiers: Modifiers::secondary_key(),
..Default::default()
};
el.child(
v_flex()
.gap_1()
.when_some(self.tooltip_text.clone(), |this, text| {
this.child(Label::new(text).size(LabelSize::Small))
})
.child(
Label::new(format!(
"{}-click to {}",
secondary_modifier, self.action_text
))
.size(LabelSize::Small)
.color(Color::Muted),
),
)
})
}
}

View File

@@ -23,10 +23,10 @@ use futures::{
stream::FuturesUnordered,
AsyncWriteExt, Future, FutureExt, StreamExt,
};
use globset::{Glob, GlobBuilder, GlobMatcher, GlobSet, GlobSetBuilder};
use globset::{Glob, GlobSet, GlobSetBuilder};
use gpui::{
AppContext, AsyncAppContext, Entity, EventEmitter, Model, ModelContext, PromptLevel, Task,
WeakModel,
AppContext, AsyncAppContext, Context, Entity, EventEmitter, Model, ModelContext, PromptLevel,
Task, WeakModel,
};
use http_client::HttpClient;
use itertools::Itertools as _;
@@ -43,13 +43,12 @@ use language::{
Unclipped,
};
use lsp::{
notification::DidRenameFiles, CodeActionKind, CompletionContext, DiagnosticSeverity,
DiagnosticTag, DidChangeWatchedFilesRegistrationOptions, Edit, FileOperationFilter,
FileOperationPatternKind, FileOperationRegistrationOptions, FileRename, FileSystemWatcher,
InsertTextFormat, LanguageServer, LanguageServerBinary, LanguageServerBinaryOptions,
LanguageServerId, LanguageServerName, LspRequestFuture, MessageActionItem, MessageType, OneOf,
RenameFilesParams, ServerHealthStatus, ServerStatus, SymbolKind, TextEdit, Url,
WillRenameFiles, WorkDoneProgressCancelParams, WorkspaceFolder,
CodeActionKind, CompletionContext, DiagnosticSeverity, DiagnosticTag,
DidChangeWatchedFilesRegistrationOptions, Edit, FileSystemWatcher, InsertTextFormat,
LanguageServer, LanguageServerBinary, LanguageServerBinaryOptions, LanguageServerId,
LanguageServerName, LspRequestFuture, MessageActionItem, MessageType, OneOf,
ServerHealthStatus, ServerStatus, SymbolKind, TextEdit, Url, WorkDoneProgressCancelParams,
WorkspaceFolder,
};
use node_runtime::read_package_installed_version;
use parking_lot::{Mutex, RwLock};
@@ -140,9 +139,7 @@ pub struct LocalLspStore {
pub language_servers: HashMap<LanguageServerId, LanguageServerState>,
buffers_being_formatted: HashSet<BufferId>,
last_workspace_edits_by_language_server: HashMap<LanguageServerId, ProjectTransaction>,
language_server_watched_paths: HashMap<LanguageServerId, LanguageServerWatchedPaths>,
language_server_paths_watched_for_rename:
HashMap<LanguageServerId, RenamePathsWatchedForServer>,
language_server_watched_paths: HashMap<LanguageServerId, Model<LanguageServerWatchedPaths>>,
language_server_watcher_registrations:
HashMap<LanguageServerId, HashMap<String, Vec<FileSystemWatcher>>>,
supplementary_language_servers:
@@ -902,7 +899,6 @@ impl LspStore {
language_servers: Default::default(),
last_workspace_edits_by_language_server: Default::default(),
language_server_watched_paths: Default::default(),
language_server_paths_watched_for_rename: Default::default(),
language_server_watcher_registrations: Default::default(),
current_lsp_settings: ProjectSettings::get_global(cx).lsp.clone(),
buffers_being_formatted: Default::default(),
@@ -4336,112 +4332,6 @@ impl LspStore {
.map(|(key, value)| (*key, value))
}
pub(super) fn did_rename_entry(
&self,
worktree_id: WorktreeId,
old_path: &Path,
new_path: &Path,
is_dir: bool,
) {
maybe!({
let local_store = self.as_local()?;
let old_uri = lsp::Url::from_file_path(old_path).ok().map(String::from)?;
let new_uri = lsp::Url::from_file_path(new_path).ok().map(String::from)?;
for language_server in self.language_servers_for_worktree(worktree_id) {
let Some(filter) = local_store
.language_server_paths_watched_for_rename
.get(&language_server.server_id())
else {
continue;
};
if filter.should_send_did_rename(&old_uri, is_dir) {
language_server
.notify::<DidRenameFiles>(RenameFilesParams {
files: vec![FileRename {
old_uri: old_uri.clone(),
new_uri: new_uri.clone(),
}],
})
.log_err();
}
}
Some(())
});
}
pub(super) fn will_rename_entry(
this: WeakModel<Self>,
worktree_id: WorktreeId,
old_path: &Path,
new_path: &Path,
is_dir: bool,
cx: AsyncAppContext,
) -> Task<()> {
let old_uri = lsp::Url::from_file_path(old_path).ok().map(String::from);
let new_uri = lsp::Url::from_file_path(new_path).ok().map(String::from);
cx.spawn(move |mut cx| async move {
let mut tasks = vec![];
this.update(&mut cx, |this, cx| {
let local_store = this.as_local()?;
let old_uri = old_uri?;
let new_uri = new_uri?;
for language_server in this.language_servers_for_worktree(worktree_id) {
let Some(filter) = local_store
.language_server_paths_watched_for_rename
.get(&language_server.server_id())
else {
continue;
};
let Some(adapter) =
this.language_server_adapter_for_id(language_server.server_id())
else {
continue;
};
if filter.should_send_will_rename(&old_uri, is_dir) {
let apply_edit = cx.spawn({
let old_uri = old_uri.clone();
let new_uri = new_uri.clone();
let language_server = language_server.clone();
|this, mut cx| async move {
let edit = language_server
.request::<WillRenameFiles>(RenameFilesParams {
files: vec![FileRename { old_uri, new_uri }],
})
.log_err()
.await
.flatten()?;
Self::deserialize_workspace_edit(
this.upgrade()?,
edit,
false,
adapter.clone(),
language_server.clone(),
&mut cx,
)
.await
.ok();
Some(())
}
});
tasks.push(apply_edit);
}
}
Some(())
})
.ok()
.flatten();
for task in tasks {
// Await on tasks sequentially so that the order of application of edits is deterministic
// (at least with regards to the order of registration of language servers)
task.await;
}
})
}
fn lsp_notify_abs_paths_changed(
&mut self,
server_id: LanguageServerId,
@@ -4479,32 +4369,6 @@ impl LspStore {
language_server_id: LanguageServerId,
cx: &mut ModelContext<Self>,
) {
let Some(watchers) = self.as_local().and_then(|local| {
local
.language_server_watcher_registrations
.get(&language_server_id)
}) else {
return;
};
let watch_builder =
self.rebuild_watched_paths_inner(language_server_id, watchers.values().flatten(), cx);
let Some(local_lsp_store) = self.as_local_mut() else {
return;
};
let watcher = watch_builder.build(local_lsp_store.fs.clone(), language_server_id, cx);
local_lsp_store
.language_server_watched_paths
.insert(language_server_id, watcher);
cx.notify();
}
fn rebuild_watched_paths_inner<'a>(
&'a self,
language_server_id: LanguageServerId,
watchers: impl Iterator<Item = &'a FileSystemWatcher>,
cx: &mut ModelContext<Self>,
) -> LanguageServerWatchedPathsBuilder {
let worktrees = self
.worktree_store
.read(cx)
@@ -4516,6 +4380,15 @@ impl LspStore {
})
.collect::<Vec<_>>();
let local_lsp_store = self.as_local_mut().unwrap();
let Some(watchers) = local_lsp_store
.language_server_watcher_registrations
.get(&language_server_id)
else {
return;
};
let mut worktree_globs = HashMap::default();
let mut abs_globs = HashMap::default();
log::trace!(
@@ -4533,7 +4406,7 @@ impl LspStore {
pattern: String,
},
}
for watcher in watchers {
for watcher in watchers.values().flatten() {
let mut found_host = false;
for worktree in &worktrees {
let glob_is_inside_worktree = worktree.update(cx, |tree, _| {
@@ -4672,7 +4545,12 @@ impl LspStore {
watch_builder.watch_abs_path(abs_path, globset);
}
}
watch_builder
let watcher = watch_builder.build(local_lsp_store.fs.clone(), language_server_id, cx);
local_lsp_store
.language_server_watched_paths
.insert(language_server_id, watcher);
cx.notify();
}
pub fn language_server_for_id(&self, id: LanguageServerId) -> Option<Arc<LanguageServer>> {
@@ -6772,23 +6650,6 @@ impl LspStore {
simulate_disk_based_diagnostics_completion: None,
},
);
if let Some(file_ops_caps) = language_server
.capabilities()
.workspace
.as_ref()
.and_then(|ws| ws.file_operations.as_ref())
{
let did_rename_caps = file_ops_caps.did_rename.as_ref();
let will_rename_caps = file_ops_caps.will_rename.as_ref();
if did_rename_caps.or(will_rename_caps).is_some() {
let watcher = RenamePathsWatchedForServer::default()
.with_did_rename_patterns(did_rename_caps)
.with_will_rename_patterns(will_rename_caps);
local
.language_server_paths_watched_for_rename
.insert(server_id, watcher);
}
}
}
self.language_server_statuses.insert(
@@ -7149,7 +7010,7 @@ impl LspStore {
if let Some(watched_paths) = local
.language_server_watched_paths
.get(server_id)
.and_then(|paths| paths.worktree_paths.get(&worktree_id))
.and_then(|paths| paths.read(cx).worktree_paths.get(&worktree_id))
{
let params = lsp::DidChangeWatchedFilesParams {
changes: changes
@@ -7254,7 +7115,7 @@ impl LspStore {
Ok(transaction)
}
pub(crate) async fn deserialize_workspace_edit(
pub async fn deserialize_workspace_edit(
this: Model<Self>,
edit: lsp::WorkspaceEdit,
push_to_history: bool,
@@ -7654,84 +7515,6 @@ pub enum LanguageServerToQuery {
Other(LanguageServerId),
}
#[derive(Default)]
struct RenamePathsWatchedForServer {
did_rename: Vec<RenameActionPredicate>,
will_rename: Vec<RenameActionPredicate>,
}
impl RenamePathsWatchedForServer {
fn with_did_rename_patterns(
mut self,
did_rename: Option<&FileOperationRegistrationOptions>,
) -> Self {
if let Some(did_rename) = did_rename {
self.did_rename = did_rename
.filters
.iter()
.filter_map(|filter| filter.try_into().log_err())
.collect();
}
self
}
fn with_will_rename_patterns(
mut self,
will_rename: Option<&FileOperationRegistrationOptions>,
) -> Self {
if let Some(will_rename) = will_rename {
self.will_rename = will_rename
.filters
.iter()
.filter_map(|filter| filter.try_into().log_err())
.collect();
}
self
}
fn should_send_did_rename(&self, path: &str, is_dir: bool) -> bool {
self.did_rename.iter().any(|pred| pred.eval(path, is_dir))
}
fn should_send_will_rename(&self, path: &str, is_dir: bool) -> bool {
self.will_rename.iter().any(|pred| pred.eval(path, is_dir))
}
}
impl TryFrom<&FileOperationFilter> for RenameActionPredicate {
type Error = globset::Error;
fn try_from(ops: &FileOperationFilter) -> Result<Self, globset::Error> {
Ok(Self {
kind: ops.pattern.matches.clone(),
glob: GlobBuilder::new(&ops.pattern.glob)
.case_insensitive(
ops.pattern
.options
.as_ref()
.map_or(false, |ops| ops.ignore_case.unwrap_or(false)),
)
.build()?
.compile_matcher(),
})
}
}
struct RenameActionPredicate {
glob: GlobMatcher,
kind: Option<FileOperationPatternKind>,
}
impl RenameActionPredicate {
// Returns true if language server should be notified
fn eval(&self, path: &str, is_dir: bool) -> bool {
self.kind.as_ref().map_or(true, |kind| {
let expected_kind = if is_dir {
FileOperationPatternKind::Folder
} else {
FileOperationPatternKind::File
};
kind == &expected_kind
}) && self.glob.is_match(path)
}
}
#[derive(Default)]
struct LanguageServerWatchedPaths {
worktree_paths: HashMap<WorktreeId, GlobSet>,
@@ -7756,65 +7539,78 @@ impl LanguageServerWatchedPathsBuilder {
fs: Arc<dyn Fs>,
language_server_id: LanguageServerId,
cx: &mut ModelContext<LspStore>,
) -> LanguageServerWatchedPaths {
) -> Model<LanguageServerWatchedPaths> {
let project = cx.weak_model();
const LSP_ABS_PATH_OBSERVE: Duration = Duration::from_millis(100);
let abs_paths = self
.abs_paths
.into_iter()
.map(|(abs_path, globset)| {
let task = cx.spawn({
let abs_path = abs_path.clone();
let fs = fs.clone();
cx.new_model(|cx| {
let this_id = cx.entity_id();
const LSP_ABS_PATH_OBSERVE: Duration = Duration::from_millis(100);
let abs_paths = self
.abs_paths
.into_iter()
.map(|(abs_path, globset)| {
let task = cx.spawn({
let abs_path = abs_path.clone();
let fs = fs.clone();
let lsp_store = project.clone();
|_, mut cx| async move {
maybe!(async move {
let mut push_updates = fs.watch(&abs_path, LSP_ABS_PATH_OBSERVE).await;
while let Some(update) = push_updates.0.next().await {
let action = lsp_store
.update(&mut cx, |this, _| {
let Some(local) = this.as_local() else {
return ControlFlow::Break(());
};
let Some(watcher) = local
.language_server_watched_paths
.get(&language_server_id)
else {
return ControlFlow::Break(());
};
let (globs, _) = watcher.abs_paths.get(&abs_path).expect(
"Watched abs path is not registered with a watcher",
);
let matching_entries = update
.into_iter()
.filter(|event| globs.is_match(&event.path))
.collect::<Vec<_>>();
this.lsp_notify_abs_paths_changed(
language_server_id,
matching_entries,
);
ControlFlow::Continue(())
})
.ok()?;
let lsp_store = project.clone();
|_, mut cx| async move {
maybe!(async move {
let mut push_updates =
fs.watch(&abs_path, LSP_ABS_PATH_OBSERVE).await;
while let Some(update) = push_updates.0.next().await {
let action = lsp_store
.update(&mut cx, |this, cx| {
let Some(local) = this.as_local() else {
return ControlFlow::Break(());
};
let Some(watcher) = local
.language_server_watched_paths
.get(&language_server_id)
else {
return ControlFlow::Break(());
};
if watcher.entity_id() != this_id {
// This watcher is no longer registered on the project, which means that we should
// cease operations.
return ControlFlow::Break(());
}
let (globs, _) = watcher
.read(cx)
.abs_paths
.get(&abs_path)
.expect(
"Watched abs path is not registered with a watcher",
);
let matching_entries = update
.into_iter()
.filter(|event| globs.is_match(&event.path))
.collect::<Vec<_>>();
this.lsp_notify_abs_paths_changed(
language_server_id,
matching_entries,
);
ControlFlow::Continue(())
})
.ok()?;
if action.is_break() {
break;
}
if action.is_break() {
break;
}
}
Some(())
})
.await;
}
Some(())
})
.await;
}
});
(abs_path, (globset, task))
});
(abs_path, (globset, task))
})
.collect();
LanguageServerWatchedPaths {
worktree_paths: self.worktree_paths,
abs_paths,
}
})
.collect();
LanguageServerWatchedPaths {
worktree_paths: self.worktree_paths,
abs_paths,
}
}
}

View File

@@ -583,8 +583,6 @@ impl Project {
client.add_model_request_handler(Self::handle_open_new_buffer);
client.add_model_message_handler(Self::handle_create_buffer_for_peer);
client.add_model_request_handler(WorktreeStore::handle_rename_project_entry);
WorktreeStore::init(&client);
BufferStore::init(&client);
LspStore::init(&client);
@@ -1491,45 +1489,11 @@ impl Project {
new_path: impl Into<Arc<Path>>,
cx: &mut ModelContext<Self>,
) -> Task<Result<CreatedEntry>> {
let worktree_store = self.worktree_store.read(cx);
let new_path = new_path.into();
let Some((worktree, old_path, is_dir)) = worktree_store
.worktree_and_entry_for_id(entry_id, cx)
.map(|(worktree, entry)| (worktree, entry.path.clone(), entry.is_dir()))
else {
let Some(worktree) = self.worktree_for_entry(entry_id, cx) else {
return Task::ready(Err(anyhow!(format!("No worktree for entry {entry_id:?}"))));
};
let worktree_id = worktree.read(cx).id();
let lsp_store = self.lsp_store().downgrade();
cx.spawn(|_, mut cx| async move {
let (old_abs_path, new_abs_path) = {
let root_path = worktree.update(&mut cx, |this, _| this.abs_path())?;
(root_path.join(&old_path), root_path.join(&new_path))
};
LspStore::will_rename_entry(
lsp_store.clone(),
worktree_id,
&old_abs_path,
&new_abs_path,
is_dir,
cx.clone(),
)
.await;
let entry = worktree
.update(&mut cx, |worktree, cx| {
worktree.rename_entry(entry_id, new_path.clone(), cx)
})?
.await?;
lsp_store
.update(&mut cx, |this, _| {
this.did_rename_entry(worktree_id, &old_abs_path, &new_abs_path, is_dir);
})
.ok();
Ok(entry)
worktree.update(cx, |worktree, cx| {
worktree.rename_entry(entry_id, new_path, cx)
})
}

View File

@@ -9,16 +9,12 @@ use language::{
tree_sitter_rust, tree_sitter_typescript, Diagnostic, DiagnosticSet, DiskState, FakeLspAdapter,
LanguageConfig, LanguageMatcher, LanguageName, LineEnding, OffsetRangeExt, Point, ToPoint,
};
use lsp::{
notification::DidRenameFiles, DiagnosticSeverity, DocumentChanges, FileOperationFilter,
NumberOrString, TextDocumentEdit, WillRenameFiles,
};
use lsp::{DiagnosticSeverity, NumberOrString};
use parking_lot::Mutex;
use pretty_assertions::{assert_eq, assert_matches};
use serde_json::json;
#[cfg(not(windows))]
use std::os;
use std::{str::FromStr, sync::OnceLock};
use std::{mem, num::NonZeroU32, ops::Range, task::Poll};
use task::{ResolvedTask, TaskContext};
@@ -3919,135 +3915,6 @@ async fn test_grouped_diagnostics(cx: &mut gpui::TestAppContext) {
);
}
#[gpui::test]
async fn test_lsp_rename_notifications(cx: &mut gpui::TestAppContext) {
init_test(cx);
let fs = FakeFs::new(cx.executor());
fs.insert_tree(
"/dir",
json!({
"one.rs": "const ONE: usize = 1;",
"two": {
"two.rs": "const TWO: usize = one::ONE + one::ONE;"
}
}),
)
.await;
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
language_registry.add(rust_lang());
let watched_paths = lsp::FileOperationRegistrationOptions {
filters: vec![
FileOperationFilter {
scheme: Some("file".to_owned()),
pattern: lsp::FileOperationPattern {
glob: "**/*.rs".to_owned(),
matches: Some(lsp::FileOperationPatternKind::File),
options: None,
},
},
FileOperationFilter {
scheme: Some("file".to_owned()),
pattern: lsp::FileOperationPattern {
glob: "**/**".to_owned(),
matches: Some(lsp::FileOperationPatternKind::Folder),
options: None,
},
},
],
};
let mut fake_servers = language_registry.register_fake_lsp(
"Rust",
FakeLspAdapter {
capabilities: lsp::ServerCapabilities {
workspace: Some(lsp::WorkspaceServerCapabilities {
workspace_folders: None,
file_operations: Some(lsp::WorkspaceFileOperationsServerCapabilities {
did_rename: Some(watched_paths.clone()),
will_rename: Some(watched_paths),
..Default::default()
}),
}),
..Default::default()
},
..Default::default()
},
);
let _ = project
.update(cx, |project, cx| {
project.open_local_buffer("/dir/one.rs", cx)
})
.await
.unwrap();
let fake_server = fake_servers.next().await.unwrap();
let response = project.update(cx, |project, cx| {
let worktree = project.worktrees(cx).next().unwrap();
let entry = worktree.read(cx).entry_for_path("one.rs").unwrap();
project.rename_entry(entry.id, "three.rs".as_ref(), cx)
});
let expected_edit = lsp::WorkspaceEdit {
changes: None,
document_changes: Some(DocumentChanges::Edits({
vec![TextDocumentEdit {
edits: vec![lsp::Edit::Plain(lsp::TextEdit {
range: lsp::Range {
start: lsp::Position {
line: 0,
character: 1,
},
end: lsp::Position {
line: 0,
character: 3,
},
},
new_text: "This is not a drill".to_owned(),
})],
text_document: lsp::OptionalVersionedTextDocumentIdentifier {
uri: Url::from_str("file:///dir/two/two.rs").unwrap(),
version: Some(1337),
},
}]
})),
change_annotations: None,
};
let resolved_workspace_edit = Arc::new(OnceLock::new());
fake_server
.handle_request::<WillRenameFiles, _, _>({
let resolved_workspace_edit = resolved_workspace_edit.clone();
let expected_edit = expected_edit.clone();
move |params, _| {
let resolved_workspace_edit = resolved_workspace_edit.clone();
let expected_edit = expected_edit.clone();
async move {
assert_eq!(params.files.len(), 1);
assert_eq!(params.files[0].old_uri, "file:///dir/one.rs");
assert_eq!(params.files[0].new_uri, "file:///dir/three.rs");
resolved_workspace_edit.set(expected_edit.clone()).unwrap();
Ok(Some(expected_edit))
}
}
})
.next()
.await
.unwrap();
let _ = response.await.unwrap();
fake_server
.handle_notification::<DidRenameFiles, _>(|params, _| {
assert_eq!(params.files.len(), 1);
assert_eq!(params.files[0].old_uri, "file:///dir/one.rs");
assert_eq!(params.files[0].new_uri, "file:///dir/three.rs");
})
.next()
.await
.unwrap();
assert_eq!(resolved_workspace_edit.get(), Some(&expected_edit));
}
#[gpui::test]
async fn test_rename(cx: &mut gpui::TestAppContext) {
// hi

View File

@@ -26,7 +26,7 @@ use text::ReplicaId;
use util::{paths::SanitizedPath, ResultExt};
use worktree::{Entry, ProjectEntryId, Worktree, WorktreeId, WorktreeSettings};
use crate::{search::SearchQuery, LspStore, ProjectPath};
use crate::{search::SearchQuery, ProjectPath};
struct MatchingEntry {
worktree_path: Arc<Path>,
@@ -69,6 +69,7 @@ impl EventEmitter<WorktreeStoreEvent> for WorktreeStore {}
impl WorktreeStore {
pub fn init(client: &AnyProtoClient) {
client.add_model_request_handler(Self::handle_create_project_entry);
client.add_model_request_handler(Self::handle_rename_project_entry);
client.add_model_request_handler(Self::handle_copy_project_entry);
client.add_model_request_handler(Self::handle_delete_project_entry);
client.add_model_request_handler(Self::handle_expand_project_entry);
@@ -183,19 +184,6 @@ impl WorktreeStore {
.find_map(|worktree| worktree.read(cx).entry_for_id(entry_id))
}
pub fn worktree_and_entry_for_id<'a>(
&'a self,
entry_id: ProjectEntryId,
cx: &'a AppContext,
) -> Option<(Model<Worktree>, &'a Entry)> {
self.worktrees().find_map(|worktree| {
worktree
.read(cx)
.entry_for_id(entry_id)
.map(|e| (worktree.clone(), e))
})
}
pub fn entry_for_path(&self, path: &ProjectPath, cx: &AppContext) -> Option<Entry> {
self.worktree_for_id(path.worktree_id, cx)?
.read(cx)
@@ -1016,56 +1004,16 @@ impl WorktreeStore {
}
pub async fn handle_rename_project_entry(
this: Model<super::Project>,
this: Model<Self>,
envelope: TypedEnvelope<proto::RenameProjectEntry>,
mut cx: AsyncAppContext,
) -> Result<proto::ProjectEntryResponse> {
let entry_id = ProjectEntryId::from_proto(envelope.payload.entry_id);
let (worktree_id, worktree, old_path, is_dir) = this
.update(&mut cx, |this, cx| {
this.worktree_store
.read(cx)
.worktree_and_entry_for_id(entry_id, cx)
.map(|(worktree, entry)| {
(
worktree.read(cx).id(),
worktree,
entry.path.clone(),
entry.is_dir(),
)
})
})?
.ok_or_else(|| anyhow!("worktree not found"))?;
let (old_abs_path, new_abs_path) = {
let root_path = worktree.update(&mut cx, |this, _| this.abs_path())?;
(
root_path.join(&old_path),
root_path.join(&envelope.payload.new_path),
)
};
let lsp_store = this
.update(&mut cx, |this, _| this.lsp_store())?
.downgrade();
LspStore::will_rename_entry(
lsp_store,
worktree_id,
&old_abs_path,
&new_abs_path,
is_dir,
cx.clone(),
)
.await;
let response = Worktree::handle_rename_entry(worktree, envelope.payload, cx.clone()).await;
this.update(&mut cx, |this, cx| {
this.lsp_store().read(cx).did_rename_entry(
worktree_id,
&old_abs_path,
&new_abs_path,
is_dir,
);
})
.ok();
response
let worktree = this.update(&mut cx, |this, cx| {
this.worktree_for_entry(entry_id, cx)
.ok_or_else(|| anyhow!("worktree not found"))
})??;
Worktree::handle_rename_entry(worktree, envelope.payload, cx).await
}
pub async fn handle_copy_project_entry(

View File

@@ -881,26 +881,7 @@ pub fn get_key_equivalents(layout: &str) -> Option<HashMap<char, char>> {
('}', 'Æ'),
('~', '>'),
],
"com.apple.keylayout.NorwegianExtended" => &[
('"', 'ˆ'),
('&', '/'),
('(', ')'),
(')', '='),
('*', '('),
('/', '´'),
(':', 'Å'),
(';', 'å'),
('<', ';'),
('=', '`'),
('>', ':'),
('@', '"'),
('[', 'ø'),
('\\', '@'),
(']', 'æ'),
('`', '<'),
('}', 'Æ'),
('~', '>'),
],
"com.apple.keylayout.NorwegianExtended" => &[('^', 'ˆ'), ('~', '˜')],
"com.apple.keylayout.NorwegianSami-PC" => &[
('"', 'ˆ'),
('&', '/'),

View File

@@ -20,7 +20,7 @@ pub struct KeymapBlock {
#[serde(default)]
context: Option<String>,
#[serde(default)]
use_key_equivalents: Option<bool>,
use_layout_keys: Option<bool>,
bindings: BTreeMap<String, KeymapAction>,
}
@@ -80,7 +80,7 @@ impl KeymapFile {
for KeymapBlock {
context,
use_key_equivalents,
use_layout_keys,
bindings,
} in self.0
{
@@ -124,10 +124,10 @@ impl KeymapFile {
&keystroke,
action,
context.as_deref(),
if use_key_equivalents.unwrap_or_default() {
key_equivalents.as_ref()
} else {
if use_layout_keys.unwrap_or_default() {
None
} else {
key_equivalents.as_ref()
},
)
})

View File

@@ -1 +0,0 @@
../../livekit-rust-sdks/soxr-sys

View File

@@ -214,13 +214,8 @@ async fn deserialize_pane_group(
.await;
let pane = panel
.update(cx, |terminal_panel, cx| {
new_terminal_pane(
workspace.clone(),
project.clone(),
terminal_panel.active_pane.read(cx).is_zoomed(),
cx,
)
.update(cx, |_, cx| {
new_terminal_pane(workspace.clone(), project.clone(), cx)
})
.log_err()?;
let active_item = serialized_pane.active_item;

View File

@@ -84,10 +84,9 @@ pub struct TerminalPanel {
impl TerminalPanel {
pub fn new(workspace: &Workspace, cx: &mut ViewContext<Self>) -> Self {
let project = workspace.project();
let pane = new_terminal_pane(workspace.weak_handle(), project.clone(), false, cx);
let pane = new_terminal_pane(workspace.weak_handle(), project.clone(), cx);
let center = PaneGroup::new(pane.clone());
let enabled = project.read(cx).supports_terminal(cx);
cx.focus_view(&pane);
let terminal_panel = Self {
center,
active_pane: pane,
@@ -300,9 +299,6 @@ impl TerminalPanel {
let pane_count_before_removal = self.center.panes().len();
let _removal_result = self.center.remove(&pane);
if pane_count_before_removal == 1 {
self.center.first_pane().update(cx, |pane, cx| {
pane.set_zoomed(false, cx);
});
cx.emit(PanelEvent::Close);
} else {
if let Some(focus_on_pane) =
@@ -312,49 +308,27 @@ impl TerminalPanel {
}
}
}
pane::Event::ZoomIn => {
for pane in self.center.panes() {
pane.update(cx, |pane, cx| {
pane.set_zoomed(true, cx);
})
}
cx.emit(PanelEvent::ZoomIn);
cx.notify();
}
pane::Event::ZoomOut => {
for pane in self.center.panes() {
pane.update(cx, |pane, cx| {
pane.set_zoomed(false, cx);
})
}
cx.emit(PanelEvent::ZoomOut);
cx.notify();
}
pane::Event::ZoomIn => cx.emit(PanelEvent::ZoomIn),
pane::Event::ZoomOut => cx.emit(PanelEvent::ZoomOut),
pane::Event::AddItem { item } => {
if let Some(workspace) = self.workspace.upgrade() {
workspace.update(cx, |workspace, cx| {
item.added_to_pane(workspace, pane.clone(), cx)
})
}
self.serialize(cx);
}
pane::Event::Split(direction) => {
let new_pane = self.new_pane_with_cloned_active_terminal(cx);
let pane = pane.clone();
let direction = *direction;
cx.spawn(move |terminal_panel, mut cx| async move {
cx.spawn(move |this, mut cx| async move {
let Some(new_pane) = new_pane.await else {
return;
};
terminal_panel
.update(&mut cx, |terminal_panel, cx| {
terminal_panel
.center
.split(&pane, &new_pane, direction)
.log_err();
cx.focus_view(&new_pane);
})
.ok();
this.update(&mut cx, |this, _| {
this.center.split(&pane, &new_pane, direction).log_err();
})
.ok();
})
.detach();
}
@@ -391,7 +365,7 @@ impl TerminalPanel {
.or_else(|| default_working_directory(workspace.read(cx), cx));
let kind = TerminalKind::Shell(working_directory);
let window = cx.window_handle();
cx.spawn(move |terminal_panel, mut cx| async move {
cx.spawn(move |this, mut cx| async move {
let terminal = project
.update(&mut cx, |project, cx| {
project.create_terminal(kind, window, cx)
@@ -406,15 +380,10 @@ impl TerminalPanel {
})
.ok()?,
);
let pane = terminal_panel
.update(&mut cx, |terminal_panel, cx| {
let pane = new_terminal_pane(
weak_workspace,
project,
terminal_panel.active_pane.read(cx).is_zoomed(),
cx,
);
terminal_panel.apply_tab_bar_buttons(&pane, cx);
let pane = this
.update(&mut cx, |this, cx| {
let pane = new_terminal_pane(weak_workspace, project, cx);
this.apply_tab_bar_buttons(&pane, cx);
pane
})
.ok()?;
@@ -423,6 +392,7 @@ impl TerminalPanel {
pane.add_item(terminal_view, true, true, None, cx);
})
.ok()?;
cx.focus_view(&pane).ok()?;
Some(pane)
})
@@ -844,7 +814,6 @@ impl TerminalPanel {
pub fn new_terminal_pane(
workspace: WeakView<Workspace>,
project: Model<Project>,
zoomed: bool,
cx: &mut ViewContext<TerminalPanel>,
) -> View<Pane> {
let is_local = project.read(cx).is_local();
@@ -858,11 +827,9 @@ pub fn new_terminal_pane(
NewTerminal.boxed_clone(),
cx,
);
pane.set_zoomed(zoomed, cx);
pane.set_can_navigate(false, cx);
pane.display_nav_history_buttons(None);
pane.set_should_display_tab_bar(|_| true);
pane.set_zoom_out_on_close(false);
let terminal_panel_for_split_check = terminal_panel.clone();
pane.set_can_split(Some(Arc::new(move |pane, dragged_item, cx| {
@@ -912,12 +879,8 @@ pub fn new_terminal_pane(
let new_pane = pane.drag_split_direction().and_then(|split_direction| {
terminal_panel.update(cx, |terminal_panel, cx| {
let new_pane = new_terminal_pane(
workspace.clone(),
project.clone(),
terminal_panel.active_pane.read(cx).is_zoomed(),
cx,
);
let new_pane =
new_terminal_pane(workspace.clone(), project.clone(), cx);
terminal_panel.apply_tab_bar_buttons(&new_pane, cx);
terminal_panel
.center
@@ -1099,21 +1062,14 @@ impl Render for TerminalPanel {
cx.focus_view(&pane);
} else {
let new_pane = terminal_panel.new_pane_with_cloned_active_terminal(cx);
cx.spawn(|terminal_panel, mut cx| async move {
cx.spawn(|this, mut cx| async move {
if let Some(new_pane) = new_pane.await {
terminal_panel
.update(&mut cx, |terminal_panel, cx| {
terminal_panel
.center
.split(
&terminal_panel.active_pane,
&new_pane,
SplitDirection::Right,
)
.log_err();
cx.focus_view(&new_pane);
})
.ok();
this.update(&mut cx, |this, _| {
this.center
.split(&this.active_pane, &new_pane, SplitDirection::Right)
.log_err();
})
.ok();
}
})
.detach();
@@ -1196,12 +1152,8 @@ impl Panel for TerminalPanel {
}
fn set_zoomed(&mut self, zoomed: bool, cx: &mut ViewContext<Self>) {
for pane in self.center.panes() {
pane.update(cx, |pane, cx| {
pane.set_zoomed(zoomed, cx);
})
}
cx.notify();
self.active_pane
.update(cx, |pane, cx| pane.set_zoomed(zoomed, cx));
}
fn set_active(&mut self, active: bool, cx: &mut ViewContext<Self>) {

View File

@@ -14,8 +14,12 @@ path = "src/ui.rs"
[dependencies]
chrono.workspace = true
collections.workspace = true
gpui.workspace = true
itertools = { workspace = true, optional = true }
lazy_static.workspace = true
linkme.workspace = true
once_cell.workspace = true
menu.workspace = true
serde.workspace = true
settings.workspace = true
@@ -24,6 +28,7 @@ story = { workspace = true, optional = true }
strum = { workspace = true, features = ["derive"] }
theme.workspace = true
ui_macros.workspace = true
component_system.workspace = true
[target.'cfg(windows)'.dependencies]
windows.workspace = true

View File

@@ -1,6 +1,12 @@
use crate::prelude::*;
use crate::{
prelude::*,
utils::{component_preview, component_preview_group},
Indicator,
};
use component_system::ComponentPreview;
use gpui::{img, AnyElement, Hsla, ImageSource, Img, IntoElement, Styled};
use ui_macros::IntoComponent;
/// An element that renders a user avatar with customizable appearance options.
///
@@ -14,7 +20,7 @@ use gpui::{img, AnyElement, Hsla, ImageSource, Img, IntoElement, Styled};
/// .grayscale(true)
/// .border_color(gpui::red());
/// ```
#[derive(IntoElement)]
#[derive(IntoElement, IntoComponent)]
pub struct Avatar {
image: Img,
size: Option<AbsoluteLength>,
@@ -96,3 +102,35 @@ impl RenderOnce for Avatar {
.children(self.indicator.map(|indicator| div().child(indicator)))
}
}
impl ComponentPreview for Avatar {
fn preview(cx: &WindowContext) -> AnyElement {
let avatar_1 = "https://avatars.githubusercontent.com/u/1789?s=70&v=4";
let avatar_2 = "https://avatars.githubusercontent.com/u/482957?s=70&v=4";
let avatar_3 = "https://avatars.githubusercontent.com/u/326587?s=70&v=4";
v_flex()
.gap_4()
.child(
component_preview_group()
.child(component_preview("Default").child(Avatar::new(avatar_1)))
.child(
component_preview("Custom Size").child(Avatar::new(avatar_2).size(px(48.))),
)
.child(
component_preview("Grayscale").child(Avatar::new(avatar_3).grayscale(true)),
),
)
.child(
component_preview_group()
.child(
component_preview("With Border")
.child(Avatar::new(avatar_1).border_color(cx.theme().colors().border)),
)
.child(component_preview("With Indicator").child(
Avatar::new(avatar_2).indicator(Indicator::dot().color(Color::Success)),
)),
)
.into_any_element()
}
}

View File

@@ -1,5 +1,5 @@
#![allow(missing_docs)]
use gpui::{AnyView, DefiniteLength};
use gpui::{AnyElement, AnyView, DefiniteLength};
use crate::{
prelude::*, Color, DynamicSpacing, ElevationIndex, IconPosition, KeyBinding, TintColor,
@@ -440,102 +440,87 @@ impl RenderOnce for Button {
}
}
impl ComponentPreview for Button {
fn description() -> impl Into<Option<&'static str>> {
"A button allows users to take actions, and make choices, with a single tap."
}
// register_components!(input, [Button]);
fn examples(_: &WindowContext) -> Vec<ComponentExampleGroup<Self>> {
vec![
example_group_with_title(
"Styles",
vec![
single_example("Default", Button::new("default", "Default")),
single_example(
"Filled",
Button::new("filled", "Filled").style(ButtonStyle::Filled),
),
single_example(
"Subtle",
Button::new("outline", "Subtle").style(ButtonStyle::Subtle),
),
single_example(
"Transparent",
Button::new("transparent", "Transparent").style(ButtonStyle::Transparent),
),
],
),
example_group_with_title(
"Tinted",
vec![
single_example(
"Accent",
Button::new("tinted_accent", "Accent")
.style(ButtonStyle::Tinted(TintColor::Accent)),
),
single_example(
"Negative",
Button::new("tinted_negative", "Negative")
.style(ButtonStyle::Tinted(TintColor::Negative)),
),
single_example(
"Warning",
Button::new("tinted_warning", "Warning")
.style(ButtonStyle::Tinted(TintColor::Warning)),
),
single_example(
"Positive",
Button::new("tinted_positive", "Positive")
.style(ButtonStyle::Tinted(TintColor::Positive)),
),
],
),
example_group_with_title(
"States",
vec![
single_example("Default", Button::new("default_state", "Default")),
single_example(
"Disabled",
Button::new("disabled", "Disabled").disabled(true),
),
single_example(
"Selected",
Button::new("selected", "Selected").selected(true),
),
],
),
example_group_with_title(
"With Icons",
vec![
single_example(
"Icon Start",
Button::new("icon_start", "Icon Start")
.icon(IconName::Check)
.icon_position(IconPosition::Start),
),
single_example(
"Icon End",
Button::new("icon_end", "Icon End")
.icon(IconName::Check)
.icon_position(IconPosition::End),
),
single_example(
"Icon Color",
Button::new("icon_color", "Icon Color")
.icon(IconName::Check)
.icon_color(Color::Accent),
),
single_example(
"Tinted Icons",
Button::new("icon_color", "Delete")
.style(ButtonStyle::Tinted(TintColor::Negative))
.color(Color::Error)
.icon_color(Color::Error)
.icon(IconName::Trash)
.icon_position(IconPosition::Start),
),
],
),
]
}
}
// impl ComponentElement for Button {
// fn scope() -> &'static str {
// "input"
// }
// fn description() -> impl Into<Option<&'static str>> {
// "A button allows users to take actions, and make choices, with a single tap."
// }
// fn preview(_cx: &WindowContext) -> Option<AnyElement> {
// Some(
// v_flex()
// .gap_4()
// .child(
// h_flex()
// .gap_4()
// .child(Button::new("default", "Default"))
// .child(Button::new("filled", "Filled").style(ButtonStyle::Filled))
// .child(Button::new("subtle", "Subtle").style(ButtonStyle::Subtle))
// .child(
// Button::new("transparent", "Transparent")
// .style(ButtonStyle::Transparent),
// ),
// )
// .child(
// h_flex()
// .gap_4()
// .child(
// Button::new("tinted_accent", "Accent")
// .style(ButtonStyle::Tinted(TintColor::Accent)),
// )
// .child(
// Button::new("tinted_negative", "Negative")
// .style(ButtonStyle::Tinted(TintColor::Negative)),
// )
// .child(
// Button::new("tinted_warning", "Warning")
// .style(ButtonStyle::Tinted(TintColor::Warning)),
// )
// .child(
// Button::new("tinted_positive", "Positive")
// .style(ButtonStyle::Tinted(TintColor::Positive)),
// ),
// )
// .child(
// h_flex()
// .gap_4()
// .child(Button::new("default_state", "Default"))
// .child(Button::new("disabled", "Disabled").disabled(true))
// .child(Button::new("selected", "Selected").selected(true)),
// )
// .child(
// h_flex()
// .gap_4()
// .child(
// Button::new("icon_start", "Icon Start")
// .icon(IconName::Check)
// .icon_position(IconPosition::Start),
// )
// .child(
// Button::new("icon_end", "Icon End")
// .icon(IconName::Check)
// .icon_position(IconPosition::End),
// )
// .child(
// Button::new("icon_color", "Icon Color")
// .icon(IconName::Check)
// .icon_color(Color::Accent),
// )
// .child(
// Button::new("icon_color", "Delete")
// .style(ButtonStyle::Tinted(TintColor::Negative))
// .color(Color::Error)
// .icon_color(Color::Error)
// .icon(IconName::Trash)
// .icon_position(IconPosition::Start),
// ),
// )
// .into_any_element(),
// )
// }
// }

View File

@@ -1,5 +1,7 @@
#![allow(missing_docs)]
use crate::{prelude::*, Icon, IconName, IconSize};
use gpui::AnyElement;
use crate::{prelude::*, Icon, IconName, IconSize, TintColor};
/// An icon that appears within a button.
///
@@ -99,3 +101,53 @@ impl RenderOnce for ButtonIcon {
Icon::new(icon).size(self.size).color(icon_color)
}
}
// register_components!(input, [ButtonIcon]);
// impl ComponentElement for ButtonIcon {
// fn scope() -> &'static str {
// "input"
// }
// fn description() -> impl Into<Option<&'static str>> {
// "A ButtonIcon is an icon that appears within a button, used either alongside a label or as a standalone icon."
// }
// fn preview(_cx: &WindowContext) -> Option<AnyElement> {
// Some(
// v_flex()
// .gap_4()
// .child(
// h_flex()
// .gap_4()
// .child(ButtonIcon::new(IconName::Check))
// .child(ButtonIcon::new(IconName::Check).color(Color::Accent))
// .child(ButtonIcon::new(IconName::Check).size(IconSize::XSmall))
// .child(ButtonIcon::new(IconName::Check).size(IconSize::Small)),
// )
// .child(
// h_flex()
// .gap_4()
// .child(ButtonIcon::new(IconName::Check).selected(true))
// .child(ButtonIcon::new(IconName::Check).disabled(true))
// .child(ButtonIcon::new(IconName::Check).selected_icon(IconName::X))
// .child(ButtonIcon::new(IconName::Check).selected_icon_color(Color::Error)),
// )
// .child(
// h_flex()
// .gap_4()
// .child(ButtonIcon::new(IconName::Check).selected_style(ButtonStyle::Filled))
// .child(ButtonIcon::new(IconName::Check).selected_style(ButtonStyle::Subtle))
// .child(
// ButtonIcon::new(IconName::Check)
// .selected_style(ButtonStyle::Transparent),
// )
// .child(
// ButtonIcon::new(IconName::Check)
// .selected_style(ButtonStyle::Tinted(TintColor::Accent)),
// ),
// )
// .into_any_element(),
// )
// }
// }

View File

@@ -1,8 +1,8 @@
#![allow(missing_docs)]
use gpui::{AnyView, DefiniteLength};
use gpui::{AnyElement, AnyView, DefiniteLength};
use super::button_like::{ButtonCommon, ButtonLike, ButtonSize, ButtonStyle};
use crate::{prelude::*, ElevationIndex, SelectableButton};
use crate::{prelude::*, ElevationIndex, SelectableButton, TintColor};
use crate::{IconName, IconSize};
use super::button_icon::ButtonIcon;
@@ -165,3 +165,86 @@ impl RenderOnce for IconButton {
)
}
}
// register_components!(input, [IconButton]);
// impl ComponentElement for IconButton {
// fn scope() -> &'static str {
// "input"
// }
// fn description() -> impl Into<Option<&'static str>> {
// "An icon button allows users to take actions with a single tap, represented by an icon."
// }
// fn preview(_cx: &WindowContext) -> Option<AnyElement> {
// Some(
// v_flex()
// .gap_4()
// .child(
// h_flex()
// .gap_4()
// .child(IconButton::new("default", IconName::Check))
// .child(
// IconButton::new("filled", IconName::Check).style(ButtonStyle::Filled),
// )
// .child(
// IconButton::new("subtle", IconName::Check).style(ButtonStyle::Subtle),
// )
// .child(
// IconButton::new("transparent", IconName::Check)
// .style(ButtonStyle::Transparent),
// ),
// )
// .child(
// h_flex()
// .gap_4()
// .child(
// IconButton::new("tinted_accent", IconName::Check)
// .style(ButtonStyle::Tinted(TintColor::Accent)),
// )
// .child(
// IconButton::new("tinted_negative", IconName::Check)
// .style(ButtonStyle::Tinted(TintColor::Negative)),
// )
// .child(
// IconButton::new("tinted_warning", IconName::Check)
// .style(ButtonStyle::Tinted(TintColor::Warning)),
// )
// .child(
// IconButton::new("tinted_positive", IconName::Check)
// .style(ButtonStyle::Tinted(TintColor::Positive)),
// ),
// )
// .child(
// h_flex()
// .gap_4()
// .child(IconButton::new("default_state", IconName::Check))
// .child(IconButton::new("disabled", IconName::Check).disabled(true))
// .child(IconButton::new("selected", IconName::Check).selected(true)),
// )
// .child(
// h_flex()
// .gap_4()
// .child(
// IconButton::new("icon_size_small", IconName::Check)
// .icon_size(IconSize::Small),
// )
// .child(
// IconButton::new("icon_size_medium", IconName::Check)
// .icon_size(IconSize::Medium),
// )
// .child(
// IconButton::new("icon_color", IconName::Check)
// .icon_color(Color::Accent),
// )
// .child(
// IconButton::new("icon_delete", IconName::Trash)
// .style(ButtonStyle::Tinted(TintColor::Negative))
// .icon_color(Color::Error),
// ),
// )
// .into_any_element(),
// )
// }
// }

View File

@@ -1,9 +1,8 @@
#![allow(missing_docs)]
use gpui::{div, prelude::*, ElementId, IntoElement, Styled, WindowContext};
use crate::prelude::*;
use crate::{Color, Icon, IconName, Selection};
use gpui::{div, prelude::*, ElementId, IntoElement, Styled, WindowContext};
use std::sync::Arc;
/// # Checkbox
///
@@ -113,55 +112,38 @@ impl RenderOnce for Checkbox {
}
}
impl ComponentPreview for Checkbox {
fn description() -> impl Into<Option<&'static str>> {
"A checkbox lets people choose between a pair of opposing states, like enabled and disabled, using a different appearance to indicate each state."
}
// impl ComponentElement for Checkbox {
// fn description() -> impl Into<Option<&'static str>> {
// "A checkbox allows people to toggle between two states, typically representing on/off, or a pair of opposites."
// }
fn examples(_: &WindowContext) -> Vec<ComponentExampleGroup<Self>> {
vec![
example_group_with_title(
"Default",
vec![
single_example(
"Unselected",
Checkbox::new("checkbox_unselected", Selection::Unselected),
),
single_example(
"Indeterminate",
Checkbox::new("checkbox_indeterminate", Selection::Indeterminate),
),
single_example(
"Selected",
Checkbox::new("checkbox_selected", Selection::Selected),
),
],
),
example_group_with_title(
"Disabled",
vec![
single_example(
"Unselected",
Checkbox::new("checkbox_disabled_unselected", Selection::Unselected)
.disabled(true),
),
single_example(
"Indeterminate",
Checkbox::new("checkbox_disabled_indeterminate", Selection::Indeterminate)
.disabled(true),
),
single_example(
"Selected",
Checkbox::new("checkbox_disabled_selected", Selection::Selected)
.disabled(true),
),
],
),
]
}
}
// fn scope() -> &'static str {
// "input"
// }
use std::sync::Arc;
// fn preview(_cx: &WindowContext) -> Option<gpui::AnyElement> {
// Some(
// component_preview_group()
// .child(
// component_preview("Default")
// .child(Checkbox::new("checkbox-1", Selection::Unselected)),
// )
// .child(
// component_preview("Selected")
// .child(Checkbox::new("checkbox-2", Selection::Selected)),
// )
// .child(
// component_preview("Indeterminate")
// .child(Checkbox::new("checkbox-3", Selection::Indeterminate)),
// )
// .child(
// component_preview("Disabled")
// .child(Checkbox::new("checkbox-4", Selection::Selected).disabled(true)),
// )
// .into_any_element(),
// )
// }
// }
/// A [`Checkbox`] that has a [`Label`].
#[derive(IntoElement)]
@@ -209,40 +191,64 @@ impl RenderOnce for CheckboxWithLabel {
}
}
impl ComponentPreview for CheckboxWithLabel {
fn description() -> impl Into<Option<&'static str>> {
"A checkbox with an associated label, allowing users to select an option while providing a descriptive text."
}
// impl ComponentElement for CheckboxWithLabel {
// fn description() -> impl Into<Option<&'static str>> {
// "A checkbox with an associated label."
// }
fn examples(_: &WindowContext) -> Vec<ComponentExampleGroup<Self>> {
vec![example_group(vec![
single_example(
"Unselected",
CheckboxWithLabel::new(
"checkbox_with_label_unselected",
Label::new("Always save on quit"),
Selection::Unselected,
|_, _| {},
),
),
single_example(
"Indeterminate",
CheckboxWithLabel::new(
"checkbox_with_label_indeterminate",
Label::new("Always save on quit"),
Selection::Indeterminate,
|_, _| {},
),
),
single_example(
"Selected",
CheckboxWithLabel::new(
"checkbox_with_label_selected",
Label::new("Always save on quit"),
Selection::Selected,
|_, _| {},
),
),
])]
}
}
// fn scope() -> &'static str {
// "input"
// }
// fn preview(_cx: &WindowContext) -> Option<gpui::AnyElement> {
// Some(
// v_flex()
// .gap_3()
// .child(
// component_preview_group()
// .child(component_preview("Default").child(CheckboxWithLabel::new(
// "checkbox-1",
// Label::new("Show Completions"),
// Selection::Unselected,
// |_, _| {},
// )))
// .child(component_preview("Selected").child(CheckboxWithLabel::new(
// "checkbox-2",
// Label::new("Show Completions"),
// Selection::Selected,
// |_, _| {},
// ))),
// )
// .child(
// component_preview_group().child(
// component_preview("Indeterminate").child(
// v_flex()
// .child(CheckboxWithLabel::new(
// "checkbox-3",
// Label::new("Show Completions"),
// Selection::Indeterminate,
// |_, _| {},
// ))
// .child(h_flex().child(div().w_5().h_full()).child(
// CheckboxWithLabel::new(
// "checkbox-4",
// Label::new("Editor"),
// Selection::Selected,
// |_, _| {},
// ),
// ))
// .child(h_flex().child(div().w_5().h_full()).child(
// CheckboxWithLabel::new(
// "checkbox-5",
// Label::new("Assistant"),
// Selection::Unselected,
// |_, _| {},
// ),
// )),
// ),
// ),
// )
// .into_any_element(),
// )
// }
// }

View File

@@ -26,7 +26,9 @@ pub fn h_group() -> ContentGroup {
pub struct ContentGroup {
base: Div,
border: bool,
padding: Pixels,
fill: bool,
outset_amount: Option<AbsoluteLength>,
children: SmallVec<[AnyElement; 2]>,
}
@@ -36,7 +38,9 @@ impl ContentGroup {
Self {
base: div(),
border: true,
padding: px(8.),
fill: true,
outset_amount: None,
children: SmallVec::new(),
}
}
@@ -52,6 +56,25 @@ impl ContentGroup {
self.fill = false;
self
}
/// Outset the [ContentBox] to create a visual "outset" effect.
///
/// This sets a default outset amount which matches the width of the border and padding,
/// useful for lining up content with other elements.
pub fn outset(mut self) -> Self {
let border_width = if self.border { px(1.) } else { px(0.) };
let outset_amount = self.padding + border_width;
self.outset_amount = Some(outset_amount.into());
self
}
/// Sets the amount of outset to apply to the [ContentBox].
///
/// This will add negative left and right margin to the [ContentBox] to create a visual "outset" effect.
pub fn outset_amount(mut self, amount: impl Into<AbsoluteLength>) -> Self {
self.outset_amount = Some(amount.into());
self
}
}
impl ParentElement for ContentGroup {
@@ -81,55 +104,14 @@ impl RenderOnce for ContentGroup {
this.border_1().border_color(cx.theme().colors().border)
})
.rounded_md()
.p_2()
.p(self.padding)
.when_some(self.outset_amount, |this, amount| {
let outset_rems: Rems = match amount {
AbsoluteLength::Pixels(p) => rems(p / cx.rem_size()),
AbsoluteLength::Rems(r) => r,
};
this.mx(-outset_rems)
})
.children(self.children)
}
}
impl ComponentPreview for ContentGroup {
fn description() -> impl Into<Option<&'static str>> {
"A flexible container component that can hold other elements. It can be customized with or without a border and background fill."
}
fn example_label_side() -> ExampleLabelSide {
ExampleLabelSide::Bottom
}
fn examples(_: &WindowContext) -> Vec<ComponentExampleGroup<Self>> {
vec![example_group(vec![
single_example(
"Default",
ContentGroup::new()
.flex_1()
.items_center()
.justify_center()
.h_48()
.child(Label::new("Default ContentBox")),
)
.grow(),
single_example(
"Without Border",
ContentGroup::new()
.flex_1()
.items_center()
.justify_center()
.h_48()
.borderless()
.child(Label::new("Borderless ContentBox")),
)
.grow(),
single_example(
"Without Fill",
ContentGroup::new()
.flex_1()
.items_center()
.justify_center()
.h_48()
.unfilled()
.child(Label::new("Unfilled ContentBox")),
)
.grow(),
])
.grow()]
}
}

View File

@@ -1,4 +1,4 @@
use crate::{prelude::*, Avatar};
use crate::prelude::*;
use gpui::{AnyElement, StyleRefinement};
use smallvec::SmallVec;
@@ -59,61 +59,3 @@ impl RenderOnce for Facepile {
)
}
}
impl ComponentPreview for Facepile {
fn description() -> impl Into<Option<&'static str>> {
"A facepile is a collection of faces stacked horizontally\
always with the leftmost face on top and descending in z-index.\
\n\nFacepiles are used to display a group of people or things,\
such as a list of participants in a collaboration session."
}
fn examples(_: &WindowContext) -> Vec<ComponentExampleGroup<Self>> {
let few_faces: [&'static str; 3] = [
"https://avatars.githubusercontent.com/u/1714999?s=60&v=4",
"https://avatars.githubusercontent.com/u/67129314?s=60&v=4",
"https://avatars.githubusercontent.com/u/482957?s=60&v=4",
];
let many_faces: [&'static str; 6] = [
"https://avatars.githubusercontent.com/u/326587?s=60&v=4",
"https://avatars.githubusercontent.com/u/2280405?s=60&v=4",
"https://avatars.githubusercontent.com/u/1789?s=60&v=4",
"https://avatars.githubusercontent.com/u/67129314?s=60&v=4",
"https://avatars.githubusercontent.com/u/482957?s=60&v=4",
"https://avatars.githubusercontent.com/u/1714999?s=60&v=4",
];
vec![example_group_with_title(
"Examples",
vec![
single_example(
"Few Faces",
Facepile::new(
few_faces
.iter()
.map(|&url| Avatar::new(url).into_any_element())
.collect(),
),
),
single_example(
"Many Faces",
Facepile::new(
many_faces
.iter()
.map(|&url| Avatar::new(url).into_any_element())
.collect(),
),
),
single_example(
"Custom Size",
Facepile::new(
few_faces
.iter()
.map(|&url| Avatar::new(url).size(px(24.)).into_any_element())
.collect(),
),
),
],
)]
}
}

View File

@@ -1,14 +1,10 @@
#![allow(missing_docs)]
use gpui::{svg, AnimationElement, Hsla, IntoElement, Point, Rems, Transformation};
use serde::{Deserialize, Serialize};
use strum::{EnumIter, EnumString, IntoEnumIterator, IntoStaticStr};
use strum::{EnumIter, EnumString, IntoStaticStr};
use ui_macros::DerivePathStr;
use crate::{
prelude::*,
traits::component_preview::{ComponentExample, ComponentPreview},
Indicator,
};
use crate::{prelude::*, Indicator};
#[derive(IntoElement)]
pub enum AnyIcon {
@@ -491,26 +487,6 @@ impl RenderOnce for IconDecoration {
}
}
impl ComponentPreview for IconDecoration {
fn examples(cx: &WindowContext) -> Vec<ComponentExampleGroup<Self>> {
let all_kinds = IconDecorationKind::iter().collect::<Vec<_>>();
let examples = all_kinds
.iter()
.map(|kind| {
let name = format!("{:?}", kind).to_string();
single_example(
name,
IconDecoration::new(*kind, cx.theme().colors().surface_background, cx),
)
})
.collect();
vec![example_group(examples)]
}
}
#[derive(IntoElement)]
pub struct DecoratedIcon {
icon: Icon,
@@ -533,66 +509,6 @@ impl RenderOnce for DecoratedIcon {
}
}
impl ComponentPreview for DecoratedIcon {
fn examples(cx: &WindowContext) -> Vec<ComponentExampleGroup<Self>> {
let icon_1 = Icon::new(IconName::FileDoc);
let icon_2 = Icon::new(IconName::FileDoc);
let icon_3 = Icon::new(IconName::FileDoc);
let icon_4 = Icon::new(IconName::FileDoc);
let decoration_x = IconDecoration::new(
IconDecorationKind::X,
cx.theme().colors().surface_background,
cx,
)
.color(cx.theme().status().error)
.position(Point {
x: px(-2.),
y: px(-2.),
});
let decoration_triangle = IconDecoration::new(
IconDecorationKind::Triangle,
cx.theme().colors().surface_background,
cx,
)
.color(cx.theme().status().error)
.position(Point {
x: px(-2.),
y: px(-2.),
});
let decoration_dot = IconDecoration::new(
IconDecorationKind::Dot,
cx.theme().colors().surface_background,
cx,
)
.color(cx.theme().status().error)
.position(Point {
x: px(-2.),
y: px(-2.),
});
let examples = vec![
single_example("no_decoration", DecoratedIcon::new(icon_1, None)),
single_example(
"with_decoration",
DecoratedIcon::new(icon_2, Some(decoration_x)),
),
single_example(
"with_decoration",
DecoratedIcon::new(icon_3, Some(decoration_triangle)),
),
single_example(
"with_decoration",
DecoratedIcon::new(icon_4, Some(decoration_dot)),
),
];
vec![example_group(examples)]
}
}
#[derive(IntoElement)]
pub struct IconWithIndicator {
icon: Icon,
@@ -651,26 +567,3 @@ impl RenderOnce for IconWithIndicator {
})
}
}
impl ComponentPreview for Icon {
fn examples(_cx: &WindowContext) -> Vec<ComponentExampleGroup<Icon>> {
let arrow_icons = vec![
IconName::ArrowDown,
IconName::ArrowLeft,
IconName::ArrowRight,
IconName::ArrowUp,
IconName::ArrowCircle,
];
vec![example_group_with_title(
"Arrow Icons",
arrow_icons
.into_iter()
.map(|icon| {
let name = format!("{:?}", icon).to_string();
ComponentExample::new(name, Icon::new(icon))
})
.collect(),
)]
}
}

View File

@@ -83,34 +83,3 @@ impl RenderOnce for Indicator {
}
}
}
impl ComponentPreview for Indicator {
fn description() -> impl Into<Option<&'static str>> {
"An indicator visually represents a status or state."
}
fn examples(_: &WindowContext) -> Vec<ComponentExampleGroup<Self>> {
vec![
example_group_with_title(
"Types",
vec![
single_example("Dot", Indicator::dot().color(Color::Info)),
single_example("Bar", Indicator::bar().color(Color::Player(2))),
single_example(
"Icon",
Indicator::icon(Icon::new(IconName::Check).color(Color::Success)),
),
],
),
example_group_with_title(
"Examples",
vec![
single_example("Info", Indicator::dot().color(Color::Info)),
single_example("Success", Indicator::dot().color(Color::Success)),
single_example("Warning", Indicator::dot().color(Color::Warning)),
single_example("Error", Indicator::dot().color(Color::Error)),
],
),
]
}
}

View File

@@ -1,4 +1,4 @@
use crate::{prelude::*, Indicator};
use crate::prelude::*;
use gpui::{div, AnyElement, FontWeight, IntoElement, Length};
/// A table component
@@ -150,90 +150,3 @@ where
TableCell::String(e.into())
}
}
impl ComponentPreview for Table {
fn description() -> impl Into<Option<&'static str>> {
"Used for showing tabular data. Tables may show both text and elements in their cells."
}
fn example_label_side() -> ExampleLabelSide {
ExampleLabelSide::Top
}
fn examples(_: &WindowContext) -> Vec<ComponentExampleGroup<Self>> {
vec![
example_group(vec![
single_example(
"Simple Table",
Table::new(vec!["Name", "Age", "City"])
.width(px(400.))
.row(vec!["Alice", "28", "New York"])
.row(vec!["Bob", "32", "San Francisco"])
.row(vec!["Charlie", "25", "London"]),
),
single_example(
"Two Column Table",
Table::new(vec!["Category", "Value"])
.width(px(300.))
.row(vec!["Revenue", "$100,000"])
.row(vec!["Expenses", "$75,000"])
.row(vec!["Profit", "$25,000"]),
),
]),
example_group(vec![single_example(
"Striped Table",
Table::new(vec!["Product", "Price", "Stock"])
.width(px(600.))
.striped()
.row(vec!["Laptop", "$999", "In Stock"])
.row(vec!["Phone", "$599", "Low Stock"])
.row(vec!["Tablet", "$399", "Out of Stock"])
.row(vec!["Headphones", "$199", "In Stock"]),
)]),
example_group_with_title(
"Mixed Content Table",
vec![single_example(
"Table with Elements",
Table::new(vec!["Status", "Name", "Priority", "Deadline", "Action"])
.width(px(840.))
.row(vec![
element_cell(Indicator::dot().color(Color::Success).into_any_element()),
string_cell("Project A"),
string_cell("High"),
string_cell("2023-12-31"),
element_cell(
Button::new("view_a", "View")
.style(ButtonStyle::Filled)
.full_width()
.into_any_element(),
),
])
.row(vec![
element_cell(Indicator::dot().color(Color::Warning).into_any_element()),
string_cell("Project B"),
string_cell("Medium"),
string_cell("2024-03-15"),
element_cell(
Button::new("view_b", "View")
.style(ButtonStyle::Filled)
.full_width()
.into_any_element(),
),
])
.row(vec![
element_cell(Indicator::dot().color(Color::Error).into_any_element()),
string_cell("Project C"),
string_cell("Low"),
string_cell("2024-06-30"),
element_cell(
Button::new("view_c", "View")
.style(ButtonStyle::Filled)
.full_width()
.into_any_element(),
),
]),
)],
),
]
}
}

View File

@@ -9,7 +9,6 @@ pub use gpui::{
pub use crate::styles::{rems_from_px, vh, vw, PlatformStyle, StyledTypography, TextSize};
pub use crate::traits::clickable::*;
pub use crate::traits::component_preview::*;
pub use crate::traits::disableable::*;
pub use crate::traits::fixed::*;
pub use crate::traits::selectable::*;

View File

@@ -1,5 +1,4 @@
pub mod clickable;
pub mod component_preview;
pub mod disableable;
pub mod fixed;
pub mod selectable;

View File

@@ -1,200 +0,0 @@
#![allow(missing_docs)]
use crate::prelude::*;
use gpui::{AnyElement, SharedString};
/// Which side of the preview to show labels on
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExampleLabelSide {
/// Left side
Left,
/// Right side
Right,
#[default]
/// Top side
Top,
/// Bottom side
Bottom,
}
/// Implement this trait to enable rich UI previews with metadata in the Theme Preview tool.
pub trait ComponentPreview: IntoElement {
fn title() -> &'static str {
std::any::type_name::<Self>()
}
fn description() -> impl Into<Option<&'static str>> {
None
}
fn example_label_side() -> ExampleLabelSide {
ExampleLabelSide::default()
}
fn examples(_cx: &WindowContext) -> Vec<ComponentExampleGroup<Self>>;
fn custom_example(_cx: &WindowContext) -> impl Into<Option<AnyElement>> {
None::<AnyElement>
}
fn component_previews(cx: &WindowContext) -> Vec<AnyElement> {
Self::examples(cx)
.into_iter()
.map(|example| Self::render_example_group(example))
.collect()
}
fn render_component_previews(cx: &WindowContext) -> AnyElement {
let title = Self::title();
let (source, title) = title
.rsplit_once("::")
.map_or((None, title), |(s, t)| (Some(s), t));
let description = Self::description().into();
v_flex()
.w_full()
.gap_6()
.p_4()
.border_1()
.border_color(cx.theme().colors().border)
.rounded_md()
.child(
v_flex()
.gap_1()
.child(
h_flex()
.gap_1()
.child(Headline::new(title).size(HeadlineSize::Small))
.when_some(source, |this, source| {
this.child(Label::new(format!("({})", source)).color(Color::Muted))
}),
)
.when_some(description, |this, description| {
this.child(
div()
.text_ui_sm(cx)
.text_color(cx.theme().colors().text_muted)
.max_w(px(600.0))
.child(description),
)
}),
)
.when_some(Self::custom_example(cx).into(), |this, custom_example| {
this.child(custom_example)
})
.children(Self::component_previews(cx))
.into_any_element()
}
fn render_example_group(group: ComponentExampleGroup<Self>) -> AnyElement {
v_flex()
.gap_6()
.when(group.grow, |this| this.w_full().flex_1())
.when_some(group.title, |this, title| {
this.child(Label::new(title).size(LabelSize::Small))
})
.child(
h_flex()
.w_full()
.gap_6()
.children(group.examples.into_iter().map(Self::render_example))
.into_any_element(),
)
.into_any_element()
}
fn render_example(example: ComponentExample<Self>) -> AnyElement {
let base = div().flex();
let base = match Self::example_label_side() {
ExampleLabelSide::Right => base.flex_row(),
ExampleLabelSide::Left => base.flex_row_reverse(),
ExampleLabelSide::Bottom => base.flex_col(),
ExampleLabelSide::Top => base.flex_col_reverse(),
};
base.gap_1()
.when(example.grow, |this| this.flex_1())
.child(example.element)
.child(
Label::new(example.variant_name)
.size(LabelSize::XSmall)
.color(Color::Muted),
)
.into_any_element()
}
}
/// A single example of a component.
pub struct ComponentExample<T> {
variant_name: SharedString,
element: T,
grow: bool,
}
impl<T> ComponentExample<T> {
/// Create a new example with the given variant name and example value.
pub fn new(variant_name: impl Into<SharedString>, example: T) -> Self {
Self {
variant_name: variant_name.into(),
element: example,
grow: false,
}
}
/// Set the example to grow to fill the available horizontal space.
pub fn grow(mut self) -> Self {
self.grow = true;
self
}
}
/// A group of component examples.
pub struct ComponentExampleGroup<T> {
pub title: Option<SharedString>,
pub examples: Vec<ComponentExample<T>>,
pub grow: bool,
}
impl<T> ComponentExampleGroup<T> {
/// Create a new group of examples with the given title.
pub fn new(examples: Vec<ComponentExample<T>>) -> Self {
Self {
title: None,
examples,
grow: false,
}
}
/// Create a new group of examples with the given title.
pub fn with_title(title: impl Into<SharedString>, examples: Vec<ComponentExample<T>>) -> Self {
Self {
title: Some(title.into()),
examples,
grow: false,
}
}
/// Set the group to grow to fill the available horizontal space.
pub fn grow(mut self) -> Self {
self.grow = true;
self
}
}
/// Create a single example
pub fn single_example<T>(variant_name: impl Into<SharedString>, example: T) -> ComponentExample<T> {
ComponentExample::new(variant_name, example)
}
/// Create a group of examples without a title
pub fn example_group<T>(examples: Vec<ComponentExample<T>>) -> ComponentExampleGroup<T> {
ComponentExampleGroup::new(examples)
}
/// Create a group of examples with a title
pub fn example_group_with_title<T>(
title: impl Into<SharedString>,
examples: Vec<ComponentExample<T>>,
) -> ComponentExampleGroup<T> {
ComponentExampleGroup::with_title(title, examples)
}

View File

@@ -1,11 +1,13 @@
//! UI-related utilities
mod color_contrast;
mod component_preview;
mod format_distance;
mod search_input;
mod with_rem_size;
pub use color_contrast::*;
pub use component_preview::*;
pub use format_distance::*;
pub use search_input::*;
pub use with_rem_size::*;

View File

@@ -0,0 +1,119 @@
use crate::prelude::*;
use gpui::{AnyElement, Axis};
use smallvec::SmallVec;
/// A component preview with a label and children.
#[derive(IntoElement)]
pub struct ComponentPreview {
label: Option<SharedString>,
children: SmallVec<[AnyElement; 2]>,
}
impl ComponentPreview {
/// Creates a new ComponentPreview with the specified label side.
pub fn new(label: impl Into<SharedString>) -> Self {
Self {
label: Some(label.into()),
children: SmallVec::new(),
}
}
/// Creates a new ComponentPreview with no label.
pub fn no_label() -> Self {
Self {
label: None,
children: SmallVec::new(),
}
}
/// Sets the label for the ComponentPreview.
pub fn label(mut self, label: impl Into<SharedString>) -> Self {
self.label = Some(label.into());
self
}
}
impl RenderOnce for ComponentPreview {
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
v_flex()
.flex_none()
.gap_3()
.when_some(self.label, |this, label| {
this.child(Label::new(label).color(Color::Muted))
})
.child(
h_flex()
.gap_1()
.w_full()
.flex_none()
.children(self.children),
)
}
}
impl ParentElement for ComponentPreview {
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
self.children.extend(elements)
}
}
/// A group of component previews.
#[derive(IntoElement)]
pub struct ComponentPreviewGroup {
direction: Axis,
children: SmallVec<[AnyElement; 2]>,
}
impl ComponentPreviewGroup {
/// Creates a new ComponentPreviewGroup.
pub fn new() -> Self {
Self {
direction: Axis::Horizontal,
children: SmallVec::new(),
}
}
/// Lay out the previews horizontally.
pub fn horizontal(mut self) -> Self {
self.direction = Axis::Horizontal;
self
}
/// Lay out the previews vertically.
pub fn vertical(mut self) -> Self {
self.direction = Axis::Vertical;
self
}
}
impl ParentElement for ComponentPreviewGroup {
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
self.children.extend(elements)
}
}
impl RenderOnce for ComponentPreviewGroup {
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
let group = match self.direction {
Axis::Horizontal => h_group(),
Axis::Vertical => v_group(),
};
group
.size_full()
.items_start()
.outset()
.gap_3()
.children(self.children)
}
}
/// Creates a new [ComponentPreview]
pub fn component_preview(label: impl Into<SharedString>) -> ComponentPreview {
ComponentPreview::new(label)
}
/// Creates a new [ComponentPreviewGroup]
pub fn component_preview_group() -> ComponentPreviewGroup {
ComponentPreviewGroup::new()
}

View File

@@ -17,3 +17,4 @@ proc-macro2 = "1.0.66"
quote = "1.0.9"
syn = { version = "1.0.72", features = ["full", "extra-traits"] }
convert_case.workspace = true
component_system.workspace = true

View File

@@ -0,0 +1,81 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput, Lit, Meta, MetaList, MetaNameValue, NestedMeta};
pub fn derive_into_component(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let mut scope_val = None;
let mut description_val = None;
for attr in &input.attrs {
if attr.path.is_ident("component") {
if let Ok(Meta::List(MetaList { nested, .. })) = attr.parse_meta() {
for item in nested {
if let NestedMeta::Meta(Meta::NameValue(MetaNameValue {
path,
lit: Lit::Str(s),
..
})) = item
{
let ident = path.get_ident().map(|i| i.to_string()).unwrap_or_default();
if ident == "scope" {
scope_val = Some(s.value());
} else if ident == "description" {
description_val = Some(s.value());
}
}
}
}
}
}
let name = &input.ident;
let scope_impl = if let Some(s) = scope_val {
quote! {
fn scope() -> Option<&'static str> {
Some(#s)
}
}
} else {
quote! {
fn scope() -> Option<&'static str> {
None
}
}
};
let description_impl = if let Some(desc) = description_val {
quote! {
fn description() -> Option<&'static str> {
Some(#desc)
}
}
} else {
quote! {}
};
let expanded = quote! {
impl component_system::Component for #name {
#scope_impl
fn name() -> &'static str {
stringify!(#name)
}
#description_impl
}
#[linkme::distributed_slice(component_system::__ALL_COMPONENTS)]
fn __register_component() {
component_system::register_component::<#name>();
}
#[linkme::distributed_slice(component_system::__ALL_PREVIEWS)]
fn __register_preview() {
component_system::register_preview::<#name>();
}
};
expanded.into()
}

View File

@@ -1,3 +1,4 @@
mod derive_into_component;
mod derive_path_str;
mod dynamic_spacing;
@@ -58,3 +59,27 @@ pub fn path_str(_args: TokenStream, input: TokenStream) -> TokenStream {
pub fn derive_dynamic_spacing(input: TokenStream) -> TokenStream {
dynamic_spacing::derive_spacing(input)
}
/// Derives the `IntoComponent` trait for a struct.
///
/// This macro generates implementations for the `Component` trait and associated
/// registration functions for the component system.
///
/// # Attributes
///
/// - `#[component(scope = "...")]`: Required. Specifies the scope of the component.
/// - `#[component(description = "...")]`: Optional. Provides a description for the component.
///
/// # Example
///
/// ```
/// use ui_macros::IntoComponent;
///
/// #[derive(IntoComponent)]
/// #[component(scope = "my_scope", description = "A sample component")]
/// struct MyComponent;
/// ```
#[proc_macro_derive(IntoComponent, attributes(component))]
pub fn derive_into_component(input: TokenStream) -> TokenStream {
derive_into_component::derive_into_component(input)
}

View File

@@ -5,11 +5,10 @@ use ui::ViewContext;
use crate::{motion::Motion, state::Mode, Vim};
actions!(vim, [HelixNormalAfter, HelixDelete]);
actions!(vim, [HelixNormalAfter]);
pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
Vim::action(editor, cx, Vim::helix_normal_after);
Vim::action(editor, cx, Vim::helix_delete);
}
impl Vim {
@@ -227,27 +226,6 @@ impl Vim {
_ => self.helix_move_and_collapse(motion, times, cx),
}
}
pub fn helix_delete(&mut self, _: &HelixDelete, cx: &mut ViewContext<Self>) {
self.store_visual_marks(cx);
self.update_editor(cx, |vim, editor, cx| {
// Fixup selections so they have helix's semantics.
// Specifically:
// - Make sure that each cursor acts as a 1 character wide selection
editor.transact(cx, |editor, cx| {
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.move_with(|map, selection| {
if selection.is_empty() && !selection.reversed {
selection.end = movement::right(map, selection.end);
}
});
});
});
vim.copy_selections_content(editor, false, cx);
editor.insert("", cx);
});
}
}
#[cfg(test)]
@@ -290,84 +268,4 @@ mod test {
Mode::HelixNormal,
);
}
#[gpui::test]
async fn test_delete(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
// test delete a selection
cx.set_state(
indoc! {"
The qu«ick ˇ»brown
fox jumps over
the lazy dog."},
Mode::HelixNormal,
);
cx.simulate_keystrokes("d");
cx.assert_state(
indoc! {"
The quˇbrown
fox jumps over
the lazy dog."},
Mode::HelixNormal,
);
// test deleting a single character
cx.simulate_keystrokes("d");
cx.assert_state(
indoc! {"
The quˇrown
fox jumps over
the lazy dog."},
Mode::HelixNormal,
);
}
#[gpui::test]
async fn test_delete_character_end_of_line(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
cx.set_state(
indoc! {"
The quick brownˇ
fox jumps over
the lazy dog."},
Mode::HelixNormal,
);
cx.simulate_keystrokes("d");
cx.assert_state(
indoc! {"
The quick brownˇfox jumps over
the lazy dog."},
Mode::HelixNormal,
);
}
#[gpui::test]
async fn test_delete_character_end_of_buffer(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
cx.set_state(
indoc! {"
The quick brown
fox jumps over
the lazy dog.ˇ"},
Mode::HelixNormal,
);
cx.simulate_keystrokes("d");
cx.assert_state(
indoc! {"
The quick brown
fox jumps over
the lazy dog.ˇ"},
Mode::HelixNormal,
);
}
}

View File

@@ -1 +0,0 @@
../../livekit-rust-sdks/webrtc-sys

View File

@@ -24,16 +24,19 @@ test-support = [
"gpui/test-support",
"fs/test-support",
]
livekit-macos = ["call/livekit-macos"]
livekit-cross-platform = ["call/livekit-cross-platform"]
[dependencies]
anyhow.workspace = true
any_vec.workspace = true
anyhow.workspace = true
async-recursion.workspace = true
bincode = "1.2.1"
call.workspace = true
client.workspace = true
clock.workspace = true
collections.workspace = true
component_system.workspace = true
db.workspace = true
derive_more.workspace = true
fs.workspace = true
@@ -47,7 +50,6 @@ node_runtime.workspace = true
parking_lot.workspace = true
postage.workspace = true
project.workspace = true
task.workspace = true
remote.workspace = true
schemars.workspace = true
serde.workspace = true
@@ -56,11 +58,12 @@ session.workspace = true
settings.workspace = true
smallvec.workspace = true
sqlez.workspace = true
strum.workspace = true
task.workspace = true
theme.workspace = true
ui.workspace = true
util.workspace = true
uuid.workspace = true
strum.workspace = true
[dev-dependencies]
call = { workspace = true, features = ["test-support"] }

View File

@@ -64,9 +64,9 @@ pub enum ClosePosition {
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ShowDiagnostics {
#[default]
Off,
Errors,
#[default]
All,
}
@@ -99,7 +99,7 @@ pub struct ItemSettingsContent {
/// Which files containing diagnostic errors/warnings to mark in the tabs.
/// This setting can take the following three values:
///
/// Default: off
/// Default: all
show_diagnostics: Option<ShowDiagnostics>,
/// Whether to always show the close button on tabs.
///

View File

@@ -306,7 +306,6 @@ pub struct Pane {
pub split_item_context_menu_handle: PopoverMenuHandle<ContextMenu>,
pinned_tab_count: usize,
diagnostics: HashMap<ProjectPath, DiagnosticSeverity>,
zoom_out_on_close: bool,
}
pub struct ActivationHistoryEntry {
@@ -508,7 +507,6 @@ impl Pane {
new_item_context_menu_handle: Default::default(),
pinned_tab_count: 0,
diagnostics: Default::default(),
zoom_out_on_close: true,
}
}
@@ -1588,7 +1586,7 @@ impl Pane {
.remove(&item.item_id());
}
if self.zoom_out_on_close && self.items.is_empty() && close_pane_if_empty && self.zoomed {
if self.items.is_empty() && close_pane_if_empty && self.zoomed {
cx.emit(Event::ZoomOut);
}
@@ -2002,8 +2000,12 @@ impl Pane {
let icon = if decorated_icon.is_none() {
match item_diagnostic {
Some(&DiagnosticSeverity::ERROR) => None,
Some(&DiagnosticSeverity::WARNING) => None,
Some(&DiagnosticSeverity::ERROR) => {
Some(Icon::new(IconName::X).color(Color::Error))
}
Some(&DiagnosticSeverity::WARNING) => {
Some(Icon::new(IconName::Triangle).color(Color::Warning))
}
_ => item.tab_icon(cx).map(|icon| icon.color(Color::Muted)),
}
.map(|icon| icon.size(IconSize::Small))
@@ -2140,17 +2142,13 @@ impl Pane {
.child(
h_flex()
.gap_1()
.items_center()
.children(
std::iter::once(if let Some(decorated_icon) = decorated_icon {
Some(div().child(decorated_icon.into_any_element()))
} else if let Some(icon) = icon {
Some(div().child(icon.into_any_element()))
} else {
None
})
.flatten(),
)
.child(if let Some(decorated_icon) = decorated_icon {
div().child(decorated_icon.into_any_element())
} else if let Some(icon) = icon {
div().mt(px(2.5)).child(icon.into_any_element())
} else {
div()
})
.child(label),
);
@@ -2789,10 +2787,6 @@ impl Pane {
pub fn drag_split_direction(&self) -> Option<SplitDirection> {
self.drag_split_direction
}
pub fn set_zoom_out_on_close(&mut self, zoom_out_on_close: bool) {
self.zoom_out_on_close = zoom_out_on_close;
}
}
impl FocusableView for Pane {

View File

@@ -1,11 +1,282 @@
#[cfg(target_os = "macos")]
mod macos;
#[cfg(any(
all(
target_os = "macos",
feature = "livekit-cross-platform",
not(feature = "livekit-macos"),
),
all(not(target_os = "macos"), feature = "livekit-cross-platform"),
))]
mod cross_platform {
use crate::{
item::{Item, ItemEvent},
ItemNavHistory, WorkspaceId,
};
use call::{RemoteVideoTrack, RemoteVideoTrackView};
use client::{proto::PeerId, User};
use gpui::{
div, AppContext, EventEmitter, FocusHandle, FocusableView, InteractiveElement,
ParentElement, Render, SharedString, Styled, View, ViewContext, VisualContext,
WindowContext,
};
use std::sync::Arc;
use ui::{prelude::*, Icon, IconName};
#[cfg(target_os = "macos")]
pub use macos::*;
pub enum Event {
Close,
}
#[cfg(not(target_os = "macos"))]
mod cross_platform;
pub struct SharedScreen {
pub peer_id: PeerId,
user: Arc<User>,
nav_history: Option<ItemNavHistory>,
view: View<RemoteVideoTrackView>,
focus: FocusHandle,
}
#[cfg(not(target_os = "macos"))]
impl SharedScreen {
pub fn new(
track: RemoteVideoTrack,
peer_id: PeerId,
user: Arc<User>,
cx: &mut ViewContext<Self>,
) -> Self {
let view = cx.new_view(|cx| RemoteVideoTrackView::new(track.clone(), cx));
cx.subscribe(&view, |_, _, ev, cx| match ev {
call::RemoteVideoTrackViewEvent::Close => cx.emit(Event::Close),
})
.detach();
Self {
view,
peer_id,
user,
nav_history: Default::default(),
focus: cx.focus_handle(),
}
}
}
impl EventEmitter<Event> for SharedScreen {}
impl FocusableView for SharedScreen {
fn focus_handle(&self, _: &AppContext) -> FocusHandle {
self.focus.clone()
}
}
impl Render for SharedScreen {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
div()
.bg(cx.theme().colors().editor_background)
.track_focus(&self.focus)
.key_context("SharedScreen")
.size_full()
.child(self.view.clone())
}
}
impl Item for SharedScreen {
type Event = Event;
fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
Some(format!("{}'s screen", self.user.github_login).into())
}
fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
if let Some(nav_history) = self.nav_history.as_mut() {
nav_history.push::<()>(None, cx);
}
}
fn tab_icon(&self, _cx: &WindowContext) -> Option<Icon> {
Some(Icon::new(IconName::Screen))
}
fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
Some(format!("{}'s screen", self.user.github_login).into())
}
fn telemetry_event_text(&self) -> Option<&'static str> {
None
}
fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
self.nav_history = Some(history);
}
fn clone_on_split(
&self,
_workspace_id: Option<WorkspaceId>,
cx: &mut ViewContext<Self>,
) -> Option<View<Self>> {
Some(cx.new_view(|cx| Self {
view: self.view.update(cx, |view, cx| view.clone(cx)),
peer_id: self.peer_id,
user: self.user.clone(),
nav_history: Default::default(),
focus: cx.focus_handle(),
}))
}
fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) {
match event {
Event::Close => f(ItemEvent::CloseItem),
}
}
}
}
#[cfg(any(
all(
target_os = "macos",
feature = "livekit-cross-platform",
not(feature = "livekit-macos"),
),
all(not(target_os = "macos"), feature = "livekit-cross-platform"),
))]
pub use cross_platform::*;
#[cfg(any(
all(target_os = "macos", feature = "livekit-macos"),
all(
not(target_os = "macos"),
feature = "livekit-macos",
not(feature = "livekit-cross-platform")
)
))]
mod macos {
use crate::{
item::{Item, ItemEvent},
ItemNavHistory, WorkspaceId,
};
use anyhow::Result;
use call::participant::{Frame, RemoteVideoTrack};
use client::{proto::PeerId, User};
use futures::StreamExt;
use gpui::{
div, surface, AppContext, EventEmitter, FocusHandle, FocusableView, InteractiveElement,
ParentElement, Render, SharedString, Styled, Task, View, ViewContext, VisualContext,
WindowContext,
};
use std::sync::{Arc, Weak};
use ui::{prelude::*, Icon, IconName};
pub enum Event {
Close,
}
pub struct SharedScreen {
track: Weak<RemoteVideoTrack>,
frame: Option<Frame>,
pub peer_id: PeerId,
user: Arc<User>,
nav_history: Option<ItemNavHistory>,
_maintain_frame: Task<Result<()>>,
focus: FocusHandle,
}
impl SharedScreen {
pub fn new(
track: Arc<RemoteVideoTrack>,
peer_id: PeerId,
user: Arc<User>,
cx: &mut ViewContext<Self>,
) -> Self {
cx.focus_handle();
let mut frames = track.frames();
Self {
track: Arc::downgrade(&track),
frame: None,
peer_id,
user,
nav_history: Default::default(),
_maintain_frame: cx.spawn(|this, mut cx| async move {
while let Some(frame) = frames.next().await {
this.update(&mut cx, |this, cx| {
this.frame = Some(frame);
cx.notify();
})?;
}
this.update(&mut cx, |_, cx| cx.emit(Event::Close))?;
Ok(())
}),
focus: cx.focus_handle(),
}
}
}
impl EventEmitter<Event> for SharedScreen {}
impl FocusableView for SharedScreen {
fn focus_handle(&self, _: &AppContext) -> FocusHandle {
self.focus.clone()
}
}
impl Render for SharedScreen {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
div()
.bg(cx.theme().colors().editor_background)
.track_focus(&self.focus)
.key_context("SharedScreen")
.size_full()
.children(
self.frame
.as_ref()
.map(|frame| surface(frame.image()).size_full()),
)
}
}
impl Item for SharedScreen {
type Event = Event;
fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
Some(format!("{}'s screen", self.user.github_login).into())
}
fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
if let Some(nav_history) = self.nav_history.as_mut() {
nav_history.push::<()>(None, cx);
}
}
fn tab_icon(&self, _cx: &WindowContext) -> Option<Icon> {
Some(Icon::new(IconName::Screen))
}
fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
Some(format!("{}'s screen", self.user.github_login).into())
}
fn telemetry_event_text(&self) -> Option<&'static str> {
None
}
fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
self.nav_history = Some(history);
}
fn clone_on_split(
&self,
_workspace_id: Option<WorkspaceId>,
cx: &mut ViewContext<Self>,
) -> Option<View<Self>> {
let track = self.track.upgrade()?;
Some(cx.new_view(|cx| Self::new(track, self.peer_id, self.user.clone(), cx)))
}
fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) {
match event {
Event::Close => f(ItemEvent::CloseItem),
}
}
}
}
#[cfg(any(
all(target_os = "macos", feature = "livekit-macos"),
all(
not(target_os = "macos"),
feature = "livekit-macos",
not(feature = "livekit-cross-platform")
)
))]
pub use macos::*;

View File

@@ -1,114 +0,0 @@
use crate::{
item::{Item, ItemEvent},
ItemNavHistory, WorkspaceId,
};
use call::{RemoteVideoTrack, RemoteVideoTrackView};
use client::{proto::PeerId, User};
use gpui::{
div, AppContext, EventEmitter, FocusHandle, FocusableView, InteractiveElement, ParentElement,
Render, SharedString, Styled, View, ViewContext, VisualContext, WindowContext,
};
use std::sync::Arc;
use ui::{prelude::*, Icon, IconName};
pub enum Event {
Close,
}
pub struct SharedScreen {
pub peer_id: PeerId,
user: Arc<User>,
nav_history: Option<ItemNavHistory>,
view: View<RemoteVideoTrackView>,
focus: FocusHandle,
}
impl SharedScreen {
pub fn new(
track: RemoteVideoTrack,
peer_id: PeerId,
user: Arc<User>,
cx: &mut ViewContext<Self>,
) -> Self {
let view = cx.new_view(|cx| RemoteVideoTrackView::new(track.clone(), cx));
cx.subscribe(&view, |_, _, ev, cx| match ev {
call::RemoteVideoTrackViewEvent::Close => cx.emit(Event::Close),
})
.detach();
Self {
view,
peer_id,
user,
nav_history: Default::default(),
focus: cx.focus_handle(),
}
}
}
impl EventEmitter<Event> for SharedScreen {}
impl FocusableView for SharedScreen {
fn focus_handle(&self, _: &AppContext) -> FocusHandle {
self.focus.clone()
}
}
impl Render for SharedScreen {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
div()
.bg(cx.theme().colors().editor_background)
.track_focus(&self.focus)
.key_context("SharedScreen")
.size_full()
.child(self.view.clone())
}
}
impl Item for SharedScreen {
type Event = Event;
fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
Some(format!("{}'s screen", self.user.github_login).into())
}
fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
if let Some(nav_history) = self.nav_history.as_mut() {
nav_history.push::<()>(None, cx);
}
}
fn tab_icon(&self, _cx: &WindowContext) -> Option<Icon> {
Some(Icon::new(IconName::Screen))
}
fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
Some(format!("{}'s screen", self.user.github_login).into())
}
fn telemetry_event_text(&self) -> Option<&'static str> {
None
}
fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
self.nav_history = Some(history);
}
fn clone_on_split(
&self,
_workspace_id: Option<WorkspaceId>,
cx: &mut ViewContext<Self>,
) -> Option<View<Self>> {
Some(cx.new_view(|cx| Self {
view: self.view.update(cx, |view, cx| view.clone(cx)),
peer_id: self.peer_id,
user: self.user.clone(),
nav_history: Default::default(),
focus: cx.focus_handle(),
}))
}
fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) {
match event {
Event::Close => f(ItemEvent::CloseItem),
}
}
}

View File

@@ -1,126 +0,0 @@
use crate::{
item::{Item, ItemEvent},
ItemNavHistory, WorkspaceId,
};
use anyhow::Result;
use call::participant::{Frame, RemoteVideoTrack};
use client::{proto::PeerId, User};
use futures::StreamExt;
use gpui::{
div, surface, AppContext, EventEmitter, FocusHandle, FocusableView, InteractiveElement,
ParentElement, Render, SharedString, Styled, Task, View, ViewContext, VisualContext,
WindowContext,
};
use std::sync::{Arc, Weak};
use ui::{prelude::*, Icon, IconName};
pub enum Event {
Close,
}
pub struct SharedScreen {
track: Weak<RemoteVideoTrack>,
frame: Option<Frame>,
pub peer_id: PeerId,
user: Arc<User>,
nav_history: Option<ItemNavHistory>,
_maintain_frame: Task<Result<()>>,
focus: FocusHandle,
}
impl SharedScreen {
pub fn new(
track: Arc<RemoteVideoTrack>,
peer_id: PeerId,
user: Arc<User>,
cx: &mut ViewContext<Self>,
) -> Self {
cx.focus_handle();
let mut frames = track.frames();
Self {
track: Arc::downgrade(&track),
frame: None,
peer_id,
user,
nav_history: Default::default(),
_maintain_frame: cx.spawn(|this, mut cx| async move {
while let Some(frame) = frames.next().await {
this.update(&mut cx, |this, cx| {
this.frame = Some(frame);
cx.notify();
})?;
}
this.update(&mut cx, |_, cx| cx.emit(Event::Close))?;
Ok(())
}),
focus: cx.focus_handle(),
}
}
}
impl EventEmitter<Event> for SharedScreen {}
impl FocusableView for SharedScreen {
fn focus_handle(&self, _: &AppContext) -> FocusHandle {
self.focus.clone()
}
}
impl Render for SharedScreen {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
div()
.bg(cx.theme().colors().editor_background)
.track_focus(&self.focus)
.key_context("SharedScreen")
.size_full()
.children(
self.frame
.as_ref()
.map(|frame| surface(frame.image()).size_full()),
)
}
}
impl Item for SharedScreen {
type Event = Event;
fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
Some(format!("{}'s screen", self.user.github_login).into())
}
fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
if let Some(nav_history) = self.nav_history.as_mut() {
nav_history.push::<()>(None, cx);
}
}
fn tab_icon(&self, _cx: &WindowContext) -> Option<Icon> {
Some(Icon::new(IconName::Screen))
}
fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
Some(format!("{}'s screen", self.user.github_login).into())
}
fn telemetry_event_text(&self) -> Option<&'static str> {
None
}
fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
self.nav_history = Some(history);
}
fn clone_on_split(
&self,
_workspace_id: Option<WorkspaceId>,
cx: &mut ViewContext<Self>,
) -> Option<View<Self>> {
let track = self.track.upgrade()?;
Some(cx.new_view(|cx| Self::new(track, self.peer_id, self.user.clone(), cx)))
}
fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) {
match event {
Event::Close => f(ItemEvent::CloseItem),
}
}
}

View File

@@ -1,12 +1,10 @@
#![allow(unused, dead_code)]
use gpui::{actions, hsla, AnyElement, AppContext, EventEmitter, FocusHandle, FocusableView, Hsla};
use gpui::{actions, AppContext, EventEmitter, FocusHandle, FocusableView, Hsla};
use strum::IntoEnumIterator;
use theme::all_theme_colors;
use ui::{
element_cell, prelude::*, string_cell, utils::calculate_contrast_ratio, AudioStatus,
Availability, Avatar, AvatarAudioStatusIndicator, AvatarAvailabilityIndicator, ButtonLike,
Checkbox, CheckboxWithLabel, ContentGroup, DecoratedIcon, ElevationIndex, Facepile,
IconDecoration, Indicator, Table, TintColor, Tooltip,
prelude::*, utils::calculate_contrast_ratio, AudioStatus, Availability, Avatar,
AvatarAudioStatusIndicator, AvatarAvailabilityIndicator, ButtonLike, ElevationIndex, Facepile,
TintColor, Tooltip,
};
use crate::{Item, Workspace};
@@ -27,7 +25,6 @@ pub fn init(cx: &mut AppContext) {
enum ThemePreviewPage {
Overview,
Typography,
Components,
}
impl ThemePreviewPage {
@@ -35,7 +32,6 @@ impl ThemePreviewPage {
match self {
Self::Overview => "Overview",
Self::Typography => "Typography",
Self::Components => "Components",
}
}
}
@@ -61,7 +57,6 @@ impl ThemePreview {
match page {
ThemePreviewPage::Overview => self.render_overview_page(cx).into_any_element(),
ThemePreviewPage::Typography => self.render_typography_page(cx).into_any_element(),
ThemePreviewPage::Components => self.render_components_page(cx).into_any_element(),
}
}
}
@@ -101,154 +96,11 @@ impl Item for ThemePreview {
}
}
const AVATAR_URL: &str = "https://avatars.githubusercontent.com/u/1714999?v=4";
impl ThemePreview {
fn preview_bg(cx: &WindowContext) -> Hsla {
cx.theme().colors().editor_background
}
fn render_avatars(&self, cx: &ViewContext<Self>) -> impl IntoElement {
v_flex()
.gap_1()
.child(
Headline::new("Avatars")
.size(HeadlineSize::Small)
.color(Color::Muted),
)
.child(
h_flex()
.items_start()
.gap_4()
.child(Avatar::new(AVATAR_URL).size(px(24.)))
.child(Avatar::new(AVATAR_URL).size(px(24.)).grayscale(true))
.child(
Avatar::new(AVATAR_URL)
.size(px(24.))
.indicator(AvatarAudioStatusIndicator::new(AudioStatus::Muted)),
)
.child(
Avatar::new(AVATAR_URL)
.size(px(24.))
.indicator(AvatarAudioStatusIndicator::new(AudioStatus::Deafened)),
)
.child(
Avatar::new(AVATAR_URL)
.size(px(24.))
.indicator(AvatarAvailabilityIndicator::new(Availability::Free)),
)
.child(
Avatar::new(AVATAR_URL)
.size(px(24.))
.indicator(AvatarAvailabilityIndicator::new(Availability::Free)),
)
.child(
Facepile::empty()
.child(
Avatar::new(AVATAR_URL)
.border_color(Self::preview_bg(cx))
.size(px(22.))
.into_any_element(),
)
.child(
Avatar::new(AVATAR_URL)
.border_color(Self::preview_bg(cx))
.size(px(22.))
.into_any_element(),
)
.child(
Avatar::new(AVATAR_URL)
.border_color(Self::preview_bg(cx))
.size(px(22.))
.into_any_element(),
)
.child(
Avatar::new(AVATAR_URL)
.border_color(Self::preview_bg(cx))
.size(px(22.))
.into_any_element(),
),
),
)
}
fn render_buttons(&self, layer: ElevationIndex, cx: &ViewContext<Self>) -> impl IntoElement {
v_flex()
.gap_1()
.child(
Headline::new("Buttons")
.size(HeadlineSize::Small)
.color(Color::Muted),
)
.child(
h_flex()
.items_start()
.gap_px()
.child(
IconButton::new("icon_button_transparent", IconName::Check)
.style(ButtonStyle::Transparent),
)
.child(
IconButton::new("icon_button_subtle", IconName::Check)
.style(ButtonStyle::Subtle),
)
.child(
IconButton::new("icon_button_filled", IconName::Check)
.style(ButtonStyle::Filled),
)
.child(
IconButton::new("icon_button_selected_accent", IconName::Check)
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
.selected(true),
)
.child(IconButton::new("icon_button_selected", IconName::Check).selected(true))
.child(
IconButton::new("icon_button_positive", IconName::Check)
.style(ButtonStyle::Tinted(TintColor::Positive)),
)
.child(
IconButton::new("icon_button_warning", IconName::Check)
.style(ButtonStyle::Tinted(TintColor::Warning)),
)
.child(
IconButton::new("icon_button_negative", IconName::Check)
.style(ButtonStyle::Tinted(TintColor::Negative)),
),
)
.child(
h_flex()
.gap_px()
.child(
Button::new("button_transparent", "Transparent")
.style(ButtonStyle::Transparent),
)
.child(Button::new("button_subtle", "Subtle").style(ButtonStyle::Subtle))
.child(Button::new("button_filled", "Filled").style(ButtonStyle::Filled))
.child(
Button::new("button_selected", "Selected")
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
.selected(true),
)
.child(
Button::new("button_selected_tinted", "Selected (Tinted)")
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
.selected(true),
)
.child(
Button::new("button_positive", "Tint::Positive")
.style(ButtonStyle::Tinted(TintColor::Positive)),
)
.child(
Button::new("button_warning", "Tint::Warning")
.style(ButtonStyle::Tinted(TintColor::Warning)),
)
.child(
Button::new("button_negative", "Tint::Negative")
.style(ButtonStyle::Tinted(TintColor::Negative)),
),
)
}
fn render_text(&self, layer: ElevationIndex, cx: &ViewContext<Self>) -> impl IntoElement {
let bg = layer.bg(cx);
@@ -410,8 +262,7 @@ impl ThemePreview {
)
}
fn render_colors(&self, layer: ElevationIndex, cx: &ViewContext<Self>) -> impl IntoElement {
let bg = layer.bg(cx);
fn render_colors(&self, _layer: ElevationIndex, cx: &ViewContext<Self>) -> impl IntoElement {
let all_colors = all_theme_colors(cx);
v_flex()
@@ -502,28 +353,6 @@ impl ThemePreview {
)
}
fn render_components_page(&self, cx: &ViewContext<Self>) -> impl IntoElement {
let layer = ElevationIndex::Surface;
v_flex()
.id("theme-preview-components")
.overflow_scroll()
.size_full()
.gap_2()
.child(ContentGroup::render_component_previews(cx))
.child(IconDecoration::render_component_previews(cx))
.child(DecoratedIcon::render_component_previews(cx))
.child(Checkbox::render_component_previews(cx))
.child(CheckboxWithLabel::render_component_previews(cx))
.child(Facepile::render_component_previews(cx))
.child(Button::render_component_previews(cx))
.child(Indicator::render_component_previews(cx))
.child(Icon::render_component_previews(cx))
.child(Table::render_component_previews(cx))
.child(self.render_avatars(cx))
.child(self.render_buttons(layer, cx))
}
fn render_page_nav(&self, cx: &ViewContext<Self>) -> impl IntoElement {
h_flex()
.id("theme-preview-nav")

View File

@@ -143,12 +143,13 @@ actions!(
FollowNextCollaborator,
NewCenterTerminal,
NewFile,
NewFileSplitVertical,
NewFileSplitHorizontal,
NewFileSplitVertical,
NewSearch,
NewTerminal,
NewWindow,
Open,
ComponentPreview,
OpenInTerminal,
ReloadActiveItem,
SaveAs,
@@ -323,6 +324,7 @@ pub fn init_settings(cx: &mut AppContext) {
pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
init_settings(cx);
notifications::init(cx);
component_system::init();
theme_preview::init(cx);
cx.on_action(Workspace::close_global);

View File

@@ -37,6 +37,7 @@ collab_ui.workspace = true
collections.workspace = true
command_palette.workspace = true
command_palette_hooks.workspace = true
component_preview.workspace = true
copilot.workspace = true
db.workspace = true
diagnostics.workspace = true
@@ -69,7 +70,6 @@ language_tools.workspace = true
languages = { workspace = true, features = ["load-grammars"] }
libc.workspace = true
log.workspace = true
markdown.workspace = true
markdown_preview.workspace = true
menu.workspace = true
mimalloc = { version = "0.1", optional = true }
@@ -127,6 +127,12 @@ welcome.workspace = true
workspace.workspace = true
zed_actions.workspace = true
[target.'cfg(target_os = "macos")'.dependencies]
workspace = { workspace = true, features = ["livekit-macos"] }
[target.'cfg(not(target_os = "macos"))'.dependencies]
workspace = { workspace = true, features = ["livekit-cross-platform"] }
[target.'cfg(target_os = "windows")'.dependencies]
windows.workspace = true

View File

@@ -461,6 +461,7 @@ fn main() {
feedback::init(cx);
markdown_preview::init(cx);
welcome::init(cx);
component_preview::init(cx);
settings_ui::init(cx);
extensions_ui::init(cx);

View File

@@ -1,15 +1,13 @@
use gpui::{
div, AppContext, EventEmitter, FocusHandle, FocusableView, FontWeight, InteractiveElement,
IntoElement, ParentElement, PromptHandle, PromptLevel, PromptResponse, Refineable, Render,
RenderablePromptHandle, Styled, TextStyleRefinement, View, ViewContext, VisualContext,
WindowContext,
IntoElement, ParentElement, PromptHandle, PromptLevel, PromptResponse, Render,
RenderablePromptHandle, Styled, ViewContext, VisualContext, WindowContext,
};
use markdown::{Markdown, MarkdownStyle};
use settings::Settings;
use theme::ThemeSettings;
use ui::{
h_flex, v_flex, ActiveTheme, ButtonCommon, ButtonStyle, Clickable, ElevationIndex,
FluentBuilder, LabelSize, TintColor,
h_flex, v_flex, ButtonCommon, ButtonStyle, Clickable, ElevationIndex, FluentBuilder, LabelSize,
TintColor,
};
use workspace::ui::StyledExt;
@@ -30,27 +28,10 @@ pub fn fallback_prompt_renderer(
|cx| FallbackPromptRenderer {
_level: level,
message: message.to_string(),
detail: detail.map(ToString::to_string),
actions: actions.iter().map(ToString::to_string).collect(),
focus: cx.focus_handle(),
active_action_id: 0,
detail: detail.filter(|text| !text.is_empty()).map(|text| {
cx.new_view(|cx| {
let settings = ThemeSettings::get_global(cx);
let mut base_text_style = cx.text_style();
base_text_style.refine(&TextStyleRefinement {
font_family: Some(settings.ui_font.family.clone()),
font_size: Some(settings.ui_font_size.into()),
color: Some(ui::Color::Muted.color(cx)),
..Default::default()
});
let markdown_style = MarkdownStyle {
base_text_style,
selection_background_color: { cx.theme().players().local().selection },
..Default::default()
};
Markdown::new(text.to_string(), markdown_style, None, None, cx)
})
}),
}
});
@@ -61,10 +42,10 @@ pub fn fallback_prompt_renderer(
pub struct FallbackPromptRenderer {
_level: PromptLevel,
message: String,
detail: Option<String>,
actions: Vec<String>,
focus: FocusHandle,
active_action_id: usize,
detail: Option<View<Markdown>>,
}
impl FallbackPromptRenderer {
@@ -130,11 +111,13 @@ impl Render for FallbackPromptRenderer {
.child(self.message.clone())
.text_color(ui::Color::Default.color(cx)),
)
.children(
self.detail
.clone()
.map(|detail| div().w_full().text_xs().child(detail)),
)
.children(self.detail.clone().map(|detail| {
div()
.w_full()
.text_xs()
.text_color(ui::Color::Muted.color(cx))
.child(detail)
}))
.child(h_flex().justify_end().gap_2().children(
self.actions.iter().enumerate().rev().map(|(ix, action)| {
ui::Button::new(ix, action.clone())

View File

@@ -192,30 +192,6 @@ The Zed Assistant comes pre-configured to use the latest version for common mode
You must provide the model's Context Window in the `max_tokens` parameter, this can be found [OpenAI Model Docs](https://platform.openai.com/docs/models). OpenAI `o1` models should set `max_completion_tokens` as well to avoid incurring high reasoning token costs. Custom models will be listed in the model dropdown in the assistant panel.
### OpenAI API Compatible
Zed supports using OpenAI compatible APIs by specifying a custom `endpoint` and `available_models` for the OpenAI provider.
#### X.ai Grok
Example configuration for using X.ai Grok with Zed:
```json
"language_models": {
"openai": {
"api_url": "https://api.x.ai/v1",
"available_models": [
{
"name": "grok-beta",
"display_name": "X.ai Grok (Beta)",
"max_tokens": 131072
}
],
"version": "1"
},
}
```
### Advanced configuration {#advanced-configuration}
#### Example Configuration

View File

@@ -139,5 +139,3 @@ New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name
```
For more information on this, please see [win32 docs](https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=powershell)
(note that you will need to restart your system after enabling longpath support)

View File

@@ -146,15 +146,20 @@ Finally keyboards that support extended Latin alphabets (usually ISO keyboards)
For example on a German QWERTZ keyboard, the `cmd->` shortcut is moved to `cmd-:` because `cmd->` is the system window switcher and this is where that shortcut is typed on a QWERTY keyboard. `cmd-+` stays the same because + is still typable without option, and as a result, `cmd-[` and `cmd-]` become `cmd-ö` and `cmd-ä`, moving out of the way of the `+` key.
If you are defining shortcuts in your personal keymap, you can opt into the key equivalent mapping by setting `use_key_equivalents` to `true` in your keymap:
If you are defining shortcuts in your personal keymap, you can opt-out of the key equivalent mapping by setting `use_layout_keys` to `true` in your keymap:
```json
[
{
"use_key_equivalents": true,
"bindings": {
"ctrl->": "editor::Indent" // parsed as ctrl-: when a German QWERTZ keyboard is active
}
},
{
"use_layout_keys": true,
"bindings": {
"ctrl->": "editor::Indent" // remains ctrl-> when a German QWERTZ keyboard is active
}
}
]
```

View File

@@ -473,15 +473,15 @@ Here's an example of these settings changed:
Here are a few general Zed settings that can help you fine-tune your Vim experience:
| Property | Description | Default Value |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- |
| cursor_blink | If `true`, the cursor blinks. | `true` |
| relative_line_numbers | If `true`, line numbers in the left gutter are relative to the cursor. | `false` |
| scrollbar | Object that controls the scrollbar display. Set to `{ "show": "never" }` to hide the scroll bar. | `{ "show": "auto" }` |
| scroll_beyond_last_line | If set to `"one_page"`, allows scrolling up to one page beyond the last line. Set to `"off"` to prevent this behavior. | `"one_page"` |
| vertical_scroll_margin | The number of lines to keep above or below the cursor when scrolling. Set to `0` to allow the cursor to go up to the edges of the screen vertically. | `3` |
| gutter.line_numbers | Controls the display of line numbers in the gutter. Set the `"line_numbers"` property to `false` to hide line numbers. | `true` |
| command_aliases | Object that defines aliases for commands in the command palette. You can use it to define shortcut names for commands you use often. Read below for examples. | `{}` |
| Property | Description | Default Value |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- |
| cursor_blink | If `true`, the cursor blinks. | `true` |
| relative_line_numbers | If `true`, line numbers in the left gutter are relative to the cursor. | `false` |
| scrollbar | Object that controls the scrollbar display. Set to `{ "show": "never" }` to hide the scroll bar. | `{ "show": "always" }` |
| scroll_beyond_last_line | If set to `"one_page"`, allows scrolling up to one page beyond the last line. Set to `"off"` to prevent this behavior. | `"one_page"` |
| vertical_scroll_margin | The number of lines to keep above or below the cursor when scrolling. Set to `0` to allow the cursor to go up to the edges of the screen vertically. | `3` |
| gutter.line_numbers | Controls the display of line numbers in the gutter. Set the `"line_numbers"` property to `false` to hide line numbers. | `true` |
| command_aliases | Object that defines aliases for commands in the command palette. You can use it to define shortcut names for commands you use often. Read below for examples. | `{}` |
Here's an example of these settings changed:

View File

@@ -1,19 +0,0 @@
#!/bin/bash
set -exuo pipefail
git apply script/patches/use-cross-platform-livekit.patch
# Re-enable error skipping for this check, so that we can unapply the patch
set +e
cargo check -p workspace
exit_code=$?
# Disable error skipping again
set -e
git apply -R script/patches/use-cross-platform-livekit.patch
exit "$exit_code"

View File

@@ -1,59 +0,0 @@
diff --git a/crates/call/Cargo.toml b/crates/call/Cargo.toml
index 9ba10e56ba..bb69440691 100644
--- a/crates/call/Cargo.toml
+++ b/crates/call/Cargo.toml
@@ -41,10 +41,10 @@ serde_derive.workspace = true
settings.workspace = true
util.workspace = true
-[target.'cfg(target_os = "macos")'.dependencies]
+[target.'cfg(any())'.dependencies]
livekit_client_macos = { workspace = true }
-[target.'cfg(not(target_os = "macos"))'.dependencies]
+[target.'cfg(all())'.dependencies]
livekit_client = { workspace = true }
[dev-dependencies]
diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs
index 5e212d35b7..a8f9e8f43e 100644
--- a/crates/call/src/call.rs
+++ b/crates/call/src/call.rs
@@ -1,13 +1,13 @@
pub mod call_settings;
-#[cfg(target_os = "macos")]
+#[cfg(any())]
mod macos;
-#[cfg(target_os = "macos")]
+#[cfg(any())]
pub use macos::*;
-#[cfg(not(target_os = "macos"))]
+#[cfg(all())]
mod cross_platform;
-#[cfg(not(target_os = "macos"))]
+#[cfg(all())]
pub use cross_platform::*;
diff --git a/crates/workspace/src/shared_screen.rs b/crates/workspace/src/shared_screen.rs
index 1d17cfa145..f845234987 100644
--- a/crates/workspace/src/shared_screen.rs
+++ b/crates/workspace/src/shared_screen.rs
@@ -1,11 +1,11 @@
-#[cfg(target_os = "macos")]
+#[cfg(any())]
mod macos;
-#[cfg(target_os = "macos")]
+#[cfg(any())]
pub use macos::*;
-#[cfg(not(target_os = "macos"))]
+#[cfg(all())]
mod cross_platform;
-#[cfg(not(target_os = "macos"))]
+#[cfg(all())]
pub use cross_platform::*;

View File

@@ -43,8 +43,6 @@ extend-exclude = [
"docs/theme/css/",
# Spellcheck triggers on `|Fixe[sd]|` regex part.
"script/danger/dangerfile.ts",
# Hashes are not typos
"script/patches/use-cross-platform-livekit.patch"
]
[default]