Compare commits

..

3 Commits

Author SHA1 Message Date
Thorsten Ball
4d41e6aa35 more debug 2024-09-06 18:32:43 +02:00
Thorsten Ball
5d0bf9904e debug project leak stuff 2024-09-06 17:50:39 +02:00
Thorsten Ball
1ca4cfe2f8 Fix Workspace references being leaked
We noticed that the `Workspace` was never released (along with the
`Project` and everything that comes along with that) when closing a
window.

After playing around with the LeakDetector and debugging with
`cx.on_release()` callbacks, we found two culprits: the inline assistant
and the outline panel.

Both held strong references to `View<Workspace>` after PR #16589 and
PR #16845.

This PR changes both references to `WeakView<Workspace>` which fixes the
leak but keeps the behaviour the same.

Co-authored-by: Bennet <bennet@zed.dev>
2024-09-06 15:21:54 +02:00
333 changed files with 5125 additions and 11047 deletions

View File

@@ -339,7 +339,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
bundle-linux-aarch64: # this runs on ubuntu22.04
bundle-linux-aarch64:
timeout-minutes: 60
name: Create arm64 Linux bundle
runs-on:
@@ -360,8 +360,8 @@ jobs:
- name: Set up Clang
run: |
sudo apt-get update
sudo apt-get install -y llvm-15 clang-15 build-essential cmake pkg-config libasound2-dev libfontconfig-dev libwayland-dev libxkbcommon-x11-dev libssl-dev libsqlite3-dev libzstd-dev libvulkan1 libgit2-dev
echo "/usr/lib/llvm-15/bin" >> $GITHUB_PATH
sudo apt-get install -y llvm-10 clang-10 build-essential cmake pkg-config libasound2-dev libfontconfig-dev libwayland-dev libxkbcommon-x11-dev libssl-dev libsqlite3-dev libzstd-dev libvulkan1 libgit2-dev
echo "/usr/lib/llvm-10/bin" >> $GITHUB_PATH
- uses: rui314/setup-mold@0bf4f07ef9048ec62a45f9dbf2f098afa49695f0 # v1
with:

164
Cargo.lock generated
View File

@@ -304,9 +304,6 @@ name = "arrayvec"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
dependencies = [
"serde",
]
[[package]]
name = "as-raw-xcb-connection"
@@ -1663,8 +1660,8 @@ dependencies = [
[[package]]
name = "blade-graphics"
version = "0.5.0"
source = "git+https://github.com/kvark/blade?rev=e142a3a5e678eb6a13e642ad8401b1f3aa38e969#e142a3a5e678eb6a13e642ad8401b1f3aa38e969"
version = "0.4.0"
source = "git+https://github.com/kvark/blade?rev=fee06c42f658b36dd9ac85444a9ee2a481383695#fee06c42f658b36dd9ac85444a9ee2a481383695"
dependencies = [
"ash",
"ash-window",
@@ -1693,8 +1690,8 @@ dependencies = [
[[package]]
name = "blade-macros"
version = "0.3.0"
source = "git+https://github.com/kvark/blade?rev=e142a3a5e678eb6a13e642ad8401b1f3aa38e969#e142a3a5e678eb6a13e642ad8401b1f3aa38e969"
version = "0.2.1"
source = "git+https://github.com/kvark/blade?rev=fee06c42f658b36dd9ac85444a9ee2a481383695#fee06c42f658b36dd9ac85444a9ee2a481383695"
dependencies = [
"proc-macro2",
"quote",
@@ -1704,7 +1701,7 @@ dependencies = [
[[package]]
name = "blade-util"
version = "0.1.0"
source = "git+https://github.com/kvark/blade?rev=e142a3a5e678eb6a13e642ad8401b1f3aa38e969#e142a3a5e678eb6a13e642ad8401b1f3aa38e969"
source = "git+https://github.com/kvark/blade?rev=fee06c42f658b36dd9ac85444a9ee2a481383695#fee06c42f658b36dd9ac85444a9ee2a481383695"
dependencies = [
"blade-graphics",
"bytemuck",
@@ -1712,19 +1709,6 @@ dependencies = [
"profiling",
]
[[package]]
name = "blake3"
version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7"
dependencies = [
"arrayref",
"arrayvec",
"cc",
"cfg-if",
"constant_time_eq",
]
[[package]]
name = "block"
version = "0.1.6"
@@ -2768,12 +2752,6 @@ dependencies = [
"tiny-keccak",
]
[[package]]
name = "constant_time_eq"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
[[package]]
name = "context_servers"
version = "0.1.0"
@@ -4060,7 +4038,6 @@ dependencies = [
"client",
"collections",
"ctor",
"editor",
"env_logger",
"fs",
"futures 0.3.30",
@@ -4071,7 +4048,6 @@ dependencies = [
"language",
"log",
"lsp",
"multi_buffer",
"node_runtime",
"parking_lot",
"paths",
@@ -4211,7 +4187,6 @@ dependencies = [
name = "feature_flags"
version = "0.1.0"
dependencies = [
"futures 0.3.30",
"gpui",
]
@@ -4911,9 +4886,9 @@ dependencies = [
[[package]]
name = "glow"
version = "0.14.0"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f865cbd94bd355b89611211e49508da98a1fce0ad755c1e8448fb96711b24528"
checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1"
dependencies = [
"js-sys",
"slotmap",
@@ -7893,12 +7868,27 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "performance"
version = "0.1.0"
dependencies = [
"anyhow",
"collections",
"gpui",
"log",
"schemars",
"serde",
"settings",
"util",
"workspace",
]
[[package]]
name = "perplexity"
version = "0.1.0"
dependencies = [
"serde",
"zed_extension_api 0.2.0",
"zed_extension_api 0.1.0",
]
[[package]]
@@ -9839,13 +9829,10 @@ name = "semantic_index"
version = "0.1.0"
dependencies = [
"anyhow",
"arrayvec",
"blake3",
"client",
"clock",
"collections",
"env_logger",
"feature_flags",
"fs",
"futures 0.3.30",
"futures-batch",
@@ -9853,7 +9840,6 @@ dependencies = [
"heed",
"http_client",
"language",
"language_model",
"languages",
"log",
"open_ai",
@@ -9957,9 +9943,9 @@ dependencies = [
[[package]]
name = "serde_json_lenient"
version = "0.2.1"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5d0bae483150302560d7cb52e7932f39b69a6fbdd099e48d33ef060a8c9c078"
checksum = "dc61c66b53a4035fcce237ef38043f4b2f0ebf918fd0e69541a5166104065581"
dependencies = [
"indexmap 2.4.0",
"itoa",
@@ -10283,7 +10269,7 @@ dependencies = [
name = "slash_commands_example"
version = "0.1.0"
dependencies = [
"zed_extension_api 0.1.0",
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -12275,7 +12261,6 @@ dependencies = [
"story",
"strum 0.25.0",
"theme",
"ui_macros",
"windows 0.58.0",
]
@@ -12290,16 +12275,6 @@ dependencies = [
"ui",
]
[[package]]
name = "ui_macros"
version = "0.1.0"
dependencies = [
"convert_case 0.6.0",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "unicase"
version = "2.7.0"
@@ -14237,7 +14212,7 @@ dependencies = [
[[package]]
name = "zed"
version = "0.154.0"
version = "0.153.0"
dependencies = [
"activity_indicator",
"anyhow",
@@ -14300,6 +14275,7 @@ dependencies = [
"outline_panel",
"parking_lot",
"paths",
"performance",
"profiling",
"project",
"project_panel",
@@ -14357,63 +14333,72 @@ name = "zed_astro"
version = "0.1.0"
dependencies = [
"serde",
"zed_extension_api 0.1.0",
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zed_clojure"
version = "0.0.3"
dependencies = [
"zed_extension_api 0.1.0",
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zed_csharp"
version = "0.0.2"
dependencies = [
"zed_extension_api 0.1.0",
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zed_dart"
version = "0.0.3"
dependencies = [
"zed_extension_api 0.1.0",
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zed_deno"
version = "0.0.2"
dependencies = [
"zed_extension_api 0.1.0",
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zed_elixir"
version = "0.0.9"
dependencies = [
"zed_extension_api 0.1.0",
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zed_elm"
version = "0.0.1"
dependencies = [
"zed_extension_api 0.1.0",
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zed_emmet"
version = "0.0.3"
dependencies = [
"zed_extension_api 0.1.0",
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zed_erlang"
version = "0.0.1"
dependencies = [
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zed_extension_api"
version = "0.1.0"
dependencies = [
"zed_extension_api 0.1.0",
"serde",
"serde_json",
"wit-bindgen",
]
[[package]]
@@ -14427,91 +14412,82 @@ dependencies = [
"wit-bindgen",
]
[[package]]
name = "zed_extension_api"
version = "0.2.0"
dependencies = [
"serde",
"serde_json",
"wit-bindgen",
]
[[package]]
name = "zed_gleam"
version = "0.2.0"
dependencies = [
"html_to_markdown 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"zed_extension_api 0.1.0",
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zed_glsl"
version = "0.1.0"
dependencies = [
"zed_extension_api 0.1.0",
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zed_haskell"
version = "0.1.1"
dependencies = [
"zed_extension_api 0.1.0",
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zed_html"
version = "0.1.2"
dependencies = [
"zed_extension_api 0.1.0",
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zed_lua"
version = "0.0.3"
dependencies = [
"zed_extension_api 0.1.0",
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zed_ocaml"
version = "0.0.2"
dependencies = [
"zed_extension_api 0.1.0",
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zed_php"
version = "0.2.0"
version = "0.1.3"
dependencies = [
"zed_extension_api 0.1.0",
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zed_prisma"
version = "0.0.3"
dependencies = [
"zed_extension_api 0.1.0",
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zed_purescript"
version = "0.0.1"
dependencies = [
"zed_extension_api 0.1.0",
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zed_ruby"
version = "0.2.0"
dependencies = [
"zed_extension_api 0.1.0",
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zed_ruff"
version = "0.0.2"
dependencies = [
"zed_extension_api 0.1.0",
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -14519,50 +14495,42 @@ name = "zed_snippets"
version = "0.0.5"
dependencies = [
"serde_json",
"zed_extension_api 0.1.0",
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zed_svelte"
version = "0.1.1"
dependencies = [
"zed_extension_api 0.1.0",
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zed_terraform"
version = "0.1.0"
dependencies = [
"zed_extension_api 0.1.0",
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zed_test_extension"
version = "0.1.0"
dependencies = [
"zed_extension_api 0.2.0",
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zed_toml"
version = "0.1.1"
dependencies = [
"zed_extension_api 0.1.0",
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zed_uiua"
version = "0.0.1"
dependencies = [
"zed_extension_api 0.1.0",
]
[[package]]
name = "zed_uppercase"
version = "0.0.5"
dependencies = [
"serde_json",
"zed_extension_api 0.2.0",
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -14570,14 +14538,14 @@ name = "zed_vue"
version = "0.1.0"
dependencies = [
"serde",
"zed_extension_api 0.1.0",
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zed_zig"
version = "0.3.0"
dependencies = [
"zed_extension_api 0.1.0",
"zed_extension_api 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]

View File

@@ -72,6 +72,7 @@ members = [
"crates/outline",
"crates/outline_panel",
"crates/paths",
"crates/performance",
"crates/picker",
"crates/prettier",
"crates/project",
@@ -118,7 +119,6 @@ members = [
"crates/title_bar",
"crates/ui",
"crates/ui_input",
"crates/ui_macros",
"crates/util",
"crates/vcs_menu",
"crates/vim",
@@ -159,7 +159,6 @@ members = [
"extensions/terraform",
"extensions/test-extension",
"extensions/toml",
"extensions/uppercase",
"extensions/uiua",
"extensions/vue",
"extensions/zig",
@@ -168,7 +167,7 @@ members = [
# Tooling
#
"tooling/xtask",
"tooling/xtask"
]
default-members = ["crates/zed"]
@@ -247,6 +246,7 @@ open_ai = { path = "crates/open_ai" }
outline = { path = "crates/outline" }
outline_panel = { path = "crates/outline_panel" }
paths = { path = "crates/paths" }
performance = { path = "crates/performance" }
picker = { path = "crates/picker" }
plugin = { path = "crates/plugin" }
plugin_macros = { path = "crates/plugin_macros" }
@@ -294,7 +294,6 @@ time_format = { path = "crates/time_format" }
title_bar = { path = "crates/title_bar" }
ui = { path = "crates/ui" }
ui_input = { path = "crates/ui_input" }
ui_macros = { path = "crates/ui_macros" }
util = { path = "crates/util" }
vcs_menu = { path = "crates/vcs_menu" }
vim = { path = "crates/vim" }
@@ -312,7 +311,6 @@ aho-corasick = "1.1"
alacritty_terminal = { git = "https://github.com/alacritty/alacritty", rev = "91d034ff8b53867143c005acfaa14609147c9a2c" }
any_vec = "0.14"
anyhow = "1.0.86"
arrayvec = { version = "0.7.4", features = ["serde"] }
ashpd = "0.9.1"
async-compression = { version = "0.4", features = ["gzip", "futures-io"] }
async-dispatcher = "0.1"
@@ -326,17 +324,15 @@ async-watch = "0.3.1"
async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
base64 = "0.22"
bitflags = "2.6.0"
blade-graphics = { git = "https://github.com/kvark/blade", rev = "e142a3a5e678eb6a13e642ad8401b1f3aa38e969" }
blade-macros = { git = "https://github.com/kvark/blade", rev = "e142a3a5e678eb6a13e642ad8401b1f3aa38e969" }
blade-util = { git = "https://github.com/kvark/blade", rev = "e142a3a5e678eb6a13e642ad8401b1f3aa38e969" }
blake3 = "1.5.3"
blade-graphics = { git = "https://github.com/kvark/blade", rev = "fee06c42f658b36dd9ac85444a9ee2a481383695" }
blade-macros = { git = "https://github.com/kvark/blade", rev = "fee06c42f658b36dd9ac85444a9ee2a481383695" }
blade-util = { git = "https://github.com/kvark/blade", rev = "fee06c42f658b36dd9ac85444a9ee2a481383695" }
cargo_metadata = "0.18"
cargo_toml = "0.20"
chrono = { version = "0.4", features = ["serde"] }
clap = { version = "4.4", features = ["derive"] }
clickhouse = "0.11.6"
cocoa = "0.26"
convert_case = "0.6.0"
core-foundation = "0.9.3"
core-foundation-sys = "0.8.6"
ctor = "0.2.6"
@@ -399,7 +395,7 @@ semver = "1.0"
serde = { version = "1.0", features = ["derive", "rc"] }
serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
serde_json = { version = "1.0", features = ["preserve_order", "raw_value"] }
serde_json_lenient = { version = "0.2", features = [
serde_json_lenient = { version = "0.1", features = [
"preserve_order",
"raw_value",
] }
@@ -489,7 +485,6 @@ version = "0.58"
features = [
"implement",
"Foundation_Numerics",
"Storage",
"System",
"System_Threading",
"UI_ViewManagement",
@@ -578,18 +573,14 @@ single_range_in_vec_init = "allow"
# There are a bunch of rules currently failing in the `style` group, so
# allow all of those, for now.
style = { level = "allow", priority = -1 }
# Temporary list of style lints that we've fixed so far.
module_inception = { level = "deny" }
question_mark = { level = "deny" }
redundant_closure = { level = "deny" }
# Individual rules that have violations in the codebase:
type_complexity = "allow"
# We often return trait objects from `new` functions.
new_ret_no_self = { level = "allow" }
# We have a few `next` functions that differ in lifetimes
# compared to Iterator::next. Yet, clippy complains about those.
should_implement_trait = { level = "allow" }
# Individual rules that have violations in the codebase:
type_complexity = "allow"
[workspace.metadata.cargo-machete]
ignored = ["bindgen", "cbindgen", "prost_build", "serde"]

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-volume-off"><path d="M16 9a5 5 0 0 1 .95 2.293"/><path d="M19.364 5.636a9 9 0 0 1 1.889 9.96"/><path d="m2 2 20 20"/><path d="m7 7-.587.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298V11"/><path d="M9.828 4.172A.686.686 0 0 1 11 4.657v.686"/></svg>

Before

Width:  |  Height:  |  Size: 527 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-volume-2"><path d="M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298z"/><path d="M16 9a5 5 0 0 1 0 6"/><path d="M19.364 18.364a9 9 0 0 0 0-12.728"/></svg>

Before

Width:  |  Height:  |  Size: 475 B

View File

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 677 B

After

Width:  |  Height:  |  Size: 677 B

View File

Before

Width:  |  Height:  |  Size: 785 B

After

Width:  |  Height:  |  Size: 785 B

View File

Before

Width:  |  Height:  |  Size: 443 B

After

Width:  |  Height:  |  Size: 443 B

View File

@@ -1,6 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13 11V11.8374C13 11.9431 12.9665 12.046 12.9044 12.1315L12.1498 13.1691C12.0557 13.2985 11.9054 13.375 11.7454 13.375H4.25461C4.09464 13.375 3.94433 13.2985 3.85024 13.1691L3.09563 12.1315C3.03348 12.046 3 11.9431 3 11.8374V3" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
<path d="M3 13V11L8 12H13V13H3Z" fill="black"/>
<path d="M6.63246 3.04418C7.44914 3.31641 8 4.08069 8 4.94155V11.7306C8 12.0924 7.62757 12.3345 7.29693 12.1875L3.79693 10.632C3.61637 10.5518 3.5 10.3727 3.5 10.1751V2.69374C3.5 2.35246 3.83435 2.11148 4.15811 2.2194L6.63246 3.04418Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.5 3C8.67157 3 8 3.67157 8 4.5V13C8 12.1954 11.2366 12.0382 12.5017 12.0075C12.7778 12.0008 13 11.7761 13 11.5V3.5C13 3.22386 12.7761 3 12.5 3H9.5Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 1009 B

View File

@@ -1,5 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 5H11" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
<path d="M3 8H13" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
<path d="M3 11H9" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 334 B

View File

@@ -1,6 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5 13C6.10457 13 7 12.1046 7 11C7 9.89543 6.10457 9 5 9C3.89543 9 3 9.89543 3 11C3 12.1046 3.89543 13 5 13Z" stroke="black" stroke-width="1.5"/>
<path d="M11 7C12.1046 7 13 6.10457 13 5C13 3.89543 12.1046 3 11 3C9.89543 3 9 3.89543 9 5C9 6.10457 9.89543 7 11 7Z" fill="black" stroke="black" stroke-width="1.5"/>
<path d="M4.625 3.625V8.375" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
<path d="M11 7C11 9.20914 9.20914 11 7 11" stroke="black" stroke-width="1.5"/>
</svg>

Before

Width:  |  Height:  |  Size: 591 B

View File

@@ -1,4 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5 5C5 3.89543 5.89543 3 7 3H9C10.1046 3 11 3.89543 11 5V6H5V5Z" stroke="black" stroke-width="1.5"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.25 6.5C3.25 5.80964 3.80964 5.25 4.5 5.25H11.5C12.1904 5.25 12.75 5.80964 12.75 6.5V12.5C12.75 13.1904 12.1904 13.75 11.5 13.75H4.5C3.80964 13.75 3.25 13.1904 3.25 12.5V6.5ZM8.75 9.66146C8.90559 9.48517 9 9.25361 9 9C9 8.44772 8.55228 8 8 8C7.44772 8 7 8.44772 7 9C7 9.25361 7.09441 9.48517 7.25 9.66146V11C7.25 11.4142 7.58579 11.75 8 11.75C8.41421 11.75 8.75 11.4142 8.75 11V9.66146Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 667 B

View File

@@ -1,4 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.27935 10.9821C5.32063 10.4038 4.9204 9.89049 4.35998 9.80276L3.60081 9.68387C3.37979 9.64945 3.20167 9.48001 3.15225 9.25614L3.01378 8.63511C2.96382 8.41235 3.05233 8.1807 3.23696 8.05125L3.8631 7.61242C4.33337 7.28297 4.47456 6.6369 4.18621 6.13364L3.79467 5.45092C3.68118 5.25261 3.69801 5.00374 3.83757 4.82321L4.22314 4.32436C4.3627 4.14438 4.59621 4.06994 4.81071 4.13772L5.57531 4.37769C6.11944 4.54879 6.70048 4.26159 6.90683 3.71886L7.1811 2.99782C7.26255 2.78395 7.46345 2.64285 7.68772 2.6423L8.31007 2.64063C8.53434 2.64007 8.73579 2.78006 8.81834 2.99337L9.09965 3.72275C9.30821 4.26214 9.88655 4.54712 10.429 4.37714L11.1632 4.14716C11.3772 4.07994 11.6096 4.15382 11.7492 4.3327L12.1374 4.83099" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.76988 11.5933C8.76988 11.6595 8.8236 11.7133 8.88988 11.7133H8.97588C9.32602 11.7133 9.60988 11.9971 9.60988 12.3472C9.60988 12.6974 9.32602 12.9812 8.97588 12.9812H7.05587C6.70573 12.9812 6.42188 12.6974 6.42188 12.3472C6.42188 11.9971 6.70573 11.7133 7.05587 11.7133H7.14188C7.20815 11.7133 7.26188 11.6595 7.26188 11.5933V7.66925C7.26188 7.60298 7.20815 7.54925 7.14188 7.54925H7.05588C6.70573 7.54925 6.42188 7.2654 6.42188 6.91525C6.42188 6.5651 6.70573 6.28125 7.05588 6.28125H9.89988C11.0518 6.28125 12.8619 6.71487 12.8619 8.15185C12.8619 8.67078 12.7284 9.10362 12.4642 9.45348C12.1981 9.79765 11.8458 10.0564 11.4056 10.2293C11.3782 10.2401 11.3673 10.273 11.3829 10.298L12.2163 11.6342C12.247 11.6834 12.3008 11.7133 12.3588 11.7133H12.7319C13.082 11.7133 13.3659 11.9971 13.3659 12.3472C13.3659 12.6974 13.082 12.9812 12.7319 12.9812H11.5637C11.4955 12.9812 11.432 12.9465 11.3952 12.889L9.96523 10.6541C9.92847 10.5966 9.86495 10.5618 9.79675 10.5618H8.96988C8.85942 10.5618 8.76988 10.6514 8.76988 10.7619V11.5933ZM9.61188 7.54925C10.0296 7.54925 11.125 7.54925 11.2339 8.18785C11.2975 8.56123 11.1181 8.86557 10.8812 9.07715C10.6423 9.29046 10.2053 9.38985 9.58788 9.38985H8.86988C8.81465 9.38985 8.76988 9.34508 8.76988 9.28985V7.64925C8.76988 7.59402 8.81465 7.54925 8.86988 7.54925H9.61188Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -1,5 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 6H10" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
<path d="M8 6V11" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
<path d="M5 3H3.5C3.22386 3 3 3.22386 3 3.5V12.5C3 12.7761 3.22386 13 3.5 13H5M11 3H12.5C12.7761 3 13 3.22386 13 3.5V12.5C13 12.7761 12.7761 13 12.5 13H11" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 472 B

View File

@@ -1,3 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.26046 3.97337C8.3527 4.17617 8.4795 4.47151 8.57375 4.69341C8.65258 4.87898 8.83437 4.99999 9.03599 4.99999H12.5C12.7761 4.99999 13 5.22385 13 5.49999V12.125C13 12.4011 12.7761 12.625 12.5 12.625H3.5C3.22386 12.625 3 12.4011 3 12.125V3.86932C3 3.59318 3.22386 3.36932 3.5 3.36932H7.34219C7.74141 3.36932 8.09483 3.60924 8.26046 3.97337Z" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 512 B

View File

@@ -1,4 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.42782 7.2487C4.43495 6.97194 4.65009 6.75 4.91441 6.75H13.5293C13.7935 6.75 14.007 6.97194 13.9998 7.2487C13.9628 8.6885 13.7533 12.75 12.5721 12.75H3.375C4.55631 12.75 4.3907 8.6885 4.42782 7.2487Z" fill="black" stroke="black" stroke-width="1.5" stroke-linejoin="round"/>
<path d="M5.19598 12.625H3.66515C3.42618 12.625 3.22289 12.4453 3.18626 12.2017L1.94333 3.93602C1.89776 3.63295 2.12496 3.35938 2.42223 3.35938H5.78585C6.11241 3.35938 6.41702 3.52903 6.59618 3.81071L6.94517 4.35938H9.92811C10.4007 4.35938 10.8044 4.71102 10.8836 5.1917L11.1251 6.65624" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 759 B

View File

Before

Width:  |  Height:  |  Size: 462 B

After

Width:  |  Height:  |  Size: 462 B

View File

@@ -1,4 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.16089 10.2476L3.99598 10.3784C4.61244 10.4749 5.05269 11.0395 5.00728 11.6755L4.94576 12.5377C4.92784 12.789 5.06165 13.0255 5.28326 13.1348L5.90091 13.4391C6.12253 13.5485 6.38717 13.5075 6.56817 13.3371L7.1888 12.7505C7.64641 12.3178 8.35245 12.3178 8.81059 12.7505L9.43121 13.3371C9.61222 13.5081 9.87629 13.5485 10.0985 13.4391L10.7173 13.1341C10.9384 13.0255 11.0716 12.7895 11.0537 12.539L10.9921 11.6755C10.9467 11.0395 11.3869 10.4749 12.0033 10.3784L12.8385 10.2476C13.0817 10.2097 13.2776 10.0233 13.3325 9.77768L13.4848 9.09455C13.5398 8.8489 13.4425 8.59408 13.2393 8.45229L12.5422 7.96404C12.0279 7.60355 11.8708 6.89963 12.1814 6.34659L12.6025 5.59745C12.7249 5.3793 12.7047 5.10616 12.5511 4.9094L12.1241 4.36128C11.9706 4.16451 11.7149 4.08325 11.4795 4.15719L10.6719 4.41016C10.0752 4.59714 9.43903 4.28367 9.20962 3.69035L8.90017 2.88803C8.80937 2.65339 8.58777 2.4994 8.34108 2.5L7.65649 2.50184C7.40979 2.50244 7.1888 2.65766 7.09921 2.89291L6.79751 3.68607C6.57053 4.28307 5.93138 4.59898 5.33284 4.41077L4.49178 4.1468C4.25583 4.07225 3.99897 4.15413 3.84545 4.35212L3.42133 4.90084C3.26781 5.09943 3.2493 5.37319 3.37414 5.59133L3.80483 6.34232C4.12201 6.89591 3.96671 7.60659 3.44941 7.96897L2.76065 8.45169C2.55756 8.59408 2.4602 8.84891 2.51516 9.09393L2.66747 9.77708C2.72184 10.0233 2.91777 10.2097 3.16089 10.2476Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.41432 6.83576C8.63332 6.05481 7.36676 6.05476 6.58575 6.83571C5.8048 7.61672 5.80476 8.88327 6.58571 9.66427C7.36671 10.4452 8.63326 10.4452 9.41426 9.66432C10.1952 8.88332 10.1952 7.61676 9.41432 6.83576Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 450 B

After

Width:  |  Height:  |  Size: 450 B

View File

@@ -0,0 +1,8 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M7.72361 1.05279C7.893 1.13749 8 1.31062 8 1.5V13.5C8 13.6894 7.893 13.8625 7.72361 13.9472C7.55421 14.0319 7.35151 14.0136 7.2 13.9L3.33333 11H1.5C0.671573 11 0 10.3284 0 9.5V5.5C0 4.67158 0.671573 4 1.5 4H3.33333L7.2 1.1C7.35151 0.986371 7.55421 0.968093 7.72361 1.05279ZM7 2.5L3.8 4.9C3.71345 4.96491 3.60819 5 3.5 5H1.5C1.22386 5 1 5.22386 1 5.5V9.5C1 9.77614 1.22386 10 1.5 10H3.5C3.60819 10 3.71345 10.0351 3.8 10.1L7 12.5V2.5ZM14.8536 5.14645C15.0488 5.34171 15.0488 5.65829 14.8536 5.85355L13.2071 7.5L14.8536 9.14645C15.0488 9.34171 15.0488 9.65829 14.8536 9.85355C14.6583 10.0488 14.3417 10.0488 14.1464 9.85355L12.5 8.20711L10.8536 9.85355C10.6583 10.0488 10.3417 10.0488 10.1464 9.85355C9.95118 9.65829 9.95118 9.34171 10.1464 9.14645L11.7929 7.5L10.1464 5.85355C9.95118 5.65829 9.95118 5.34171 10.1464 5.14645C10.3417 4.95118 10.6583 4.95118 10.8536 5.14645L12.5 6.79289L14.1464 5.14645C14.3417 4.95118 14.6583 4.95118 14.8536 5.14645Z"
fill="currentColor"
/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

Before

Width:  |  Height:  |  Size: 345 B

After

Width:  |  Height:  |  Size: 345 B

View File

Before

Width:  |  Height:  |  Size: 610 B

After

Width:  |  Height:  |  Size: 610 B

View File

Before

Width:  |  Height:  |  Size: 947 B

After

Width:  |  Height:  |  Size: 947 B

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 238 B

After

Width:  |  Height:  |  Size: 238 B

View File

@@ -1,10 +0,0 @@
<svg width="96" height="96" viewBox="0 0 96 96" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1957_1318)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 6C7.34315 6 6 7.34315 6 9V75H0V9C0 4.02944 4.02944 0 9 0H89.3787C93.3878 0 95.3955 4.84715 92.5607 7.68198L43.0551 57.1875H57V51H63V58.6875C63 61.1728 60.9853 63.1875 58.5 63.1875H37.0551L26.7426 73.5H73.5V36H79.5V73.5C79.5 76.8137 76.8137 79.5 73.5 79.5H20.7426L10.2426 90H87C88.6569 90 90 88.6569 90 87V21H96V87C96 91.9706 91.9706 96 87 96H6.62132C2.61224 96 0.604504 91.1529 3.43934 88.318L52.7574 39H39V45H33V37.5C33 35.0147 35.0147 33 37.5 33H58.7574L69.2574 22.5H22.5V60H16.5V22.5C16.5 19.1863 19.1863 16.5 22.5 16.5H75.2574L85.7574 6H9Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_1957_1318">
<rect width="96" height="96" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 861 B

View File

@@ -1,14 +0,0 @@
<svg width="93" height="32" viewBox="0 0 93 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.03994 7.04962C8.00934 7.67635 7.30394 8.63219 7.30394 10.0149C7.30394 11.6908 7.72423 12.5893 8.20468 13.0744C8.68379 13.5581 9.40524 13.8149 10.4054 13.8149C11.815 13.8149 13.0291 13.5336 13.8802 12.9464C14.6756 12.3977 15.2708 11.5042 15.3438 9.96182C15.3991 8.79382 15.3678 8.01341 15.0568 7.45711C14.8094 7.01449 14.2326 6.47436 12.4901 6.27416C11.4684 6.15678 10.1114 6.39804 9.03994 7.04962ZM7.8731 5.13084C9.39145 4.2075 11.2531 3.87155 12.7464 4.04312C14.8843 4.28874 16.2844 5.05049 17.0171 6.36142C17.6863 7.55867 17.6384 8.98348 17.587 10.068C17.484 12.2439 16.5804 13.8118 15.1554 14.7949C13.7861 15.7396 12.0582 16.0606 10.4054 16.0606C9.04199 16.0606 7.65126 15.7069 6.60911 14.6547C5.5683 13.6038 5.05823 12.0408 5.05823 10.0149C5.05823 7.6958 6.31388 6.07903 7.8731 5.13084Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.983 18.2811C14.6595 18.2811 15.2079 18.8295 15.2079 19.506V22.16C15.2079 22.8365 14.6595 23.385 13.983 23.385C13.3065 23.385 12.758 22.8365 12.758 22.16V19.506C12.758 18.8295 13.3065 18.2811 13.983 18.2811Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.25567 14.8903C5.48362 14.039 5.99219 12.9059 7.20735 12.5361L7.92069 14.8798L7.92376 14.8785C7.92375 14.8785 7.92323 14.8778 7.92069 14.8798C7.91365 14.8852 7.89134 14.9052 7.85879 14.951C7.78817 15.0505 7.70058 15.2311 7.62217 15.524C7.46355 16.1164 7.40615 16.9168 7.40617 17.7813V22.1675C7.43457 22.3571 7.45083 22.438 7.48149 22.5189C7.50698 22.5862 7.55955 22.695 7.72277 22.8617C8.0938 23.2406 8.94056 23.8264 10.9275 24.7081C12.5399 25.4236 13.2749 25.7456 13.9586 25.9166C14.6053 26.0784 15.2382 26.1115 16.6666 26.1115V28.5613C15.2536 28.5613 14.3395 28.5372 13.3641 28.2932C12.452 28.0651 11.5174 27.6502 10.0614 27.004C10.0193 26.9853 9.9768 26.9664 9.93383 26.9474C7.89761 26.0438 6.6867 25.3053 5.97233 24.5757C5.59285 24.1882 5.34579 23.7967 5.19056 23.387C5.05419 23.0271 5.00551 22.6875 4.9751 22.4754C4.97301 22.4608 4.971 22.4468 4.96906 22.4335L4.9563 22.3458V17.7814C4.95628 16.8606 5.01201 15.8003 5.25567 14.8903Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.98306 15.3589C4.62844 14.6695 5.39976 13.9884 5.99652 13.6348L7.24552 15.7424C6.93706 15.9252 6.34938 16.4159 5.77155 17.0332C5.21994 17.6224 4.85695 18.1377 4.73071 18.4254C4.70277 18.6674 4.71195 19.116 4.78977 19.648C4.87834 20.2536 5.01731 20.7033 5.10492 20.8628L5.19261 21.0224L5.23006 21.2007C5.28661 21.4698 5.55952 21.8651 6.30146 22.3907C6.88143 22.8015 7.56502 23.1703 8.29605 23.5648C8.45794 23.6521 8.62215 23.7407 8.78809 23.8313C9.5952 24.2156 10.848 24.7773 12.0191 25.2425C12.613 25.4784 13.1698 25.6831 13.6288 25.8268C13.8584 25.8987 14.0505 25.9512 14.2021 25.9847C14.3503 26.0175 14.4185 26.0227 14.4277 26.0234C14.4288 26.0234 14.4281 26.0234 14.4277 26.0234L14.4287 28.4733C13.9646 28.4733 13.3889 28.3188 12.8968 28.1648C12.3562 27.9955 11.7367 27.7664 11.1147 27.5193C9.86871 27.0244 8.54832 26.4314 7.70298 26.028L7.67249 26.0135L7.64284 25.9973C7.49779 25.918 7.34502 25.8357 7.18702 25.7506C6.4505 25.354 5.60004 24.896 4.88539 24.3898C4.08363 23.8219 3.18153 23.0135 2.87437 21.8785C2.61828 21.3365 2.4557 20.618 2.3657 20.0026C2.26537 19.3167 2.2169 18.4951 2.33888 17.8727L2.35434 17.7938L2.37996 17.7176C2.6522 16.9085 3.36219 16.0221 3.98306 15.3589Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.48529 21.264V17.1402H5.93516V21.264H3.48529Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.2932 7.04962C25.3238 7.67635 26.0292 8.63219 26.0292 10.0149C26.0292 11.6908 25.609 12.5893 25.1285 13.0744C24.6494 13.5581 23.9279 13.8149 22.9278 13.8149C21.5182 13.8149 20.3041 13.5336 19.453 12.9464C18.6576 12.3977 18.0624 11.5042 17.9894 9.96182C17.9341 8.79382 17.9654 8.01341 18.2764 7.45711C18.5238 7.01449 19.1006 6.47436 20.8431 6.27416C21.8648 6.15678 23.2218 6.39804 24.2932 7.04962ZM25.4601 5.13084C23.9417 4.2075 22.0801 3.87155 20.5868 4.04312C18.4489 4.28874 17.0488 5.05049 16.3161 6.36142C15.6469 7.55867 15.6948 8.98348 15.7462 10.068C15.8492 12.2439 16.7528 13.8118 18.1778 14.7949C19.5471 15.7396 21.275 16.0606 22.9278 16.0606C24.2912 16.0606 25.6819 15.7069 26.7241 14.6547C27.7649 13.6038 28.275 12.0408 28.275 10.0149C28.275 7.6958 27.0193 6.07903 25.4601 5.13084Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.3502 18.2811C18.6737 18.2811 18.1253 18.8295 18.1253 19.506V22.16C18.1253 22.8365 18.6737 23.385 19.3502 23.385C20.0267 23.385 20.5752 22.8365 20.5752 22.16V19.506C20.5752 18.8295 20.0267 18.2811 19.3502 18.2811Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M28.0775 14.8903C27.8496 14.039 27.341 12.9059 26.1259 12.5361L25.4125 14.8798L25.4095 14.8785C25.4095 14.8785 25.41 14.8778 25.4125 14.8798C25.4196 14.8852 25.4419 14.9052 25.4744 14.951C25.545 15.0505 25.6326 15.2311 25.711 15.524C25.8697 16.1164 25.9271 16.9168 25.927 17.7813V22.1675C25.8986 22.3571 25.8824 22.438 25.8517 22.5189C25.8262 22.5862 25.7737 22.695 25.6104 22.8617C25.2394 23.2406 24.3927 23.8264 22.4057 24.7081C20.7933 25.4236 20.0583 25.7456 19.3746 25.9166C18.7279 26.0784 18.0949 26.1115 16.6666 26.1115V28.5613C18.0796 28.5613 18.9937 28.5372 19.9691 28.2932C20.8812 28.0651 21.8158 27.6502 23.2718 27.004C23.3139 26.9853 23.3564 26.9664 23.3994 26.9474C25.4356 26.0438 26.6465 25.3053 27.3609 24.5757C27.7404 24.1882 27.9874 23.7967 28.1427 23.387C28.279 23.0271 28.3277 22.6875 28.3581 22.4754C28.3602 22.4608 28.3622 22.4468 28.3642 22.4335L28.3769 22.3458V17.7814C28.3769 16.8606 28.3212 15.8003 28.0775 14.8903Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M29.3501 15.3589C28.7048 14.6695 27.9334 13.9884 27.3367 13.6348L26.0877 15.7424C26.3961 15.9252 26.9838 16.4159 27.5616 17.0332C28.1133 17.6224 28.4763 18.1377 28.6025 18.4254C28.6304 18.6674 28.6213 19.116 28.5434 19.648C28.4549 20.2536 28.3159 20.7033 28.2283 20.8628L28.1406 21.0224L28.1031 21.2007C28.0466 21.4698 27.7737 21.8651 27.0317 22.3907C26.4518 22.8015 25.7682 23.1703 25.0372 23.5648C24.8753 23.6521 24.711 23.7407 24.5451 23.8313C23.738 24.2156 22.4852 24.7773 21.3141 25.2425C20.7202 25.4784 20.1634 25.6831 19.7044 25.8268C19.4748 25.8987 19.2827 25.9512 19.1311 25.9847C18.9829 26.0175 18.9147 26.0227 18.9055 26.0234C18.9051 26.0234 18.9044 26.0234 18.9055 26.0234L18.9045 28.4733C19.3686 28.4733 19.9443 28.3188 20.4364 28.1648C20.977 27.9955 21.5965 27.7664 22.2185 27.5193C23.4645 27.0244 24.7849 26.4314 25.6302 26.028L25.6607 26.0135L25.6904 25.9973C25.8354 25.918 25.9882 25.8357 26.1462 25.7506C26.8827 25.354 27.7332 24.896 28.4478 24.3898C29.2496 23.8219 30.1517 23.0135 30.4588 21.8785C30.7149 21.3365 30.8775 20.618 30.9675 20.0026C31.0678 19.3167 31.1163 18.4951 30.9943 17.8727L30.9789 17.7938L30.9532 17.7176C30.681 16.9085 29.971 16.0221 29.3501 15.3589Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M29.8479 21.264V17.1402H27.398V21.264H29.8479Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M48.6666 11C49.2189 11 49.6666 11.4477 49.6666 12V15H52.6666C53.2189 15 53.6666 15.4477 53.6666 16C53.6666 16.5523 53.2189 17 52.6666 17H49.6666V20C49.6666 20.5523 49.2189 21 48.6666 21C48.1143 21 47.6666 20.5523 47.6666 20V17H44.6666C44.1143 17 43.6666 16.5523 43.6666 16C43.6666 15.4477 44.1143 15 44.6666 15H47.6666V12C47.6666 11.4477 48.1143 11 48.6666 11Z" fill="black" fill-opacity="0.5"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M67.1666 4.33329C66.7064 4.33329 66.3333 4.70639 66.3333 5.16663V23.5H64.6666V5.16663C64.6666 3.78591 65.7859 2.66663 67.1666 2.66663H89.494C90.6077 2.66663 91.1654 4.01306 90.3779 4.80051L76.6264 18.552H80.5V16.8333H82.1666V18.9687C82.1666 19.6591 81.607 20.2187 80.9166 20.2187H74.9597L72.0951 23.0833H85.0833V12.6666H86.75V23.0833C86.75 24.0038 86.0038 24.75 85.0833 24.75H70.4285L67.5118 27.6666H88.8333C89.2935 27.6666 89.6666 27.2935 89.6666 26.8333V8.49996H91.3333V26.8333C91.3333 28.214 90.214 29.3333 88.8333 29.3333H66.5059C65.3922 29.3333 64.8345 27.9869 65.622 27.1994L79.3214 13.5H75.5V15.1666H73.8333V13.0833C73.8333 12.3929 74.3929 11.8333 75.0833 11.8333H80.9881L83.9048 8.91663H70.9166V19.3333H69.25V8.91663C69.25 7.99615 69.9962 7.24996 70.9166 7.24996H85.5714L88.4881 4.33329H67.1666Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 8.2 KiB

View File

@@ -245,8 +245,6 @@
"bindings": {
"ctrl-pageup": "pane::ActivatePrevItem",
"ctrl-pagedown": "pane::ActivateNextItem",
"ctrl-shift-pageup": "pane::SwapItemLeft",
"ctrl-shift-pagedown": "pane::SwapItemRight",
"ctrl-w": "pane::CloseActiveItem",
"ctrl-f4": "pane::CloseActiveItem",
"alt-ctrl-t": "pane::CloseInactiveItems",
@@ -555,7 +553,6 @@
"ctrl-backspace": ["project_panel::Delete", { "skip_prompt": false }],
"ctrl-delete": ["project_panel::Delete", { "skip_prompt": false }],
"alt-ctrl-r": "project_panel::RevealInFileManager",
"ctrl-shift-enter": "project_panel::OpenWithSystem",
"alt-shift-f": "project_panel::NewSearchInDirectory",
"shift-down": "menu::SelectNext",
"shift-up": "menu::SelectPrev",

View File

@@ -285,8 +285,6 @@
"cmd-}": "pane::ActivateNextItem",
"alt-cmd-left": "pane::ActivatePrevItem",
"alt-cmd-right": "pane::ActivateNextItem",
"ctrl-shift-pageup": "pane::SwapItemLeft",
"ctrl-shift-pagedown": "pane::SwapItemRight",
"cmd-w": "pane::CloseActiveItem",
"alt-cmd-t": "pane::CloseInactiveItems",
"ctrl-alt-cmd-w": "workspace::CloseInactiveTabsAndPanes",
@@ -565,8 +563,8 @@
"cmd-backspace": ["project_panel::Trash", { "skip_prompt": true }],
"cmd-delete": ["project_panel::Delete", { "skip_prompt": false }],
"alt-cmd-r": "project_panel::RevealInFileManager",
"ctrl-shift-enter": "project_panel::OpenWithSystem",
"cmd-alt-backspace": ["project_panel::Delete", { "skip_prompt": false }],
"alt-shift-f": "project_panel::NewSearchInDirectory",
"shift-down": "menu::SelectNext",
"shift-up": "menu::SelectPrev",

View File

@@ -214,7 +214,7 @@
"shift-d": "vim::DeleteToEndOfLine",
"shift-j": "vim::JoinLines",
"y": ["vim::PushOperator", "Yank"],
"shift-y": "vim::YankLine",
"shift-y": "vim::YankToEndOfLine",
"i": "vim::InsertBefore",
"shift-i": "vim::InsertFirstNonWhitespace",
"a": "vim::InsertAfter",
@@ -323,14 +323,9 @@
"ctrl-t": "vim::Indent",
"ctrl-d": "vim::Outdent",
"ctrl-k": ["vim::PushOperator", { "Digraph": {} }],
"ctrl-r": ["vim::PushOperator", "Register"]
}
},
{
"context": "vim_mode == insert && !(showing_code_actions || showing_completions)",
"bindings": {
"ctrl-p": "editor::ShowCompletions",
"ctrl-n": "editor::ShowCompletions"
"ctrl-n": "editor::ShowCompletions",
"ctrl-r": ["vim::PushOperator", "Register"]
}
},
{
@@ -493,7 +488,6 @@
"v": "project_panel::OpenPermanent",
"p": "project_panel::Open",
"x": "project_panel::RevealInFileManager",
"s": "project_panel::OpenWithSystem",
"shift-g": "menu::SelectLast",
"g g": "menu::SelectFirst",
"-": "project_panel::SelectParent",

View File

@@ -279,13 +279,6 @@
"relative_line_numbers": false,
// If 'search_wrap' is disabled, search result do not wrap around the end of the file.
"search_wrap": true,
// Search options to enable by default when opening new project and buffer searches.
"search": {
"whole_word": false,
"case_sensitive": false,
"include_ignored": false,
"regex": false
},
// When to populate a new search's query based on the text under the cursor.
// This setting can take the following three values:
//
@@ -916,8 +909,7 @@
},
"openai": {
"version": "1",
"api_url": "https://api.openai.com/v1",
"low_speed_timeout_in_seconds": 600
"api_url": "https://api.openai.com/v1"
}
},
// Zed's Prettier integration settings.

View File

@@ -1 +0,0 @@
allow-private-module-inception = true

View File

@@ -262,7 +262,7 @@ impl ActivityIndicator {
if !failed.is_empty() {
return Some(Content {
icon: Some(
Icon::new(IconName::Warning)
Icon::new(IconName::ExclamationTriangle)
.size(IconSize::Small)
.into_any_element(),
),
@@ -280,7 +280,7 @@ impl ActivityIndicator {
if let Some(failure) = self.project.read(cx).last_formatting_failure() {
return Some(Content {
icon: Some(
Icon::new(IconName::Warning)
Icon::new(IconName::ExclamationTriangle)
.size(IconSize::Small)
.into_any_element(),
),
@@ -333,7 +333,7 @@ impl ActivityIndicator {
}),
AutoUpdateStatus::Errored => Some(Content {
icon: Some(
Icon::new(IconName::Warning)
Icon::new(IconName::ExclamationTriangle)
.size(IconSize::Small)
.into_any_element(),
),

View File

@@ -8,7 +8,6 @@ use rust_embed::RustEmbed;
#[folder = "../../assets"]
#[include = "fonts/**/*"]
#[include = "icons/**/*"]
#[include = "images/**/*"]
#[include = "themes/**/*"]
#[exclude = "themes/src/*"]
#[include = "sounds/**/*"]

View File

@@ -37,13 +37,13 @@ use language_model::{
pub(crate) use model_selector::*;
pub use prompts::PromptBuilder;
use prompts::PromptLoadingParams;
use semantic_index::{CloudEmbeddingProvider, SemanticDb};
use semantic_index::{CloudEmbeddingProvider, SemanticIndex};
use serde::{Deserialize, Serialize};
use settings::{update_settings_file, Settings, SettingsStore};
use slash_command::{
auto_command, context_server_command, default_command, diagnostics_command, docs_command,
fetch_command, file_command, now_command, project_command, prompt_command, search_command,
symbols_command, tab_command, terminal_command, workflow_command,
context_server_command, default_command, diagnostics_command, docs_command, fetch_command,
file_command, now_command, project_command, prompt_command, search_command, symbols_command,
tab_command, terminal_command, workflow_command,
};
use std::path::PathBuf;
use std::sync::Arc;
@@ -210,13 +210,12 @@ pub fn init(
let client = client.clone();
async move {
let embedding_provider = CloudEmbeddingProvider::new(client.clone());
let semantic_index = SemanticDb::new(
let semantic_index = SemanticIndex::new(
paths::embeddings_dir().join("semantic-index-db.0.mdb"),
Arc::new(embedding_provider),
&mut cx,
)
.await?;
cx.update(|cx| cx.set_global(semantic_index))
}
})
@@ -365,7 +364,6 @@ fn update_active_language_model_from_settings(cx: &mut AppContext) {
fn register_slash_commands(prompt_builder: Option<Arc<PromptBuilder>>, cx: &mut AppContext) {
let slash_command_registry = SlashCommandRegistry::global(cx);
slash_command_registry.register_command(file_command::FileSlashCommand, true);
slash_command_registry.register_command(symbols_command::OutlineSlashCommand, true);
slash_command_registry.register_command(tab_command::TabSlashCommand, true);
@@ -384,17 +382,6 @@ fn register_slash_commands(prompt_builder: Option<Arc<PromptBuilder>>, cx: &mut
}
slash_command_registry.register_command(fetch_command::FetchSlashCommand, false);
cx.observe_flag::<auto_command::AutoSlashCommandFeatureFlag, _>({
let slash_command_registry = slash_command_registry.clone();
move |is_enabled, _cx| {
if is_enabled {
// [#auto-staff-ship] TODO remove this when /auto is no longer staff-shipped
slash_command_registry.register_command(auto_command::AutoCommand, true);
}
}
})
.detach();
update_slash_commands_from_settings(cx);
cx.observe_global::<SettingsStore>(update_slash_commands_from_settings)
.detach();

View File

@@ -11,7 +11,7 @@ use crate::{
},
slash_command_picker,
terminal_inline_assistant::TerminalInlineAssistant,
Assist, CacheStatus, ConfirmCommand, Content, Context, ContextEvent, ContextId, ContextStore,
Assist, CacheStatus, ConfirmCommand, Context, ContextEvent, ContextId, ContextStore,
ContextStoreEvent, CycleMessageRole, DeployHistory, DeployPromptLibrary, InlineAssistId,
InlineAssistant, InsertDraggedFiles, InsertIntoEditor, Message, MessageId, MessageMetadata,
MessageStatus, ModelPickerDelegate, ModelSelector, NewContext, PendingSlashCommand,
@@ -26,15 +26,14 @@ use collections::{BTreeSet, HashMap, HashSet};
use editor::{
actions::{FoldAt, MoveToEndOfLine, Newline, ShowCompletions, UnfoldAt},
display_map::{
BlockDisposition, BlockId, BlockProperties, BlockStyle, Crease, CreaseMetadata,
CustomBlockId, FoldId, RenderBlock, ToDisplayPoint,
BlockDisposition, BlockId, BlockProperties, BlockStyle, Crease, CustomBlockId, FoldId,
RenderBlock, ToDisplayPoint,
},
scroll::{Autoscroll, AutoscrollStrategy, ScrollAnchor},
Anchor, Editor, EditorEvent, ExcerptRange, MultiBuffer, RowExt, ToOffset as _, ToPoint,
};
use editor::{display_map::CreaseId, FoldPlaceholder};
use fs::Fs;
use futures::FutureExt;
use gpui::{
canvas, div, img, percentage, point, pulsating_between, size, Action, Animation, AnimationExt,
AnyElement, AnyView, AppContext, AsyncWindowContext, ClipboardEntry, ClipboardItem,
@@ -51,19 +50,17 @@ use language_model::{
provider::cloud::PROVIDER_ID, LanguageModelProvider, LanguageModelProviderId,
LanguageModelRegistry, Role,
};
use language_model::{LanguageModelImage, LanguageModelToolUse};
use multi_buffer::MultiBufferRow;
use picker::{Picker, PickerDelegate};
use project::lsp_store::ProjectLspAdapterDelegate;
use project::{Project, Worktree};
use project::{Project, ProjectLspAdapterDelegate, Worktree};
use search::{buffer_search::DivRegistrar, BufferSearchBar};
use serde::{Deserialize, Serialize};
use settings::{update_settings_file, Settings};
use smol::stream::StreamExt;
use std::{
borrow::Cow,
cmp,
collections::hash_map,
fmt::Write,
ops::{ControlFlow, Range},
path::PathBuf,
sync::Arc,
@@ -332,7 +329,7 @@ impl AssistantPanel {
cx: &mut ViewContext<Self>,
) -> Self {
let model_selector_menu_handle = PopoverMenuHandle::default();
let model_summary_editor = cx.new_view(Editor::single_line);
let model_summary_editor = cx.new_view(|cx| Editor::single_line(cx));
let context_editor_toolbar = cx.new_view(|_| {
ContextEditorToolbarItem::new(
workspace,
@@ -344,7 +341,7 @@ impl AssistantPanel {
let pane = cx.new_view(|cx| {
let mut pane = Pane::new(
workspace.weak_handle(),
workspace.project().clone(),
workspace.project().downgrade(),
Default::default(),
None,
NewContext.boxed_clone(),
@@ -942,16 +939,9 @@ impl AssistantPanel {
cx: &mut ViewContext<Workspace>,
) {
if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {
let did_create_context = panel
.update(cx, |panel, cx| {
panel.new_context(cx)?;
Some(())
})
.is_some();
if did_create_context {
ContextEditor::quote_selection(workspace, &Default::default(), cx);
}
panel.update(cx, |panel, cx| {
panel.new_context(cx);
});
}
}
@@ -1107,7 +1097,7 @@ impl AssistantPanel {
pane.activate_item(configuration_item_ix, true, true, cx);
});
} else {
let configuration = cx.new_view(ConfigurationView::new);
let configuration = cx.new_view(|cx| ConfigurationView::new(cx));
self.configuration_subscription = Some(cx.subscribe(
&configuration,
|this, _, event: &ConfigurationViewEvent, cx| match event {
@@ -1998,20 +1988,6 @@ impl ContextEditor {
let buffer_row = MultiBufferRow(start.to_point(&buffer).row);
buffer_rows_to_fold.insert(buffer_row);
self.context.update(cx, |context, cx| {
context.insert_content(
Content::ToolUse {
range: tool_use.source_range.clone(),
tool_use: LanguageModelToolUse {
id: tool_use.id.to_string(),
name: tool_use.name.clone(),
input: tool_use.input.clone(),
},
},
cx,
);
});
Crease::new(
start..end,
placeholder,
@@ -2515,26 +2491,20 @@ impl ContextEditor {
.unwrap();
let buffer_row = MultiBufferRow(start.to_point(&buffer).row);
buffer_rows_to_fold.insert(buffer_row);
creases.push(
Crease::new(
start..end,
FoldPlaceholder {
render: render_fold_icon_button(
cx.view().downgrade(),
section.icon,
section.label.clone(),
),
constrain_width: false,
merge_adjacent: false,
},
render_slash_command_output_toggle,
|_, _, _| Empty.into_any_element(),
)
.with_metadata(CreaseMetadata {
icon: section.icon,
label: section.label,
}),
);
creases.push(Crease::new(
start..end,
FoldPlaceholder {
render: render_fold_icon_button(
cx.view().downgrade(),
section.icon,
section.label.clone(),
),
constrain_width: false,
merge_adjacent: false,
},
render_slash_command_output_toggle,
|_, _, _| Empty.into_any_element(),
));
}
editor.insert_creases(creases, cx);
@@ -3210,93 +3180,87 @@ impl ContextEditor {
return;
};
let mut creases = vec![];
editor.update(cx, |editor, cx| {
let selections = editor.selections.all_adjusted(cx);
let buffer = editor.buffer().read(cx).snapshot(cx);
for selection in selections {
let range = editor::ToOffset::to_offset(&selection.start, &buffer)
..editor::ToOffset::to_offset(&selection.end, &buffer);
let selected_text = buffer.text_for_range(range.clone()).collect::<String>();
if selected_text.is_empty() {
continue;
}
let start_language = buffer.language_at(range.start);
let end_language = buffer.language_at(range.end);
let language_name = if start_language == end_language {
start_language.map(|language| language.code_fence_block_name())
let selection = editor.update(cx, |editor, cx| editor.selections.newest_adjusted(cx));
let editor = editor.read(cx);
let buffer = editor.buffer().read(cx).snapshot(cx);
let range = editor::ToOffset::to_offset(&selection.start, &buffer)
..editor::ToOffset::to_offset(&selection.end, &buffer);
let selected_text = buffer.text_for_range(range.clone()).collect::<String>();
if selected_text.is_empty() {
return;
}
let start_language = buffer.language_at(range.start);
let end_language = buffer.language_at(range.end);
let language_name = if start_language == end_language {
start_language.map(|language| language.code_fence_block_name())
} else {
None
};
let language_name = language_name.as_deref().unwrap_or("");
let filename = buffer
.file_at(selection.start)
.map(|file| file.full_path(cx));
let text = if language_name == "markdown" {
selected_text
.lines()
.map(|line| format!("> {}", line))
.collect::<Vec<_>>()
.join("\n")
} else {
let start_symbols = buffer
.symbols_containing(selection.start, None)
.map(|(_, symbols)| symbols);
let end_symbols = buffer
.symbols_containing(selection.end, None)
.map(|(_, symbols)| symbols);
let outline_text =
if let Some((start_symbols, end_symbols)) = start_symbols.zip(end_symbols) {
Some(
start_symbols
.into_iter()
.zip(end_symbols)
.take_while(|(a, b)| a == b)
.map(|(a, _)| a.text)
.collect::<Vec<_>>()
.join(" > "),
)
} else {
None
};
let language_name = language_name.as_deref().unwrap_or("");
let filename = buffer
.file_at(selection.start)
.map(|file| file.full_path(cx));
let text = if language_name == "markdown" {
selected_text
.lines()
.map(|line| format!("> {}", line))
.collect::<Vec<_>>()
.join("\n")
} else {
let start_symbols = buffer
.symbols_containing(selection.start, None)
.map(|(_, symbols)| symbols);
let end_symbols = buffer
.symbols_containing(selection.end, None)
.map(|(_, symbols)| symbols);
let outline_text = if let Some((start_symbols, end_symbols)) =
start_symbols.zip(end_symbols)
{
Some(
start_symbols
.into_iter()
.zip(end_symbols)
.take_while(|(a, b)| a == b)
.map(|(a, _)| a.text)
.collect::<Vec<_>>()
.join(" > "),
)
} else {
None
};
let line_comment_prefix = start_language
.and_then(|l| l.default_scope().line_comment_prefixes().first().cloned());
let line_comment_prefix = start_language
.and_then(|l| l.default_scope().line_comment_prefixes().first().cloned());
let fence = codeblock_fence_for_path(
filename.as_deref(),
Some(selection.start.row..selection.end.row),
);
let fence = codeblock_fence_for_path(
filename.as_deref(),
Some(selection.start.row..selection.end.row),
);
if let Some((line_comment_prefix, outline_text)) =
line_comment_prefix.zip(outline_text)
{
let breadcrumb =
format!("{line_comment_prefix}Excerpt from: {outline_text}\n");
format!("{fence}{breadcrumb}{selected_text}\n```")
} else {
format!("{fence}{selected_text}\n```")
}
};
let crease_title = if let Some(path) = filename {
let start_line = selection.start.row + 1;
let end_line = selection.end.row + 1;
if start_line == end_line {
format!("{}, Line {}", path.display(), start_line)
} else {
format!("{}, Lines {} to {}", path.display(), start_line, end_line)
}
} else {
"Quoted selection".to_string()
};
creases.push((text, crease_title));
if let Some((line_comment_prefix, outline_text)) = line_comment_prefix.zip(outline_text)
{
let breadcrumb = format!("{line_comment_prefix}Excerpt from: {outline_text}\n");
format!("{fence}{breadcrumb}{selected_text}\n```")
} else {
format!("{fence}{selected_text}\n```")
}
});
if creases.is_empty() {
return;
}
};
let crease_title = if let Some(path) = filename {
let start_line = selection.start.row + 1;
let end_line = selection.end.row + 1;
if start_line == end_line {
format!("{}, Line {}", path.display(), start_line)
} else {
format!("{}, Lines {} to {}", path.display(), start_line, end_line)
}
} else {
"Quoted selection".to_string()
};
// Activate the panel
if !panel.focus_handle(cx).contains_focused(cx) {
workspace.toggle_panel_focus::<AssistantPanel>(cx);
@@ -3313,40 +3277,39 @@ impl ContextEditor {
context.update(cx, |context, cx| {
context.editor.update(cx, |editor, cx| {
editor.insert("\n", cx);
for (text, crease_title) in creases {
let point = editor.selections.newest::<Point>(cx).head();
let start_row = MultiBufferRow(point.row);
editor.insert(&text, cx);
let point = editor.selections.newest::<Point>(cx).head();
let start_row = MultiBufferRow(point.row);
let snapshot = editor.buffer().read(cx).snapshot(cx);
let anchor_before = snapshot.anchor_after(point);
let anchor_after = editor
.selections
.newest_anchor()
.head()
.bias_left(&snapshot);
editor.insert(&text, cx);
editor.insert("\n", cx);
let snapshot = editor.buffer().read(cx).snapshot(cx);
let anchor_before = snapshot.anchor_after(point);
let anchor_after = editor
.selections
.newest_anchor()
.head()
.bias_left(&snapshot);
let fold_placeholder = quote_selection_fold_placeholder(
crease_title,
cx.view().downgrade(),
);
let crease = Crease::new(
anchor_before..anchor_after,
fold_placeholder,
render_quote_selection_output_toggle,
|_, _, _| Empty.into_any(),
);
editor.insert_creases(vec![crease], cx);
editor.fold_at(
&FoldAt {
buffer_row: start_row,
},
cx,
);
}
editor.insert("\n", cx);
let fold_placeholder = quote_selection_fold_placeholder(
crease_title,
cx.view().downgrade(),
);
let crease = Crease::new(
anchor_before..anchor_after,
fold_placeholder,
render_quote_selection_output_toggle,
|_, _, _| Empty.into_any(),
);
editor.insert_creases(vec![crease], cx);
editor.fold_at(
&FoldAt {
buffer_row: start_row,
},
cx,
);
})
});
};
@@ -3355,113 +3318,39 @@ impl ContextEditor {
}
fn copy(&mut self, _: &editor::actions::Copy, cx: &mut ViewContext<Self>) {
if self.editor.read(cx).selections.count() == 1 {
let (copied_text, metadata) = self.get_clipboard_contents(cx);
cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
copied_text,
metadata,
));
cx.stop_propagation();
return;
}
cx.propagate();
}
fn cut(&mut self, _: &editor::actions::Cut, cx: &mut ViewContext<Self>) {
if self.editor.read(cx).selections.count() == 1 {
let (copied_text, metadata) = self.get_clipboard_contents(cx);
self.editor.update(cx, |editor, cx| {
let selections = editor.selections.all::<Point>(cx);
editor.transact(cx, |this, cx| {
this.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.select(selections);
});
this.insert("", cx);
cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
copied_text,
metadata,
));
});
});
cx.stop_propagation();
return;
}
cx.propagate();
}
fn get_clipboard_contents(&mut self, cx: &mut ViewContext<Self>) -> (String, CopyMetadata) {
let creases = self.editor.update(cx, |editor, cx| {
let selection = editor.selections.newest::<Point>(cx);
let selection_start = editor.selections.newest::<usize>(cx).start;
let snapshot = editor.buffer().read(cx).snapshot(cx);
editor.display_map.update(cx, |display_map, cx| {
display_map
.snapshot(cx)
.crease_snapshot
.creases_in_range(
MultiBufferRow(selection.start.row)..MultiBufferRow(selection.end.row + 1),
&snapshot,
)
.filter_map(|crease| {
if let Some(metadata) = &crease.metadata {
let start = crease
.range
.start
.to_offset(&snapshot)
.saturating_sub(selection_start);
let end = crease
.range
.end
.to_offset(&snapshot)
.saturating_sub(selection_start);
let range_relative_to_selection = start..end;
if range_relative_to_selection.is_empty() {
None
} else {
Some(SelectedCreaseMetadata {
range_relative_to_selection,
crease: metadata.clone(),
})
}
} else {
None
}
})
.collect::<Vec<_>>()
})
});
let editor = self.editor.read(cx);
let context = self.context.read(cx);
let selection = self.editor.read(cx).selections.newest::<usize>(cx);
let mut text = String::new();
for message in context.messages(cx) {
if message.offset_range.start >= selection.range().end {
break;
} else if message.offset_range.end >= selection.range().start {
let range = cmp::max(message.offset_range.start, selection.range().start)
..cmp::min(message.offset_range.end, selection.range().end);
if !range.is_empty() {
for chunk in context.buffer().read(cx).text_for_range(range) {
text.push_str(chunk);
if editor.selections.count() == 1 {
let selection = editor.selections.newest::<usize>(cx);
let mut copied_text = String::new();
let mut spanned_messages = 0;
for message in context.messages(cx) {
if message.offset_range.start >= selection.range().end {
break;
} else if message.offset_range.end >= selection.range().start {
let range = cmp::max(message.offset_range.start, selection.range().start)
..cmp::min(message.offset_range.end, selection.range().end);
if !range.is_empty() {
spanned_messages += 1;
write!(&mut copied_text, "## {}\n\n", message.role).unwrap();
for chunk in context.buffer().read(cx).text_for_range(range) {
copied_text.push_str(chunk);
}
copied_text.push('\n');
}
text.push('\n');
}
}
if spanned_messages > 1 {
cx.write_to_clipboard(ClipboardItem::new_string(copied_text));
return;
}
}
(text, CopyMetadata { creases })
cx.propagate();
}
fn paste(&mut self, action: &editor::actions::Paste, cx: &mut ViewContext<Self>) {
cx.stop_propagation();
fn paste(&mut self, _: &editor::actions::Paste, cx: &mut ViewContext<Self>) {
let images = if let Some(item) = cx.read_from_clipboard() {
item.into_entries()
.filter_map(|entry| {
@@ -3476,62 +3365,9 @@ impl ContextEditor {
Vec::new()
};
let metadata = if let Some(item) = cx.read_from_clipboard() {
item.entries().first().and_then(|entry| {
if let ClipboardEntry::String(text) = entry {
text.metadata_json::<CopyMetadata>()
} else {
None
}
})
} else {
None
};
if images.is_empty() {
self.editor.update(cx, |editor, cx| {
let paste_position = editor.selections.newest::<usize>(cx).head();
editor.paste(action, cx);
if let Some(metadata) = metadata {
let buffer = editor.buffer().read(cx).snapshot(cx);
let mut buffer_rows_to_fold = BTreeSet::new();
let weak_editor = cx.view().downgrade();
editor.insert_creases(
metadata.creases.into_iter().map(|metadata| {
let start = buffer.anchor_after(
paste_position + metadata.range_relative_to_selection.start,
);
let end = buffer.anchor_before(
paste_position + metadata.range_relative_to_selection.end,
);
let buffer_row = MultiBufferRow(start.to_point(&buffer).row);
buffer_rows_to_fold.insert(buffer_row);
Crease::new(
start..end,
FoldPlaceholder {
constrain_width: false,
render: render_fold_icon_button(
weak_editor.clone(),
metadata.crease.icon,
metadata.crease.label.clone(),
),
merge_adjacent: false,
},
render_slash_command_output_toggle,
|_, _, _| Empty.into_any(),
)
.with_metadata(metadata.crease.clone())
}),
cx,
);
for buffer_row in buffer_rows_to_fold.into_iter().rev() {
editor.fold_at(&FoldAt { buffer_row }, cx);
}
}
});
// If we didn't find any valid image data to paste, propagate to let normal pasting happen.
cx.propagate();
} else {
let mut image_positions = Vec::new();
self.editor.update(cx, |editor, cx| {
@@ -3552,22 +3388,10 @@ impl ContextEditor {
self.context.update(cx, |context, cx| {
for image in images {
let Some(render_image) = image.to_image_data(cx).log_err() else {
continue;
};
let image_id = image.id();
let image_task = LanguageModelImage::from_image(image, cx).shared();
context.insert_image(image, cx);
for image_position in image_positions.iter() {
context.insert_content(
Content::Image {
anchor: image_position.text_anchor,
image_id,
image: image_task.clone(),
render_image: render_image.clone(),
},
cx,
);
context.insert_image_anchor(image_id, image_position.text_anchor, cx);
}
}
});
@@ -3582,23 +3406,11 @@ impl ContextEditor {
let new_blocks = self
.context
.read(cx)
.contents(cx)
.filter_map(|content| {
if let Content::Image {
anchor,
render_image,
..
} = content
{
Some((anchor, render_image))
} else {
None
}
})
.filter_map(|(anchor, render_image)| {
.images(cx)
.filter_map(|image| {
const MAX_HEIGHT_IN_LINES: u32 = 8;
let anchor = buffer.anchor_in_excerpt(excerpt_id, anchor).unwrap();
let image = render_image.clone();
let anchor = buffer.anchor_in_excerpt(excerpt_id, image.anchor).unwrap();
let image = image.render_image.clone();
anchor.is_valid(&buffer).then(|| BlockProperties {
position: anchor,
height: MAX_HEIGHT_IN_LINES,
@@ -4110,7 +3922,7 @@ impl ContextEditor {
h_flex()
.gap_3()
.child(
Icon::new(IconName::Warning)
Icon::new(IconName::ExclamationTriangle)
.size(IconSize::Small)
.color(Color::Warning),
)
@@ -4225,17 +4037,6 @@ fn render_fold_icon_button(
})
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct CopyMetadata {
creases: Vec<SelectedCreaseMetadata>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct SelectedCreaseMetadata {
range_relative_to_selection: Range<usize>,
crease: CreaseMetadata,
}
impl EventEmitter<EditorEvent> for ContextEditor {}
impl EventEmitter<SearchEvent> for ContextEditor {}
@@ -4261,7 +4062,6 @@ impl Render for ContextEditor {
.capture_action(cx.listener(ContextEditor::cancel))
.capture_action(cx.listener(ContextEditor::save))
.capture_action(cx.listener(ContextEditor::copy))
.capture_action(cx.listener(ContextEditor::cut))
.capture_action(cx.listener(ContextEditor::paste))
.capture_action(cx.listener(ContextEditor::cycle_message_role))
.capture_action(cx.listener(ContextEditor::confirm_command))
@@ -4422,7 +4222,8 @@ impl Item for ContextEditor {
}
fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
self.editor.update(cx, Item::deactivated)
self.editor
.update(cx, |editor, cx| Item::deactivated(editor, cx))
}
}
@@ -4723,20 +4524,6 @@ impl Render for ContextEditorToolbarItem {
let weak_self = cx.view().downgrade();
let right_side = h_flex()
.gap_2()
// TODO display this in a nicer way, once we have a design for it.
// .children({
// let project = self
// .workspace
// .upgrade()
// .map(|workspace| workspace.read(cx).project().downgrade());
//
// let scan_items_remaining = cx.update_global(|db: &mut SemanticDb, cx| {
// project.and_then(|project| db.remaining_summaries(&project, cx))
// });
// scan_items_remaining
// .map(|remaining_items| format!("Files to scan: {}", remaining_items))
// })
.child(
ModelSelector::new(
self.fs.clone(),
@@ -5235,7 +5022,7 @@ fn quote_selection_fold_placeholder(title: String, editor: WeakView<Editor>) ->
ButtonLike::new(fold_id)
.style(ButtonStyle::Filled)
.layer(ElevationIndex::ElevatedSurface)
.child(Icon::new(IconName::CursorIBeam))
.child(Icon::new(IconName::TextSelect))
.child(Label::new(title.clone()).single_line())
.on_click(move |_, cx| {
editor
@@ -5339,7 +5126,7 @@ fn render_docs_slash_command_trailer(
div()
.id(("latest-error", row.0))
.child(
Icon::new(IconName::Warning)
Icon::new(IconName::ExclamationTriangle)
.size(IconSize::Small)
.color(Color::Warning),
)
@@ -5368,17 +5155,9 @@ fn make_lsp_adapter_delegate(
.worktrees(cx)
.next()
.ok_or_else(|| anyhow!("no worktrees when constructing ProjectLspAdapterDelegate"))?;
let fs = if project.is_local() {
Some(project.fs().clone())
} else {
None
};
let http_client = project.client().http_client().clone();
project.lsp_store().update(cx, |lsp_store, cx| {
Ok(
ProjectLspAdapterDelegate::new(lsp_store, &worktree, http_client, fs, None, cx)
as Arc<dyn LspAdapterDelegate>,
)
Ok(ProjectLspAdapterDelegate::new(lsp_store, &worktree, cx)
as Arc<dyn LspAdapterDelegate>)
})
})
}

View File

@@ -160,16 +160,12 @@ impl AssistantSettingsContent {
.filter_map(|model| match model {
OpenAiModel::Custom {
name,
display_name,
max_tokens,
max_output_tokens,
max_completion_tokens: None,
} => Some(open_ai::AvailableModel {
name,
display_name,
max_tokens,
max_output_tokens,
max_completion_tokens: None,
}),
_ => None,
})
@@ -521,7 +517,6 @@ impl Settings for AssistantSettings {
&mut settings.default_model,
value.default_model.map(Into::into),
);
// merge(&mut settings.infer_context, value.infer_context); TODO re-enable this once we ship context inference
}
Ok(settings)

View File

@@ -17,27 +17,28 @@ use feature_flags::{FeatureFlag, FeatureFlagAppExt};
use fs::{Fs, RemoveOptions};
use futures::{
future::{self, Shared},
stream::FuturesUnordered,
FutureExt, StreamExt,
};
use gpui::{
AppContext, AsyncAppContext, Context as _, EventEmitter, Model, ModelContext, RenderImage,
SharedString, Subscription, Task,
AppContext, AsyncAppContext, Context as _, EventEmitter, Image, Model, ModelContext,
RenderImage, SharedString, Subscription, Task,
};
use language::{AnchorRangeExt, Bias, Buffer, LanguageRegistry, OffsetRangeExt, Point, ToOffset};
use language_model::{
LanguageModel, LanguageModelCacheConfiguration, LanguageModelCompletionEvent,
LanguageModelImage, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage,
LanguageModelRequestTool, LanguageModelToolResult, LanguageModelToolUse, MessageContent, Role,
StopReason,
LanguageModelRequestTool, MessageContent, Role, StopReason,
};
use open_ai::Model as OpenAiModel;
use paths::contexts_dir;
use paths::{context_images_dir, contexts_dir};
use project::Project;
use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
use std::{
cmp::{self, max, Ordering},
collections::hash_map,
fmt::Debug,
iter, mem,
ops::Range,
@@ -48,7 +49,7 @@ use std::{
};
use telemetry_events::AssistantKind;
use text::BufferSnapshot;
use util::{post_inc, TryFutureExt};
use util::{post_inc, ResultExt, TryFutureExt};
use uuid::Uuid;
#[derive(Clone, Eq, PartialEq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
@@ -376,8 +377,23 @@ impl MessageMetadata {
}
}
#[derive(Clone, Debug)]
pub struct MessageImage {
image_id: u64,
image: Shared<Task<Option<LanguageModelImage>>>,
}
impl PartialEq for MessageImage {
fn eq(&self, other: &Self) -> bool {
self.image_id == other.image_id
}
}
impl Eq for MessageImage {}
#[derive(Clone, Debug)]
pub struct Message {
pub image_offsets: SmallVec<[(usize, MessageImage); 1]>,
pub offset_range: Range<usize>,
pub index_range: Range<usize>,
pub anchor_range: Range<language::Anchor>,
@@ -387,43 +403,60 @@ pub struct Message {
pub cache: Option<MessageCacheMetadata>,
}
#[derive(Debug, Clone)]
pub enum Content {
Image {
anchor: language::Anchor,
image_id: u64,
render_image: Arc<RenderImage>,
image: Shared<Task<Option<LanguageModelImage>>>,
},
ToolUse {
range: Range<language::Anchor>,
tool_use: LanguageModelToolUse,
},
ToolResult {
range: Range<language::Anchor>,
tool_use_id: Arc<str>,
},
impl Message {
fn to_request_message(&self, buffer: &Buffer) -> Option<LanguageModelRequestMessage> {
let mut content = Vec::new();
let mut range_start = self.offset_range.start;
for (image_offset, message_image) in self.image_offsets.iter() {
if *image_offset != range_start {
if let Some(text) = Self::collect_text_content(buffer, range_start..*image_offset) {
content.push(text);
}
}
if let Some(image) = message_image.image.clone().now_or_never().flatten() {
content.push(language_model::MessageContent::Image(image));
}
range_start = *image_offset;
}
if range_start != self.offset_range.end {
if let Some(text) =
Self::collect_text_content(buffer, range_start..self.offset_range.end)
{
content.push(text);
}
}
if content.is_empty() {
return None;
}
Some(LanguageModelRequestMessage {
role: self.role,
content,
cache: self.cache.as_ref().map_or(false, |cache| cache.is_anchor),
})
}
fn collect_text_content(buffer: &Buffer, range: Range<usize>) -> Option<MessageContent> {
let text: String = buffer.text_for_range(range.clone()).collect();
if text.trim().is_empty() {
None
} else {
Some(MessageContent::Text(text))
}
}
}
impl Content {
fn range(&self) -> Range<language::Anchor> {
match self {
Self::Image { anchor, .. } => *anchor..*anchor,
Self::ToolUse { range, .. } | Self::ToolResult { range, .. } => range.clone(),
}
}
fn cmp(&self, other: &Self, buffer: &BufferSnapshot) -> Ordering {
let self_range = self.range();
let other_range = other.range();
if self_range.end.cmp(&other_range.start, buffer).is_lt() {
Ordering::Less
} else if self_range.start.cmp(&other_range.end, buffer).is_gt() {
Ordering::Greater
} else {
Ordering::Equal
}
}
#[derive(Clone, Debug)]
pub struct ImageAnchor {
pub anchor: language::Anchor,
pub image_id: u64,
pub render_image: Arc<RenderImage>,
pub image: Shared<Task<Option<LanguageModelImage>>>,
}
struct PendingCompletion {
@@ -467,7 +500,8 @@ pub struct Context {
slash_command_output_sections: Vec<SlashCommandOutputSection<language::Anchor>>,
pending_tool_uses_by_id: HashMap<Arc<str>, PendingToolUse>,
message_anchors: Vec<MessageAnchor>,
contents: Vec<Content>,
images: HashMap<u64, (Arc<RenderImage>, Shared<Task<Option<LanguageModelImage>>>)>,
image_anchors: Vec<ImageAnchor>,
messages_metadata: HashMap<MessageId, MessageMetadata>,
summary: Option<ContextSummary>,
pending_summary: Task<Option<()>>,
@@ -561,7 +595,8 @@ impl Context {
pending_ops: Vec::new(),
operations: Vec::new(),
message_anchors: Default::default(),
contents: Default::default(),
image_anchors: Default::default(),
images: Default::default(),
messages_metadata: Default::default(),
pending_slash_commands: Vec::new(),
finished_slash_commands: HashSet::default(),
@@ -624,6 +659,11 @@ impl Context {
id: message.id,
start: message.offset_range.start,
metadata: self.messages_metadata[&message.id].clone(),
image_offsets: message
.image_offsets
.iter()
.map(|image_offset| (image_offset.0, image_offset.1.image_id))
.collect(),
})
.collect(),
summary: self
@@ -660,7 +700,7 @@ impl Context {
telemetry: Option<Arc<Telemetry>>,
cx: &mut ModelContext<Self>,
) -> Self {
let id = saved_context.id.clone().unwrap_or_else(ContextId::new);
let id = saved_context.id.clone().unwrap_or_else(|| ContextId::new());
let mut this = Self::new(
id,
ReplicaId::default(),
@@ -1917,14 +1957,6 @@ impl Context {
output_range
});
this.insert_content(
Content::ToolResult {
range: anchor_range.clone(),
tool_use_id: tool_use_id.clone(),
},
cx,
);
cx.emit(ContextEvent::ToolFinished {
tool_use_id,
output_range: anchor_range,
@@ -2006,7 +2038,6 @@ impl Context {
let stream_completion = async {
let request_start = Instant::now();
let mut events = stream.await?;
let mut stop_reason = StopReason::EndTurn;
while let Some(event) = events.next().await {
if response_latency.is_none() {
@@ -2019,7 +2050,7 @@ impl Context {
.message_anchors
.iter()
.position(|message| message.id == assistant_message_id)?;
this.buffer.update(cx, |buffer, cx| {
let event_to_emit = this.buffer.update(cx, |buffer, cx| {
let message_old_end_offset = this.message_anchors[message_ix + 1..]
.iter()
.find(|message| message.start.is_valid(buffer))
@@ -2028,9 +2059,13 @@ impl Context {
});
match event {
LanguageModelCompletionEvent::Stop(reason) => {
stop_reason = reason;
}
LanguageModelCompletionEvent::Stop(reason) => match reason {
StopReason::ToolUse => {
return Some(ContextEvent::UsePendingTools);
}
StopReason::EndTurn => {}
StopReason::MaxTokens => {}
},
LanguageModelCompletionEvent::Text(chunk) => {
buffer.edit(
[(
@@ -2081,9 +2116,14 @@ impl Context {
);
}
}
None
});
cx.emit(ContextEvent::StreamedCompletion);
if let Some(event) = event_to_emit {
cx.emit(event);
}
Some(())
})?;
@@ -2096,14 +2136,13 @@ impl Context {
this.update_cache_status_for_completion(cx);
})?;
anyhow::Ok(stop_reason)
anyhow::Ok(())
};
let result = stream_completion.await;
this.update(&mut cx, |this, cx| {
let error_message = result
.as_ref()
.err()
.map(|error| error.to_string().trim().to_string());
@@ -2131,16 +2170,6 @@ impl Context {
error_message,
);
}
if let Ok(stop_reason) = result {
match stop_reason {
StopReason::ToolUse => {
cx.emit(ContextEvent::UsePendingTools);
}
StopReason::EndTurn => {}
StopReason::MaxTokens => {}
}
}
})
.ok();
}
@@ -2157,94 +2186,18 @@ impl Context {
pub fn to_completion_request(&self, cx: &AppContext) -> LanguageModelRequest {
let buffer = self.buffer.read(cx);
let request_messages = self
.messages(cx)
.filter(|message| message.status == MessageStatus::Done)
.filter_map(|message| message.to_request_message(&buffer))
.collect();
let mut contents = self.contents(cx).peekable();
fn collect_text_content(buffer: &Buffer, range: Range<usize>) -> Option<String> {
let text: String = buffer.text_for_range(range.clone()).collect();
if text.trim().is_empty() {
None
} else {
Some(text)
}
}
let mut completion_request = LanguageModelRequest {
messages: Vec::new(),
LanguageModelRequest {
messages: request_messages,
tools: Vec::new(),
stop: Vec::new(),
temperature: 1.0,
};
for message in self.messages(cx) {
if message.status != MessageStatus::Done {
continue;
}
let mut offset = message.offset_range.start;
let mut request_message = LanguageModelRequestMessage {
role: message.role,
content: Vec::new(),
cache: message
.cache
.as_ref()
.map_or(false, |cache| cache.is_anchor),
};
while let Some(content) = contents.peek() {
if content
.range()
.end
.cmp(&message.anchor_range.end, buffer)
.is_lt()
{
let content = contents.next().unwrap();
let range = content.range().to_offset(buffer);
request_message.content.extend(
collect_text_content(buffer, offset..range.start).map(MessageContent::Text),
);
match content {
Content::Image { image, .. } => {
if let Some(image) = image.clone().now_or_never().flatten() {
request_message
.content
.push(language_model::MessageContent::Image(image));
}
}
Content::ToolUse { tool_use, .. } => {
request_message
.content
.push(language_model::MessageContent::ToolUse(tool_use.clone()));
}
Content::ToolResult { tool_use_id, .. } => {
request_message.content.push(
language_model::MessageContent::ToolResult(
LanguageModelToolResult {
tool_use_id: tool_use_id.to_string(),
is_error: false,
content: collect_text_content(buffer, range.clone())
.unwrap_or_default(),
},
),
);
}
}
offset = range.end;
} else {
break;
}
}
request_message.content.extend(
collect_text_content(buffer, offset..message.offset_range.end)
.map(MessageContent::Text),
);
completion_request.messages.push(request_message);
}
completion_request
}
pub fn cancel_last_assist(&mut self, cx: &mut ModelContext<Self>) -> bool {
@@ -2371,31 +2324,53 @@ impl Context {
}
}
pub fn insert_content(&mut self, content: Content, cx: &mut ModelContext<Self>) {
let buffer = self.buffer.read(cx);
let insertion_ix = match self
.contents
.binary_search_by(|probe| probe.cmp(&content, buffer))
{
Ok(ix) => {
self.contents.remove(ix);
ix
}
Err(ix) => ix,
};
self.contents.insert(insertion_ix, content);
cx.emit(ContextEvent::MessagesEdited);
pub fn insert_image(&mut self, image: Image, cx: &mut ModelContext<Self>) -> Option<()> {
if let hash_map::Entry::Vacant(entry) = self.images.entry(image.id()) {
entry.insert((
image.to_image_data(cx).log_err()?,
LanguageModelImage::from_image(image, cx).shared(),
));
}
Some(())
}
pub fn contents<'a>(&'a self, cx: &'a AppContext) -> impl 'a + Iterator<Item = Content> {
pub fn insert_image_anchor(
&mut self,
image_id: u64,
anchor: language::Anchor,
cx: &mut ModelContext<Self>,
) -> bool {
cx.emit(ContextEvent::MessagesEdited);
let buffer = self.buffer.read(cx);
self.contents
.iter()
.filter(|content| {
let range = content.range();
range.start.is_valid(buffer) && range.end.is_valid(buffer)
})
.cloned()
let insertion_ix = match self
.image_anchors
.binary_search_by(|existing_anchor| anchor.cmp(&existing_anchor.anchor, buffer))
{
Ok(ix) => ix,
Err(ix) => ix,
};
if let Some((render_image, image)) = self.images.get(&image_id) {
self.image_anchors.insert(
insertion_ix,
ImageAnchor {
anchor,
image_id,
image: image.clone(),
render_image: render_image.clone(),
},
);
true
} else {
false
}
}
pub fn images<'a>(&'a self, _cx: &'a AppContext) -> impl 'a + Iterator<Item = ImageAnchor> {
self.image_anchors.iter().cloned()
}
pub fn split_message(
@@ -2558,14 +2533,22 @@ impl Context {
return;
}
let mut request = self.to_completion_request(cx);
request.messages.push(LanguageModelRequestMessage {
role: Role::User,
content: vec![
"Summarize the context into a short title without punctuation.".into(),
],
cache: false,
});
let messages = self
.messages(cx)
.filter_map(|message| message.to_request_message(self.buffer.read(cx)))
.chain(Some(LanguageModelRequestMessage {
role: Role::User,
content: vec![
"Summarize the context into a short title without punctuation.".into(),
],
cache: false,
}));
let request = LanguageModelRequest {
messages: messages.collect(),
tools: Vec::new(),
stop: Vec::new(),
temperature: 1.0,
};
self.pending_summary = cx.spawn(|this, mut cx| {
async move {
@@ -2665,8 +2648,10 @@ impl Context {
cx: &'a AppContext,
) -> impl 'a + Iterator<Item = Message> {
let buffer = self.buffer.read(cx);
let messages = message_anchors.enumerate();
let images = self.image_anchors.iter();
Self::messages_from_iters(buffer, &self.messages_metadata, message_anchors.enumerate())
Self::messages_from_iters(buffer, &self.messages_metadata, messages, images)
}
pub fn messages<'a>(&'a self, cx: &'a AppContext) -> impl 'a + Iterator<Item = Message> {
@@ -2677,8 +2662,10 @@ impl Context {
buffer: &'a Buffer,
metadata: &'a HashMap<MessageId, MessageMetadata>,
messages: impl Iterator<Item = (usize, &'a MessageAnchor)> + 'a,
images: impl Iterator<Item = &'a ImageAnchor> + 'a,
) -> impl 'a + Iterator<Item = Message> {
let mut messages = messages.peekable();
let mut images = images.peekable();
iter::from_fn(move || {
if let Some((start_ix, message_anchor)) = messages.next() {
@@ -2699,6 +2686,22 @@ impl Context {
let message_end_anchor = message_end.unwrap_or(language::Anchor::MAX);
let message_end = message_end_anchor.to_offset(buffer);
let mut image_offsets = SmallVec::new();
while let Some(image_anchor) = images.peek() {
if image_anchor.anchor.cmp(&message_end_anchor, buffer).is_lt() {
image_offsets.push((
image_anchor.anchor.to_offset(buffer),
MessageImage {
image_id: image_anchor.image_id,
image: image_anchor.image.clone(),
},
));
images.next();
} else {
break;
}
}
return Some(Message {
index_range: start_ix..end_ix,
offset_range: message_start..message_end,
@@ -2707,6 +2710,7 @@ impl Context {
role: metadata.role,
status: metadata.status.clone(),
cache: metadata.cache.clone(),
image_offsets,
});
}
None
@@ -2744,6 +2748,9 @@ impl Context {
})?;
if let Some(summary) = summary {
this.read_with(&cx, |this, cx| this.serialize_images(fs.clone(), cx))?
.await;
let context = this.read_with(&cx, |this, cx| this.serialize(cx))?;
let mut discriminant = 1;
let mut new_path;
@@ -2783,6 +2790,45 @@ impl Context {
});
}
pub fn serialize_images(&self, fs: Arc<dyn Fs>, cx: &AppContext) -> Task<()> {
let mut images_to_save = self
.images
.iter()
.map(|(id, (_, llm_image))| {
let fs = fs.clone();
let llm_image = llm_image.clone();
let id = *id;
async move {
if let Some(llm_image) = llm_image.await {
let path: PathBuf =
context_images_dir().join(&format!("{}.png.base64", id));
if fs
.metadata(path.as_path())
.await
.log_err()
.flatten()
.is_none()
{
fs.atomic_write(path, llm_image.source.to_string())
.await
.log_err();
}
}
}
})
.collect::<FuturesUnordered<_>>();
cx.background_executor().spawn(async move {
if fs
.create_dir(context_images_dir().as_ref())
.await
.log_err()
.is_some()
{
while let Some(_) = images_to_save.next().await {}
}
})
}
pub(crate) fn custom_summary(&mut self, custom_summary: String, cx: &mut ModelContext<Self>) {
let timestamp = self.next_timestamp();
let summary = self.summary.get_or_insert(ContextSummary::default());
@@ -2868,6 +2914,9 @@ pub struct SavedMessage {
pub id: MessageId,
pub start: usize,
pub metadata: MessageMetadata,
#[serde(default)]
// This is defaulted for backwards compatibility with JSON files created before August 2024. We didn't always have this field.
pub image_offsets: Vec<(usize, u64)>,
}
#[derive(Serialize, Deserialize)]
@@ -3053,6 +3102,7 @@ impl SavedContextV0_3_0 {
timestamp,
cache: None,
},
image_offsets: Vec::new(),
})
})
.collect(),

View File

@@ -390,7 +390,7 @@ impl ContextStore {
context_proto
.operations
.into_iter()
.map(ContextOperation::from_proto)
.map(|op| ContextOperation::from_proto(op))
.collect::<Result<Vec<_>>>()
})
.await?;
@@ -527,7 +527,7 @@ impl ContextStore {
context_proto
.operations
.into_iter()
.map(ContextOperation::from_proto)
.map(|op| ContextOperation::from_proto(op))
.collect::<Result<Vec<_>>>()
})
.await?;

View File

@@ -1478,7 +1478,7 @@ impl Render for PromptEditor {
.child(
ModelSelector::new(
self.fs.clone(),
IconButton::new("context", IconName::SettingsAlt)
IconButton::new("context", IconName::SlidersAlt)
.shape(IconButtonShape::Square)
.icon_size(IconSize::Small)
.icon_color(Color::Muted)
@@ -1921,7 +1921,7 @@ impl PromptEditor {
font_family: settings.ui_font.family.clone(),
font_features: settings.ui_font.features.clone(),
font_fallbacks: settings.ui_font.fallbacks.clone(),
font_size: settings.ui_font_size.into(),
font_size: rems(0.875).into(),
font_weight: settings.ui_font.weight,
line_height: relative(1.3),
..Default::default()
@@ -2373,7 +2373,20 @@ impl Codegen {
None
};
let language_name = language_name.as_ref();
// Higher Temperature increases the randomness of model outputs.
// If Markdown or No Language is Known, increase the randomness for more creative output
// If Code, decrease temperature to get more deterministic outputs
let temperature = if let Some(language) = language_name.clone() {
if language.as_ref() == "Markdown" {
1.0
} else {
0.5
}
} else {
1.0
};
let language_name = language_name.as_deref();
let start = buffer.point_to_buffer_offset(edit_range.start);
let end = buffer.point_to_buffer_offset(edit_range.end);
let (buffer, range) = if let Some((start, end)) = start.zip(end) {
@@ -2407,8 +2420,8 @@ impl Codegen {
Ok(LanguageModelRequest {
messages,
tools: Vec::new(),
stop: Vec::new(),
temperature: 1.,
stop: vec!["|END|>".to_string()],
temperature,
})
}
@@ -3060,7 +3073,7 @@ mod tests {
codegen.handle_stream(
String::new(),
range,
future::ready(Ok(chunks_rx.map(Ok).boxed())),
future::ready(Ok(chunks_rx.map(|chunk| Ok(chunk)).boxed())),
cx,
)
});
@@ -3132,7 +3145,7 @@ mod tests {
codegen.handle_stream(
String::new(),
range.clone(),
future::ready(Ok(chunks_rx.map(Ok).boxed())),
future::ready(Ok(chunks_rx.map(|chunk| Ok(chunk)).boxed())),
cx,
)
});
@@ -3207,7 +3220,7 @@ mod tests {
codegen.handle_stream(
String::new(),
range.clone(),
future::ready(Ok(chunks_rx.map(Ok).boxed())),
future::ready(Ok(chunks_rx.map(|chunk| Ok(chunk)).boxed())),
cx,
)
});
@@ -3281,7 +3294,7 @@ mod tests {
codegen.handle_stream(
String::new(),
range.clone(),
future::ready(Ok(chunks_rx.map(Ok).boxed())),
future::ready(Ok(chunks_rx.map(|chunk| Ok(chunk)).boxed())),
cx,
)
});

View File

@@ -4,7 +4,7 @@ use fs::Fs;
use futures::StreamExt;
use gpui::AssetSource;
use handlebars::{Handlebars, RenderError};
use language::{BufferSnapshot, LanguageName};
use language::BufferSnapshot;
use parking_lot::Mutex;
use serde::Serialize;
use std::{ops::Range, path::PathBuf, sync::Arc, time::Duration};
@@ -123,7 +123,7 @@ impl PromptBuilder {
if params.fs.is_dir(parent_dir).await {
let (mut changes, _watcher) = params.fs.watch(parent_dir, Duration::from_secs(1)).await;
while let Some(changed_paths) = changes.next().await {
if changed_paths.iter().any(|p| &p.path == &templates_dir) {
if changed_paths.iter().any(|p| p == &templates_dir) {
let mut log_message = format!("Prompt template overrides directory detected at {}", templates_dir.display());
if let Ok(target) = params.fs.read_link(&templates_dir).await {
log_message.push_str(" -> ");
@@ -162,18 +162,18 @@ impl PromptBuilder {
let mut combined_changes = futures::stream::select(changes, parent_changes);
while let Some(changed_paths) = combined_changes.next().await {
if changed_paths.iter().any(|p| &p.path == &templates_dir) {
if changed_paths.iter().any(|p| p == &templates_dir) {
if !params.fs.is_dir(&templates_dir).await {
log::info!("Prompt template overrides directory removed. Restoring built-in prompt templates.");
Self::register_built_in_templates(&mut handlebars.lock()).log_err();
break;
}
}
for event in changed_paths {
if event.path.starts_with(&templates_dir) && event.path.extension().map_or(false, |ext| ext == "hbs") {
log::info!("Reloading prompt template override: {}", event.path.display());
if let Some(content) = params.fs.load(&event.path).await.log_err() {
let file_name = event.path.file_stem().unwrap().to_string_lossy();
for changed_path in changed_paths {
if changed_path.starts_with(&templates_dir) && changed_path.extension().map_or(false, |ext| ext == "hbs") {
log::info!("Reloading prompt template override: {}", changed_path.display());
if let Some(content) = params.fs.load(&changed_path).await.log_err() {
let file_name = changed_path.file_stem().unwrap().to_string_lossy();
handlebars.lock().register_template_string(&file_name, content).log_err();
}
}
@@ -204,11 +204,11 @@ impl PromptBuilder {
pub fn generate_content_prompt(
&self,
user_prompt: String,
language_name: Option<&LanguageName>,
language_name: Option<&str>,
buffer: BufferSnapshot,
range: Range<usize>,
) -> Result<String, RenderError> {
let content_type = match language_name.as_ref().map(|l| l.0.as_ref()) {
let content_type = match language_name {
None | Some("Markdown" | "Plain Text") => "text",
Some(_) => "code",
};

View File

@@ -19,7 +19,6 @@ use std::{
use ui::ActiveTheme;
use workspace::Workspace;
pub mod auto_command;
pub mod context_server_command;
pub mod default_command;
pub mod diagnostics_command;

View File

@@ -1,360 +0,0 @@
use super::create_label_for_command;
use super::{SlashCommand, SlashCommandOutput};
use anyhow::{anyhow, Result};
use assistant_slash_command::ArgumentCompletion;
use feature_flags::FeatureFlag;
use futures::StreamExt;
use gpui::{AppContext, AsyncAppContext, Task, WeakView};
use language::{CodeLabel, LspAdapterDelegate};
use language_model::{
LanguageModelCompletionEvent, LanguageModelRegistry, LanguageModelRequest,
LanguageModelRequestMessage, Role,
};
use semantic_index::{FileSummary, SemanticDb};
use smol::channel;
use std::sync::{atomic::AtomicBool, Arc};
use ui::{BorrowAppContext, WindowContext};
use util::ResultExt;
use workspace::Workspace;
pub struct AutoSlashCommandFeatureFlag;
impl FeatureFlag for AutoSlashCommandFeatureFlag {
const NAME: &'static str = "auto-slash-command";
}
pub(crate) struct AutoCommand;
impl SlashCommand for AutoCommand {
fn name(&self) -> String {
"auto".into()
}
fn description(&self) -> String {
"Automatically infer what context to add, based on your prompt".into()
}
fn menu_text(&self) -> String {
"Automatically Infer Context".into()
}
fn label(&self, cx: &AppContext) -> CodeLabel {
create_label_for_command("auto", &["--prompt"], cx)
}
fn complete_argument(
self: Arc<Self>,
_arguments: &[String],
_cancel: Arc<AtomicBool>,
workspace: Option<WeakView<Workspace>>,
cx: &mut WindowContext,
) -> Task<Result<Vec<ArgumentCompletion>>> {
// There's no autocomplete for a prompt, since it's arbitrary text.
// However, we can use this opportunity to kick off a drain of the backlog.
// That way, it can hopefully be done resummarizing by the time we've actually
// typed out our prompt. This re-runs on every keystroke during autocomplete,
// but in the future, we could instead do it only once, when /auto is first entered.
let Some(workspace) = workspace.and_then(|ws| ws.upgrade()) else {
log::warn!("workspace was dropped or unavailable during /auto autocomplete");
return Task::ready(Ok(Vec::new()));
};
let project = workspace.read(cx).project().clone();
let Some(project_index) =
cx.update_global(|index: &mut SemanticDb, cx| index.project_index(project, cx))
else {
return Task::ready(Err(anyhow!("No project indexer, cannot use /auto")));
};
let cx: &mut AppContext = cx;
cx.spawn(|cx: gpui::AsyncAppContext| async move {
let task = project_index.read_with(&cx, |project_index, cx| {
project_index.flush_summary_backlogs(cx)
})?;
cx.background_executor().spawn(task).await;
anyhow::Ok(Vec::new())
})
}
fn requires_argument(&self) -> bool {
true
}
fn run(
self: Arc<Self>,
arguments: &[String],
workspace: WeakView<Workspace>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
let Some(workspace) = workspace.upgrade() else {
return Task::ready(Err(anyhow::anyhow!("workspace was dropped")));
};
if arguments.is_empty() {
return Task::ready(Err(anyhow!("missing prompt")));
};
let argument = arguments.join(" ");
let original_prompt = argument.to_string();
let project = workspace.read(cx).project().clone();
let Some(project_index) =
cx.update_global(|index: &mut SemanticDb, cx| index.project_index(project, cx))
else {
return Task::ready(Err(anyhow!("no project indexer")));
};
let task = cx.spawn(|cx: gpui::AsyncWindowContext| async move {
let summaries = project_index
.read_with(&cx, |project_index, cx| project_index.all_summaries(cx))?
.await?;
commands_for_summaries(&summaries, &original_prompt, &cx).await
});
// As a convenience, append /auto's argument to the end of the prompt
// so you don't have to write it again.
let original_prompt = argument.to_string();
cx.background_executor().spawn(async move {
let commands = task.await?;
let mut prompt = String::new();
log::info!(
"Translating this response into slash-commands: {:?}",
commands
);
for command in commands {
prompt.push('/');
prompt.push_str(&command.name);
prompt.push(' ');
prompt.push_str(&command.arg);
prompt.push('\n');
}
prompt.push('\n');
prompt.push_str(&original_prompt);
Ok(SlashCommandOutput {
text: prompt,
sections: Vec::new(),
run_commands_in_text: true,
})
})
}
}
const PROMPT_INSTRUCTIONS_BEFORE_SUMMARY: &str = include_str!("prompt_before_summary.txt");
const PROMPT_INSTRUCTIONS_AFTER_SUMMARY: &str = include_str!("prompt_after_summary.txt");
fn summaries_prompt(summaries: &[FileSummary], original_prompt: &str) -> String {
let json_summaries = serde_json::to_string(summaries).unwrap();
format!("{PROMPT_INSTRUCTIONS_BEFORE_SUMMARY}\n{json_summaries}\n{PROMPT_INSTRUCTIONS_AFTER_SUMMARY}\n{original_prompt}")
}
/// The slash commands that the model is told about, and which we look for in the inference response.
const SUPPORTED_SLASH_COMMANDS: &[&str] = &["search", "file"];
#[derive(Debug, Clone)]
struct CommandToRun {
name: String,
arg: String,
}
/// Given the pre-indexed file summaries for this project, as well as the original prompt
/// string passed to `/auto`, get a list of slash commands to run, along with their arguments.
///
/// The prompt's output does not include the slashes (to reduce the chance that it makes a mistake),
/// so taking one of these returned Strings and turning it into a real slash-command-with-argument
/// involves prepending a slash to it.
///
/// This function will validate that each of the returned lines begins with one of SUPPORTED_SLASH_COMMANDS.
/// Any other lines it encounters will be discarded, with a warning logged.
async fn commands_for_summaries(
summaries: &[FileSummary],
original_prompt: &str,
cx: &AsyncAppContext,
) -> Result<Vec<CommandToRun>> {
if summaries.is_empty() {
log::warn!("Inferring no context because there were no summaries available.");
return Ok(Vec::new());
}
// Use the globally configured model to translate the summaries into slash-commands,
// because Qwen2-7B-Instruct has not done a good job at that task.
let Some(model) = cx.update(|cx| LanguageModelRegistry::read_global(cx).active_model())? else {
log::warn!("Can't infer context because there's no active model.");
return Ok(Vec::new());
};
// Only go up to 90% of the actual max token count, to reduce chances of
// exceeding the token count due to inaccuracies in the token counting heuristic.
let max_token_count = (model.max_token_count() * 9) / 10;
// Rather than recursing (which would require this async function use a pinned box),
// we use an explicit stack of arguments and answers for when we need to "recurse."
let mut stack = vec![summaries];
let mut final_response = Vec::new();
let mut prompts = Vec::new();
// TODO We only need to create multiple Requests because we currently
// don't have the ability to tell if a CompletionProvider::complete response
// was a "too many tokens in this request" error. If we had that, then
// we could try the request once, instead of having to make separate requests
// to check the token count and then afterwards to run the actual prompt.
let make_request = |prompt: String| LanguageModelRequest {
messages: vec![LanguageModelRequestMessage {
role: Role::User,
content: vec![prompt.into()],
// Nothing in here will benefit from caching
cache: false,
}],
tools: Vec::new(),
stop: Vec::new(),
temperature: 1.0,
};
while let Some(current_summaries) = stack.pop() {
// The split can result in one slice being empty and the other having one element.
// Whenever that happens, skip the empty one.
if current_summaries.is_empty() {
continue;
}
log::info!(
"Inferring prompt context using {} file summaries",
current_summaries.len()
);
let prompt = summaries_prompt(&current_summaries, original_prompt);
let start = std::time::Instant::now();
// Per OpenAI, 1 token ~= 4 chars in English (we go with 4.5 to overestimate a bit, because failed API requests cost a lot of perf)
// Verifying this against an actual model.count_tokens() confirms that it's usually within ~5% of the correct answer, whereas
// getting the correct answer from tiktoken takes hundreds of milliseconds (compared to this arithmetic being ~free).
// source: https://help.openai.com/en/articles/4936856-what-are-tokens-and-how-to-count-them
let token_estimate = prompt.len() * 2 / 9;
let duration = start.elapsed();
log::info!(
"Time taken to count tokens for prompt of length {:?}B: {:?}",
prompt.len(),
duration
);
if token_estimate < max_token_count {
prompts.push(prompt);
} else if current_summaries.len() == 1 {
log::warn!("Inferring context for a single file's summary failed because the prompt's token length exceeded the model's token limit.");
} else {
log::info!(
"Context inference using file summaries resulted in a prompt containing {token_estimate} tokens, which exceeded the model's max of {max_token_count}. Retrying as two separate prompts, each including half the number of summaries.",
);
let (left, right) = current_summaries.split_at(current_summaries.len() / 2);
stack.push(right);
stack.push(left);
}
}
let all_start = std::time::Instant::now();
let (tx, rx) = channel::bounded(1024);
let completion_streams = prompts
.into_iter()
.map(|prompt| {
let request = make_request(prompt.clone());
let model = model.clone();
let tx = tx.clone();
let stream = model.stream_completion(request, &cx);
(stream, tx)
})
.collect::<Vec<_>>();
cx.background_executor()
.spawn(async move {
let futures = completion_streams
.into_iter()
.enumerate()
.map(|(ix, (stream, tx))| async move {
let start = std::time::Instant::now();
let events = stream.await?;
log::info!("Time taken for awaiting /await chunk stream #{ix}: {:?}", start.elapsed());
let completion: String = events
.filter_map(|event| async {
if let Ok(LanguageModelCompletionEvent::Text(text)) = event {
Some(text)
} else {
None
}
})
.collect()
.await;
log::info!("Time taken for all /auto chunks to come back for #{ix}: {:?}", start.elapsed());
for line in completion.split('\n') {
if let Some(first_space) = line.find(' ') {
let command = &line[..first_space].trim();
let arg = &line[first_space..].trim();
tx.send(CommandToRun {
name: command.to_string(),
arg: arg.to_string(),
})
.await?;
} else if !line.trim().is_empty() {
// All slash-commands currently supported in context inference need a space for the argument.
log::warn!(
"Context inference returned a non-blank line that contained no spaces (meaning no argument for the slash command): {:?}",
line
);
}
}
anyhow::Ok(())
})
.collect::<Vec<_>>();
let _ = futures::future::try_join_all(futures).await.log_err();
let duration = all_start.elapsed();
eprintln!("All futures completed in {:?}", duration);
})
.await;
drop(tx); // Close the channel so that rx.collect() won't hang. This is safe because all futures have completed.
let results = rx.collect::<Vec<_>>().await;
eprintln!(
"Finished collecting from the channel with {} results",
results.len()
);
for command in results {
// Don't return empty or duplicate commands
if !command.name.is_empty()
&& !final_response
.iter()
.any(|cmd: &CommandToRun| cmd.name == command.name && cmd.arg == command.arg)
{
if SUPPORTED_SLASH_COMMANDS
.iter()
.any(|supported| &command.name == supported)
{
final_response.push(command);
} else {
log::warn!(
"Context inference returned an unrecognized slash command: {:?}",
command
);
}
}
}
// Sort the commands by name (reversed just so that /search appears before /file)
final_response.sort_by(|cmd1, cmd2| cmd1.name.cmp(&cmd2.name).reverse());
Ok(final_response)
}

View File

@@ -44,9 +44,10 @@ impl SlashCommand for ContextServerSlashCommand {
}
fn requires_argument(&self) -> bool {
self.prompt.arguments.as_ref().map_or(false, |args| {
args.iter().any(|arg| arg.required == Some(true))
})
self.prompt
.arguments
.as_ref()
.map_or(false, |args| !args.is_empty())
}
fn complete_argument(
@@ -178,8 +179,6 @@ fn prompt_arguments(prompt: &PromptInfo, arguments: &[String]) -> Result<HashMap
let mut map = HashMap::default();
map.insert(args[0].name.clone(), arguments.join(" "));
Ok(map)
} else if arguments.is_empty() && args[0].required == Some(false) {
Ok(HashMap::default())
} else {
Err(anyhow!("Prompt expects argument but none given"))
}
@@ -200,7 +199,7 @@ fn prompt_arguments(prompt: &PromptInfo, arguments: &[String]) -> Result<HashMap
pub fn acceptable_prompt(prompt: &PromptInfo) -> bool {
match &prompt.arguments {
None => true,
Some(args) if args.len() <= 1 => true,
Some(args) if args.len() == 1 => true,
_ => false,
}
}

View File

@@ -193,11 +193,11 @@ impl SlashCommand for DiagnosticsSlashCommand {
.map(|(range, placeholder_type)| SlashCommandOutputSection {
range,
icon: match placeholder_type {
PlaceholderType::Root(_, _) => IconName::Warning,
PlaceholderType::Root(_, _) => IconName::ExclamationTriangle,
PlaceholderType::File(_) => IconName::File,
PlaceholderType::Diagnostic(DiagnosticType::Error, _) => IconName::XCircle,
PlaceholderType::Diagnostic(DiagnosticType::Warning, _) => {
IconName::Warning
IconName::ExclamationTriangle
}
},
label: match placeholder_type {

View File

@@ -164,7 +164,11 @@ impl SlashCommand for FileSlashCommand {
Some(ArgumentCompletion {
label,
new_text: text,
after_completion: AfterCompletion::Compose,
after_completion: if path_match.is_dir {
AfterCompletion::Compose
} else {
AfterCompletion::Run
},
replace_previous_arguments: false,
})
})

View File

@@ -1,24 +0,0 @@
Actions have a cost, so only include actions that you think
will be helpful to you in doing a great job answering the
prompt in the future.
You must respond ONLY with a list of actions you would like to
perform. Each action should be on its own line, and followed by a space and then its parameter.
Actions can be performed more than once with different parameters.
Here is an example valid response:
```
file path/to/my/file.txt
file path/to/another/file.txt
search something to search for
search something else to search for
```
Once again, do not forget: you must respond ONLY in the format of
one action per line, and the action name should be followed by
its parameter. Your response must not include anything other
than a list of actions, with one action per line, in this format.
It is extremely important that you do not deviate from this format even slightly!
This is the end of my instructions for how to respond. The rest is the prompt:

View File

@@ -1,31 +0,0 @@
I'm going to give you a prompt. I don't want you to respond
to the prompt itself. I want you to figure out which of the following
actions on my project, if any, would help you answer the prompt.
Here are the actions:
## file
This action's parameter is a file path to one of the files
in the project. If you ask for this action, I will tell you
the full contents of the file, so you can learn all the
details of the file.
## search
This action's parameter is a string to do a semantic search for
across the files in the project. (You will have a JSON summary
of all the files in the project.) It will tell you which files this string
(or similar strings; it is a semantic search) appear in,
as well as some context of the lines surrounding each result.
It's very important that you only use this action when you think
that searching across the specific files in this project for the query
in question will be useful. For example, don't use this command to search
for queries you might put into a general Web search engine, because those
will be too general to give useful results in this project-specific search.
---
That was the end of the list of actions.
Here is a JSON summary of each of the files in my project:

View File

@@ -8,7 +8,7 @@ use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
use feature_flags::FeatureFlag;
use gpui::{AppContext, Task, WeakView};
use language::{CodeLabel, LineEnding, LspAdapterDelegate};
use semantic_index::SemanticDb;
use semantic_index::SemanticIndex;
use std::{
fmt::Write,
path::PathBuf,
@@ -92,11 +92,8 @@ impl SlashCommand for SearchSlashCommand {
let project = workspace.read(cx).project().clone();
let fs = project.read(cx).fs().clone();
let Some(project_index) =
cx.update_global(|index: &mut SemanticDb, cx| index.project_index(project, cx))
else {
return Task::ready(Err(anyhow::anyhow!("no project indexer")));
};
let project_index =
cx.update_global(|index: &mut SemanticIndex, cx| index.project_index(project, cx));
cx.spawn(|cx| async move {
let results = project_index

View File

@@ -253,7 +253,7 @@ fn tab_items_for_queries(
.fold(HashMap::default(), |mut candidates, (id, path_string)| {
candidates
.entry(path_string)
.or_insert_with(Vec::new)
.or_insert_with(|| Vec::new())
.push(id);
candidates
});

View File

@@ -465,8 +465,7 @@ impl EventEmitter<PromptEditorEvent> for PromptEditor {}
impl Render for PromptEditor {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let status = &self.codegen.read(cx).status;
let buttons = match status {
let buttons = match &self.codegen.read(cx).status {
CodegenStatus::Idle => {
vec![
IconButton::new("cancel", IconName::Close)
@@ -517,8 +516,7 @@ impl Render for PromptEditor {
.tooltip(|cx| Tooltip::for_action("Cancel Assist", &menu::Cancel, cx))
.on_click(cx.listener(|_, _, cx| cx.emit(PromptEditorEvent::CancelRequested)));
let has_error = matches!(status, CodegenStatus::Error(_));
if has_error || self.edited_since_done {
if self.edited_since_done {
vec![
cancel,
IconButton::new("restart", IconName::RotateCw)
@@ -585,7 +583,7 @@ impl Render for PromptEditor {
.gap_2()
.child(ModelSelector::new(
self.fs.clone(),
IconButton::new("context", IconName::SettingsAlt)
IconButton::new("context", IconName::SlidersAlt)
.shape(IconButtonShape::Square)
.icon_size(IconSize::Small)
.icon_color(Color::Muted)
@@ -990,7 +988,7 @@ impl TerminalTransaction {
pub fn push(&mut self, hunk: String, cx: &mut AppContext) {
// Ensure that the assistant cannot accidentally execute commands that are streamed into the terminal
let input = Self::sanitize_input(hunk);
let input = hunk.replace(CARRIAGE_RETURN, " ");
self.terminal
.update(cx, |terminal, _| terminal.input(input));
}
@@ -1005,10 +1003,6 @@ impl TerminalTransaction {
terminal.input(CARRIAGE_RETURN.to_string())
});
}
fn sanitize_input(input: String) -> String {
input.replace(['\r', '\n'], "")
}
}
pub struct Codegen {

View File

@@ -529,13 +529,14 @@ mod test {
let (a, b) = cx.update(|cx| {
(
one_at_a_time.spawn(cx, |_| async {
panic!("");
assert!(false);
Ok(2)
}),
one_at_a_time.spawn(cx, |_| async { Ok(3) }),
)
});
assert_eq!(a.await.unwrap(), None::<u32>);
assert_eq!(a.await.unwrap(), None);
assert_eq!(b.await.unwrap(), Some(3));
let promise = cx.update(|cx| one_at_a_time.spawn(cx, |_| async { Ok(4) }));

View File

@@ -149,16 +149,16 @@ spec:
secretKeyRef:
name: google-ai
key: api_key
- name: RUNPOD_API_KEY
- name: QWEN2_7B_API_KEY
valueFrom:
secretKeyRef:
name: runpod
name: hugging-face
key: api_key
- name: RUNPOD_API_SUMMARY_URL
- name: QWEN2_7B_API_URL
valueFrom:
secretKeyRef:
name: runpod
key: summary
name: hugging-face
key: qwen2_api_url
- name: BLOB_STORE_ACCESS_KEY
valueFrom:
secretKeyRef:

View File

@@ -30,7 +30,7 @@ struct CheckIsContributorParams {
}
impl CheckIsContributorParams {
fn into_contributor_selector(self) -> Result<ContributorSelector> {
fn as_contributor_selector(self) -> Result<ContributorSelector> {
if let Some(github_user_id) = self.github_user_id {
return Ok(ContributorSelector::GitHubUserId { github_user_id });
}
@@ -54,7 +54,7 @@ async fn check_is_contributor(
Extension(app): Extension<Arc<AppState>>,
Query(params): Query<CheckIsContributorParams>,
) -> Result<Json<CheckIsContributorResponse>> {
let params = params.into_contributor_selector()?;
let params = params.as_contributor_selector()?;
if RenovateBot::is_renovate_bot(&params) {
return Ok(Json(CheckIsContributorResponse {

View File

@@ -1326,7 +1326,9 @@ impl ActionEventRow {
}
pub fn calculate_json_checksum(app: Arc<AppState>, json: &impl AsRef<[u8]>) -> Option<Vec<u8>> {
let checksum_seed = app.config.zed_client_checksum_seed.as_ref()?;
let Some(checksum_seed) = app.config.zed_client_checksum_seed.as_ref() else {
return None;
};
let mut summer = Sha256::new();
summer.update(checksum_seed);

View File

@@ -52,7 +52,7 @@ async fn get_extensions(
let extension_id = filter.to_lowercase();
let mut exact_match = None;
extensions.retain(|extension| {
if extension.id.as_ref() == extension_id {
if extension.id.as_ref() == &extension_id {
exact_match = Some(extension.clone());
false
} else {

View File

@@ -872,7 +872,7 @@ fn operation_from_storage(
})
}
fn version_to_storage(version: &[proto::VectorClockEntry]) -> Vec<storage::VectorClockEntry> {
fn version_to_storage(version: &Vec<proto::VectorClockEntry>) -> Vec<storage::VectorClockEntry> {
version
.iter()
.map(|entry| storage::VectorClockEntry {
@@ -882,7 +882,7 @@ fn version_to_storage(version: &[proto::VectorClockEntry]) -> Vec<storage::Vecto
.collect()
}
fn version_from_storage(version: &[storage::VectorClockEntry]) -> Vec<proto::VectorClockEntry> {
fn version_from_storage(version: &Vec<storage::VectorClockEntry>) -> Vec<proto::VectorClockEntry> {
version
.iter()
.map(|entry| proto::VectorClockEntry {

View File

@@ -146,11 +146,11 @@ impl Database {
pub async fn update_dev_server_project(
&self,
id: DevServerProjectId,
paths: &[String],
paths: &Vec<String>,
user_id: UserId,
) -> crate::Result<(dev_server_project::Model, proto::DevServerProjectsUpdate)> {
self.transaction(move |tx| async move {
let paths = paths.to_owned();
let paths = paths.clone();
let Some((project, Some(dev_server))) = dev_server_project::Entity::find_by_id(id)
.find_also_related(dev_server::Entity)
.one(&*tx)

View File

@@ -5,7 +5,7 @@ use super::*;
impl Database {
pub async fn get_hosted_projects(
&self,
channel_ids: &[ChannelId],
channel_ids: &Vec<ChannelId>,
roles: &HashMap<ChannelId, ChannelRole>,
tx: &DatabaseTransaction,
) -> Result<Vec<proto::HostedProject>> {

View File

@@ -728,11 +728,6 @@ impl Database {
is_ignored: db_entry.is_ignored,
is_external: db_entry.is_external,
git_status: db_entry.git_status.map(|status| status as i32),
// This is only used in the summarization backlog, so if it's None,
// that just means we won't be able to detect when to resummarize
// based on total number of backlogged bytes - instead, we'd go
// on number of files only. That shouldn't be a huge deal in practice.
size: None,
is_fifo: db_entry.is_fifo,
});
}

View File

@@ -663,11 +663,6 @@ impl Database {
is_ignored: db_entry.is_ignored,
is_external: db_entry.is_external,
git_status: db_entry.git_status.map(|status| status as i32),
// This is only used in the summarization backlog, so if it's None,
// that just means we won't be able to detect when to resummarize
// based on total number of backlogged bytes - instead, we'd go
// on number of files only. That shouldn't be a huge deal in practice.
size: None,
is_fifo: db_entry.is_fifo,
});
}

View File

@@ -170,8 +170,8 @@ pub struct Config {
pub anthropic_api_key: Option<Arc<str>>,
pub anthropic_staff_api_key: Option<Arc<str>>,
pub llm_closed_beta_model_name: Option<Arc<str>>,
pub runpod_api_key: Option<Arc<str>>,
pub runpod_api_summary_url: Option<Arc<str>>,
pub qwen2_7b_api_key: Option<Arc<str>>,
pub qwen2_7b_api_url: Option<Arc<str>>,
pub zed_client_checksum_seed: Option<String>,
pub slack_panics_webhook: Option<String>,
pub auto_join_channel_id: Option<ChannelId>,
@@ -235,8 +235,8 @@ impl Config {
stripe_api_key: None,
stripe_price_id: None,
supermaven_admin_api_key: None,
runpod_api_key: None,
runpod_api_summary_url: None,
qwen2_7b_api_key: None,
qwen2_7b_api_url: None,
user_backfiller_github_access_token: None,
}
}

View File

@@ -402,12 +402,12 @@ async fn perform_completion(
LanguageModelProvider::Zed => {
let api_key = state
.config
.runpod_api_key
.qwen2_7b_api_key
.as_ref()
.context("no Qwen2-7B API key configured on the server")?;
let api_url = state
.config
.runpod_api_summary_url
.qwen2_7b_api_url
.as_ref()
.context("no Qwen2-7B URL configured on the server")?;
let chunks = open_ai::stream_completion(

View File

@@ -1,5 +1,5 @@
use super::*;
use sea_orm::{sea_query::OnConflict, QueryOrder};
use sea_orm::QueryOrder;
use std::str::FromStr;
use strum::IntoEnumIterator as _;
@@ -99,17 +99,6 @@ impl LlmDatabase {
..Default::default()
}
}))
.on_conflict(
OnConflict::columns([model::Column::ProviderId, model::Column::Name])
.update_columns([
model::Column::MaxRequestsPerMinute,
model::Column::MaxTokensPerMinute,
model::Column::MaxTokensPerDay,
model::Column::PricePerMillionInputTokens,
model::Column::PricePerMillionOutputTokens,
])
.to_owned(),
)
.exec_without_returning(&*tx)
.await?;
Ok(())

View File

@@ -40,15 +40,6 @@ pub async fn seed_database(_config: &Config, db: &mut LlmDatabase, _force: bool)
price_per_million_input_tokens: 25, // $0.25/MTok
price_per_million_output_tokens: 125, // $1.25/MTok
},
ModelParams {
provider: LanguageModelProvider::Zed,
name: "Qwen/Qwen2-7B-Instruct".into(),
max_requests_per_minute: 5,
max_tokens_per_minute: 25_000, // These are arbitrary limits we've set to cap costs; we control this number
max_tokens_per_day: 300_000,
price_per_million_input_tokens: 25,
price_per_million_output_tokens: 125,
},
])
.await
}

View File

@@ -88,7 +88,7 @@ async fn main() -> Result<()> {
.route("/healthz", get(handle_liveness_probe))
.layer(Extension(mode));
let listener = TcpListener::bind(format!("0.0.0.0:{}", config.http_port))
let listener = TcpListener::bind(&format!("0.0.0.0:{}", config.http_port))
.expect("failed to bind TCP listener");
let mut on_shutdown = None;

View File

@@ -2328,11 +2328,11 @@ async fn test_propagate_saves_and_fs_changes(
.unwrap();
buffer_b.read_with(cx_b, |buffer, _| {
assert_eq!(buffer.language().unwrap().name(), "Rust".into());
assert_eq!(&*buffer.language().unwrap().name(), "Rust");
});
buffer_c.read_with(cx_c, |buffer, _| {
assert_eq!(buffer.language().unwrap().name(), "Rust".into());
assert_eq!(&*buffer.language().unwrap().name(), "Rust");
});
buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "i-am-b, ")], None, cx));
buffer_c.update(cx_c, |buf, cx| buf.edit([(0..0, "i-am-c, ")], None, cx));
@@ -2432,17 +2432,17 @@ async fn test_propagate_saves_and_fs_changes(
buffer_a.read_with(cx_a, |buffer, _| {
assert_eq!(buffer.file().unwrap().path().to_str(), Some("file1.js"));
assert_eq!(buffer.language().unwrap().name(), "JavaScript".into());
assert_eq!(&*buffer.language().unwrap().name(), "JavaScript");
});
buffer_b.read_with(cx_b, |buffer, _| {
assert_eq!(buffer.file().unwrap().path().to_str(), Some("file1.js"));
assert_eq!(buffer.language().unwrap().name(), "JavaScript".into());
assert_eq!(&*buffer.language().unwrap().name(), "JavaScript");
});
buffer_c.read_with(cx_c, |buffer, _| {
assert_eq!(buffer.file().unwrap().path().to_str(), Some("file1.js"));
assert_eq!(buffer.language().unwrap().name(), "JavaScript".into());
assert_eq!(&*buffer.language().unwrap().name(), "JavaScript");
});
let new_buffer_a = project_a
@@ -3324,7 +3324,7 @@ async fn test_local_settings(
let store = cx.global::<SettingsStore>();
assert_eq!(
store
.local_settings(worktree_b.read(cx).id())
.local_settings(worktree_b.entity_id().as_u64() as _)
.collect::<Vec<_>>(),
&[
(Path::new("").into(), r#"{"tab_size":2}"#.to_string()),
@@ -3343,7 +3343,7 @@ async fn test_local_settings(
let store = cx.global::<SettingsStore>();
assert_eq!(
store
.local_settings(worktree_b.read(cx).id())
.local_settings(worktree_b.entity_id().as_u64() as _)
.collect::<Vec<_>>(),
&[
(Path::new("").into(), r#"{}"#.to_string()),
@@ -3372,7 +3372,7 @@ async fn test_local_settings(
let store = cx.global::<SettingsStore>();
assert_eq!(
store
.local_settings(worktree_b.read(cx).id())
.local_settings(worktree_b.entity_id().as_u64() as _)
.collect::<Vec<_>>(),
&[
(Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
@@ -3404,7 +3404,7 @@ async fn test_local_settings(
let store = cx.global::<SettingsStore>();
assert_eq!(
store
.local_settings(worktree_b.read(cx).id())
.local_settings(worktree_b.entity_id().as_u64() as _)
.collect::<Vec<_>>(),
&[(Path::new("a").into(), r#"{"hard_tabs":true}"#.to_string()),]
)

View File

@@ -100,7 +100,7 @@ async fn test_sharing_an_ssh_remote_project(
let file = buffer_b.read(cx).file();
assert_eq!(
all_language_settings(file, cx)
.language(Some(&("Rust".into())))
.language(Some("Rust"))
.language_servers,
["override-rust-analyzer".into()]
)

View File

@@ -679,8 +679,8 @@ impl TestServer {
stripe_api_key: None,
stripe_price_id: None,
supermaven_admin_api_key: None,
runpod_api_key: None,
runpod_api_summary_url: None,
qwen2_7b_api_key: None,
qwen2_7b_api_url: None,
user_backfiller_github_access_token: None,
},
})

View File

@@ -346,7 +346,7 @@ impl MessageEditor {
) -> Option<(Anchor, String, Vec<StringMatchCandidate>)> {
let end_offset = end_anchor.to_offset(buffer.read(cx));
let query = buffer.update(cx, |buffer, _| {
let Some(query) = buffer.update(cx, |buffer, _| {
let mut query = String::new();
for ch in buffer.reversed_chars_at(end_offset).take(100) {
if ch == '@' {
@@ -358,7 +358,9 @@ impl MessageEditor {
query.push(ch);
}
None
})?;
}) else {
return None;
};
let start_offset = end_offset - query.len();
let start_anchor = buffer.read(cx).anchor_before(start_offset);
@@ -412,7 +414,7 @@ impl MessageEditor {
let end_offset = end_anchor.to_offset(buffer.read(cx));
let query = buffer.update(cx, |buffer, _| {
let Some(query) = buffer.update(cx, |buffer, _| {
let mut query = String::new();
for ch in buffer.reversed_chars_at(end_offset).take(100) {
if ch == ':' {
@@ -448,7 +450,9 @@ impl MessageEditor {
query.push(ch);
}
None
})?;
}) else {
return None;
};
let start_offset = end_offset - query.len() - 1;
let start_anchor = buffer.read(cx).anchor_before(start_offset);

View File

@@ -2831,7 +2831,7 @@ impl Panel for CollabPanel {
fn icon(&self, cx: &gpui::WindowContext) -> Option<ui::IconName> {
CollaborationPanelSettings::get_global(cx)
.button
.then_some(ui::IconName::UserGroup)
.then_some(ui::IconName::Collab)
}
fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> {

View File

@@ -210,9 +210,10 @@ impl CommandPaletteDelegate {
positions,
}) = intercept_result
{
if let Some(idx) = matches.iter().position(|m| {
commands[m.candidate_id].action.action_type_id() == action.action_type_id()
}) {
if let Some(idx) = matches
.iter()
.position(|m| commands[m.candidate_id].action.type_id() == action.type_id())
{
matches.remove(idx);
}
commands.push(Command {

View File

@@ -2,9 +2,11 @@
#![deny(missing_docs)]
use std::any::TypeId;
use collections::HashSet;
use derive_more::{Deref, DerefMut};
use gpui::{Action, ActionTypeId, AppContext, BorrowAppContext, Global};
use gpui::{Action, AppContext, BorrowAppContext, Global};
/// Initializes the command palette hooks.
pub fn init(cx: &mut AppContext) {
@@ -16,7 +18,7 @@ pub fn init(cx: &mut AppContext) {
#[derive(Default)]
pub struct CommandPaletteFilter {
hidden_namespaces: HashSet<&'static str>,
hidden_action_types: HashSet<ActionTypeId>,
hidden_action_types: HashSet<TypeId>,
}
#[derive(Deref, DerefMut, Default)]
@@ -50,7 +52,7 @@ impl CommandPaletteFilter {
let namespace = name.split("::").next().unwrap_or("malformed action name");
self.hidden_namespaces.contains(namespace)
|| self.hidden_action_types.contains(&action.action_type_id())
|| self.hidden_action_types.contains(&action.type_id())
}
/// Hides all actions in the given namespace.
@@ -64,13 +66,12 @@ impl CommandPaletteFilter {
}
/// Hides all actions with the given types.
pub fn hide_action_types(&mut self, action_types: &[ActionTypeId]) {
self.hidden_action_types
.extend(action_types.iter().cloned());
pub fn hide_action_types(&mut self, action_types: &[TypeId]) {
self.hidden_action_types.extend(action_types);
}
/// Shows all actions with the given types.
pub fn show_action_types<'a>(&mut self, action_types: impl Iterator<Item = &'a ActionTypeId>) {
pub fn show_action_types<'a>(&mut self, action_types: impl Iterator<Item = &'a TypeId>) {
for action_type in action_types {
self.hidden_action_types.remove(action_type);
}

View File

@@ -11,8 +11,8 @@ use collections::{HashMap, HashSet};
use command_palette_hooks::CommandPaletteFilter;
use futures::{channel::oneshot, future::Shared, Future, FutureExt, TryFutureExt};
use gpui::{
actions, ActionTypeId, AppContext, AsyncAppContext, Context, Entity, EntityId, EventEmitter,
Global, Model, ModelContext, Task, WeakModel,
actions, AppContext, AsyncAppContext, Context, Entity, EntityId, EventEmitter, Global, Model,
ModelContext, Task, WeakModel,
};
use http_client::github::latest_github_release;
use http_client::HttpClient;
@@ -69,13 +69,13 @@ pub fn init(
Copilot::set_global(copilot.clone(), cx);
cx.observe(&copilot, |handle, cx| {
let copilot_action_types = [
ActionTypeId::of::<Suggest>(),
ActionTypeId::of::<NextSuggestion>(),
ActionTypeId::of::<PreviousSuggestion>(),
ActionTypeId::of::<Reinstall>(),
TypeId::of::<Suggest>(),
TypeId::of::<NextSuggestion>(),
TypeId::of::<PreviousSuggestion>(),
TypeId::of::<Reinstall>(),
];
let copilot_auth_action_types = [ActionTypeId::of::<SignOut>()];
let copilot_no_auth_action_types = [ActionTypeId::of::<SignIn>()];
let copilot_auth_action_types = [TypeId::of::<SignOut>()];
let copilot_no_auth_action_types = [TypeId::of::<SignIn>()];
let status = handle.read(cx).status();
let filter = CommandPaletteFilter::global_mut(cx);
@@ -1250,8 +1250,8 @@ mod tests {
unimplemented!()
}
fn worktree_id(&self, _: &AppContext) -> settings::WorktreeId {
settings::WorktreeId::from_usize(0)
fn worktree_id(&self) -> usize {
0
}
fn is_private(&self) -> bool {

View File

@@ -1,10 +1,10 @@
use crate::{request::PromptUserDeviceFlow, Copilot, Status};
use gpui::{
div, AppContext, ClipboardItem, DismissEvent, Element, EventEmitter, FocusHandle,
div, svg, AppContext, ClipboardItem, DismissEvent, Element, EventEmitter, FocusHandle,
FocusableView, InteractiveElement, IntoElement, Model, MouseDownEvent, ParentElement, Render,
Styled, Subscription, ViewContext,
};
use ui::{prelude::*, Button, Label, Vector, VectorName};
use ui::{prelude::*, Button, IconName, Label};
use workspace::ModalView;
const COPILOT_SIGN_UP_URL: &str = "https://github.com/features/copilot";
@@ -198,8 +198,12 @@ impl Render for CopilotCodeVerification {
cx.focus(&this.focus_handle);
}))
.child(
Vector::new(VectorName::ZedXCopilot, rems(8.), rems(4.))
.color(Color::Custom(cx.theme().colors().icon)),
svg()
.w_32()
.h_16()
.flex_none()
.path(IconName::ZedXCopilot.path())
.text_color(cx.theme().colors().icon),
)
.child(prompt)
}

View File

@@ -667,7 +667,7 @@ impl Item for ProjectDiagnosticsEditor {
then.child(
h_flex()
.gap_1()
.child(Icon::new(IconName::Warning).color(Color::Warning))
.child(Icon::new(IconName::ExclamationTriangle).color(Color::Warning))
.child(
Label::new(self.summary.warning_count.to_string())
.color(params.text_color()),
@@ -804,7 +804,7 @@ fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
icon.path(IconName::XCircle.path())
.text_color(Color::Error.color(cx))
} else {
icon.path(IconName::Warning.path())
icon.path(IconName::ExclamationTriangle.path())
.text_color(Color::Warning.color(cx))
}
}),

View File

@@ -30,7 +30,7 @@ impl Render for DiagnosticIndicator {
(0, warning_count) => h_flex()
.gap_1()
.child(
Icon::new(IconName::Warning)
Icon::new(IconName::ExclamationTriangle)
.size(IconSize::Small)
.color(Color::Warning),
)
@@ -52,7 +52,7 @@ impl Render for DiagnosticIndicator {
)
.child(Label::new(error_count.to_string()).size(LabelSize::Small))
.child(
Icon::new(IconName::Warning)
Icon::new(IconName::ExclamationTriangle)
.size(IconSize::Small)
.color(Color::Warning),
)

View File

@@ -50,7 +50,7 @@ impl Render for ToolbarControls {
)
})
.child(
IconButton::new("toggle-warnings", IconName::Warning)
IconButton::new("toggle-warnings", IconName::ExclamationTriangle)
.tooltip(move |cx| Tooltip::text(tooltip, cx))
.on_click(cx.listener(|this, _, cx| {
if let Some(editor) = this.editor() {

View File

@@ -35,7 +35,7 @@ chrono.workspace = true
client.workspace = true
clock.workspace = true
collections.workspace = true
convert_case.workspace = true
convert_case = "0.6.0"
db.workspace = true
emojis.workspace = true
file_icons.workspace = true

View File

@@ -9,7 +9,7 @@ use settings::Settings;
use std::hash::Hash;
use theme::ThemeSettings;
use time::UtcOffset;
use ui::{prelude::*, tooltip_container, Avatar, Divider, IconButtonShape};
use ui::{prelude::*, tooltip_container, Avatar};
use workspace::Workspace;
use crate::git::blame::{CommitDetails, GitRemote};
@@ -160,7 +160,6 @@ impl Render for BlameEntryTooltip {
.gap_4()
.child(
h_flex()
.pb_1p5()
.gap_x_2()
.overflow_x_hidden()
.flex_wrap()
@@ -174,7 +173,7 @@ impl Render for BlameEntryTooltip {
)
})
.border_b_1()
.border_color(cx.theme().colors().border_variant),
.border_color(cx.theme().colors().border),
)
.child(
div()
@@ -190,13 +189,10 @@ impl Render for BlameEntryTooltip {
.text_color(cx.theme().colors().text_muted)
.w_full()
.justify_between()
.pt_1p5()
.border_t_1()
.border_color(cx.theme().colors().border_variant)
.child(absolute_timestamp)
.child(
h_flex()
.gap_1p5()
.gap_2()
.when_some(pull_request, |this, pr| {
this.child(
Button::new(
@@ -207,20 +203,19 @@ impl Render for BlameEntryTooltip {
.icon(IconName::PullRequest)
.icon_color(Color::Muted)
.icon_position(IconPosition::Start)
.style(ButtonStyle::Subtle)
.style(ButtonStyle::Transparent)
.on_click(move |_, cx| {
cx.stop_propagation();
cx.open_url(pr.url.as_str())
}),
)
})
.child(Divider::vertical())
.child(
Button::new(
"commit-sha-button",
short_commit_id.clone(),
)
.style(ButtonStyle::Subtle)
.style(ButtonStyle::Transparent)
.color(Color::Muted)
.icon(IconName::FileGit)
.icon_color(Color::Muted)
@@ -244,8 +239,6 @@ impl Render for BlameEntryTooltip {
)
.child(
IconButton::new("copy-sha-button", IconName::Copy)
.shape(IconButtonShape::Square)
.icon_size(IconSize::Small)
.icon_color(Color::Muted)
.on_click(move |_, cx| {
cx.stop_propagation();

View File

@@ -12,7 +12,7 @@ use crate::{element::register_action, Editor, SwitchSourceHeader};
static CLANGD_SERVER_NAME: &str = "clangd";
fn is_c_language(language: &Language) -> bool {
return language.name() == "C++".into() || language.name() == "C".into();
return language.name().as_ref() == "C++" || language.name().as_ref() == "C";
}
pub fn switch_source_header(

View File

@@ -1,11 +1,10 @@
use collections::HashMap;
use gpui::{AnyElement, IntoElement};
use multi_buffer::{Anchor, AnchorRangeExt, MultiBufferRow, MultiBufferSnapshot, ToPoint};
use serde::{Deserialize, Serialize};
use std::{cmp::Ordering, ops::Range, sync::Arc};
use sum_tree::{Bias, SeekTarget, SumTree};
use text::Point;
use ui::{IconName, SharedString, WindowContext};
use ui::WindowContext;
use crate::FoldPlaceholder;
@@ -50,31 +49,6 @@ impl CreaseSnapshot {
None
}
pub fn creases_in_range<'a>(
&'a self,
range: Range<MultiBufferRow>,
snapshot: &'a MultiBufferSnapshot,
) -> impl '_ + Iterator<Item = &'a Crease> {
let start = snapshot.anchor_before(Point::new(range.start.0, 0));
let mut cursor = self.creases.cursor::<ItemSummary>();
cursor.seek(&start, Bias::Left, snapshot);
std::iter::from_fn(move || {
while let Some(item) = cursor.item() {
cursor.next(snapshot);
let crease_start = item.crease.range.start.to_point(snapshot);
let crease_end = item.crease.range.end.to_point(snapshot);
if crease_end.row > range.end.0 {
continue;
}
if crease_start.row >= range.start.0 && crease_end.row < range.end.0 {
return Some(&item.crease);
}
}
None
})
}
pub fn crease_items_with_offsets(
&self,
snapshot: &MultiBufferSnapshot,
@@ -113,14 +87,6 @@ pub struct Crease {
pub placeholder: FoldPlaceholder,
pub render_toggle: RenderToggleFn,
pub render_trailer: RenderTrailerFn,
pub metadata: Option<CreaseMetadata>,
}
/// Metadata about a [`Crease`], that is used for serialization.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct CreaseMetadata {
pub icon: IconName,
pub label: SharedString,
}
impl Crease {
@@ -158,14 +124,8 @@ impl Crease {
render_trailer: Arc::new(move |row, folded, cx| {
render_trailer(row, folded, cx).into_any_element()
}),
metadata: None,
}
}
pub fn with_metadata(mut self, metadata: CreaseMetadata) -> Self {
self.metadata = Some(metadata);
self
}
}
impl std::fmt::Debug for Crease {
@@ -344,54 +304,4 @@ mod test {
.query_row(MultiBufferRow(3), &snapshot)
.is_none());
}
#[gpui::test]
fn test_creases_in_range(cx: &mut AppContext) {
let text = "line1\nline2\nline3\nline4\nline5\nline6\nline7";
let buffer = MultiBuffer::build_simple(text, cx);
let snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
let mut crease_map = CreaseMap::default();
let creases = [
Crease::new(
snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(1, 5)),
FoldPlaceholder::test(),
|_row, _folded, _toggle, _cx| div(),
|_row, _folded, _cx| div(),
),
Crease::new(
snapshot.anchor_before(Point::new(3, 0))..snapshot.anchor_after(Point::new(3, 5)),
FoldPlaceholder::test(),
|_row, _folded, _toggle, _cx| div(),
|_row, _folded, _cx| div(),
),
Crease::new(
snapshot.anchor_before(Point::new(5, 0))..snapshot.anchor_after(Point::new(5, 5)),
FoldPlaceholder::test(),
|_row, _folded, _toggle, _cx| div(),
|_row, _folded, _cx| div(),
),
];
crease_map.insert(creases, &snapshot);
let crease_snapshot = crease_map.snapshot();
let range = MultiBufferRow(0)..MultiBufferRow(7);
let creases: Vec<_> = crease_snapshot.creases_in_range(range, &snapshot).collect();
assert_eq!(creases.len(), 3);
let range = MultiBufferRow(2)..MultiBufferRow(5);
let creases: Vec<_> = crease_snapshot.creases_in_range(range, &snapshot).collect();
assert_eq!(creases.len(), 1);
assert_eq!(creases[0].range.start.to_point(&snapshot).row, 3);
let range = MultiBufferRow(0)..MultiBufferRow(2);
let creases: Vec<_> = crease_snapshot.creases_in_range(range, &snapshot).collect();
assert_eq!(creases.len(), 1);
assert_eq!(creases[0].range.start.to_point(&snapshot).row, 1);
let range = MultiBufferRow(6)..MultiBufferRow(7);
let creases: Vec<_> = crease_snapshot.creases_in_range(range, &snapshot).collect();
assert_eq!(creases.len(), 0);
}
}

View File

@@ -59,9 +59,7 @@ use convert_case::{Case, Casing};
use debounced_delay::DebouncedDelay;
use display_map::*;
pub use display_map::{DisplayPoint, FoldPlaceholder};
pub use editor_settings::{
CurrentLineHighlight, EditorSettings, ScrollBeyondLastLine, SearchSettings,
};
pub use editor_settings::{CurrentLineHighlight, EditorSettings, ScrollBeyondLastLine};
pub use editor_settings_controls::*;
use element::LineWithInvisibles;
pub use element::{
@@ -72,14 +70,14 @@ use fuzzy::{StringMatch, StringMatchCandidate};
use git::blame::GitBlame;
use git::diff_hunk_to_display;
use gpui::{
div, impl_actions, point, prelude::*, px, relative, size, uniform_list, Action, ActionTypeId,
AnyElement, AppContext, AsyncWindowContext, AvailableSpace, BackgroundExecutor, Bounds,
ClipboardEntry, ClipboardItem, Context, DispatchPhase, ElementId, EntityId, EventEmitter,
FocusHandle, FocusOutEvent, FocusableView, FontId, FontWeight, HighlightStyle, Hsla,
InteractiveText, KeyContext, ListSizingBehavior, Model, MouseButton, PaintQuad, ParentElement,
Pixels, Render, SharedString, Size, StrikethroughStyle, Styled, StyledText, Subscription, Task,
TextStyle, UTF16Selection, UnderlineStyle, UniformListScrollHandle, View, ViewContext,
ViewInputHandler, VisualContext, WeakFocusHandle, WeakView, WindowContext,
div, impl_actions, point, prelude::*, px, relative, size, uniform_list, Action, AnyElement,
AppContext, AsyncWindowContext, AvailableSpace, BackgroundExecutor, Bounds, ClipboardEntry,
ClipboardItem, Context, DispatchPhase, ElementId, EntityId, EventEmitter, FocusHandle,
FocusOutEvent, FocusableView, FontId, FontWeight, HighlightStyle, Hsla, InteractiveText,
KeyContext, ListSizingBehavior, Model, MouseButton, PaintQuad, ParentElement, Pixels, Render,
SharedString, Size, StrikethroughStyle, Styled, StyledText, Subscription, Task, TextStyle,
UTF16Selection, UnderlineStyle, UniformListScrollHandle, View, ViewContext, ViewInputHandler,
VisualContext, WeakFocusHandle, WeakView, WindowContext,
};
use highlight_matching_bracket::refresh_matching_bracket_highlights;
use hover_popover::{hide_hover, HoverState};
@@ -118,17 +116,16 @@ use parking_lot::{Mutex, RwLock};
use project::project_settings::{GitGutterSetting, ProjectSettings};
use project::{
CodeAction, Completion, CompletionIntent, FormatTrigger, Item, Location, Project, ProjectPath,
ProjectTransaction, TaskSourceKind,
ProjectTransaction, TaskSourceKind, WorktreeId,
};
use rand::prelude::*;
use rpc::{proto::*, ErrorExt};
use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
use serde::{Deserialize, Serialize};
use settings::{update_settings_file, Settings, SettingsLocation, SettingsStore};
use settings::{update_settings_file, Settings, SettingsStore};
use smallvec::SmallVec;
use snippet::Snippet;
use std::any::Any;
use std::{
any::TypeId,
borrow::Cow,
@@ -4976,10 +4973,9 @@ impl Editor {
let cursor = self.selections.newest_anchor().head();
let (buffer, cursor_buffer_position) =
self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
if !user_requested
&& (!self.enable_inline_completions
|| !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx))
&& self.enable_inline_completions
&& !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx)
{
self.discard_inline_completion(false, cx);
return None;
@@ -6672,11 +6668,7 @@ impl Editor {
let is_entire_line = selection.is_empty() || self.selections.line_mode;
if is_entire_line {
selection.start = Point::new(selection.start.row, 0);
if !selection.is_empty() && selection.end.column == 0 {
selection.end = cmp::min(max_point, selection.end);
} else {
selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
}
selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
selection.goal = SelectionGoal::None;
}
if is_first {
@@ -8750,7 +8742,7 @@ impl Editor {
let (worktree_id, file) = project
.buffer_for_id(runnable.buffer, cx)
.and_then(|buffer| buffer.read(cx).file())
.map(|file| (file.worktree_id(cx), file.clone()))
.map(|file| (WorktreeId::from_usize(file.worktree_id()), file.clone()))
.unzip();
(project.task_inventory().clone(), worktree_id, file)
@@ -11429,14 +11421,7 @@ impl Editor {
.redacted_ranges(search_range, |file| {
if let Some(file) = file {
file.is_private()
&& EditorSettings::get(
Some(SettingsLocation {
worktree_id: file.worktree_id(cx),
path: file.path().as_ref(),
}),
cx,
)
.redact_private_values
&& EditorSettings::get(Some(file.as_ref().into()), cx).redact_private_values
} else {
false
}
@@ -12052,12 +12037,10 @@ impl Editor {
cx.notify();
}
pub fn register_runtime_action(
pub fn register_action<A: Action>(
&mut self,
action_type_id: ActionTypeId,
listener: impl Fn(&dyn Any, &mut WindowContext) + 'static,
listener: impl Fn(&A, &mut WindowContext) + 'static,
) -> Subscription {
dbg!("register runtime action", action_type_id.clone());
let id = self.next_editor_action_id.post_inc();
let listener = Arc::new(listener);
self.editor_actions.borrow_mut().insert(
@@ -12065,8 +12048,8 @@ impl Editor {
Box::new(move |cx| {
let cx = cx.window_context();
let listener = listener.clone();
cx.on_action(action_type_id.clone(), move |action, phase, cx| {
eprintln!("Action fired; phase is {phase:?}");
cx.on_action(TypeId::of::<A>(), move |action, phase, cx| {
let action = action.downcast_ref().unwrap();
if phase == DispatchPhase::Bubble {
listener(action, cx)
}
@@ -12080,16 +12063,6 @@ impl Editor {
})
}
pub fn register_action<A: Action>(
&mut self,
listener: impl Fn(&A, &mut WindowContext) + 'static,
) -> Subscription {
self.register_runtime_action(ActionTypeId::of::<A>(), move |action, cx| {
let action = action.downcast_ref().unwrap();
listener(action, cx)
})
}
pub fn file_header_size(&self) -> u32 {
self.file_header_size
}
@@ -12483,7 +12456,7 @@ fn inlay_hint_settings(
let language = snapshot.language_at(location);
let settings = all_language_settings(file, cx);
settings
.language(language.map(|l| l.name()).as_ref())
.language(language.map(|l| l.name()).as_deref())
.inlay_hints
}

View File

@@ -28,8 +28,6 @@ pub struct EditorSettings {
#[serde(default)]
pub double_click_in_multibuffer: DoubleClickInMultibuffer,
pub search_wrap: bool,
#[serde(default)]
pub search: SearchSettings,
pub auto_signature_help: bool,
pub show_signature_help_after_edits: bool,
pub jupyter: Jupyter,
@@ -158,19 +156,6 @@ pub enum ScrollBeyondLastLine {
VerticalScrollMargin,
}
/// Default options for buffer and project search items.
#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub struct SearchSettings {
#[serde(default)]
pub whole_word: bool,
#[serde(default)]
pub case_sensitive: bool,
#[serde(default)]
pub include_ignored: bool,
#[serde(default)]
pub regex: bool,
}
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
pub struct EditorSettingsContent {
/// Whether the cursor blinks in the editor.
@@ -266,11 +251,6 @@ pub struct EditorSettingsContent {
/// Default: true
pub search_wrap: Option<bool>,
/// Defaults to use when opening a new buffer and project search items.
///
/// Default: nothing is enabled
pub search: Option<SearchSettings>,
/// Whether to automatically show a signature help pop-up or not.
///
/// Default: false

View File

@@ -20,8 +20,8 @@ use language::{
},
BracketPairConfig,
Capability::ReadWrite,
FakeLspAdapter, IndentGuide, LanguageConfig, LanguageConfigOverride, LanguageMatcher,
LanguageName, Override, ParsedMarkdown, Point,
FakeLspAdapter, IndentGuide, LanguageConfig, LanguageConfigOverride, LanguageMatcher, Override,
ParsedMarkdown, Point,
};
use language_settings::{Formatter, FormatterList, IndentGuideSettings};
use multi_buffer::MultiBufferIndentGuide;
@@ -9587,12 +9587,12 @@ async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::Test
let server_restarts = Arc::new(AtomicUsize::new(0));
let closure_restarts = Arc::clone(&server_restarts);
let language_server_name = "test language server";
let language_name: LanguageName = "Rust".into();
let language_name: Arc<str> = "Rust".into();
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
language_registry.add(Arc::new(Language::new(
LanguageConfig {
name: language_name.clone(),
name: Arc::clone(&language_name),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
@@ -9629,7 +9629,7 @@ async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::Test
let _fake_server = fake_servers.next().await.unwrap();
update_test_language_settings(cx, |language_settings| {
language_settings.languages.insert(
language_name.clone(),
Arc::clone(&language_name),
LanguageSettingsContent {
tab_size: NonZeroU32::new(8),
..Default::default()

View File

@@ -29,6 +29,7 @@ use crate::{
use client::ParticipantIndex;
use collections::{BTreeMap, HashMap};
use git::{blame::BlameEntry, diff::DiffHunkStatus, Oid};
use gpui::Subscription;
use gpui::{
anchored, deferred, div, fill, outline, point, px, quad, relative, size, svg,
transparent_black, Action, AnchorCorner, AnyElement, AvailableSpace, Bounds, ClipboardItem,
@@ -39,7 +40,6 @@ use gpui::{
StatefulInteractiveElement, Style, Styled, TextRun, TextStyle, TextStyleRefinement, View,
ViewContext, WeakView, WindowContext,
};
use gpui::{ActionTypeId, Subscription};
use itertools::Itertools;
use language::{
language_settings::{
@@ -2732,7 +2732,7 @@ impl EditorElement {
.position(position)
.child(context_menu)
.anchor(AnchorCorner::TopLeft)
.snap_to_window_with_margin(px(8.)),
.snap_to_window(),
)
.with_priority(1)
.into_any(),
@@ -6258,7 +6258,7 @@ pub fn register_action<T: Action>(
listener: impl Fn(&mut Editor, &T, &mut ViewContext<Editor>) + 'static,
) {
let view = view.clone();
cx.on_action(ActionTypeId::of::<T>(), move |action, phase, cx| {
cx.on_action(TypeId::of::<T>(), move |action, phase, cx| {
let action = action.downcast_ref().unwrap();
if phase == DispatchPhase::Bubble {
view.update(cx, |editor, cx| {

View File

@@ -1,5 +1,4 @@
use gpui::ViewContext;
use language::CursorShape;
use crate::{Editor, RangeToAnchorExt};
@@ -14,18 +13,11 @@ pub fn refresh_matching_bracket_highlights(editor: &mut Editor, cx: &mut ViewCon
return;
}
let snapshot = editor.snapshot(cx);
let head = newest_selection.head();
let mut tail = head;
if (editor.cursor_shape == CursorShape::Block || editor.cursor_shape == CursorShape::Hollow)
&& head < snapshot.buffer_snapshot.len()
{
tail += 1;
}
let snapshot = editor.snapshot(cx);
if let Some((opening_range, closing_range)) = snapshot
.buffer_snapshot
.innermost_enclosing_bracket_ranges(head..tail, None)
.innermost_enclosing_bracket_ranges(head..head, None)
{
editor.highlight_background::<MatchingBracketHighlight>(
&[

View File

@@ -713,42 +713,17 @@ pub(crate) async fn find_file(
cx: &mut AsyncWindowContext,
) -> Option<(Range<text::Anchor>, ResolvedPath)> {
let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot()).ok()?;
let scope = snapshot.language_scope_at(position);
let (range, candidate_file_path) = surrounding_filename(snapshot, position)?;
async fn check_path(
candidate_file_path: &str,
project: &Model<Project>,
buffer: &Model<language::Buffer>,
cx: &mut AsyncWindowContext,
) -> Option<ResolvedPath> {
project
.update(cx, |project, cx| {
project.resolve_existing_file_path(&candidate_file_path, buffer, cx)
})
.ok()?
.await
}
let existing_path = project
.update(cx, |project, cx| {
project.resolve_existing_file_path(&candidate_file_path, buffer, cx)
})
.ok()?
.await?;
if let Some(existing_path) = check_path(&candidate_file_path, &project, buffer, cx).await {
return Some((range, existing_path));
}
if let Some(scope) = scope {
for suffix in scope.path_suffixes() {
if candidate_file_path.ends_with(format!(".{suffix}").as_str()) {
continue;
}
let suffixed_candidate = format!("{candidate_file_path}.{suffix}");
if let Some(existing_path) = check_path(&suffixed_candidate, &project, buffer, cx).await
{
return Some((range, existing_path));
}
}
}
None
Some((range, existing_path))
}
fn surrounding_filename(
@@ -1515,8 +1490,7 @@ mod tests {
You can't go to a file that does_not_exist.txt.
Go to file2.rs if you want.
Or go to ../dir/file2.rs if you want.
Or go to /root/dir/file2.rs if project is local.
Or go to /root/dir/file2 if this is a Rust file.ˇ
Or go to /root/dir/file2.rs if project is local.ˇ
"});
// File does not exist
@@ -1525,7 +1499,6 @@ mod tests {
Go to file2.rs if you want.
Or go to ../dir/file2.rs if you want.
Or go to /root/dir/file2.rs if project is local.
Or go to /root/dir/file2 if this is a Rust file.
"});
cx.simulate_mouse_move(screen_coord, None, Modifiers::secondary_key());
// No highlight
@@ -1544,7 +1517,6 @@ mod tests {
Go to fˇile2.rs if you want.
Or go to ../dir/file2.rs if you want.
Or go to /root/dir/file2.rs if project is local.
Or go to /root/dir/file2 if this is a Rust file.
"});
cx.simulate_mouse_move(screen_coord, None, Modifiers::secondary_key());
@@ -1553,7 +1525,6 @@ mod tests {
Go to «file2.rsˇ» if you want.
Or go to ../dir/file2.rs if you want.
Or go to /root/dir/file2.rs if project is local.
Or go to /root/dir/file2 if this is a Rust file.
"});
// Moving the mouse over a relative path that does exist should highlight it
@@ -1562,7 +1533,6 @@ mod tests {
Go to file2.rs if you want.
Or go to ../dir/fˇile2.rs if you want.
Or go to /root/dir/file2.rs if project is local.
Or go to /root/dir/file2 if this is a Rust file.
"});
cx.simulate_mouse_move(screen_coord, None, Modifiers::secondary_key());
@@ -1571,7 +1541,6 @@ mod tests {
Go to file2.rs if you want.
Or go to «../dir/file2.rsˇ» if you want.
Or go to /root/dir/file2.rs if project is local.
Or go to /root/dir/file2 if this is a Rust file.
"});
// Moving the mouse over an absolute path that does exist should highlight it
@@ -1580,7 +1549,6 @@ mod tests {
Go to file2.rs if you want.
Or go to ../dir/file2.rs if you want.
Or go to /root/diˇr/file2.rs if project is local.
Or go to /root/dir/file2 if this is a Rust file.
"});
cx.simulate_mouse_move(screen_coord, None, Modifiers::secondary_key());
@@ -1589,25 +1557,6 @@ mod tests {
Go to file2.rs if you want.
Or go to ../dir/file2.rs if you want.
Or go to «/root/dir/file2.rsˇ» if project is local.
Or go to /root/dir/file2 if this is a Rust file.
"});
// Moving the mouse over a path that exists, if we add the language-specific suffix, it should highlight it
let screen_coord = cx.pixel_position(indoc! {"
You can't go to a file that does_not_exist.txt.
Go to file2.rs if you want.
Or go to ../dir/file2.rs if you want.
Or go to /root/dir/file2.rs if project is local.
Or go to /root/diˇr/file2 if this is a Rust file.
"});
cx.simulate_mouse_move(screen_coord, None, Modifiers::secondary_key());
cx.assert_editor_text_highlights::<HoveredLinkState>(indoc! {"
You can't go to a file that does_not_exist.txt.
Go to file2.rs if you want.
Or go to ../dir/file2.rs if you want.
Or go to /root/dir/file2.rs if project is local.
Or go to «/root/dir/file2ˇ» if this is a Rust file.
"});
cx.simulate_click(screen_coord, Modifiers::secondary_key());

View File

@@ -518,22 +518,19 @@ async fn parse_blocks(
let rendered_block = cx
.new_view(|cx| {
let settings = ThemeSettings::get_global(cx);
let ui_font_family = settings.ui_font.family.clone();
let buffer_font_family = settings.buffer_font.family.clone();
let mut base_text_style = cx.text_style();
base_text_style.refine(&TextStyleRefinement {
font_family: Some(ui_font_family.clone()),
let mut base_style = cx.text_style();
base_style.refine(&TextStyleRefinement {
font_family: Some(buffer_font_family.clone()),
color: Some(cx.theme().colors().editor_foreground),
..Default::default()
});
let markdown_style = MarkdownStyle {
base_text_style,
code_block: StyleRefinement::default().my(rems(1.)).font_buffer(cx),
base_text_style: base_style,
code_block: StyleRefinement::default().mt(rems(1.)).mb(rems(1.)),
inline_code: TextStyleRefinement {
background_color: Some(cx.theme().colors().background),
font_family: Some(buffer_font_family),
..Default::default()
},
rule_color: Color::Muted.color(cx),

View File

@@ -1054,12 +1054,28 @@ impl SerializableItem for Editor {
let buffer = buffer_task.await?;
pane.update(&mut cx, |_, cx| {
cx.new_view(|cx| {
let editor = cx.new_view(|cx| {
let mut editor = Editor::for_buffer(buffer, Some(project), cx);
editor.read_scroll_position_from_db(item_id, workspace_id, cx);
editor
})
});
// let weak = editor.model.downgrade();
// cx.spawn(|_, cx| async move {
// for i in 0..5 {
// println!("{i}/5: going to sleep for 5 seconds");
// cx.background_executor()
// .timer(std::time::Duration::from_secs(5))
// .await;
// }
// println!("done sleeping");
// weak.assert_released();
// })
// .detach();
editor
})
})
}
@@ -1705,8 +1721,8 @@ mod tests {
let buffer = editor.buffer().read(cx).as_singleton().unwrap().read(cx);
assert_eq!(
buffer.language().map(|lang| lang.name()),
Some("Rust".into())
buffer.language().map(|lang| lang.name()).as_deref(),
Some("Rust")
); // Language should be set to Rust
assert!(buffer.file().is_none()); // The buffer should not have an associated file
});

View File

@@ -13,7 +13,7 @@ use crate::{
static RUST_ANALYZER_NAME: &str = "rust-analyzer";
fn is_rust_language(language: &Language) -> bool {
language.name() == "Rust".into()
language.name().as_ref() == "Rust"
}
pub fn apply_related_actions(editor: &View<Editor>, cx: &mut WindowContext) {

View File

@@ -58,7 +58,7 @@ impl EditorLspTestContext {
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
let mut fake_servers = language_registry.register_fake_lsp_adapter(
language.name(),
language.name().as_ref(),
FakeLspAdapter {
capabilities,
..Default::default()

View File

@@ -24,7 +24,6 @@ async-trait.workspace = true
client.workspace = true
collections.workspace = true
fs.workspace = true
editor.workspace = true
futures.workspace = true
gpui.workspace = true
http_client.workspace = true
@@ -33,9 +32,7 @@ isahc.workspace = true
language.workspace = true
log.workspace = true
lsp.workspace = true
multi_buffer.workspace = true
node_runtime.workspace = true
parking_lot.workspace = true
paths.workspace = true
project.workspace = true
release_channel.workspace = true

View File

@@ -6,21 +6,17 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
copy_extension_api_rust_files()
}
/// rust-analyzer doesn't support include! for files from outside the crate.
/// Copy them to the OUT_DIR, so we can include them from there, which is supported.
// rust-analyzer doesn't support include! for files from outside the crate.
// Copy them to the OUT_DIR, so we can include them from there, which is supported.
fn copy_extension_api_rust_files() -> Result<(), Box<dyn std::error::Error>> {
let out_dir = env::var("OUT_DIR")?;
let input_dir = PathBuf::from("../extension_api/wit");
let output_dir = PathBuf::from(out_dir);
println!("cargo:rerun-if-changed={}", input_dir.display());
for entry in fs::read_dir(&input_dir)? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
println!("cargo:rerun-if-changed={}", path.display());
for subentry in fs::read_dir(&path)? {
let subentry = subentry?;
let subpath = subentry.path();
@@ -30,6 +26,7 @@ fn copy_extension_api_rust_files() -> Result<(), Box<dyn std::error::Error>> {
fs::create_dir_all(destination.parent().unwrap())?;
fs::copy(&subpath, &destination)?;
println!("cargo:rerun-if-changed={}", subpath.display());
}
}
} else if path.extension() == Some(std::ffi::OsStr::new("rs")) {

View File

@@ -38,6 +38,7 @@ impl LspAdapter for ExtensionLspAdapter {
fn get_language_server_command<'a>(
self: Arc<Self>,
_: Arc<Language>,
_: Arc<Path>,
delegate: Arc<dyn LspAdapterDelegate>,
_: futures::lock::MutexGuard<'a, Option<LanguageServerBinary>>,

View File

@@ -1,7 +1,7 @@
use anyhow::{anyhow, Context, Result};
use collections::{BTreeMap, HashMap};
use fs::Fs;
use language::{LanguageName, LanguageServerName};
use language::LanguageServerName;
use semantic_version::SemanticVersion;
use serde::{Deserialize, Serialize};
use std::{
@@ -80,8 +80,6 @@ pub struct ExtensionManifest {
pub indexed_docs_providers: BTreeMap<Arc<str>, IndexedDocsProviderEntry>,
#[serde(default)]
pub snippets: Option<PathBuf>,
#[serde(default)]
pub editor_actions: BTreeMap<Arc<str>, EditorActionManifestEntry>,
}
#[derive(Clone, Default, PartialEq, Eq, Debug, Deserialize, Serialize)]
@@ -108,19 +106,16 @@ pub struct GrammarManifestEntry {
pub struct LanguageServerManifestEntry {
/// Deprecated in favor of `languages`.
#[serde(default)]
language: Option<LanguageName>,
language: Option<Arc<str>>,
/// The list of languages this language server should work with.
#[serde(default)]
languages: Vec<LanguageName>,
languages: Vec<Arc<str>>,
#[serde(default)]
pub language_ids: HashMap<String, String>,
#[serde(default)]
pub code_action_kinds: Option<Vec<lsp::CodeActionKind>>,
}
#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
pub struct ActionManifestEntry {}
impl LanguageServerManifestEntry {
/// Returns the list of languages for the language server.
///
@@ -129,7 +124,7 @@ impl LanguageServerManifestEntry {
///
/// We can replace this with just field access for the `languages` field once
/// we have removed `language`.
pub fn languages(&self) -> impl IntoIterator<Item = LanguageName> + '_ {
pub fn languages(&self) -> impl IntoIterator<Item = Arc<str>> + '_ {
let language = if self.languages.is_empty() {
self.language.clone()
} else {
@@ -145,11 +140,6 @@ pub struct SlashCommandManifestEntry {
pub requires_argument: bool,
}
#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
pub struct EditorActionManifestEntry {
pub name: String, // <extension_id>:: "Name"
}
#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
pub struct IndexedDocsProviderEntry {}
@@ -197,7 +187,6 @@ fn manifest_from_old_manifest(
authors: manifest_json.authors,
schema_version: SchemaVersion::ZERO,
lib: Default::default(),
editor_actions: Default::default(),
themes: {
let mut themes = manifest_json.themes.into_values().collect::<Vec<_>>();
themes.sort();

View File

@@ -18,8 +18,7 @@ use assistant_slash_command::SlashCommandRegistry;
use async_compression::futures::bufread::GzipDecoder;
use async_tar::Archive;
use client::{telemetry::Telemetry, Client, ExtensionMetadata, GetExtensionsResponse};
use collections::{btree_map, BTreeMap, HashMap, HashSet};
use editor::Editor;
use collections::{btree_map, BTreeMap, HashSet};
use extension_builder::{CompileExtensionOptions, ExtensionBuilder};
use fs::{Fs, RemoveOptions};
use futures::{
@@ -31,15 +30,13 @@ use futures::{
select_biased, AsyncReadExt as _, Future, FutureExt as _, StreamExt as _,
};
use gpui::{
actions, Action, ActionTypeId, AnyWindowHandle, AppContext, AsyncAppContext,
BackgroundExecutor, Context, EventEmitter, Global, Model, ModelContext, Subscription, Task,
WeakModel, WeakView, WindowHandle,
actions, AppContext, AsyncAppContext, Context, EventEmitter, Global, Model, ModelContext, Task,
WeakModel,
};
use http_client::{AsyncBody, HttpClient, HttpClientWithUrl};
use indexed_docs::{IndexedDocsRegistry, ProviderId};
use language::{
LanguageConfig, LanguageMatcher, LanguageName, LanguageQueries, LanguageRegistry,
QUERY_FILENAME_PREFIXES,
LanguageConfig, LanguageMatcher, LanguageQueries, LanguageRegistry, QUERY_FILENAME_PREFIXES,
};
use node_runtime::NodeRuntime;
use project::ContextProviderWithTasks;
@@ -50,7 +47,6 @@ use settings::Settings;
use snippet_provider::SnippetRegistry;
use std::ops::RangeInclusive;
use std::str::FromStr;
use std::sync::LazyLock;
use std::{
cmp::Ordering,
path::{self, Path, PathBuf},
@@ -58,7 +54,6 @@ use std::{
time::{Duration, Instant},
};
use theme::{ThemeRegistry, ThemeSettings};
use ui::SharedString;
use url::Url;
use util::{maybe, ResultExt};
use wasm_host::{
@@ -106,143 +101,6 @@ pub fn is_version_compatible(
true
}
#[derive(Clone, PartialEq, Eq)]
pub struct ExtensionAction {
name: Arc<str>,
action_type_id: ActionTypeId,
}
static NAME_TO_TYPE_ID: LazyLock<Arc<parking_lot::Mutex<HashMap<String, ActionTypeId>>>> =
LazyLock::new(|| Default::default());
impl gpui::Action for ExtensionAction {
fn boxed_clone(&self) -> Box<dyn gpui::Action> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn std::any::Any {
self as _
}
fn partial_eq(&self, action: &dyn gpui::Action) -> bool {
let Some(other) = action.as_any().downcast_ref::<ExtensionAction>() else {
return false;
};
other == self
}
fn name(&self) -> &str {
&self.name
}
fn debug_name() -> &'static str
where
Self: Sized,
{
"extension::ActionDebugName"
}
fn action_type_id(&self) -> gpui::ActionTypeId {
self.action_type_id.clone()
}
fn build(name: &str, value: serde_json::Value) -> Result<Box<dyn gpui::Action>>
where
Self: Sized,
{
let Some(action_type_id) = NAME_TO_TYPE_ID.lock().get(name).cloned() else {
anyhow::bail!("lol")
};
Ok(Box::new(Self {
name: name.into(),
action_type_id,
}))
}
}
pub struct EditorActionRegistry {
executor: BackgroundExecutor,
wasm_host: Arc<WasmHost>,
editors: Vec<(AnyWindowHandle, WeakView<Editor>)>,
actions: Vec<String>,
}
impl EditorActionRegistry {
fn new(wasm_host: Arc<WasmHost>, cx: &mut AppContext) -> Self {
EditorActionRegistry {
executor: cx.background_executor().clone(),
wasm_host: wasm_host.clone(),
editors: Default::default(),
actions: Default::default(),
}
}
fn add_editor(
&mut self,
editor: &mut Editor,
handle: WeakView<Editor>,
window: AnyWindowHandle,
cx: &mut AppContext,
) {
self.editors.push((window, handle));
dbg!(&self.actions);
for action in self.actions.iter() {
let Some(type_id) = NAME_TO_TYPE_ID.lock().get(action).cloned() else {
panic!("yikes");
};
dbg!("Registering runtime action");
editor
.register_runtime_action(type_id.clone(), |any, cx| {
dbg!("oh wow!");
})
.detach();
}
}
fn add_actions(&mut self, actions: Vec<String>) {
self.actions.extend(actions.clone());
for action in actions.into_iter() {
let editors = self.editors.clone();
let task = self.wasm_host.on_main_thread(|cx| {
{
async move {
cx.update(|cx| {
let type_id = NAME_TO_TYPE_ID
.lock()
.entry(action.clone())
.or_insert_with(|| {
cx.register_action_type(action.into(), ExtensionAction::build)
})
.clone();
dbg!(&type_id, &editors.len());
for (window, editor) in editors {
window
.update(cx, |_, cx| {
editor.update(cx, |editor, cx| {
editor
.register_runtime_action(
type_id.clone(),
|any, cx| {
dbg!("oh wow!");
},
)
.detach()
})
})
.log_err();
}
})
.log_err()
}
}
.boxed_local()
});
self.executor.spawn(task).detach();
}
}
}
pub struct ExtensionStore {
builder: Arc<ExtensionBuilder>,
extension_index: ExtensionIndex,
@@ -255,7 +113,6 @@ pub struct ExtensionStore {
outstanding_operations: BTreeMap<Arc<str>, ExtensionOperation>,
index_path: PathBuf,
language_registry: Arc<LanguageRegistry>,
editor_actions_registry: EditorActionRegistry,
theme_registry: Arc<ThemeRegistry>,
slash_command_registry: Arc<SlashCommandRegistry>,
indexed_docs_registry: Arc<IndexedDocsRegistry>,
@@ -291,7 +148,7 @@ impl Global for GlobalExtensionStore {}
pub struct ExtensionIndex {
pub extensions: BTreeMap<Arc<str>, ExtensionIndexEntry>,
pub themes: BTreeMap<Arc<str>, ExtensionIndexThemeEntry>,
pub languages: BTreeMap<LanguageName, ExtensionIndexLanguageEntry>,
pub languages: BTreeMap<Arc<str>, ExtensionIndexLanguageEntry>,
}
#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
@@ -381,37 +238,11 @@ impl ExtensionStore {
let installed_dir = extensions_dir.join("installed");
let index_path = extensions_dir.join("index.json");
let handle = cx.handle();
cx.observe_new_views(move |editor: &mut Editor, cx| {
let editor_handle = cx.view().downgrade();
let window = cx.window_handle();
handle.update(cx, |extension_store, cx| {
dbg!("registering!");
extension_store.editor_actions_registry.add_editor(
editor,
editor_handle,
window,
cx,
)
})
})
.detach();
let wasm_host = WasmHost::new(
fs.clone(),
http_client.clone(),
node_runtime,
language_registry.clone(),
work_dir,
cx,
);
let (reload_tx, mut reload_rx) = unbounded();
let mut this = Self {
extension_index: Default::default(),
installed_dir,
index_path,
editor_actions_registry: EditorActionRegistry::new(wasm_host.clone(), cx),
builder: Arc::new(ExtensionBuilder::new(
// Construct a real HTTP client for the extension builder, as we
// don't want to use a fake one in the tests.
@@ -421,8 +252,15 @@ impl ExtensionStore {
outstanding_operations: Default::default(),
modified_extensions: Default::default(),
reload_complete_senders: Vec::new(),
wasm_host: WasmHost::new(
fs.clone(),
http_client.clone(),
node_runtime,
language_registry.clone(),
work_dir,
cx,
),
wasm_extensions: Vec::new(),
wasm_host,
fs,
http_client,
telemetry,
@@ -531,9 +369,9 @@ impl ExtensionStore {
let installed_dir = this.installed_dir.clone();
async move {
let (mut paths, _) = fs.watch(&installed_dir, FS_WATCH_LATENCY).await;
while let Some(events) = paths.next().await {
for event in events {
let Ok(event_path) = event.path.strip_prefix(&installed_dir) else {
while let Some(paths) = paths.next().await {
for path in paths {
let Ok(event_path) = path.strip_prefix(&installed_dir) else {
continue;
};
@@ -1100,7 +938,7 @@ impl ExtensionStore {
///
/// First, this unloads any themes, languages, or grammars that are
/// no longer in the manifest, or whose files have changed on disk.
/// Then it loads any themes, languages, edits, or grammars that are newly
/// Then it loads any themes, languages, or grammars that are newly
/// added to the manifest, or whose files have changed on disk.
fn extensions_updated(
&mut self,
@@ -1225,7 +1063,6 @@ impl ExtensionStore {
let mut grammars_to_add = Vec::new();
let mut themes_to_add = Vec::new();
let mut snippets_to_add = Vec::new();
let mut actions_to_add = Vec::new();
for extension_id in &extensions_to_load {
let Some(extension) = new_index.extensions.get(extension_id) else {
continue;
@@ -1248,25 +1085,8 @@ impl ExtensionStore {
path.extend([Path::new(extension_id.as_ref()), snippets_path.as_path()]);
path
}));
actions_to_add.extend(
extension
.manifest
.editor_actions
.iter()
.map(|(k, v)| dbg!(format!("{}::{}", extension_id, v.name))),
);
actions_to_add.extend(
extension
.manifest
.editor_actions
.iter()
.map(|(k, v)| dbg!(format!("editor::{}", v.name))),
);
}
dbg!("hello!", &actions_to_add);
self.editor_actions_registry.add_actions(actions_to_add);
self.language_registry
.register_wasm_grammars(grammars_to_add);

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