Compare commits

..

1 Commits

Author SHA1 Message Date
Conrad Irwin
d797750f74 Disable binary downloads
Co-Authored-By: Mikayla <mikayla@zed.dev>
Co-Authored-By: Marshall <marshall@zed.dev>
2024-07-09 16:39:35 -06:00
303 changed files with 7649 additions and 19062 deletions

View File

@@ -1,28 +0,0 @@
# .git-blame-ignore-revs
#
# This file consists of a list of commits that should be ignored for
# `git blame` purposes. This is useful for ignoring commits that only
# changed whitespace / indentation / formatting, but did not change
# the underlying syntax tree.
#
# GitHub will pick this up automatically for blame views:
# https://docs.github.com/en/repositories/working-with-files/using-files/viewing-a-file#ignore-commits-in-the-blame-view
# To use this file locally, run:
# git blame --ignore-revs-file .git-blame-ignore-revs
# To always use this file by default, run:
# git config --local blame.ignoreRevsFile .git-blame-ignore-revs
# To disable this functionality, run:
# git config --local blame.ignoreRevsFile ""
# Comments are optional, but may provide helpful context.
# 2023-04-20 Set default tab_size for JSON to 2 and apply new formatting
# https://github.com/zed-industries/zed/pull/2394
eca93c124a488b4e538946cd2d313bd571aa2b86
# 2024-02-25 Format JSON files in assets/
# https://github.com/zed-industries/zed/pull/8405
ffdda588b41f7d9d270ffe76cab116f828ad545e
# 2024-07-05 Improved formatting of default keymaps (single line per bind)
# https://github.com/zed-industries/zed/pull/13887
813cc3f5e537372fc86720b5e71b6e1c815440ab

View File

@@ -27,8 +27,7 @@ body:
attributes:
label: If applicable, attach your `~/Library/Logs/Zed/Zed.log` file to this issue.
description: |
macOS: `~/Library/Logs/Zed/Zed.log`
Linux: `~/.local/share/zed/logs/Zed.log` or $XDG_DATA_HOME
Drag Zed.log into the text input below.
If you only need the most recent lines, you can run the `zed: open log` command palette action to see the last 1000.
value: |
<details><summary>Zed.log</summary><pre>

View File

@@ -319,6 +319,7 @@ jobs:
- name: Upload app bundle to release
uses: softprops/action-gh-release@v1
if: ${{ env.RELEASE_CHANNEL == 'preview' }}
with:
draft: true
prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }}

View File

@@ -93,9 +93,9 @@ jobs:
- name: Upload Zed Nightly
run: script/upload-nightly macos
bundle-linux-x86:
bundle-deb:
timeout-minutes: 60
name: Create a Linux *.tar.gz bundle for x86
name: Create a Linux *.tar.gz bundle
if: github.repository_owner == 'zed-industries'
runs-on:
- self-hosted
@@ -121,57 +121,8 @@ jobs:
echo "Publishing version: ${version} on release channel nightly"
echo "nightly" > crates/zed/RELEASE_CHANNEL
- name: Create Linux .tar.gz bundle
run: script/bundle-linux
- name: Upload Zed Nightly
run: script/upload-nightly linux-targz
bundle-linux-arm:
timeout-minutes: 60
name: Create a Linux *.tar.gz bundle for ARM
if: github.repository_owner == 'zed-industries'
runs-on:
- self-hosted
- hosted-linux-arm-1
needs: tests
env:
DIGITALOCEAN_SPACES_ACCESS_KEY: ${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }}
DIGITALOCEAN_SPACES_SECRET_KEY: ${{ secrets.DIGITALOCEAN_SPACES_SECRET_KEY }}
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
clean: false
- name: "Setup jq"
uses: dcarbone/install-jq-action@v2
- name: Set up Clang
run: |
sudo apt-get update
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@v1
with:
mold-version: 2.32.0
- name: rustup
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Limit target directory size
run: script/clear-target-dir-if-larger-than 100
- name: Set release channel to nightly
run: |
set -euo pipefail
version=$(git rev-parse --short HEAD)
echo "Publishing version: ${version} on release channel nightly"
echo "nightly" > crates/zed/RELEASE_CHANNEL
- name: Generate license file
run: script/generate-licenses
- name: Create Linux .tar.gz bundle
run: script/bundle-linux

247
Cargo.lock generated
View File

@@ -87,7 +87,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6d1ea4484c8676f295307a4892d478c70ac8da1dbd8c7c10830a504b7f1022f"
dependencies = [
"base64 0.22.0",
"bitflags 2.6.0",
"bitflags 2.4.2",
"home",
"libc",
"log",
@@ -110,7 +110,7 @@ version = "0.24.1-dev"
source = "git+https://github.com/alacritty/alacritty?rev=cacdb5bb3b72bad2c729227537979d95af75978f#cacdb5bb3b72bad2c729227537979d95af75978f"
dependencies = [
"base64 0.22.0",
"bitflags 2.6.0",
"bitflags 2.4.2",
"home",
"libc",
"log",
@@ -341,9 +341,8 @@ dependencies = [
[[package]]
name = "ashpd"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfe7e0dd0ac5a401dc116ed9f9119cf9decc625600474cb41f0fc0a0050abc9a"
version = "0.9.0"
source = "git+https://github.com/bilelmoussaoui/ashpd?rev=29f2e1a#29f2e1a6f4b0911f504658f5f4630c02e01b13f2"
dependencies = [
"async-fs 2.1.1",
"async-net 2.0.0",
@@ -378,7 +377,6 @@ dependencies = [
"cargo_toml",
"chrono",
"client",
"clock",
"collections",
"command_palette_hooks",
"ctor",
@@ -421,7 +419,6 @@ dependencies = [
"telemetry_events",
"terminal",
"terminal_view",
"text",
"theme",
"tiktoken-rs",
"toml 0.8.10",
@@ -1584,16 +1581,7 @@ version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
dependencies = [
"bit-vec 0.6.3",
]
[[package]]
name = "bit-set"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0481a0e032742109b1133a095184ee93d88f3dc9e0d28a5d033dc77a073f44f"
dependencies = [
"bit-vec 0.7.0",
"bit-vec",
]
[[package]]
@@ -1602,12 +1590,6 @@ version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
[[package]]
name = "bit-vec"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2c54ff287cfc0a34f38a6b832ea1bd8e448a330b3e40a50859e6488bee07f22"
[[package]]
name = "bit_field"
version = "0.10.2"
@@ -1622,9 +1604,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.6.0"
version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
dependencies = [
"serde",
]
@@ -1650,11 +1632,11 @@ dependencies = [
[[package]]
name = "blade-graphics"
version = "0.4.0"
source = "git+https://github.com/zed-industries/blade?rev=a477c2008db27db0b9f745715e119b3ee7ab7818#a477c2008db27db0b9f745715e119b3ee7ab7818"
source = "git+https://github.com/kvark/blade?rev=21a56f780e21e4cb42c70a1dcf4b59842d1ad7f7#21a56f780e21e4cb42c70a1dcf4b59842d1ad7f7"
dependencies = [
"ash",
"ash-window",
"bitflags 2.6.0",
"bitflags 2.4.2",
"block",
"bytemuck",
"codespan-reporting",
@@ -1680,7 +1662,7 @@ dependencies = [
[[package]]
name = "blade-macros"
version = "0.2.1"
source = "git+https://github.com/zed-industries/blade?rev=a477c2008db27db0b9f745715e119b3ee7ab7818#a477c2008db27db0b9f745715e119b3ee7ab7818"
source = "git+https://github.com/kvark/blade?rev=21a56f780e21e4cb42c70a1dcf4b59842d1ad7f7#21a56f780e21e4cb42c70a1dcf4b59842d1ad7f7"
dependencies = [
"proc-macro2",
"quote",
@@ -1690,7 +1672,7 @@ dependencies = [
[[package]]
name = "blade-util"
version = "0.1.0"
source = "git+https://github.com/zed-industries/blade?rev=a477c2008db27db0b9f745715e119b3ee7ab7818#a477c2008db27db0b9f745715e119b3ee7ab7818"
source = "git+https://github.com/kvark/blade?rev=21a56f780e21e4cb42c70a1dcf4b59842d1ad7f7#21a56f780e21e4cb42c70a1dcf4b59842d1ad7f7"
dependencies = [
"blade-graphics",
"bytemuck",
@@ -1938,7 +1920,7 @@ version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.4.2",
"log",
"polling 3.3.2",
"rustix 0.38.32",
@@ -2407,13 +2389,22 @@ dependencies = [
"worktree",
]
[[package]]
name = "clipboard-win"
version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fdf5e01086b6be750428ba4a40619f847eb2e95756eee84b18e06e5f0b50342"
dependencies = [
"lazy-bytes-cast",
"winapi",
]
[[package]]
name = "clock"
version = "0.1.0"
dependencies = [
"chrono",
"parking_lot",
"serde",
"smallvec",
]
@@ -2472,7 +2463,6 @@ version = "0.44.0"
dependencies = [
"anthropic",
"anyhow",
"assistant",
"async-trait",
"async-tungstenite",
"audio",
@@ -2865,7 +2855,7 @@ name = "cosmic-text"
version = "0.11.2"
source = "git+https://github.com/pop-os/cosmic-text?rev=542b20c#542b20ca4376a3b5de5fa629db1a4ace44e18e0c"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.4.2",
"fontdb",
"log",
"rangemap",
@@ -3402,13 +3392,11 @@ dependencies = [
"ctor",
"editor",
"env_logger",
"feature_flags",
"futures 0.3.28",
"gpui",
"language",
"log",
"lsp",
"multi_buffer",
"pretty_assertions",
"project",
"rand 0.8.5",
@@ -3613,7 +3601,6 @@ dependencies = [
"linkify",
"log",
"lsp",
"markdown",
"multi_buffer",
"ordered-float 2.10.0",
"parking_lot",
@@ -3945,7 +3932,6 @@ dependencies = [
"serde_json",
"serde_json_lenient",
"settings",
"snippet_provider",
"task",
"theme",
"toml 0.8.10",
@@ -3987,7 +3973,6 @@ version = "0.1.0"
dependencies = [
"anyhow",
"client",
"collections",
"db",
"editor",
"extension",
@@ -4007,7 +3992,6 @@ dependencies = [
"theme_selector",
"ui",
"util",
"vim",
"workspace",
]
@@ -4023,7 +4007,7 @@ version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7493d4c459da9f84325ad297371a6b2b8a162800873a22e3b6b6512e61d18c05"
dependencies = [
"bit-set 0.5.3",
"bit-set",
"regex",
]
@@ -4080,7 +4064,7 @@ name = "feedback"
version = "0.1.0"
dependencies = [
"anyhow",
"bitflags 2.6.0",
"bitflags 2.4.2",
"client",
"db",
"editor",
@@ -4280,7 +4264,7 @@ checksum = "e32eac81c1135c1df01d4e6d4233c47ba11f6a6d07f33e0bba09d18797077770"
dependencies = [
"fontconfig-parser",
"log",
"memmap2",
"memmap2 0.9.4",
"slotmap",
"tinyvec",
"ttf-parser",
@@ -4414,7 +4398,7 @@ dependencies = [
name = "fsevent"
version = "0.1.0"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.4.2",
"core-foundation",
"fsevent-sys 3.1.0",
"parking_lot",
@@ -4725,7 +4709,7 @@ version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.4.2",
"libc",
"libgit2-sys",
"log",
@@ -4836,7 +4820,7 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.4.2",
"gpu-alloc-types",
]
@@ -4857,7 +4841,7 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.4.2",
]
[[package]]
@@ -4878,6 +4862,7 @@ dependencies = [
"calloop",
"calloop-wayland-source",
"cbindgen",
"clipboard-win",
"cocoa",
"collections",
"core-foundation",
@@ -4906,7 +4891,6 @@ dependencies = [
"num_cpus",
"objc",
"oo7",
"open",
"parking",
"parking_lot",
"pathfinder_geometry",
@@ -5069,7 +5053,6 @@ version = "0.1.0"
dependencies = [
"anyhow",
"client",
"extension",
"fs",
"futures 0.3.28",
"gpui",
@@ -5115,7 +5098,7 @@ version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f7acb9683d7c7068aa46d47557bfa4e35a277964b350d9504a87b03610163fd"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.4.2",
"byteorder",
"heed-traits",
"heed-types",
@@ -5719,25 +5702,6 @@ version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6"
[[package]]
name = "is-docker"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3"
dependencies = [
"once_cell",
]
[[package]]
name = "is-wsl"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5"
dependencies = [
"is-docker",
"once_cell",
]
[[package]]
name = "isahc"
version = "1.7.2"
@@ -6083,6 +6047,12 @@ dependencies = [
"workspace",
]
[[package]]
name = "lazy-bytes-cast"
version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10257499f089cd156ad82d0a9cd57d9501fa2c989068992a97eb3c27836f206b"
[[package]]
name = "lazy_static"
version = "1.4.0"
@@ -6549,6 +6519,15 @@ dependencies = [
"rustix 0.38.32",
]
[[package]]
name = "memmap2"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed"
dependencies = [
"libc",
]
[[package]]
name = "memmap2"
version = "0.9.4"
@@ -6682,17 +6661,17 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
[[package]]
name = "naga"
version = "0.20.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=425526828f738c95ec50b016c6a761bc00d2fb25#425526828f738c95ec50b016c6a761bc00d2fb25"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae585df4b6514cf8842ac0f1ab4992edc975892704835b549cf818dc0191249e"
dependencies = [
"arrayvec",
"bit-set 0.6.0",
"bitflags 2.6.0",
"cfg_aliases",
"bit-set",
"bitflags 2.4.2",
"codespan-reporting",
"hexf-parse",
"indexmap 2.2.6",
"log",
"num-traits",
"rustc-hash",
"spirv",
"termcolor",
@@ -6788,7 +6767,7 @@ version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.4.2",
"cfg-if",
"libc",
"memoffset",
@@ -6800,7 +6779,7 @@ version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.4.2",
"cfg-if",
"cfg_aliases",
"libc",
@@ -6818,6 +6797,7 @@ dependencies = [
"async_zip",
"futures 0.3.28",
"http 0.1.0",
"language",
"log",
"paths",
"semver",
@@ -6869,7 +6849,7 @@ version = "6.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.4.2",
"crossbeam-channel",
"filetime",
"fsevent-sys 4.1.0",
@@ -7082,15 +7062,6 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "num_threads"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
dependencies = [
"libc",
]
[[package]]
name = "nvim-rs"
version = "0.6.0-pre"
@@ -7220,17 +7191,6 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "open"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61a877bf6abd716642a53ef1b89fb498923a4afca5c754f9050b4d081c05c4b3"
dependencies = [
"is-wsl",
"libc",
"pathdiff",
]
[[package]]
name = "open_ai"
version = "0.1.0"
@@ -7251,7 +7211,7 @@ version = "0.10.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.4.2",
"cfg-if",
"foreign-types 0.3.2",
"libc",
@@ -8078,7 +8038,6 @@ dependencies = [
"serde_json",
"settings",
"sha2 0.10.7",
"shellexpand 2.1.2",
"shlex",
"similar",
"smol",
@@ -8220,7 +8179,6 @@ version = "0.1.0"
dependencies = [
"anyhow",
"collections",
"futures 0.3.28",
"prost",
"prost-build",
"serde",
@@ -8267,7 +8225,7 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dce76ce678ffc8e5675b22aa1405de0b7037e2fdf8913fea40d1926c6fe1e6e7"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.4.2",
"memchr",
"unicase",
]
@@ -8312,13 +8270,10 @@ dependencies = [
"assistant",
"editor",
"gpui",
"repl",
"search",
"settings",
"ui",
"util",
"workspace",
"zed_actions",
]
[[package]]
@@ -8525,6 +8480,7 @@ dependencies = [
"client",
"dev_server_projects",
"editor",
"feature_flags",
"fuzzy",
"gpui",
"language",
@@ -8702,7 +8658,6 @@ dependencies = [
"image",
"language",
"log",
"multi_buffer",
"project",
"runtimelib",
"schemars",
@@ -9081,7 +9036,7 @@ version = "0.38.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.4.2",
"errno 0.3.8",
"itoa",
"libc",
@@ -9156,7 +9111,7 @@ version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.4.2",
"bytemuck",
"libm",
"smallvec",
@@ -9379,7 +9334,7 @@ version = "0.1.0"
dependencies = [
"any_vec",
"anyhow",
"bitflags 2.6.0",
"bitflags 2.4.2",
"client",
"collections",
"editor",
@@ -9807,13 +9762,13 @@ dependencies = [
[[package]]
name = "simplelog"
version = "0.12.2"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16257adbfaef1ee58b1363bdc0664c9b8e1e30aed86049635fb5f147d065a9c0"
checksum = "4bc0ffd69814a9b251d43afcabf96dad1b29f5028378056257be9e3fecc9f720"
dependencies = [
"chrono",
"log",
"termcolor",
"time",
]
[[package]]
@@ -9913,7 +9868,6 @@ dependencies = [
"fs",
"futures 0.3.28",
"gpui",
"parking_lot",
"serde",
"serde_json",
"snippet",
@@ -9966,11 +9920,12 @@ dependencies = [
[[package]]
name = "spirv"
version = "0.3.0+sdk-1.3.268.0"
version = "0.2.0+1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844"
checksum = "246bfa38fe3db3f1dfc8ca5a2cdeb7348c78be2112740cc0ec8ef18b6d94f830"
dependencies = [
"bitflags 2.6.0",
"bitflags 1.3.2",
"num-traits",
]
[[package]]
@@ -10146,7 +10101,7 @@ dependencies = [
"atoi",
"base64 0.21.7",
"bigdecimal",
"bitflags 2.6.0",
"bitflags 2.4.2",
"byteorder",
"bytes 1.5.0",
"chrono",
@@ -10193,7 +10148,7 @@ dependencies = [
"atoi",
"base64 0.21.7",
"bigdecimal",
"bitflags 2.6.0",
"bitflags 2.4.2",
"byteorder",
"chrono",
"crc",
@@ -10615,7 +10570,7 @@ version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aef1f9d4c1dbdd1cb3a63be9efd2f04d8ddbc919d46112982c76818ffc2f1a7"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.4.2",
"cap-fs-ext",
"cap-std",
"fd-lock",
@@ -10756,9 +10711,9 @@ dependencies = [
[[package]]
name = "termcolor"
version = "1.4.1"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
dependencies = [
"winapi-util",
]
@@ -10775,7 +10730,6 @@ dependencies = [
"gpui",
"libc",
"rand 0.8.5",
"release_channel",
"schemars",
"serde",
"serde_derive",
@@ -10919,18 +10873,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.61"
version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.61"
version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524"
dependencies = [
"proc-macro2",
"quote",
@@ -10981,9 +10935,7 @@ checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
dependencies = [
"deranged",
"itoa",
"libc",
"num-conv",
"num_threads",
"powerfmt",
"serde",
"time-core",
@@ -11342,7 +11294,7 @@ version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.4.2",
"bytes 1.5.0",
"futures-core",
"futures-util",
@@ -12113,7 +12065,7 @@ version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40eb22ae96f050e0c0d6f7ce43feeae26c348fc4dea56928ca81537cfaa6188b"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.4.2",
"cursor-icon",
"log",
"serde",
@@ -12265,7 +12217,7 @@ version = "0.201.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84e5df6dba6c0d7fafc63a450f1738451ed7a0b52295d83e868218fa286bf708"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.4.2",
"indexmap 2.2.6",
"semver",
]
@@ -12532,7 +12484,7 @@ checksum = "371d828b6849ea06d598ae7dd1c316e8dd9e99b76f77d93d5886cb25c7f8e188"
dependencies = [
"anyhow",
"async-trait",
"bitflags 2.6.0",
"bitflags 2.4.2",
"bytes 1.5.0",
"cap-fs-ext",
"cap-net-ext",
@@ -12619,7 +12571,7 @@ version = "0.31.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82fb96ee935c2cea6668ccb470fb7771f6215d1691746c2d896b447a00ad3f1f"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.4.2",
"rustix 0.38.32",
"wayland-backend",
"wayland-scanner",
@@ -12642,7 +12594,7 @@ version = "0.31.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.4.2",
"wayland-backend",
"wayland-client",
"wayland-scanner",
@@ -12654,7 +12606,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.4.2",
"wayland-backend",
"wayland-client",
"wayland-protocols",
@@ -12773,7 +12725,7 @@ checksum = "ae1136a209614ace00b0c11f04dc7cf42540773be3b22eff6ad165110aba29c1"
dependencies = [
"anyhow",
"async-trait",
"bitflags 2.6.0",
"bitflags 2.4.2",
"thiserror",
"tracing",
"wasmtime",
@@ -13203,7 +13155,7 @@ version = "0.36.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9643b83820c0cd246ecabe5fa454dd04ba4fa67996369466d0747472d337346"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.4.2",
"windows-sys 0.52.0",
]
@@ -13222,7 +13174,7 @@ version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "288f992ea30e6b5c531b52cdd5f3be81c148554b09ea416f058d16556ba92c27"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.4.2",
"wit-bindgen-rt",
"wit-bindgen-rust-macro",
]
@@ -13278,7 +13230,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "421c0c848a0660a8c22e2fd217929a0191f14476b68962afd2af89fd22e39825"
dependencies = [
"anyhow",
"bitflags 2.6.0",
"bitflags 2.4.2",
"indexmap 2.2.6",
"log",
"serde",
@@ -13336,7 +13288,6 @@ dependencies = [
"derive_more",
"dev_server_projects",
"env_logger",
"file_icons",
"fs",
"futures 0.3.28",
"gpui",
@@ -13485,17 +13436,18 @@ name = "xim-parser"
version = "0.2.1"
source = "git+https://github.com/npmania/xim-rs?rev=27132caffc5b9bc9c432ca4afad184ab6e7c16af#27132caffc5b9bc9c432ca4afad184ab6e7c16af"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.4.2",
]
[[package]]
name = "xkbcommon"
version = "0.7.0"
source = "git+https://github.com/ConradIrwin/xkbcommon-rs?rev=fcbb4612185cc129ceeff51d22f7fb51810a03b2#fcbb4612185cc129ceeff51d22f7fb51810a03b2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e"
dependencies = [
"as-raw-xcb-connection",
"libc",
"memmap2",
"memmap2 0.8.0",
"xkeysym",
]
@@ -13628,7 +13580,7 @@ dependencies = [
[[package]]
name = "zed"
version = "0.145.0"
version = "0.144.0"
dependencies = [
"activity_indicator",
"anyhow",
@@ -13703,7 +13655,6 @@ dependencies = [
"settings",
"simplelog",
"smol",
"snippet_provider",
"supermaven",
"tab_switcher",
"task",
@@ -13756,7 +13707,7 @@ dependencies = [
[[package]]
name = "zed_dart"
version = "0.0.3"
version = "0.0.2"
dependencies = [
"zed_extension_api 0.0.6",
]
@@ -13870,9 +13821,9 @@ dependencies = [
[[package]]
name = "zed_php"
version = "0.1.1"
version = "0.0.6"
dependencies = [
"zed_extension_api 0.0.6",
"zed_extension_api 0.0.4",
]
[[package]]
@@ -13906,7 +13857,7 @@ dependencies = [
[[package]]
name = "zed_svelte"
version = "0.0.3"
version = "0.0.1"
dependencies = [
"zed_extension_api 0.0.6",
]

View File

@@ -274,7 +274,7 @@ zed_actions = { path = "crates/zed_actions" }
alacritty_terminal = "0.23"
any_vec = "0.13"
anyhow = "1.0.57"
ashpd = "0.9.1"
ashpd = { git = "https://github.com/bilelmoussaoui/ashpd", rev = "29f2e1a" }
async-compression = { version = "0.4", features = ["gzip", "futures-io"] }
async-dispatcher = { version = "0.1" }
async-fs = "1.6"
@@ -284,10 +284,10 @@ async-trait = "0.1"
async-watch = "0.3.1"
async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
base64 = "0.13"
bitflags = "2.6.0"
blade-graphics = { git = "https://github.com/zed-industries/blade", rev = "a477c2008db27db0b9f745715e119b3ee7ab7818" }
blade-macros = { git = "https://github.com/zed-industries/blade", rev = "a477c2008db27db0b9f745715e119b3ee7ab7818" }
blade-util = { git = "https://github.com/zed-industries/blade", rev = "a477c2008db27db0b9f745715e119b3ee7ab7818" }
bitflags = "2.4.2"
blade-graphics = { git = "https://github.com/kvark/blade", rev = "21a56f780e21e4cb42c70a1dcf4b59842d1ad7f7" }
blade-macros = { git = "https://github.com/kvark/blade", rev = "21a56f780e21e4cb42c70a1dcf4b59842d1ad7f7" }
blade-util = { git = "https://github.com/kvark/blade", rev = "21a56f780e21e4cb42c70a1dcf4b59842d1ad7f7" }
cap-std = "3.0"
cargo_toml = "0.20"
chrono = { version = "0.4", features = ["serde"] }
@@ -365,7 +365,6 @@ shellexpand = "2.1.0"
shlex = "1.3.0"
signal-hook = "0.3.17"
similar = "1.3"
simplelog = "0.12.2"
smallvec = { version = "1.6", features = ["union"] }
smol = "1.2"
strum = { version = "0.25.0", features = ["derive"] }
@@ -452,7 +451,6 @@ features = [
"Win32_System_Com_StructuredStorage",
"Win32_System_DataExchange",
"Win32_System_LibraryLoader",
"Win32_System_Memory",
"Win32_System_Ole",
"Win32_System_SystemInformation",
"Win32_System_SystemServices",
@@ -520,7 +518,7 @@ 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 }
style = "allow"
# Individual rules that have violations in the codebase:
almost_complete_range = "allow"

View File

@@ -1,3 +0,0 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.49574 4.74787L5.99574 7.25214L8.49574 4.74787" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 246 B

View File

@@ -1,13 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_62_95)">
<path d="M4.5 6C3.67157 6 3 5.32843 3 4.5C3 3.67157 3.67157 3 4.5 3C5.32843 3 6 3.67157 6 4.5C6 5.32843 5.32843 6 4.5 6Z" stroke="white" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6.54433 13.4334C6.87775 13.5227 7.22046 13.3249 7.3098 12.9914C7.39914 12.658 7.20127 12.3153 6.86786 12.226L6.54433 13.4334ZM3.77426 6.86772L3.93603 6.26401L2.72862 5.94049L2.56686 6.54419L3.77426 6.86772ZM6.86786 12.226C4.53394 11.6006 3.14889 9.20163 3.77426 6.86772L2.56686 6.54419C1.76281 9.54494 3.54359 12.6293 6.54433 13.4334L6.86786 12.226Z" fill="white"/>
<path d="M11.5 13C10.6716 13 10 12.3284 10 11.5C10 10.6716 10.6716 10 11.5 10C12.3284 10 13 10.6716 13 11.5C13 12.3284 12.3284 13 11.5 13Z" stroke="white" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10.1875 4.21113C9.88852 4.03854 9.7861 3.65629 9.95869 3.35736C10.1313 3.05843 10.5135 2.95601 10.8125 3.12859L10.1875 4.21113ZM11.7888 10.1875C12.9969 8.09496 12.28 5.41925 10.1875 4.21113L10.8125 3.12859C13.5028 4.6819 14.4246 8.12209 12.8713 10.8125L11.7888 10.1875Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_62_95">
<rect width="16" height="16" fill="white" transform="matrix(-1 0 0 1 16 0)"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1,20 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_39_129)">
<path d="M22.0209 11.9553C22.0059 10.0068 21.4219 8.10512 20.3408 6.48401" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10.1001 2.18C11.355 1.93537 12.1493 1.93674 13.5027 2.10594" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M21.8198 10.1C22.0644 11.3548 22.0644 12.6451 21.8198 13.9" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M20.2898 17.6C19.5716 18.6622 18.6548 19.5757 17.5898 20.29" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.9008 21.82C12.6459 22.0644 11.6432 22.1543 10.3883 21.91" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2.18005 13.9C1.93543 12.6451 1.93543 11.3548 2.18005 10.1" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3.70996 6.40002C4.42822 5.33775 5.34503 4.42433 6.40996 3.71002" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 13C12.5523 13 13 12.5523 13 12C13 11.4477 12.5523 11 12 11C11.4477 11 11 11.4477 11 12C11 12.5523 11.4477 13 12 13Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M19 7C20.1046 7 21 6.10457 21 5C21 3.89543 20.1046 3 19 3C17.8954 3 17 3.89543 17 5C17 6.10457 17.8954 7 19 7Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5 21C6.10457 21 7 20.1046 7 19C7 17.8954 6.10457 17 5 17C3.89543 17 3 17.8954 3 19C3 20.1046 3.89543 21 5 21Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M1.99072 12.0748C2.00804 14.0118 2.58758 15.9021 3.65891 17.516" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_39_129">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -1,15 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_32_70)">
<path d="M19 7C20.1046 7 21 6.10457 21 5C21 3.89543 20.1046 3 19 3C17.8954 3 17 3.89543 17 5C17 6.10457 17.8954 7 19 7Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5 21C6.10457 21 7 20.1046 7 19C7 17.8954 6.10457 17 5 17C3.89543 17 3 17.8954 3 19C3 20.1046 3.89543 21 5 21Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10.3999 21.9C12.3227 22.2159 14.2958 21.9632 16.0769 21.173C17.858 20.3827 19.3694 19.0893 20.4254 17.4517C21.4814 15.8142 22.036 13.9037 22.021 11.9553C22.006 10.0068 21.422 8.10512 20.3409 6.48401" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.4998 2.10002C11.5849 1.8076 9.62631 2.07763 7.86198 2.87732C6.09765 3.677 4.60356 4.9719 3.56126 6.60468C2.51896 8.23745 1.97332 10.1378 1.99063 12.0748C2.00795 14.0118 2.58749 15.9021 3.65882 17.516" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10 15V9" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14 15V9" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_32_70">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -1,14 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_32_64)">
<path d="M19 7C20.1046 7 21 6.10457 21 5C21 3.89543 20.1046 3 19 3C17.8954 3 17 3.89543 17 5C17 6.10457 17.8954 7 19 7Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5 21C6.10457 21 7 20.1046 7 19C7 17.8954 6.10457 17 5 17C3.89543 17 3 17.8954 3 19C3 20.1046 3.89543 21 5 21Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10.3999 21.9C12.3227 22.2159 14.2958 21.9632 16.0769 21.173C17.858 20.3827 19.3694 19.0893 20.4254 17.4517C21.4814 15.8142 22.036 13.9037 22.021 11.9553C22.006 10.0068 21.422 8.10512 20.3409 6.48401" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.4998 2.10002C11.5849 1.8076 9.62631 2.07763 7.86198 2.87732C6.09765 3.677 4.60356 4.9719 3.56126 6.60468C2.51896 8.23745 1.97332 10.1378 1.99063 12.0748C2.00795 14.0118 2.58749 15.9021 3.65882 17.516" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10 8.56055C10 8.32095 10.267 8.17803 10.4664 8.31094L15.6256 11.7504C15.8037 11.8691 15.8037 12.1309 15.6256 12.2496L10.4664 15.6891C10.267 15.822 10 15.6791 10 15.4394V8.56055Z" fill="white" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_32_64">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -19,6 +19,7 @@
"escape": "menu::Cancel",
"ctrl-escape": "menu::Cancel",
"ctrl-c": "menu::Cancel",
"shift-enter": "picker::UseSelectedQuery",
"alt-enter": ["picker::ConfirmInput", { "secondary": false }],
"ctrl-alt-enter": ["picker::ConfirmInput", { "secondary": true }],
"ctrl-shift-w": "workspace::CloseWindow",
@@ -78,8 +79,10 @@
"shift-down": "editor::SelectDown",
"shift-left": "editor::SelectLeft",
"shift-right": "editor::SelectRight",
"ctrl-shift-left": "editor::SelectToPreviousWordStart", // cursorWordLeftSelect
"ctrl-shift-right": "editor::SelectToNextWordEnd", // cursorWordRightSelect
"ctrl-shift-left": "editor::SelectToPreviousWordStart",
"ctrl-shift-right": "editor::SelectToNextWordEnd",
"ctrl-shift-up": "editor::AddSelectionAbove",
"ctrl-shift-down": "editor::AddSelectionBelow",
"ctrl-shift-home": "editor::SelectToBeginning",
"ctrl-shift-end": "editor::SelectToEnd",
"ctrl-a": "editor::SelectAll",
@@ -97,7 +100,6 @@
"ctrl-k ctrl-r": "editor::RevertSelectedHunks",
"ctrl-'": "editor::ToggleHunkDiff",
"ctrl-\"": "editor::ExpandAllHunkDiffs",
"ctrl-i": "editor::ShowSignatureHelp",
"alt-g b": "editor::ToggleGitBlame"
}
},
@@ -258,19 +260,20 @@
"bindings": {
"ctrl-[": "editor::Outdent",
"ctrl-]": "editor::Indent",
"shift-alt-up": "editor::AddSelectionAbove", // Insert Cursor Above
"shift-alt-down": "editor::AddSelectionBelow", // Insert Cursor Below
"shift-alt-up": "editor::AddSelectionAbove",
"shift-alt-down": "editor::AddSelectionBelow",
"ctrl-shift-k": "editor::DeleteLine",
"alt-up": "editor::MoveLineUp",
"alt-down": "editor::MoveLineDown",
"ctrl-alt-shift-up": "editor::DuplicateLineUp",
"ctrl-alt-shift-down": "editor::DuplicateLineDown",
"alt-shift-right": "editor::SelectLargerSyntaxNode", // Expand Selection
"alt-shift-left": "editor::SelectSmallerSyntaxNode", // Shrink Selection
"ctrl-shift-l": "editor::SelectAllMatches", // Select all occurrences of current selection
"ctrl-f2": "editor::SelectAllMatches", // Select all occurrences of current word
"ctrl-shift-down": ["editor::SelectNext", { "replace_newest": false }], // Add selection to Next Find Match
"ctrl-shift-up": ["editor::SelectPrevious", { "replace_newest": false }],
"ctrl-shift-left": "editor::SelectToPreviousWordStart",
"ctrl-shift-right": "editor::SelectToNextWordEnd",
"ctrl-shift-up": "editor::SelectLargerSyntaxNode", //todo(linux) tmp keybinding
"ctrl-shift-down": "editor::SelectSmallerSyntaxNode", //todo(linux) tmp keybinding
"ctrl-d": ["editor::SelectNext", { "replace_newest": false }],
"ctrl-shift-l": "editor::SelectAllMatches",
"ctrl-shift-d": ["editor::SelectPrevious", { "replace_newest": false }],
"ctrl-k ctrl-d": ["editor::SelectNext", { "replace_newest": true }],
"ctrl-k ctrl-shift-d": ["editor::SelectPrevious", { "replace_newest": true }],
"ctrl-k ctrl-i": "editor::Hover",
@@ -323,7 +326,7 @@
"alt-9": ["pane::ActivateItem", 8],
"alt-0": "pane::ActivateLastItem",
"ctrl-alt--": "pane::GoBack",
"ctrl-alt-_": "pane::GoForward",
"ctrl-alt-shift--": "pane::GoForward",
"ctrl-shift-t": "pane::ReopenClosedItem",
"f3": "search::SelectNextMatch",
"shift-f3": "search::SelectPrevMatch",
@@ -353,7 +356,7 @@
"alt-7": ["workspace::ActivatePane", 6],
"alt-8": ["workspace::ActivatePane", 7],
"alt-9": ["workspace::ActivatePane", 8],
"ctrl-alt-b": "workspace::ToggleRightDock",
"ctrl-alt-b": "workspace::ToggleLeftDock",
"ctrl-b": "workspace::ToggleLeftDock",
"ctrl-j": "workspace::ToggleBottomDock",
"ctrl-alt-y": "workspace::CloseAllDocks",
@@ -394,7 +397,7 @@
"bindings": {
"ctrl-shift-k": "editor::DeleteLine",
"ctrl-shift-d": "editor::DuplicateLineDown",
"ctrl-shift-j": "editor::JoinLines",
"ctrl-j": "editor::JoinLines",
"ctrl-alt-backspace": "editor::DeleteToPreviousSubwordStart",
"ctrl-alt-h": "editor::DeleteToPreviousSubwordStart",
"ctrl-alt-delete": "editor::DeleteToNextSubwordEnd",
@@ -563,13 +566,6 @@
"tab": "channel_modal::ToggleMode"
}
},
{
"context": "Picker > Editor",
"bindings": {
"tab": "picker::ConfirmCompletion",
"alt-enter": ["picker::ConfirmInput", { "secondary": false }]
}
},
{
"context": "ChannelModal > Picker > Editor",
"bindings": {

View File

@@ -83,7 +83,7 @@
"ctrl-n": "editor::MoveDown",
"ctrl-b": "editor::MoveLeft",
"ctrl-f": "editor::MoveRight",
"ctrl-l": "editor::ScrollCursorCenter",
"ctrl-l": "editor::NextScreen",
"alt-left": "editor::MoveToPreviousWordStart",
"alt-b": "editor::MoveToPreviousWordStart",
"alt-right": "editor::MoveToNextWordEnd",
@@ -102,9 +102,9 @@
"ctrl-shift-b": "editor::SelectLeft",
"shift-right": "editor::SelectRight",
"ctrl-shift-f": "editor::SelectRight",
"alt-shift-left": "editor::SelectToPreviousWordStart", // cursorWordLeftSelect
"alt-shift-left": "editor::SelectToPreviousWordStart",
"alt-shift-b": "editor::SelectToPreviousWordStart",
"alt-shift-right": "editor::SelectToNextWordEnd", // cursorWordRightSelect
"alt-shift-right": "editor::SelectToNextWordEnd",
"alt-shift-f": "editor::SelectToNextWordEnd",
"ctrl-shift-up": "editor::SelectToStartOfParagraph",
"ctrl-shift-down": "editor::SelectToEndOfParagraph",
@@ -126,8 +126,7 @@
"cmd-alt-z": "editor::RevertSelectedHunks",
"cmd-'": "editor::ToggleHunkDiff",
"cmd-\"": "editor::ExpandAllHunkDiffs",
"cmd-alt-g b": "editor::ToggleGitBlame",
"cmd-i": "editor::ShowSignatureHelp"
"cmd-alt-g b": "editor::ToggleGitBlame"
}
},
{
@@ -302,20 +301,19 @@
"bindings": {
"cmd-[": "editor::Outdent",
"cmd-]": "editor::Indent",
"cmd-alt-up": "editor::AddSelectionAbove", // Insert cursor above
"cmd-alt-up": "editor::AddSelectionAbove",
"cmd-ctrl-p": "editor::AddSelectionAbove",
"cmd-alt-down": "editor::AddSelectionBelow", // Insert cursor below
"cmd-alt-down": "editor::AddSelectionBelow",
"cmd-ctrl-n": "editor::AddSelectionBelow",
"cmd-shift-k": "editor::DeleteLine",
"alt-up": "editor::MoveLineUp",
"alt-down": "editor::MoveLineDown",
"alt-shift-up": "editor::DuplicateLineUp",
"alt-shift-down": "editor::DuplicateLineDown",
"ctrl-shift-right": "editor::SelectLargerSyntaxNode", // Expand Selection
"ctrl-shift-left": "editor::SelectSmallerSyntaxNode", // Shrink Selection
"cmd-d": ["editor::SelectNext", { "replace_newest": false }], // Add selection to Next Find Match
"cmd-shift-l": "editor::SelectAllMatches", // Select all occurrences of current selection
"cmd-f2": "editor::SelectAllMatches", // Select all occurrences of current word
"ctrl-shift-right": "editor::SelectLargerSyntaxNode",
"ctrl-shift-left": "editor::SelectSmallerSyntaxNode",
"cmd-d": ["editor::SelectNext", { "replace_newest": false }],
"cmd-shift-l": "editor::SelectAllMatches",
"ctrl-cmd-d": ["editor::SelectPrevious", { "replace_newest": false }],
"cmd-k cmd-d": ["editor::SelectNext", { "replace_newest": true }],
"cmd-k ctrl-cmd-d": ["editor::SelectPrevious", { "replace_newest": true }],
@@ -571,7 +569,7 @@
}
},
{
"context": "Editor && jupyter && !ContextEditor",
"context": "Editor && jupyter",
"bindings": {
"cmd-enter": "repl::Run"
}
@@ -595,14 +593,6 @@
"tab": "channel_modal::ToggleMode"
}
},
{
"context": "Picker > Editor",
"bindings": {
"tab": "picker::ConfirmCompletion",
"alt-enter": ["picker::ConfirmInput", { "secondary": false }],
"cmd-alt-enter": ["picker::ConfirmInput", { "secondary": true }]
}
},
{
"context": "ChannelModal > Picker > Editor",
"bindings": {
@@ -622,6 +612,14 @@
"ctrl-backspace": "tab_switcher::CloseSelectedItem"
}
},
{
"context": "Picker",
"bindings": {
"f2": "picker::UseSelectedQuery",
"alt-enter": ["picker::ConfirmInput", { "secondary": false }],
"cmd-alt-enter": ["picker::ConfirmInput", { "secondary": true }]
}
},
{
"context": "Terminal",
"bindings": {

View File

@@ -1,21 +0,0 @@
// Zed keymap
//
// For information on binding keys, see the Zed
// documentation: https://zed.dev/docs/key-bindings
//
// To see the default key bindings run `zed: Open Default Keymap`
// from the command palette.
[
{
"context": "Workspace",
"bindings": {
// "shift shift": "file_finder::Toggle"
}
},
{
"context": "Editor",
"bindings": {
// "j k": ["workspace::SendKeystrokes", "escape"]
}
}
]

View File

@@ -25,7 +25,6 @@
"ctrl-shift-d": "editor::DuplicateLineDown", // editor:duplicate-lines
"ctrl-up": "editor::MoveLineUp", // editor:move-line-up
"ctrl-down": "editor::MoveLineDown", // editor:move-line-down
"ctrl-\\": "workspace::ToggleLeftDock", // tree-view:toggle
"ctrl-shift-m": "markdown::OpenPreviewToTheSide" // markdown-preview:toggle
}
},

View File

@@ -13,7 +13,6 @@
"ctrl-shift-j": "editor::JoinLines",
"ctrl-d": "editor::DuplicateLineDown",
"ctrl-y": "editor::DeleteLine",
"ctrl-m": "editor::ScrollCursorCenter",
"ctrl-pagedown": "editor::MovePageDown",
"ctrl-pageup": "editor::MovePageUp",
// "ctrl-alt-shift-b": "editor::SelectToPreviousWordStart",

View File

@@ -3,10 +3,10 @@
"bindings": {
"ctrl-shift-[": "pane::ActivatePrevItem",
"ctrl-shift-]": "pane::ActivateNextItem",
"ctrl-pageup": "pane::ActivatePrevItem",
"ctrl-pagedown": "pane::ActivateNextItem",
"ctrl-tab": "pane::ActivateNextItem",
"ctrl-shift-tab": "pane::ActivatePrevItem"
"ctrl-pagedown": "pane::ActivatePrevItem",
"ctrl-pageup": "pane::ActivateNextItem",
"ctrl-shift-tab": "pane::ActivateNextItem",
"ctrl-tab": "pane::ActivatePrevItem"
}
},
{
@@ -24,8 +24,6 @@
"ctrl-shift-f12": "editor::FindAllReferences",
"ctrl-.": "editor::GoToHunk",
"ctrl-,": "editor::GoToPrevHunk",
"ctrl-k ctrl-u": "editor::ConvertToUpperCase",
"ctrl-k ctrl-l": "editor::ConvertToLowerCase",
"shift-alt-m": "markdown::OpenPreviewToTheSide",
"ctrl-backspace": "editor::DeleteToPreviousWordStart",
"ctrl-delete": "editor::DeleteToNextWordEnd"

View File

@@ -26,7 +26,6 @@
"cmd-shift-d": "editor::DuplicateLineDown",
"ctrl-cmd-up": "editor::MoveLineUp",
"ctrl-cmd-down": "editor::MoveLineDown",
"cmd-\\": "workspace::ToggleLeftDock",
"ctrl-shift-m": "markdown::OpenPreviewToTheSide"
}
},

View File

@@ -3,10 +3,10 @@
"bindings": {
"cmd-shift-[": "pane::ActivatePrevItem",
"cmd-shift-]": "pane::ActivateNextItem",
"ctrl-pageup": "pane::ActivatePrevItem",
"ctrl-pagedown": "pane::ActivateNextItem",
"ctrl-tab": "pane::ActivateNextItem",
"ctrl-shift-tab": "pane::ActivatePrevItem"
"ctrl-pagedown": "pane::ActivatePrevItem",
"ctrl-pageup": "pane::ActivateNextItem",
"ctrl-shift-tab": "pane::ActivateNextItem",
"ctrl-tab": "pane::ActivatePrevItem"
}
},
{
@@ -27,7 +27,6 @@
"ctrl-,": "editor::GoToPrevHunk",
"cmd-k cmd-u": "editor::ConvertToUpperCase",
"cmd-k cmd-l": "editor::ConvertToLowerCase",
"cmd-shift-j": "editor::JoinLines",
"shift-alt-m": "markdown::OpenPreviewToTheSide",
"ctrl-backspace": "editor::DeleteToPreviousWordStart",
"ctrl-delete": "editor::DeleteToNextWordEnd"

View File

@@ -1,6 +1,12 @@
[
{
"context": "VimControl && !menu",
"context": "ProjectPanel || Editor",
"bindings": {
"ctrl-6": "pane::AlternateFile"
}
},
{
"context": "Editor && VimControl && !VimWaiting && !menu",
"bindings": {
"i": ["vim::PushOperator", { "Object": { "around": false } }],
"a": ["vim::PushOperator", { "Object": { "around": true } }],
@@ -11,9 +17,6 @@
"j": "vim::Down",
"down": "vim::Down",
"enter": "vim::NextLineStart",
"ctrl-m": "vim::NextLineStart",
"+": "vim::NextLineStart",
"-": "vim::PreviousLineStart",
"tab": "vim::Tab",
"shift-tab": "vim::Tab",
"k": "vim::Up",
@@ -194,20 +197,20 @@
"ctrl-w g shift-d": "editor::GoToTypeDefinitionSplit",
"ctrl-w space": "editor::OpenExcerptsSplit",
"ctrl-w g space": "editor::OpenExcerptsSplit",
"ctrl-6": "pane::AlternateFile"
"-": "pane::RevealInProjectPanel"
}
},
{
"context": "VimControl && VimCount",
"bindings": {
"0": ["vim::Number", 0]
}
},
{
"context": "vim_mode == normal",
// escape is in its own section so that it cancels a pending count.
"context": "Editor && vim_mode == normal && vim_operator == none && !VimWaiting",
"bindings": {
"escape": "editor::Cancel",
"ctrl-[": "editor::Cancel",
"ctrl-[": "editor::Cancel"
}
},
{
"context": "Editor && vim_mode == normal && vim_operator == none && !VimWaiting",
"bindings": {
".": "vim::Repeat",
"c": ["vim::PushOperator", "Change"],
"shift-c": "vim::ChangeToEndOfLine",
@@ -251,12 +254,127 @@
"] d": "editor::GoToDiagnostic",
"[ d": "editor::GoToPrevDiagnostic",
"] c": "editor::GoToHunk",
"[ c": "editor::GoToPrevHunk",
"g c c": "vim::ToggleComments"
"[ c": "editor::GoToPrevHunk"
}
},
{
"context": "vim_mode == visual",
"context": "Editor && vim_mode == visual && vim_operator == none && !VimWaiting",
"bindings": {
"\"": ["vim::PushOperator", "Register"],
// tree-sitter related commands
"[ x": "editor::SelectLargerSyntaxNode",
"] x": "editor::SelectSmallerSyntaxNode"
}
},
{
"context": "Editor && VimCount && vim_mode != insert",
"bindings": {
"0": ["vim::Number", 0]
}
},
{
"context": "Editor && vim_operator == c",
"bindings": {
"c": "vim::CurrentLine",
"d": "editor::Rename" // zed specific
}
},
{
"context": "Editor && vim_mode == normal && vim_operator == c",
"bindings": {
"s": ["vim::PushOperator", { "ChangeSurrounds": {} }]
}
},
{
"context": "Editor && vim_operator == d",
"bindings": {
"d": "vim::CurrentLine"
}
},
{
"context": "Editor && vim_operator == gu",
"bindings": {
"g u": "vim::CurrentLine",
"u": "vim::CurrentLine"
}
},
{
"context": "Editor && vim_operator == gU",
"bindings": {
"g shift-u": "vim::CurrentLine",
"shift-u": "vim::CurrentLine"
}
},
{
"context": "Editor && vim_operator == g~",
"bindings": {
"g ~": "vim::CurrentLine",
"~": "vim::CurrentLine"
}
},
{
"context": "Editor && vim_mode == normal && vim_operator == d",
"bindings": {
"s": ["vim::PushOperator", "DeleteSurrounds"]
}
},
{
"context": "Editor && vim_operator == y",
"bindings": {
"y": "vim::CurrentLine"
}
},
{
"context": "Editor && vim_mode == normal && vim_operator == y",
"bindings": {
"s": ["vim::PushOperator", { "AddSurrounds": {} }]
}
},
{
"context": "Editor && vim_operator == ys",
"bindings": {
"s": "vim::CurrentLine"
}
},
{
"context": "Editor && vim_operator == >",
"bindings": {
">": "vim::CurrentLine"
}
},
{
"context": "Editor && vim_operator == <",
"bindings": {
"<": "vim::CurrentLine"
}
},
{
"context": "Editor && VimObject",
"bindings": {
"w": "vim::Word",
"shift-w": ["vim::Word", { "ignorePunctuation": true }],
"t": "vim::Tag",
"s": "vim::Sentence",
"p": "vim::Paragraph",
"'": "vim::Quotes",
"`": "vim::BackQuotes",
"\"": "vim::DoubleQuotes",
"|": "vim::VerticalBars",
"(": "vim::Parentheses",
")": "vim::Parentheses",
"b": "vim::Parentheses",
"[": "vim::SquareBrackets",
"]": "vim::SquareBrackets",
"{": "vim::CurlyBrackets",
"}": "vim::CurlyBrackets",
"shift-b": "vim::CurlyBrackets",
"<": "vim::AngleBrackets",
">": "vim::AngleBrackets",
"a": "vim::Argument"
}
},
{
"context": "Editor && vim_mode == visual && !VimWaiting && !VimObject",
"bindings": {
"u": "vim::ConvertToLowerCase",
"U": "vim::ConvertToUpperCase",
@@ -291,16 +409,23 @@
">": "vim::Indent",
"<": "vim::Outdent",
"i": ["vim::PushOperator", { "Object": { "around": false } }],
"a": ["vim::PushOperator", { "Object": { "around": true } }],
"g c": "vim::ToggleComments",
"\"": ["vim::PushOperator", "Register"],
// tree-sitter related commands
"[ x": "editor::SelectLargerSyntaxNode",
"] x": "editor::SelectSmallerSyntaxNode"
"a": ["vim::PushOperator", { "Object": { "around": true } }]
}
},
{
"context": "vim_mode == insert",
"context": "Editor && vim_mode == normal && !VimWaiting",
"bindings": {
"g c c": "vim::ToggleComments"
}
},
{
"context": "Editor && vim_mode == visual",
"bindings": {
"g c": "vim::ToggleComments"
}
},
{
"context": "Editor && vim_mode == insert",
"bindings": {
"escape": "vim::NormalBefore",
"ctrl-c": "vim::NormalBefore",
@@ -319,118 +444,30 @@
}
},
{
"context": "vim_mode == replace",
"context": "Editor && vim_mode == replace",
"bindings": {
"escape": "vim::NormalBefore",
"ctrl-c": "vim::NormalBefore",
"ctrl-[": "vim::NormalBefore",
"backspace": "vim::UndoReplace",
"tab": "vim::Tab",
"enter": "vim::Enter"
"enter": "vim::Enter",
"backspace": "vim::UndoReplace"
}
},
{
"context": "vim_mode == waiting",
"context": "Editor && vim_mode != replace && VimWaiting",
"bindings": {
"tab": "vim::Tab",
"enter": "vim::Enter",
"escape": "vim::ClearOperators",
"ctrl-c": "vim::ClearOperators",
"ctrl-[": "vim::ClearOperators"
"escape": ["vim::SwitchMode", "Normal"],
"ctrl-[": ["vim::SwitchMode", "Normal"]
}
},
{
"context": "vim_mode == operator",
"context": "Editor && vim_mode == insert && VimWaiting",
"bindings": {
"escape": "vim::ClearOperators",
"ctrl-c": "vim::ClearOperators",
"ctrl-[": "vim::ClearOperators"
}
},
{
"context": "vim_operator == a || vim_operator == i || vim_operator == cs",
"bindings": {
"w": "vim::Word",
"shift-w": ["vim::Word", { "ignorePunctuation": true }],
"t": "vim::Tag",
"s": "vim::Sentence",
"p": "vim::Paragraph",
"'": "vim::Quotes",
"`": "vim::BackQuotes",
"\"": "vim::DoubleQuotes",
"|": "vim::VerticalBars",
"(": "vim::Parentheses",
")": "vim::Parentheses",
"b": "vim::Parentheses",
"[": "vim::SquareBrackets",
"]": "vim::SquareBrackets",
"{": "vim::CurlyBrackets",
"}": "vim::CurlyBrackets",
"shift-b": "vim::CurlyBrackets",
"<": "vim::AngleBrackets",
">": "vim::AngleBrackets",
"a": "vim::Argument"
}
},
{
"context": "vim_operator == c",
"bindings": {
"c": "vim::CurrentLine",
"d": "editor::Rename", // zed specific
"s": ["vim::PushOperator", { "ChangeSurrounds": {} }]
}
},
{
"context": "vim_operator == d",
"bindings": {
"d": "vim::CurrentLine",
"s": ["vim::PushOperator", "DeleteSurrounds"]
}
},
{
"context": "vim_operator == gu",
"bindings": {
"g u": "vim::CurrentLine",
"u": "vim::CurrentLine"
}
},
{
"context": "vim_operator == gU",
"bindings": {
"g shift-u": "vim::CurrentLine",
"shift-u": "vim::CurrentLine"
}
},
{
"context": "vim_operator == g~",
"bindings": {
"g ~": "vim::CurrentLine",
"~": "vim::CurrentLine"
}
},
{
"context": "vim_operator == y",
"bindings": {
"y": "vim::CurrentLine",
"s": ["vim::PushOperator", { "AddSurrounds": {} }]
}
},
{
"context": "vim_operator == ys",
"bindings": {
"s": "vim::CurrentLine"
}
},
{
"context": "vim_operator == >",
"bindings": {
">": "vim::CurrentLine"
}
},
{
"context": "vim_operator == <",
"bindings": {
"<": "vim::CurrentLine"
"escape": "vim::NormalBefore",
"ctrl-[": "vim::NormalBefore"
}
},
{
@@ -470,8 +507,7 @@
"x": "project_panel::RevealInFileManager",
"shift-g": "menu::SelectLast",
"g g": "menu::SelectFirst",
"-": "project_panel::SelectParent",
"ctrl-6": "pane::AlternateFile"
"-": "project_panel::SelectParent"
}
},
{

View File

@@ -47,7 +47,7 @@
// },
"buffer_line_height": "comfortable",
// The name of a font to use for rendering text in the UI
// (On macOS) You can set this to ".SystemUIFont" to use the system font
// (On macOS) You can set this to ".SysmtemUIFont" to use the system font
"ui_font_family": "Zed Plex Sans",
// The OpenType features to enable for text in the UI
"ui_font_features": {
@@ -83,6 +83,9 @@
"confirm_quit": false,
// Whether to restore last closed project when fresh Zed instance is opened.
"restore_on_startup": "last_workspace",
// Whether zed should automatically download binaries to run
// language servers if they are missing.
"download_missing_binaries": true,
// Size of the drop target in the editor.
"drop_target_size": 0.2,
// Whether the window should be closed when using 'close active item' on a window with no tabs.
@@ -94,9 +97,6 @@
// 3. Never close the window
// "when_closing_with_no_tabs": "keep_window_open",
"when_closing_with_no_tabs": "platform_default",
// Whether to use the system provided dialogs for Open and Save As.
// When set to false, Zed will use the built-in keyboard-first pickers.
"use_system_path_prompts": true,
// Whether the cursor blinks in the editor.
"cursor_blink": true,
// How to highlight the current line in the editor.
@@ -119,11 +119,6 @@
// The debounce delay before re-querying the language server for completion
// documentation when not included in original completion list.
"completion_documentation_secondary_query_debounce": 300,
// Show method signatures in the editor, when inside parentheses.
"auto_signature_help": false,
/// Whether to show the signature help after completion or a bracket pair inserted.
/// If `auto_signature_help` is enabled, this setting will be treated as enabled also.
"show_signature_help_after_edits": true,
// Whether to show wrap guides (vertical rulers) in the editor.
// Setting this to true will show a guide at the 'preferred_line_length' value
// if softwrap is set to 'preferred_line_length', and will show any
@@ -263,8 +258,6 @@
// to both the horizontal and vertical delta values while scrolling.
"scroll_sensitivity": 1.0,
"relative_line_numbers": false,
// If 'search_wrap' is disabled, search result do not wrap around the end of the file.
"search_wrap": true,
// When to populate a new search's query based on the text under the cursor.
// This setting can take the following three values:
//
@@ -434,9 +427,7 @@
// Show git status colors in the editor tabs.
"git_status": false,
// Position of the close button on the editor tabs.
"close_position": "right",
// Whether to show the file icon for a tab.
"file_icons": false
"close_position": "right"
},
// Settings related to preview tabs.
"preview_tabs": {
@@ -544,14 +535,6 @@
// "delay_ms": 600
}
},
// Configuration for how direnv configuration should be loaded. May take 2 values:
// 1. Load direnv configuration through the shell hook, works for POSIX shells and fish.
// "load_direnv": "shell_hook"
// 2. Load direnv configuration using `direnv export json` directly.
// This can help with some shells that otherwise would not detect
// the direnv environment, such as nushell or elvish.
// "load_direnv": "direct"
"load_direnv": "shell_hook",
"inline_completions": {
// A list of globs representing files that inline completions should be disabled for.
"disabled_globs": [".env"]
@@ -727,12 +710,10 @@
}
},
"C": {
"format_on_save": "off",
"use_on_type_format": false
"format_on_save": "off"
},
"C++": {
"format_on_save": "off",
"use_on_type_format": false
"format_on_save": "off"
},
"CSS": {
"prettier": {
@@ -784,13 +765,11 @@
},
"Markdown": {
"format_on_save": "off",
"use_on_type_format": false,
"prettier": {
"allowed": true
}
},
"PHP": {
"language_servers": ["phpactor", "!intelephense", "..."],
"prettier": {
"allowed": true,
"plugins": ["@prettier/plugin-php"],

View File

@@ -4,8 +4,8 @@
// documentation: https://zed.dev/docs/configuring-zed
//
// To see all of Zed's default settings without changing your
// custom settings, run the `zed: Open Default Settings` command
// from the command palette
// custom settings, run the `open default settings` command
// from the command palette or from `Zed` application menu.
{
"ui_font_size": 16,
"buffer_font_size": 16,

View File

@@ -38,7 +38,6 @@
"icon.accent": "#10a793ff",
"status_bar.background": "#262933ff",
"title_bar.background": "#262933ff",
"title_bar.inactive_background": "#21242bff",
"toolbar.background": "#1e2025ff",
"tab_bar.background": "#21242bff",
"tab.inactive_background": "#21242bff",

View File

@@ -38,7 +38,6 @@
"icon.accent": "#566ddaff",
"status_bar.background": "#3a353fff",
"title_bar.background": "#3a353fff",
"title_bar.inactive_background": "#221f26ff",
"toolbar.background": "#19171cff",
"tab_bar.background": "#221f26ff",
"tab.inactive_background": "#221f26ff",
@@ -423,7 +422,6 @@
"icon.accent": "#586cdaff",
"status_bar.background": "#bfbcc5ff",
"title_bar.background": "#bfbcc5ff",
"title_bar.inactive_background": "#e6e3ebff",
"toolbar.background": "#efecf4ff",
"tab_bar.background": "#e6e3ebff",
"tab.inactive_background": "#e6e3ebff",
@@ -808,7 +806,6 @@
"icon.accent": "#6684e0ff",
"status_bar.background": "#45433bff",
"title_bar.background": "#45433bff",
"title_bar.inactive_background": "#262622ff",
"toolbar.background": "#20201dff",
"tab_bar.background": "#262622ff",
"tab.inactive_background": "#262622ff",
@@ -1193,7 +1190,6 @@
"icon.accent": "#6684dfff",
"status_bar.background": "#cecab4ff",
"title_bar.background": "#cecab4ff",
"title_bar.inactive_background": "#eeebd7ff",
"toolbar.background": "#fefbecff",
"tab_bar.background": "#eeebd7ff",
"tab.inactive_background": "#eeebd7ff",
@@ -1578,7 +1574,6 @@
"icon.accent": "#36a165ff",
"status_bar.background": "#424136ff",
"title_bar.background": "#424136ff",
"title_bar.inactive_background": "#2c2b23ff",
"toolbar.background": "#22221bff",
"tab_bar.background": "#2c2b23ff",
"tab.inactive_background": "#2c2b23ff",
@@ -1963,7 +1958,6 @@
"icon.accent": "#37a165ff",
"status_bar.background": "#c5c4b9ff",
"title_bar.background": "#c5c4b9ff",
"title_bar.inactive_background": "#ebeae3ff",
"toolbar.background": "#f4f3ecff",
"tab_bar.background": "#ebeae3ff",
"tab.inactive_background": "#ebeae3ff",
@@ -2348,7 +2342,6 @@
"icon.accent": "#407ee6ff",
"status_bar.background": "#443c39ff",
"title_bar.background": "#443c39ff",
"title_bar.inactive_background": "#27211eff",
"toolbar.background": "#1b1918ff",
"tab_bar.background": "#27211eff",
"tab.inactive_background": "#27211eff",
@@ -2733,7 +2726,6 @@
"icon.accent": "#407ee6ff",
"status_bar.background": "#ccc7c5ff",
"title_bar.background": "#ccc7c5ff",
"title_bar.inactive_background": "#e9e6e4ff",
"toolbar.background": "#f0eeedff",
"tab_bar.background": "#e9e6e4ff",
"tab.inactive_background": "#e9e6e4ff",
@@ -3118,7 +3110,6 @@
"icon.accent": "#5169ebff",
"status_bar.background": "#433a43ff",
"title_bar.background": "#433a43ff",
"title_bar.inactive_background": "#252025ff",
"toolbar.background": "#1b181bff",
"tab_bar.background": "#252025ff",
"tab.inactive_background": "#252025ff",
@@ -3503,7 +3494,6 @@
"icon.accent": "#5169ebff",
"status_bar.background": "#c6b8c6ff",
"title_bar.background": "#c6b8c6ff",
"title_bar.inactive_background": "#e0d5e0ff",
"toolbar.background": "#f7f3f7ff",
"tab_bar.background": "#e0d5e0ff",
"tab.inactive_background": "#e0d5e0ff",
@@ -3888,7 +3878,6 @@
"icon.accent": "#267eadff",
"status_bar.background": "#33444dff",
"title_bar.background": "#33444dff",
"title_bar.inactive_background": "#1c2529ff",
"toolbar.background": "#161b1dff",
"tab_bar.background": "#1c2529ff",
"tab.inactive_background": "#1c2529ff",
@@ -4273,7 +4262,6 @@
"icon.accent": "#267eadff",
"status_bar.background": "#a6cadcff",
"title_bar.background": "#a6cadcff",
"title_bar.inactive_background": "#cdeaf9ff",
"toolbar.background": "#ebf8ffff",
"tab_bar.background": "#cdeaf9ff",
"tab.inactive_background": "#cdeaf9ff",
@@ -4658,7 +4646,6 @@
"icon.accent": "#7272caff",
"status_bar.background": "#3b3535ff",
"title_bar.background": "#3b3535ff",
"title_bar.inactive_background": "#252020ff",
"toolbar.background": "#1b1818ff",
"tab_bar.background": "#252020ff",
"tab.inactive_background": "#252020ff",
@@ -5043,7 +5030,6 @@
"icon.accent": "#7272caff",
"status_bar.background": "#c1bbbbff",
"title_bar.background": "#c1bbbbff",
"title_bar.inactive_background": "#ebe3e3ff",
"toolbar.background": "#f4ececff",
"tab_bar.background": "#ebe3e3ff",
"tab.inactive_background": "#ebe3e3ff",
@@ -5428,7 +5414,6 @@
"icon.accent": "#468b8fff",
"status_bar.background": "#353f39ff",
"title_bar.background": "#353f39ff",
"title_bar.inactive_background": "#1f2621ff",
"toolbar.background": "#171c19ff",
"tab_bar.background": "#1f2621ff",
"tab.inactive_background": "#1f2621ff",
@@ -5813,7 +5798,6 @@
"icon.accent": "#488b90ff",
"status_bar.background": "#bcc5bfff",
"title_bar.background": "#bcc5bfff",
"title_bar.inactive_background": "#e3ebe6ff",
"toolbar.background": "#ecf4eeff",
"tab_bar.background": "#e3ebe6ff",
"tab.inactive_background": "#e3ebe6ff",
@@ -6198,7 +6182,6 @@
"icon.accent": "#3e62f4ff",
"status_bar.background": "#3b453bff",
"title_bar.background": "#3b453bff",
"title_bar.inactive_background": "#1f231fff",
"toolbar.background": "#131513ff",
"tab_bar.background": "#1f231fff",
"tab.inactive_background": "#1f231fff",
@@ -6583,7 +6566,6 @@
"icon.accent": "#3e61f4ff",
"status_bar.background": "#b4ceb4ff",
"title_bar.background": "#b4ceb4ff",
"title_bar.inactive_background": "#daeedaff",
"toolbar.background": "#f3faf3ff",
"tab_bar.background": "#daeedaff",
"tab.inactive_background": "#daeedaff",
@@ -6968,7 +6950,6 @@
"icon.accent": "#3e8ed0ff",
"status_bar.background": "#3e4769ff",
"title_bar.background": "#3e4769ff",
"title_bar.inactive_background": "#262f51ff",
"toolbar.background": "#202646ff",
"tab_bar.background": "#262f51ff",
"tab.inactive_background": "#262f51ff",
@@ -7353,7 +7334,6 @@
"icon.accent": "#3e8fd0ff",
"status_bar.background": "#c1c5d8ff",
"title_bar.background": "#c1c5d8ff",
"title_bar.inactive_background": "#e5e8f5ff",
"toolbar.background": "#f5f7ffff",
"tab_bar.background": "#e5e8f5ff",
"tab.inactive_background": "#e5e8f5ff",

View File

@@ -38,7 +38,6 @@
"icon.accent": "#5ac1feff",
"status_bar.background": "#313337ff",
"title_bar.background": "#313337ff",
"title_bar.inactive_background": "#1f2127ff",
"toolbar.background": "#0d1016ff",
"tab_bar.background": "#1f2127ff",
"tab.inactive_background": "#1f2127ff",
@@ -408,7 +407,6 @@
"icon.accent": "#3b9ee5ff",
"status_bar.background": "#dcdddeff",
"title_bar.background": "#dcdddeff",
"title_bar.inactive_background": "#ececedff",
"toolbar.background": "#fcfcfcff",
"tab_bar.background": "#ececedff",
"tab.inactive_background": "#ececedff",
@@ -778,7 +776,6 @@
"icon.accent": "#72cffeff",
"status_bar.background": "#464a52ff",
"title_bar.background": "#464a52ff",
"title_bar.inactive_background": "#353944ff",
"toolbar.background": "#242835ff",
"tab_bar.background": "#353944ff",
"tab.inactive_background": "#353944ff",

View File

@@ -47,7 +47,6 @@
"icon.accent": "#83a598ff",
"status_bar.background": "#4c4642ff",
"title_bar.background": "#4c4642ff",
"title_bar.inactive_background": "#3a3735ff",
"toolbar.background": "#282828ff",
"tab_bar.background": "#3a3735ff",
"tab.inactive_background": "#3a3735ff",
@@ -431,7 +430,6 @@
"icon.accent": "#83a598ff",
"status_bar.background": "#4c4642ff",
"title_bar.background": "#4c4642ff",
"title_bar.inactive_background": "#393634ff",
"toolbar.background": "#1d2021ff",
"tab_bar.background": "#393634ff",
"tab.inactive_background": "#393634ff",
@@ -815,7 +813,6 @@
"icon.accent": "#83a598ff",
"status_bar.background": "#4c4642ff",
"title_bar.background": "#4c4642ff",
"title_bar.inactive_background": "#3b3735ff",
"toolbar.background": "#32302fff",
"tab_bar.background": "#3b3735ff",
"tab.inactive_background": "#3b3735ff",
@@ -1199,7 +1196,6 @@
"icon.accent": "#0b6678ff",
"status_bar.background": "#d9c8a4ff",
"title_bar.background": "#d9c8a4ff",
"title_bar.inactive_background": "#ecddb4ff",
"toolbar.background": "#fbf1c7ff",
"tab_bar.background": "#ecddb4ff",
"tab.inactive_background": "#ecddb4ff",
@@ -1583,7 +1579,6 @@
"icon.accent": "#0b6678ff",
"status_bar.background": "#d9c8a4ff",
"title_bar.background": "#d9c8a4ff",
"title_bar.inactive_background": "#ecddb5ff",
"toolbar.background": "#f9f5d7ff",
"tab_bar.background": "#ecddb5ff",
"tab.inactive_background": "#ecddb5ff",
@@ -1967,7 +1962,6 @@
"icon.accent": "#0b6678ff",
"status_bar.background": "#d9c8a4ff",
"title_bar.background": "#d9c8a4ff",
"title_bar.inactive_background": "#ecdcb3ff",
"toolbar.background": "#f2e5bcff",
"tab_bar.background": "#ecdcb3ff",
"tab.inactive_background": "#ecdcb3ff",

View File

@@ -38,7 +38,6 @@
"icon.accent": "#74ade8ff",
"status_bar.background": "#3b414dff",
"title_bar.background": "#3b414dff",
"title_bar.inactive_background": "#2e343eff",
"toolbar.background": "#282c33ff",
"tab_bar.background": "#2f343eff",
"tab.inactive_background": "#2f343eff",
@@ -413,7 +412,6 @@
"icon.accent": "#5c78e2ff",
"status_bar.background": "#dcdcddff",
"title_bar.background": "#dcdcddff",
"title_bar.inactive_background": "#ebebecff",
"toolbar.background": "#fafafaff",
"tab_bar.background": "#ebebecff",
"tab.inactive_background": "#ebebecff",
@@ -493,7 +491,7 @@
"info": "#5c78e2ff",
"info.background": "#e2e2faff",
"info.border": "#cbcdf6ff",
"modified": "#a47a23ff",
"modified": "#dec184ff",
"modified.background": "#faf2e6ff",
"modified.border": "#f4e7d1ff",
"predictive": "#9b9ec6ff",

View File

@@ -38,7 +38,6 @@
"icon.accent": "#9bced6ff",
"status_bar.background": "#292738ff",
"title_bar.background": "#292738ff",
"title_bar.inactive_background": "#1c1b2aff",
"toolbar.background": "#191724ff",
"tab_bar.background": "#1c1b2aff",
"tab.inactive_background": "#1c1b2aff",
@@ -418,7 +417,6 @@
"icon.accent": "#57949fff",
"status_bar.background": "#dcd8d8ff",
"title_bar.background": "#dcd8d8ff",
"title_bar.inactive_background": "#fef9f2ff",
"toolbar.background": "#faf4edff",
"tab_bar.background": "#fef9f2ff",
"tab.inactive_background": "#fef9f2ff",
@@ -798,7 +796,6 @@
"icon.accent": "#9bced6ff",
"status_bar.background": "#38354eff",
"title_bar.background": "#38354eff",
"title_bar.inactive_background": "#28253cff",
"toolbar.background": "#232136ff",
"tab_bar.background": "#28253cff",
"tab.inactive_background": "#28253cff",

View File

@@ -38,7 +38,6 @@
"icon.accent": "#518b8bff",
"status_bar.background": "#333944ff",
"title_bar.background": "#333944ff",
"title_bar.inactive_background": "#2b3038ff",
"toolbar.background": "#282c33ff",
"tab_bar.background": "#2b3038ff",
"tab.inactive_background": "#2b3038ff",

View File

@@ -38,7 +38,6 @@
"icon.accent": "#278ad1ff",
"status_bar.background": "#073743ff",
"title_bar.background": "#073743ff",
"title_bar.inactive_background": "#04313bff",
"toolbar.background": "#002a35ff",
"tab_bar.background": "#04313bff",
"tab.inactive_background": "#04313bff",
@@ -408,7 +407,6 @@
"icon.accent": "#288bd1ff",
"status_bar.background": "#cfd0c4ff",
"title_bar.background": "#cfd0c4ff",
"title_bar.inactive_background": "#f3eddaff",
"toolbar.background": "#fdf6e3ff",
"tab_bar.background": "#f3eddaff",
"tab.inactive_background": "#f3eddaff",

View File

@@ -38,7 +38,6 @@
"icon.accent": "#499befff",
"status_bar.background": "#2a261cff",
"title_bar.background": "#2a261cff",
"title_bar.inactive_background": "#231f16ff",
"toolbar.background": "#1b1810ff",
"tab_bar.background": "#231f16ff",
"tab.inactive_background": "#231f16ff",

View File

@@ -12,14 +12,6 @@ workspace = true
path = "src/assistant.rs"
doctest = false
[features]
test-support = [
"editor/test-support",
"language/test-support",
"project/test-support",
"text/test-support",
]
[dependencies]
anthropic = { workspace = true, features = ["schemars"] }
anyhow.workspace = true
@@ -29,7 +21,6 @@ breadcrumbs.workspace = true
cargo_toml.workspace = true
chrono.workspace = true
client.workspace = true
clock.workspace = true
collections.workspace = true
command_palette_hooks.workspace = true
editor.workspace = true
@@ -81,9 +72,7 @@ picker.workspace = true
ctor.workspace = true
editor = { workspace = true, features = ["test-support"] }
env_logger.workspace = true
language = { workspace = true, features = ["test-support"] }
log.workspace = true
project = { workspace = true, features = ["test-support"] }
rand.workspace = true
text = { workspace = true, features = ["test-support"] }
unindent.workspace = true

View File

@@ -1,8 +1,7 @@
pub mod assistant_panel;
pub mod assistant_settings;
mod completion_provider;
mod context;
pub mod context_store;
mod context_store;
mod inline_assistant;
mod model_selector;
mod prompt_library;
@@ -17,9 +16,8 @@ use assistant_settings::{AnthropicModel, AssistantSettings, CloudModel, OllamaMo
use assistant_slash_command::SlashCommandRegistry;
use client::{proto, Client};
use command_palette_hooks::CommandPaletteFilter;
pub use completion_provider::*;
pub use context::*;
pub use context_store::*;
pub(crate) use completion_provider::*;
pub(crate) use context_store::*;
use fs::Fs;
use gpui::{actions, AppContext, Global, SharedString, UpdateGlobal};
use indexed_docs::IndexedDocsRegistry;
@@ -30,8 +28,8 @@ use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsStore};
use slash_command::{
active_command, default_command, diagnostics_command, docs_command, fetch_command,
file_command, now_command, project_command, prompt_command, search_command, symbols_command,
tabs_command, term_command,
file_command, now_command, project_command, prompt_command, search_command, tabs_command,
term_command,
};
use std::{
fmt::{self, Display},
@@ -59,14 +57,10 @@ actions!(
]
);
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct MessageId(clock::Lamport);
impl MessageId {
pub fn as_u64(self) -> u64 {
self.0.as_u64()
}
}
#[derive(
Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize,
)]
struct MessageId(usize);
#[derive(Clone, Copy, Serialize, Deserialize, Debug, Eq, PartialEq)]
#[serde(rename_all = "lowercase")]
@@ -77,26 +71,8 @@ pub enum Role {
}
impl Role {
pub fn from_proto(role: i32) -> Role {
match proto::LanguageModelRole::from_i32(role) {
Some(proto::LanguageModelRole::LanguageModelUser) => Role::User,
Some(proto::LanguageModelRole::LanguageModelAssistant) => Role::Assistant,
Some(proto::LanguageModelRole::LanguageModelSystem) => Role::System,
Some(proto::LanguageModelRole::LanguageModelTool) => Role::System,
None => Role::User,
}
}
pub fn to_proto(&self) -> proto::LanguageModelRole {
match self {
Role::User => proto::LanguageModelRole::LanguageModelUser,
Role::Assistant => proto::LanguageModelRole::LanguageModelAssistant,
Role::System => proto::LanguageModelRole::LanguageModelSystem,
}
}
pub fn cycle(self) -> Role {
match self {
pub fn cycle(&mut self) {
*self = match self {
Role::User => Role::Assistant,
Role::Assistant => Role::System,
Role::System => Role::User,
@@ -175,7 +151,11 @@ pub struct LanguageModelRequestMessage {
impl LanguageModelRequestMessage {
pub fn to_proto(&self) -> proto::LanguageModelRequestMessage {
proto::LanguageModelRequestMessage {
role: self.role.to_proto() as i32,
role: match self.role {
Role::User => proto::LanguageModelRole::LanguageModelUser,
Role::Assistant => proto::LanguageModelRole::LanguageModelAssistant,
Role::System => proto::LanguageModelRole::LanguageModelSystem,
} as i32,
content: self.content.clone(),
tool_calls: Vec::new(),
tool_call_id: None,
@@ -242,48 +222,19 @@ pub struct LanguageModelChoiceDelta {
pub finish_reason: Option<String>,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum MessageStatus {
#[derive(Clone, Debug, Serialize, Deserialize)]
struct MessageMetadata {
role: Role,
status: MessageStatus,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
enum MessageStatus {
Pending,
Done,
Error(SharedString),
}
impl MessageStatus {
pub fn from_proto(status: proto::ContextMessageStatus) -> MessageStatus {
match status.variant {
Some(proto::context_message_status::Variant::Pending(_)) => MessageStatus::Pending,
Some(proto::context_message_status::Variant::Done(_)) => MessageStatus::Done,
Some(proto::context_message_status::Variant::Error(error)) => {
MessageStatus::Error(error.message.into())
}
None => MessageStatus::Pending,
}
}
pub fn to_proto(&self) -> proto::ContextMessageStatus {
match self {
MessageStatus::Pending => proto::ContextMessageStatus {
variant: Some(proto::context_message_status::Variant::Pending(
proto::context_message_status::Pending {},
)),
},
MessageStatus::Done => proto::ContextMessageStatus {
variant: Some(proto::context_message_status::Variant::Done(
proto::context_message_status::Done {},
)),
},
MessageStatus::Error(message) => proto::ContextMessageStatus {
variant: Some(proto::context_message_status::Variant::Error(
proto::context_message_status::Error {
message: message.to_string(),
},
)),
},
}
}
}
/// The state pertaining to the Assistant.
#[derive(Default)]
struct Assistant {
@@ -336,7 +287,6 @@ pub fn init(fs: Arc<dyn Fs>, client: Arc<Client>, cx: &mut AppContext) {
})
.detach();
context_store::init(&client);
prompt_library::init(cx);
completion_provider::init(client.clone(), cx);
assistant_slash_command::init(cx);
@@ -367,7 +317,6 @@ fn register_slash_commands(cx: &mut AppContext) {
let slash_command_registry = SlashCommandRegistry::global(cx);
slash_command_registry.register_command(file_command::FileSlashCommand, true);
slash_command_registry.register_command(active_command::ActiveSlashCommand, true);
slash_command_registry.register_command(symbols_command::OutlineSlashCommand, true);
slash_command_registry.register_command(tabs_command::TabsSlashCommand, true);
slash_command_registry.register_command(project_command::ProjectSlashCommand, true);
slash_command_registry.register_command(search_command::SearchSlashCommand, true);

File diff suppressed because it is too large Load Diff

View File

@@ -27,8 +27,6 @@ pub enum CloudModel {
Claude3Opus,
Claude3Sonnet,
Claude3Haiku,
Gemini15Pro,
Gemini15Flash,
Custom(String),
}
@@ -111,8 +109,6 @@ impl CloudModel {
Self::Claude3Opus => "claude-3-opus",
Self::Claude3Sonnet => "claude-3-sonnet",
Self::Claude3Haiku => "claude-3-haiku",
Self::Gemini15Pro => "gemini-1.5-pro",
Self::Gemini15Flash => "gemini-1.5-flash",
Self::Custom(id) => id,
}
}
@@ -127,8 +123,6 @@ impl CloudModel {
Self::Claude3Opus => "Claude 3 Opus",
Self::Claude3Sonnet => "Claude 3 Sonnet",
Self::Claude3Haiku => "Claude 3 Haiku",
Self::Gemini15Pro => "Gemini 1.5 Pro",
Self::Gemini15Flash => "Gemini 1.5 Flash",
Self::Custom(id) => id.as_str(),
}
}
@@ -142,8 +136,6 @@ impl CloudModel {
| Self::Claude3Opus
| Self::Claude3Sonnet
| Self::Claude3Haiku => 200000,
Self::Gemini15Pro => 128000,
Self::Gemini15Flash => 32000,
Self::Custom(_) => 4096, // TODO: Make this configurable
}
}

View File

@@ -1,13 +1,13 @@
mod anthropic;
mod cloud;
#[cfg(any(test, feature = "test-support"))]
#[cfg(test)]
mod fake;
mod ollama;
mod open_ai;
pub use anthropic::*;
pub use cloud::*;
#[cfg(any(test, feature = "test-support"))]
#[cfg(test)]
pub use fake::*;
pub use ollama::*;
pub use open_ai::*;

View File

@@ -13,6 +13,7 @@ pub struct FakeCompletionProvider {
}
impl FakeCompletionProvider {
#[cfg(test)]
pub fn setup_test(cx: &mut AppContext) -> Self {
use crate::CompletionProvider;
use parking_lot::RwLock;

File diff suppressed because it is too large Load Diff

View File

@@ -1,117 +1,97 @@
use crate::{
Context, ContextEvent, ContextId, ContextOperation, ContextVersion, SavedContext,
SavedContextMetadata,
};
use anyhow::{anyhow, Context as _, Result};
use client::{proto, telemetry::Telemetry, Client, TypedEnvelope};
use clock::ReplicaId;
use crate::{assistant_settings::OpenAiModel, MessageId, MessageMetadata};
use anyhow::{anyhow, Result};
use assistant_slash_command::SlashCommandOutputSection;
use collections::HashMap;
use fs::Fs;
use futures::StreamExt;
use fuzzy::StringMatchCandidate;
use gpui::{AppContext, AsyncAppContext, Context as _, Model, ModelContext, Task, WeakModel};
use language::LanguageRegistry;
use gpui::{AppContext, Model, ModelContext, Task};
use paths::contexts_dir;
use project::Project;
use regex::Regex;
use std::{
cmp::Reverse,
ffi::OsStr,
mem,
path::{Path, PathBuf},
sync::Arc,
time::Duration,
};
use serde::{Deserialize, Serialize};
use std::{cmp::Reverse, ffi::OsStr, path::PathBuf, sync::Arc, time::Duration};
use ui::Context;
use util::{ResultExt, TryFutureExt};
pub fn init(client: &Arc<Client>) {
client.add_model_message_handler(ContextStore::handle_advertise_contexts);
client.add_model_request_handler(ContextStore::handle_open_context);
client.add_model_message_handler(ContextStore::handle_update_context);
client.add_model_request_handler(ContextStore::handle_synchronize_contexts);
#[derive(Serialize, Deserialize)]
pub struct SavedMessage {
pub id: MessageId,
pub start: usize,
}
#[derive(Serialize, Deserialize)]
pub struct SavedContext {
pub id: Option<String>,
pub zed: String,
pub version: String,
pub text: String,
pub messages: Vec<SavedMessage>,
pub message_metadata: HashMap<MessageId, MessageMetadata>,
pub summary: String,
pub slash_command_output_sections: Vec<SlashCommandOutputSection<usize>>,
}
impl SavedContext {
pub const VERSION: &'static str = "0.3.0";
}
#[derive(Serialize, Deserialize)]
pub struct SavedContextV0_2_0 {
pub id: Option<String>,
pub zed: String,
pub version: String,
pub text: String,
pub messages: Vec<SavedMessage>,
pub message_metadata: HashMap<MessageId, MessageMetadata>,
pub summary: String,
}
#[derive(Serialize, Deserialize)]
struct SavedContextV0_1_0 {
id: Option<String>,
zed: String,
version: String,
text: String,
messages: Vec<SavedMessage>,
message_metadata: HashMap<MessageId, MessageMetadata>,
summary: String,
api_url: Option<String>,
model: OpenAiModel,
}
#[derive(Clone)]
pub struct RemoteContextMetadata {
pub id: ContextId,
pub summary: Option<String>,
pub struct SavedContextMetadata {
pub title: String,
pub path: PathBuf,
pub mtime: chrono::DateTime<chrono::Local>,
}
pub struct ContextStore {
contexts: Vec<ContextHandle>,
contexts_metadata: Vec<SavedContextMetadata>,
host_contexts: Vec<RemoteContextMetadata>,
fs: Arc<dyn Fs>,
languages: Arc<LanguageRegistry>,
telemetry: Arc<Telemetry>,
_watch_updates: Task<Option<()>>,
client: Arc<Client>,
project: Model<Project>,
project_is_shared: bool,
client_subscription: Option<client::Subscription>,
_project_subscriptions: Vec<gpui::Subscription>,
}
enum ContextHandle {
Weak(WeakModel<Context>),
Strong(Model<Context>),
}
impl ContextHandle {
fn upgrade(&self) -> Option<Model<Context>> {
match self {
ContextHandle::Weak(weak) => weak.upgrade(),
ContextHandle::Strong(strong) => Some(strong.clone()),
}
}
fn downgrade(&self) -> WeakModel<Context> {
match self {
ContextHandle::Weak(weak) => weak.clone(),
ContextHandle::Strong(strong) => strong.downgrade(),
}
}
}
impl ContextStore {
pub fn new(project: Model<Project>, cx: &mut AppContext) -> Task<Result<Model<Self>>> {
let fs = project.read(cx).fs().clone();
let languages = project.read(cx).languages().clone();
let telemetry = project.read(cx).client().telemetry().clone();
pub fn new(fs: Arc<dyn Fs>, cx: &mut AppContext) -> Task<Result<Model<Self>>> {
cx.spawn(|mut cx| async move {
const CONTEXT_WATCH_DURATION: Duration = Duration::from_millis(100);
let (mut events, _) = fs.watch(contexts_dir(), CONTEXT_WATCH_DURATION).await;
let this = cx.new_model(|cx: &mut ModelContext<Self>| {
let mut this = Self {
contexts: Vec::new(),
contexts_metadata: Vec::new(),
host_contexts: Vec::new(),
fs,
languages,
telemetry,
_watch_updates: cx.spawn(|this, mut cx| {
async move {
while events.next().await.is_some() {
this.update(&mut cx, |this, cx| this.reload(cx))?
.await
.log_err();
}
anyhow::Ok(())
let this = cx.new_model(|cx: &mut ModelContext<Self>| Self {
contexts_metadata: Vec::new(),
fs,
_watch_updates: cx.spawn(|this, mut cx| {
async move {
while events.next().await.is_some() {
this.update(&mut cx, |this, cx| this.reload(cx))?
.await
.log_err();
}
.log_err()
}),
client_subscription: None,
_project_subscriptions: vec![
cx.observe(&project, Self::handle_project_changed),
cx.subscribe(&project, Self::handle_project_event),
],
project_is_shared: false,
client: project.read(cx).client(),
project: project.clone(),
};
this.handle_project_changed(project, cx);
this.synchronize_contexts(cx);
this
anyhow::Ok(())
}
.log_err()
}),
})?;
this.update(&mut cx, |this, cx| this.reload(cx))?
.await
@@ -120,431 +100,52 @@ impl ContextStore {
})
}
async fn handle_advertise_contexts(
this: Model<Self>,
envelope: TypedEnvelope<proto::AdvertiseContexts>,
mut cx: AsyncAppContext,
) -> Result<()> {
this.update(&mut cx, |this, cx| {
this.host_contexts = envelope
.payload
.contexts
.into_iter()
.map(|context| RemoteContextMetadata {
id: ContextId::from_proto(context.context_id),
summary: context.summary,
})
.collect();
cx.notify();
})
}
async fn handle_open_context(
this: Model<Self>,
envelope: TypedEnvelope<proto::OpenContext>,
mut cx: AsyncAppContext,
) -> Result<proto::OpenContextResponse> {
let context_id = ContextId::from_proto(envelope.payload.context_id);
let operations = this.update(&mut cx, |this, cx| {
if this.project.read(cx).is_remote() {
return Err(anyhow!("only the host contexts can be opened"));
}
let context = this
.loaded_context_for_id(&context_id, cx)
.context("context not found")?;
if context.read(cx).replica_id() != ReplicaId::default() {
return Err(anyhow!("context must be opened via the host"));
}
anyhow::Ok(
context
.read(cx)
.serialize_ops(&ContextVersion::default(), cx),
)
})??;
let operations = operations.await;
Ok(proto::OpenContextResponse {
context: Some(proto::Context { operations }),
})
}
async fn handle_update_context(
this: Model<Self>,
envelope: TypedEnvelope<proto::UpdateContext>,
mut cx: AsyncAppContext,
) -> Result<()> {
this.update(&mut cx, |this, cx| {
let context_id = ContextId::from_proto(envelope.payload.context_id);
if let Some(context) = this.loaded_context_for_id(&context_id, cx) {
let operation_proto = envelope.payload.operation.context("invalid operation")?;
let operation = ContextOperation::from_proto(operation_proto)?;
context.update(cx, |context, cx| context.apply_ops([operation], cx))?;
}
Ok(())
})?
}
async fn handle_synchronize_contexts(
this: Model<Self>,
envelope: TypedEnvelope<proto::SynchronizeContexts>,
mut cx: AsyncAppContext,
) -> Result<proto::SynchronizeContextsResponse> {
this.update(&mut cx, |this, cx| {
if this.project.read(cx).is_remote() {
return Err(anyhow!("only the host can synchronize contexts"));
}
let mut local_versions = Vec::new();
for remote_version_proto in envelope.payload.contexts {
let remote_version = ContextVersion::from_proto(&remote_version_proto);
let context_id = ContextId::from_proto(remote_version_proto.context_id);
if let Some(context) = this.loaded_context_for_id(&context_id, cx) {
let context = context.read(cx);
let operations = context.serialize_ops(&remote_version, cx);
local_versions.push(context.version(cx).to_proto(context_id.clone()));
let client = this.client.clone();
let project_id = envelope.payload.project_id;
cx.background_executor()
.spawn(async move {
let operations = operations.await;
for operation in operations {
client.send(proto::UpdateContext {
project_id,
context_id: context_id.to_proto(),
operation: Some(operation),
})?;
}
anyhow::Ok(())
})
.detach_and_log_err(cx);
}
}
this.advertise_contexts(cx);
anyhow::Ok(proto::SynchronizeContextsResponse {
contexts: local_versions,
})
})?
}
fn handle_project_changed(&mut self, _: Model<Project>, cx: &mut ModelContext<Self>) {
let is_shared = self.project.read(cx).is_shared();
let was_shared = mem::replace(&mut self.project_is_shared, is_shared);
if is_shared == was_shared {
return;
}
if is_shared {
self.contexts.retain_mut(|context| {
if let Some(strong_context) = context.upgrade() {
*context = ContextHandle::Strong(strong_context);
true
} else {
false
}
});
let remote_id = self.project.read(cx).remote_id().unwrap();
self.client_subscription = self
.client
.subscribe_to_entity(remote_id)
.log_err()
.map(|subscription| subscription.set_model(&cx.handle(), &mut cx.to_async()));
self.advertise_contexts(cx);
} else {
self.client_subscription = None;
}
}
fn handle_project_event(
&mut self,
_: Model<Project>,
event: &project::Event,
cx: &mut ModelContext<Self>,
) {
match event {
project::Event::Reshared => {
self.advertise_contexts(cx);
}
project::Event::HostReshared | project::Event::Rejoined => {
self.synchronize_contexts(cx);
}
project::Event::DisconnectedFromHost => {
self.contexts.retain_mut(|context| {
if let Some(strong_context) = context.upgrade() {
*context = ContextHandle::Weak(context.downgrade());
strong_context.update(cx, |context, cx| {
if context.replica_id() != ReplicaId::default() {
context.set_capability(language::Capability::ReadOnly, cx);
}
});
true
} else {
false
}
});
self.host_contexts.clear();
cx.notify();
}
_ => {}
}
}
pub fn create(&mut self, cx: &mut ModelContext<Self>) -> Model<Context> {
let context = cx.new_model(|cx| {
Context::local(self.languages.clone(), Some(self.telemetry.clone()), cx)
});
self.register_context(&context, cx);
context
}
pub fn open_local_context(
&mut self,
path: PathBuf,
cx: &ModelContext<Self>,
) -> Task<Result<Model<Context>>> {
if let Some(existing_context) = self.loaded_context_for_path(&path, cx) {
return Task::ready(Ok(existing_context));
}
pub fn load(&self, path: PathBuf, cx: &AppContext) -> Task<Result<SavedContext>> {
let fs = self.fs.clone();
let languages = self.languages.clone();
let telemetry = self.telemetry.clone();
let load = cx.background_executor().spawn({
let path = path.clone();
async move {
let saved_context = fs.load(&path).await?;
SavedContext::from_json(&saved_context)
}
});
cx.spawn(|this, mut cx| async move {
let saved_context = load.await?;
let context = cx.new_model(|cx| {
Context::deserialize(saved_context, path.clone(), languages, Some(telemetry), cx)
})?;
this.update(&mut cx, |this, cx| {
if let Some(existing_context) = this.loaded_context_for_path(&path, cx) {
existing_context
} else {
this.register_context(&context, cx);
context
}
})
})
}
fn loaded_context_for_path(&self, path: &Path, cx: &AppContext) -> Option<Model<Context>> {
self.contexts.iter().find_map(|context| {
let context = context.upgrade()?;
if context.read(cx).path() == Some(path) {
Some(context)
} else {
None
}
})
}
fn loaded_context_for_id(&self, id: &ContextId, cx: &AppContext) -> Option<Model<Context>> {
self.contexts.iter().find_map(|context| {
let context = context.upgrade()?;
if context.read(cx).id() == id {
Some(context)
} else {
None
}
})
}
pub fn open_remote_context(
&mut self,
context_id: ContextId,
cx: &mut ModelContext<Self>,
) -> Task<Result<Model<Context>>> {
let project = self.project.read(cx);
let Some(project_id) = project.remote_id() else {
return Task::ready(Err(anyhow!("project was not remote")));
};
if project.is_local() {
return Task::ready(Err(anyhow!("cannot open remote contexts as the host")));
}
if let Some(context) = self.loaded_context_for_id(&context_id, cx) {
return Task::ready(Ok(context));
}
let replica_id = project.replica_id();
let capability = project.capability();
let language_registry = self.languages.clone();
let telemetry = self.telemetry.clone();
let request = self.client.request(proto::OpenContext {
project_id,
context_id: context_id.to_proto(),
});
cx.spawn(|this, mut cx| async move {
let response = request.await?;
let context_proto = response.context.context("invalid context")?;
let context = cx.new_model(|cx| {
Context::new(
context_id.clone(),
replica_id,
capability,
language_registry,
Some(telemetry),
cx,
)
})?;
let operations = cx
.background_executor()
.spawn(async move {
context_proto
.operations
.into_iter()
.map(|op| ContextOperation::from_proto(op))
.collect::<Result<Vec<_>>>()
})
.await?;
context.update(&mut cx, |context, cx| context.apply_ops(operations, cx))??;
this.update(&mut cx, |this, cx| {
if let Some(existing_context) = this.loaded_context_for_id(&context_id, cx) {
existing_context
} else {
this.register_context(&context, cx);
this.synchronize_contexts(cx);
context
}
})
})
}
fn register_context(&mut self, context: &Model<Context>, cx: &mut ModelContext<Self>) {
let handle = if self.project_is_shared {
ContextHandle::Strong(context.clone())
} else {
ContextHandle::Weak(context.downgrade())
};
self.contexts.push(handle);
self.advertise_contexts(cx);
cx.subscribe(context, Self::handle_context_event).detach();
}
fn handle_context_event(
&mut self,
context: Model<Context>,
event: &ContextEvent,
cx: &mut ModelContext<Self>,
) {
let Some(project_id) = self.project.read(cx).remote_id() else {
return;
};
match event {
ContextEvent::SummaryChanged => {
self.advertise_contexts(cx);
}
ContextEvent::Operation(operation) => {
let context_id = context.read(cx).id().to_proto();
let operation = operation.to_proto();
self.client
.send(proto::UpdateContext {
project_id,
context_id,
operation: Some(operation),
})
.log_err();
}
_ => {}
}
}
fn advertise_contexts(&self, cx: &AppContext) {
let Some(project_id) = self.project.read(cx).remote_id() else {
return;
};
// For now, only the host can advertise their open contexts.
if self.project.read(cx).is_remote() {
return;
}
let contexts = self
.contexts
.iter()
.rev()
.filter_map(|context| {
let context = context.upgrade()?.read(cx);
if context.replica_id() == ReplicaId::default() {
Some(proto::ContextMetadata {
context_id: context.id().to_proto(),
summary: context.summary().map(|summary| summary.text.clone()),
})
} else {
None
}
})
.collect();
self.client
.send(proto::AdvertiseContexts {
project_id,
contexts,
})
.ok();
}
fn synchronize_contexts(&mut self, cx: &mut ModelContext<Self>) {
let Some(project_id) = self.project.read(cx).remote_id() else {
return;
};
let contexts = self
.contexts
.iter()
.filter_map(|context| {
let context = context.upgrade()?.read(cx);
if context.replica_id() != ReplicaId::default() {
Some(context.version(cx).to_proto(context.id().clone()))
} else {
None
}
})
.collect();
let client = self.client.clone();
let request = self.client.request(proto::SynchronizeContexts {
project_id,
contexts,
});
cx.spawn(|this, cx| async move {
let response = request.await?;
let mut context_ids = Vec::new();
let mut operations = Vec::new();
this.read_with(&cx, |this, cx| {
for context_version_proto in response.contexts {
let context_version = ContextVersion::from_proto(&context_version_proto);
let context_id = ContextId::from_proto(context_version_proto.context_id);
if let Some(context) = this.loaded_context_for_id(&context_id, cx) {
context_ids.push(context_id);
operations.push(context.read(cx).serialize_ops(&context_version, cx));
cx.background_executor().spawn(async move {
let saved_context = fs.load(&path).await?;
let saved_context_json = serde_json::from_str::<serde_json::Value>(&saved_context)?;
match saved_context_json
.get("version")
.ok_or_else(|| anyhow!("version not found"))?
{
serde_json::Value::String(version) => match version.as_str() {
SavedContext::VERSION => {
Ok(serde_json::from_value::<SavedContext>(saved_context_json)?)
}
}
})?;
let operations = futures::future::join_all(operations).await;
for (context_id, operations) in context_ids.into_iter().zip(operations) {
for operation in operations {
client.send(proto::UpdateContext {
project_id,
context_id: context_id.to_proto(),
operation: Some(operation),
})?;
}
"0.2.0" => {
let saved_context =
serde_json::from_value::<SavedContextV0_2_0>(saved_context_json)?;
Ok(SavedContext {
id: saved_context.id,
zed: saved_context.zed,
version: saved_context.version,
text: saved_context.text,
messages: saved_context.messages,
message_metadata: saved_context.message_metadata,
summary: saved_context.summary,
slash_command_output_sections: Vec::new(),
})
}
"0.1.0" => {
let saved_context =
serde_json::from_value::<SavedContextV0_1_0>(saved_context_json)?;
Ok(SavedContext {
id: saved_context.id,
zed: saved_context.zed,
version: saved_context.version,
text: saved_context.text,
messages: saved_context.messages,
message_metadata: saved_context.message_metadata,
summary: saved_context.summary,
slash_command_output_sections: Vec::new(),
})
}
_ => Err(anyhow!("unrecognized saved context version: {}", version)),
},
_ => Err(anyhow!("version not found on saved context")),
}
anyhow::Ok(())
})
.detach_and_log_err(cx);
}
pub fn search(&self, query: String, cx: &AppContext) -> Task<Vec<SavedContextMetadata>> {
@@ -577,10 +178,6 @@ impl ContextStore {
})
}
pub fn host_contexts(&self) -> &[RemoteContextMetadata] {
&self.host_contexts
}
fn reload(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
let fs = self.fs.clone();
cx.spawn(|this, mut cx| async move {

View File

@@ -3,6 +3,7 @@ use crate::{
InlineAssist, InlineAssistant, LanguageModelRequest, LanguageModelRequestMessage, Role,
};
use anyhow::{anyhow, Result};
use assistant_slash_command::SlashCommandRegistry;
use chrono::{DateTime, Utc};
use collections::{HashMap, HashSet};
use editor::{actions::Tab, CurrentLineHighlight, Editor, EditorElement, EditorEvent, EditorStyle};
@@ -268,7 +269,7 @@ impl PickerDelegate for PromptPickerDelegate {
.flex_none()
.py_1()
.px_2()
.mx_1()
.mx_2()
.child(editor.clone())
}
}
@@ -447,6 +448,7 @@ impl PromptLibrary {
self.set_active_prompt(Some(prompt_id), cx);
} else if let Some(prompt_metadata) = self.store.metadata(prompt_id) {
let language_registry = self.language_registry.clone();
let commands = SlashCommandRegistry::global(cx);
let prompt = self.store.load(prompt_id);
self.pending_load = cx.spawn(|this, mut cx| async move {
let prompt = prompt.await;
@@ -475,7 +477,7 @@ impl PromptLibrary {
editor.set_use_modal_editing(false);
editor.set_current_line_highlight(Some(CurrentLineHighlight::None));
editor.set_completion_provider(Box::new(
SlashCommandCompletionProvider::new(None, None),
SlashCommandCompletionProvider::new(commands, None, None),
));
if focus {
editor.focus(cx);
@@ -766,7 +768,6 @@ impl PromptLibrary {
.capture_action(cx.listener(Self::focus_active_prompt))
.bg(cx.theme().colors().panel_background)
.h_full()
.px_1()
.w_1_3()
.overflow_x_hidden()
.child(

View File

@@ -27,11 +27,11 @@ pub mod now_command;
pub mod project_command;
pub mod prompt_command;
pub mod search_command;
pub mod symbols_command;
pub mod tabs_command;
pub mod term_command;
pub(crate) struct SlashCommandCompletionProvider {
commands: Arc<SlashCommandRegistry>,
cancel_flag: Mutex<Arc<AtomicBool>>,
editor: Option<WeakView<ContextEditor>>,
workspace: Option<WeakView<Workspace>>,
@@ -46,12 +46,14 @@ pub(crate) struct SlashCommandLine {
impl SlashCommandCompletionProvider {
pub fn new(
commands: Arc<SlashCommandRegistry>,
editor: Option<WeakView<ContextEditor>>,
workspace: Option<WeakView<Workspace>>,
) -> Self {
Self {
cancel_flag: Mutex::new(Arc::new(AtomicBool::new(false))),
editor,
commands,
workspace,
}
}
@@ -63,8 +65,8 @@ impl SlashCommandCompletionProvider {
name_range: Range<Anchor>,
cx: &mut WindowContext,
) -> Task<Result<Vec<project::Completion>>> {
let commands = SlashCommandRegistry::global(cx);
let candidates = commands
let candidates = self
.commands
.command_names()
.into_iter()
.enumerate()
@@ -74,6 +76,7 @@ impl SlashCommandCompletionProvider {
char_bag: def.as_ref().into(),
})
.collect::<Vec<_>>();
let commands = self.commands.clone();
let command_name = command_name.to_string();
let editor = self.editor.clone();
let workspace = self.workspace.clone();
@@ -152,8 +155,7 @@ impl SlashCommandCompletionProvider {
flag.store(true, SeqCst);
*flag = new_cancel_flag.clone();
let commands = SlashCommandRegistry::global(cx);
if let Some(command) = commands.command(command_name) {
if let Some(command) = self.commands.command(command_name) {
let completions = command.complete_argument(
argument,
new_cancel_flag.clone(),

View File

@@ -8,8 +8,7 @@ use assistant_slash_command::{
};
use gpui::{AppContext, Model, Task, WeakView};
use indexed_docs::{
DocsDotRsProvider, IndexedDocsRegistry, IndexedDocsStore, LocalRustdocProvider, PackageName,
ProviderId,
IndexedDocsRegistry, IndexedDocsStore, LocalProvider, PackageName, ProviderId, RustdocIndexer,
};
use language::LspAdapterDelegate;
use project::{Project, ProjectPath};
@@ -35,22 +34,22 @@ impl DocsSlashCommand {
))
}
/// Ensures that the indexed doc providers for Rust are registered.
/// Ensures that the rustdoc provider is registered.
///
/// Ideally we would do this sooner, but we need to wait until we're able to
/// access the workspace so we can read the project.
fn ensure_rust_doc_providers_are_registered(
fn ensure_rustdoc_provider_is_registered(
&self,
workspace: Option<WeakView<Workspace>>,
cx: &mut AppContext,
) {
let indexed_docs_registry = IndexedDocsRegistry::global(cx);
if indexed_docs_registry
.get_provider_store(LocalRustdocProvider::id())
.get_provider_store(ProviderId::rustdoc())
.is_none()
{
let index_provider_deps = maybe!({
let workspace = workspace.clone().ok_or_else(|| anyhow!("no workspace"))?;
let workspace = workspace.ok_or_else(|| anyhow!("no workspace"))?;
let workspace = workspace
.upgrade()
.ok_or_else(|| anyhow!("workspace was dropped"))?;
@@ -64,29 +63,9 @@ impl DocsSlashCommand {
});
if let Some((fs, cargo_workspace_root)) = index_provider_deps.log_err() {
indexed_docs_registry.register_provider(Box::new(LocalRustdocProvider::new(
fs,
cargo_workspace_root,
)));
}
}
if indexed_docs_registry
.get_provider_store(DocsDotRsProvider::id())
.is_none()
{
let http_client = maybe!({
let workspace = workspace.ok_or_else(|| anyhow!("no workspace"))?;
let workspace = workspace
.upgrade()
.ok_or_else(|| anyhow!("workspace was dropped"))?;
let project = workspace.read(cx).project().clone();
anyhow::Ok(project.read(cx).client().http_client().clone())
});
if let Some(http_client) = http_client.log_err() {
indexed_docs_registry
.register_provider(Box::new(DocsDotRsProvider::new(http_client)));
indexed_docs_registry.register_provider(Box::new(RustdocIndexer::new(Box::new(
LocalProvider::new(fs, cargo_workspace_root),
))));
}
}
}
@@ -116,7 +95,7 @@ impl SlashCommand for DocsSlashCommand {
workspace: Option<WeakView<Workspace>>,
cx: &mut AppContext,
) -> Task<Result<Vec<ArgumentCompletion>>> {
self.ensure_rust_doc_providers_are_registered(workspace, cx);
self.ensure_rustdoc_provider_is_registered(workspace, cx);
let indexed_docs_registry = IndexedDocsRegistry::global(cx);
let args = DocsSlashCommandArgs::parse(&query);
@@ -142,14 +121,6 @@ impl SlashCommand for DocsSlashCommand {
match args {
DocsSlashCommandArgs::NoProvider => {
let providers = indexed_docs_registry.list_providers();
if providers.is_empty() {
return Ok(vec![ArgumentCompletion {
label: "No available docs providers.".to_string(),
new_text: String::new(),
run_command: false,
}]);
}
Ok(providers
.into_iter()
.map(|provider| ArgumentCompletion {
@@ -200,65 +171,46 @@ impl SlashCommand for DocsSlashCommand {
};
let args = DocsSlashCommandArgs::parse(argument);
let task = cx.background_executor().spawn({
let text = cx.background_executor().spawn({
let store = args
.provider()
.ok_or_else(|| anyhow!("no docs provider specified"))
.and_then(|provider| IndexedDocsStore::try_global(provider, cx));
async move {
let (provider, key) = match args {
match args {
DocsSlashCommandArgs::NoProvider => bail!("no docs provider specified"),
DocsSlashCommandArgs::SearchPackageDocs {
provider, package, ..
} => (provider, package),
} => {
let store = store?;
let item_docs = store.load(package.clone()).await?;
anyhow::Ok((provider, package, item_docs.to_string()))
}
DocsSlashCommandArgs::SearchItemDocs {
provider,
item_path,
..
} => (provider, item_path),
};
} => {
let store = store?;
let item_docs = store.load(item_path.clone()).await?;
let store = store?;
let (text, ranges) = if let Some((prefix, _)) = key.split_once('*') {
let docs = store.load_many_by_prefix(prefix.to_string()).await?;
let mut text = String::new();
let mut ranges = Vec::new();
for (key, docs) in docs {
let prev_len = text.len();
text.push_str(&docs.0);
text.push_str("\n");
ranges.push((key, prev_len..text.len()));
text.push_str("\n");
anyhow::Ok((provider, item_path, item_docs.to_string()))
}
(text, ranges)
} else {
let item_docs = store.load(key.clone()).await?;
let text = item_docs.to_string();
let range = 0..text.len();
(text, vec![(key, range)])
};
anyhow::Ok((provider, text, ranges))
}
}
});
cx.foreground_executor().spawn(async move {
let (provider, text, ranges) = task.await?;
let (provider, path, text) = text.await?;
let range = 0..text.len();
Ok(SlashCommandOutput {
text,
sections: ranges
.into_iter()
.map(|(key, range)| SlashCommandOutputSection {
range,
icon: IconName::FileDoc,
label: format!("docs ({provider}): {key}",).into(),
})
.collect(),
sections: vec![SlashCommandOutputSection {
range,
icon: IconName::FileRust,
label: format!("docs ({provider}): {path}",).into(),
}],
run_commands_in_text: false,
})
})

View File

@@ -27,7 +27,7 @@ pub(crate) struct FetchSlashCommand;
impl FetchSlashCommand {
async fn build_message(http_client: Arc<HttpClientWithUrl>, url: &str) -> Result<String> {
let mut url = url.to_owned();
if !url.starts_with("https://") && !url.starts_with("http://") {
if !url.starts_with("https://") {
url = format!("https://{url}");
}

View File

@@ -23,7 +23,7 @@ impl SlashCommand for NowSlashCommand {
}
fn menu_text(&self) -> String {
"Insert Current Date and Time".into()
"Insert current date and time".into()
}
fn requires_argument(&self) -> bool {

View File

@@ -1,89 +0,0 @@
use super::{SlashCommand, SlashCommandOutput};
use anyhow::{anyhow, Context as _, Result};
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
use editor::Editor;
use gpui::{AppContext, Task, WeakView};
use language::LspAdapterDelegate;
use std::sync::Arc;
use std::{path::Path, sync::atomic::AtomicBool};
use ui::{IconName, WindowContext};
use workspace::Workspace;
pub(crate) struct OutlineSlashCommand;
impl SlashCommand for OutlineSlashCommand {
fn name(&self) -> String {
"symbols".into()
}
fn description(&self) -> String {
"insert symbols for active tab".into()
}
fn menu_text(&self) -> String {
"Insert Symbols for Active Tab".into()
}
fn complete_argument(
self: Arc<Self>,
_query: String,
_cancel: Arc<AtomicBool>,
_workspace: Option<WeakView<Workspace>>,
_cx: &mut AppContext,
) -> Task<Result<Vec<ArgumentCompletion>>> {
Task::ready(Err(anyhow!("this command does not require argument")))
}
fn requires_argument(&self) -> bool {
false
}
fn run(
self: Arc<Self>,
_argument: Option<&str>,
workspace: WeakView<Workspace>,
_delegate: Arc<dyn LspAdapterDelegate>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
let output = workspace.update(cx, |workspace, cx| {
let Some(active_item) = workspace.active_item(cx) else {
return Task::ready(Err(anyhow!("no active tab")));
};
let Some(buffer) = active_item
.downcast::<Editor>()
.and_then(|editor| editor.read(cx).buffer().read(cx).as_singleton())
else {
return Task::ready(Err(anyhow!("active tab is not an editor")));
};
let snapshot = buffer.read(cx).snapshot();
let path = snapshot.resolve_file_path(cx, true);
cx.background_executor().spawn(async move {
let outline = snapshot
.outline(None)
.context("no symbols for active tab")?;
let path = path.as_deref().unwrap_or(Path::new("untitled"));
let mut outline_text = format!("Symbols for {}:\n", path.display());
for item in &outline.path_candidates {
outline_text.push_str("- ");
outline_text.push_str(&item.string);
outline_text.push('\n');
}
Ok(SlashCommandOutput {
sections: vec![SlashCommandOutputSection {
range: 0..outline_text.len(),
icon: IconName::ListTree,
label: path.to_string_lossy().to_string().into(),
}],
text: outline_text,
run_commands_in_text: false,
})
})
});
output.unwrap_or_else(|error| Task::ready(Err(error)))
}
}

View File

@@ -31,7 +31,7 @@ impl SlashCommand for TermSlashCommand {
}
fn menu_text(&self) -> String {
"Insert Terminal Output".into()
"Insert terminal output".into()
}
fn requires_argument(&self) -> bool {

View File

@@ -67,7 +67,7 @@ pub struct SlashCommandOutput {
pub run_commands_in_text: bool,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Clone, Serialize, Deserialize)]
pub struct SlashCommandOutputSection<T> {
pub range: Range<T>,
pub icon: IconName,

View File

@@ -72,7 +72,7 @@ impl Render for Breadcrumbs {
.into_any()
});
let breadcrumbs = Itertools::intersperse_with(highlighted_segments, || {
Label::new("").color(Color::Placeholder).into_any_element()
Label::new("").color(Color::Muted).into_any_element()
});
let breadcrumbs_stack = h_flex().gap_1().children(breadcrumbs);
@@ -83,7 +83,7 @@ impl Render for Breadcrumbs {
Some(editor) => element.child(
ButtonLike::new("toggle outline view")
.child(breadcrumbs_stack)
.style(ButtonStyle::Transparent)
.style(ButtonStyle::Subtle)
.on_click(move |_, cx| {
if let Some(editor) = editor.upgrade() {
outline::toggle(editor, &editor::actions::ToggleOutline, cx)

View File

@@ -11,7 +11,6 @@ pub struct IpcHandshake {
pub enum CliRequest {
Open {
paths: Vec<String>,
urls: Vec<String>,
wait: bool,
open_new_workspace: Option<bool>,
dev_server_token: Option<String>,

View File

@@ -5,7 +5,6 @@ use clap::Parser;
use cli::{ipc::IpcOneShotServer, CliRequest, CliResponse, IpcHandshake};
use parking_lot::Mutex;
use std::{
convert::Infallible,
env, fs, io,
path::{Path, PathBuf},
process::ExitStatus,
@@ -38,7 +37,8 @@ struct Args {
///
/// Use `path:line:row` syntax to open a file at a specific location.
/// Non-existing paths and directories will ignore `:line:row` suffix.
paths_with_position: Vec<String>,
#[arg(value_parser = parse_path_with_position)]
paths_with_position: Vec<PathLikeWithPosition<PathBuf>>,
/// Print Zed's version and the app path.
#[arg(short, long)]
version: bool,
@@ -53,30 +53,12 @@ struct Args {
dev_server_token: Option<String>,
}
fn parse_path_with_position(argument_str: &str) -> Result<String, std::io::Error> {
let path_like = PathLikeWithPosition::parse_str::<Infallible>(argument_str, |_, path_str| {
fn parse_path_with_position(
argument_str: &str,
) -> Result<PathLikeWithPosition<PathBuf>, std::convert::Infallible> {
PathLikeWithPosition::parse_str(argument_str, |_, path_str| {
Ok(Path::new(path_str).to_path_buf())
})
.unwrap();
let curdir = env::current_dir()?;
let canonicalized = path_like.map_path_like(|path| match fs::canonicalize(&path) {
Ok(path) => Ok(path),
Err(e) => {
if let Some(mut parent) = path.parent() {
if parent == Path::new("") {
parent = &curdir
}
match fs::canonicalize(parent) {
Ok(parent) => Ok(parent.join(path.file_name().unwrap())),
Err(_) => Err(e),
}
} else {
Err(e)
}
}
})?;
Ok(canonicalized.to_string(|path| path.display().to_string()))
}
fn main() -> Result<()> {
@@ -109,6 +91,28 @@ fn main() -> Result<()> {
return Ok(());
}
let curdir = env::current_dir()?;
let mut paths = vec![];
for path in args.paths_with_position {
let canonicalized = path.map_path_like(|path| match fs::canonicalize(&path) {
Ok(path) => Ok(path),
Err(e) => {
if let Some(mut parent) = path.parent() {
if parent == Path::new("") {
parent = &curdir;
}
match fs::canonicalize(parent) {
Ok(parent) => Ok(parent.join(path.file_name().unwrap())),
Err(_) => Err(e),
}
} else {
Err(e)
}
}
})?;
paths.push(canonicalized.to_string(|path| path.display().to_string()))
}
let (server, server_name) =
IpcOneShotServer::<IpcHandshake>::new().context("Handshake before Zed spawn")?;
let url = format!("zed-cli://{server_name}");
@@ -122,19 +126,6 @@ fn main() -> Result<()> {
};
let exit_status = Arc::new(Mutex::new(None));
let mut paths = vec![];
let mut urls = vec![];
for path in args.paths_with_position.iter() {
if path.starts_with("zed://")
|| path.starts_with("http://")
|| path.starts_with("https://")
|| path.starts_with("file://")
{
urls.push(path.to_string());
} else {
paths.push(parse_path_with_position(path)?)
}
}
let sender: JoinHandle<anyhow::Result<()>> = thread::spawn({
let exit_status = exit_status.clone();
@@ -143,7 +134,6 @@ fn main() -> Result<()> {
let (tx, rx) = (handshake.requests, handshake.responses);
tx.send(CliRequest::Open {
paths,
urls,
wait: args.wait,
open_new_workspace,
dev_server_token: args.dev_server_token,

View File

@@ -13,9 +13,8 @@ use async_tungstenite::tungstenite::{
use clock::SystemClock;
use collections::HashMap;
use futures::{
channel::oneshot,
future::{BoxFuture, LocalBoxFuture},
AsyncReadExt, FutureExt, SinkExt, Stream, StreamExt, TryFutureExt as _, TryStreamExt,
channel::oneshot, future::LocalBoxFuture, AsyncReadExt, FutureExt, SinkExt, Stream, StreamExt,
TryFutureExt as _, TryStreamExt,
};
use gpui::{
actions, AnyModel, AnyWeakModel, AppContext, AsyncAppContext, Global, Model, Task, WeakModel,
@@ -24,7 +23,6 @@ use http::{HttpClient, HttpClientWithUrl};
use lazy_static::lazy_static;
use parking_lot::RwLock;
use postage::watch;
use proto::ProtoClient;
use rand::prelude::*;
use release_channel::{AppVersion, ReleaseChannel};
use rpc::proto::{AnyTypedEnvelope, EntityMessage, EnvelopedMessage, PeerId, RequestMessage};
@@ -219,9 +217,6 @@ pub struct Client {
>,
>,
>,
#[cfg(any(test, feature = "test-support"))]
rpc_url: RwLock<Option<Url>>,
}
#[derive(Error, Debug)]
@@ -532,8 +527,6 @@ impl Client {
authenticate: Default::default(),
#[cfg(any(test, feature = "test-support"))]
establish_connection: Default::default(),
#[cfg(any(test, feature = "test-support"))]
rpc_url: RwLock::default(),
})
}
@@ -591,12 +584,6 @@ impl Client {
self
}
#[cfg(any(test, feature = "test-support"))]
pub fn override_rpc_url(&self, url: Url) -> &Self {
*self.rpc_url.write() = Some(url);
self
}
pub fn global(cx: &AppContext) -> Arc<Self> {
cx.global::<GlobalClient>().0.clone()
}
@@ -1099,50 +1086,38 @@ impl Client {
self.establish_websocket_connection(credentials, cx)
}
fn rpc_url(
&self,
async fn get_rpc_url(
http: Arc<HttpClientWithUrl>,
release_channel: Option<ReleaseChannel>,
) -> impl Future<Output = Result<Url>> {
#[cfg(any(test, feature = "test-support"))]
let url_override = self.rpc_url.read().clone();
async move {
#[cfg(any(test, feature = "test-support"))]
if let Some(url) = url_override {
return Ok(url);
}
if let Some(url) = &*ZED_RPC_URL {
return Url::parse(url).context("invalid rpc url");
}
let mut url = http.build_url("/rpc");
if let Some(preview_param) =
release_channel.and_then(|channel| channel.release_query_param())
{
url += "?";
url += preview_param;
}
let response = http.get(&url, Default::default(), false).await?;
let collab_url = if response.status().is_redirection() {
response
.headers()
.get("Location")
.ok_or_else(|| anyhow!("missing location header in /rpc response"))?
.to_str()
.map_err(EstablishConnectionError::other)?
.to_string()
} else {
Err(anyhow!(
"unexpected /rpc response status {}",
response.status()
))?
};
Url::parse(&collab_url).context("invalid rpc url")
) -> Result<Url> {
if let Some(url) = &*ZED_RPC_URL {
return Url::parse(url).context("invalid rpc url");
}
let mut url = http.build_url("/rpc");
if let Some(preview_param) =
release_channel.and_then(|channel| channel.release_query_param())
{
url += "?";
url += preview_param;
}
let response = http.get(&url, Default::default(), false).await?;
let collab_url = if response.status().is_redirection() {
response
.headers()
.get("Location")
.ok_or_else(|| anyhow!("missing location header in /rpc response"))?
.to_str()
.map_err(EstablishConnectionError::other)?
.to_string()
} else {
Err(anyhow!(
"unexpected /rpc response status {}",
response.status()
))?
};
Url::parse(&collab_url).context("invalid rpc url")
}
fn establish_websocket_connection(
@@ -1169,9 +1144,8 @@ impl Client {
);
let http = self.http.clone();
let rpc_url = self.rpc_url(http, release_channel);
cx.background_executor().spawn(async move {
let mut rpc_url = rpc_url.await?;
let mut rpc_url = Self::get_rpc_url(http, release_channel).await?;
let rpc_host = rpc_url
.host_str()
.zip(rpc_url.port_or_known_default())
@@ -1212,7 +1186,6 @@ impl Client {
cx: &AsyncAppContext,
) -> Task<Result<Credentials>> {
let http = self.http.clone();
let this = self.clone();
cx.spawn(|cx| async move {
let background = cx.background_executor().clone();
@@ -1242,8 +1215,7 @@ impl Client {
{
eprintln!("authenticate as admin {login}, {token}");
return this
.authenticate_as_admin(http, login.clone(), token.clone())
return Self::authenticate_as_admin(http, login.clone(), token.clone())
.await;
}
@@ -1331,7 +1303,6 @@ impl Client {
}
async fn authenticate_as_admin(
self: &Arc<Self>,
http: Arc<HttpClientWithUrl>,
login: String,
mut api_token: String,
@@ -1348,7 +1319,7 @@ impl Client {
// Use the collab server's admin API to retrieve the id
// of the impersonated user.
let mut url = self.rpc_url(http.clone(), None).await?;
let mut url = Self::get_rpc_url(http.clone(), None).await?;
url.set_path("/user");
url.set_query(Some(&format!("github_login={login}")));
let request = Request::get(url.as_str())
@@ -1410,11 +1381,6 @@ impl Client {
self.peer.send(self.connection_id()?, message)
}
fn send_dynamic(&self, envelope: proto::Envelope) -> Result<()> {
let connection_id = self.connection_id()?;
self.peer.send_dynamic(connection_id, envelope)
}
pub fn request<T: RequestMessage>(
&self,
request: T,
@@ -1613,20 +1579,6 @@ impl Client {
}
}
impl ProtoClient for Client {
fn request(
&self,
envelope: proto::Envelope,
request_type: &'static str,
) -> BoxFuture<'static, Result<proto::Envelope>> {
self.request_dynamic(envelope, request_type).boxed()
}
fn send(&self, envelope: proto::Envelope) -> Result<()> {
self.send_dynamic(envelope)
}
}
#[derive(Serialize, Deserialize)]
struct DevelopmentCredentials {
user_id: u64,

View File

@@ -18,5 +18,4 @@ test-support = ["dep:parking_lot"]
[dependencies]
chrono.workspace = true
parking_lot = { workspace = true, optional = true }
serde.workspace = true
smallvec.workspace = true

View File

@@ -1,6 +1,5 @@
mod system_clock;
use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
use std::{
cmp::{self, Ordering},
@@ -17,7 +16,7 @@ pub type Seq = u32;
/// A [Lamport timestamp](https://en.wikipedia.org/wiki/Lamport_timestamp),
/// used to determine the ordering of events in the editor.
#[derive(Clone, Copy, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Copy, Default, Eq, Hash, PartialEq)]
pub struct Lamport {
pub replica_id: ReplicaId,
pub value: Seq,
@@ -162,10 +161,6 @@ impl Lamport {
}
}
pub fn as_u64(self) -> u64 {
((self.value as u64) << 32) | (self.replica_id as u64)
}
pub fn tick(&mut self) -> Self {
let timestamp = *self;
self.value += 1;

View File

@@ -71,7 +71,6 @@ util.workspace = true
uuid.workspace = true
[dev-dependencies]
assistant = { workspace = true, features = ["test-support"] }
async-trait.workspace = true
audio.workspace = true
call = { workspace = true, features = ["test-support"] }

View File

@@ -414,5 +414,5 @@ CREATE TABLE dev_servers (
CREATE TABLE dev_server_projects (
id INTEGER PRIMARY KEY AUTOINCREMENT,
dev_server_id INTEGER NOT NULL REFERENCES dev_servers(id),
paths TEXT NOT NULL
path TEXT NOT NULL
);

View File

@@ -1,4 +0,0 @@
ALTER TABLE dev_server_projects ADD COLUMN paths JSONB NULL;
UPDATE dev_server_projects SET paths = to_json(ARRAY[path]);
ALTER TABLE dev_server_projects ALTER COLUMN paths SET NOT NULL;
ALTER TABLE dev_server_projects ALTER COLUMN path DROP NOT NULL;

View File

@@ -43,36 +43,11 @@ async fn get_extensions(
Extension(app): Extension<Arc<AppState>>,
Query(params): Query<GetExtensionsParams>,
) -> Result<Json<GetExtensionsResponse>> {
let mut extensions = app
let extensions = app
.db
.get_extensions(params.filter.as_deref(), params.max_schema_version, 500)
.await?;
if let Some(filter) = params.filter.as_deref() {
let extension_id = filter.to_lowercase();
let mut exact_match = None;
extensions.retain(|extension| {
if extension.id.as_ref() == &extension_id {
exact_match = Some(extension.clone());
false
} else {
true
}
});
if exact_match.is_none() {
exact_match = app
.db
.get_extensions_by_ids(&[&extension_id], None)
.await?
.first()
.cloned();
}
if let Some(exact_match) = exact_match {
extensions.insert(0, exact_match);
}
};
if let Some(query) = params.filter.as_deref() {
let count = extensions.len();
tracing::info!(query, count, "extension_search")

View File

@@ -5,7 +5,7 @@ use rpc::{
};
use sea_orm::{
ActiveModelTrait, ActiveValue, ColumnTrait, Condition, DatabaseTransaction, EntityTrait,
IntoActiveModel, ModelTrait, QueryFilter,
ModelTrait, QueryFilter,
};
use crate::db::ProjectId;
@@ -56,7 +56,12 @@ impl Database {
.await?;
Ok(servers
.into_iter()
.map(|(dev_server_project, project)| dev_server_project.to_proto(project))
.map(|(dev_server_project, project)| proto::DevServerProject {
id: dev_server_project.id.to_proto(),
project_id: project.map(|p| p.id.to_proto()),
dev_server_id: dev_server_project.dev_server_id.to_proto(),
path: dev_server_project.path,
})
.collect())
}
@@ -129,7 +134,7 @@ impl Database {
let project = dev_server_project::Entity::insert(dev_server_project::ActiveModel {
id: ActiveValue::NotSet,
dev_server_id: ActiveValue::Set(dev_server_id),
paths: ActiveValue::Set(dev_server_project::JSONPaths(vec![path.to_string()])),
path: ActiveValue::Set(path.to_string()),
})
.exec_with_returning(&*tx)
.await?;
@@ -143,38 +148,6 @@ impl Database {
.await
}
pub async fn update_dev_server_project(
&self,
id: DevServerProjectId,
paths: &Vec<String>,
user_id: UserId,
) -> crate::Result<(dev_server_project::Model, proto::DevServerProjectsUpdate)> {
self.transaction(move |tx| async move {
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)
.await?
else {
return Err(anyhow!("no such dev server project"))?;
};
if dev_server.user_id != user_id {
return Err(anyhow!("not your dev server"))?;
}
let mut project = project.into_active_model();
project.paths = ActiveValue::Set(dev_server_project::JSONPaths(paths));
let project = project.update(&*tx).await?;
let status = self
.dev_server_projects_update_internal(user_id, &tx)
.await?;
Ok((project, status))
})
.await
}
pub async fn delete_dev_server_project(
&self,
dev_server_project_id: DevServerProjectId,
@@ -285,6 +258,7 @@ impl Database {
dev_server_id: DevServerId,
connection: ConnectionId,
) -> crate::Result<Vec<ResharedProject>> {
// todo!() project_transaction? (maybe we can make the lock per-dev-server instead of per-project?)
self.transaction(|tx| async move {
let mut ret = Vec::new();
for reshared_project in reshared_projects {
@@ -348,6 +322,7 @@ impl Database {
user_id: UserId,
connection_id: ConnectionId,
) -> crate::Result<Vec<RejoinedProject>> {
// todo!() project_transaction? (maybe we can make the lock per-dev-server instead of per-project?)
self.transaction(|tx| async move {
let mut ret = Vec::new();
for rejoined_project in rejoined_projects {

View File

@@ -19,28 +19,6 @@ impl Database {
.await
}
pub async fn get_dev_server_for_user(
&self,
dev_server_id: DevServerId,
user_id: UserId,
) -> crate::Result<dev_server::Model> {
self.transaction(|tx| async move {
let server = dev_server::Entity::find_by_id(dev_server_id)
.one(&*tx)
.await?
.ok_or_else(|| anyhow::anyhow!("no dev server with id {}", dev_server_id))?;
if server.user_id != user_id {
return Err(anyhow::anyhow!(
"dev server {} is not owned by user {}",
dev_server_id,
user_id
))?;
}
Ok(server)
})
.await
}
pub async fn get_dev_servers(&self, user_id: UserId) -> crate::Result<Vec<dev_server::Model>> {
self.transaction(|tx| async move {
Ok(dev_server::Entity::find()

View File

@@ -1,8 +1,7 @@
use super::project;
use crate::db::{DevServerId, DevServerProjectId};
use rpc::proto;
use sea_orm::{entity::prelude::*, FromJsonQueryResult};
use serde::{Deserialize, Serialize};
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "dev_server_projects")]
@@ -10,12 +9,9 @@ pub struct Model {
#[sea_orm(primary_key)]
pub id: DevServerProjectId,
pub dev_server_id: DevServerId,
pub paths: JSONPaths,
pub path: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromJsonQueryResult)]
pub struct JSONPaths(pub Vec<String>);
impl ActiveModelBehavior for ActiveModel {}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
@@ -48,12 +44,7 @@ impl Model {
id: self.id.to_proto(),
project_id: project.map(|p| p.id.to_proto()),
dev_server_id: self.dev_server_id.to_proto(),
path: self.paths().get(0).cloned().unwrap_or_default(),
paths: self.paths().clone(),
path: self.path.clone(),
}
}
pub fn paths(&self) -> &Vec<String> {
&self.paths.0
}
}

View File

@@ -562,7 +562,7 @@ fn test_fuzzy_like_string() {
assert_eq!(Database::fuzzy_like_string(" z "), "%z%");
}
#[cfg(target_os = "macos")]
#[cfg(target = "macos")]
#[gpui::test]
async fn test_fuzzy_search_users(cx: &mut gpui::TestAppContext) {
let test_db = tests::TestDb::postgres(cx.executor());

View File

@@ -431,13 +431,11 @@ impl Server {
.add_request_handler(user_handler(join_hosted_project))
.add_request_handler(user_handler(rejoin_dev_server_projects))
.add_request_handler(user_handler(create_dev_server_project))
.add_request_handler(user_handler(update_dev_server_project))
.add_request_handler(user_handler(delete_dev_server_project))
.add_request_handler(user_handler(create_dev_server))
.add_request_handler(user_handler(regenerate_dev_server_token))
.add_request_handler(user_handler(rename_dev_server))
.add_request_handler(user_handler(delete_dev_server))
.add_request_handler(user_handler(list_remote_directory))
.add_request_handler(dev_server_handler(share_dev_server_project))
.add_request_handler(dev_server_handler(shutdown_dev_server))
.add_request_handler(dev_server_handler(reconnect_dev_server))
@@ -597,14 +595,6 @@ impl Server {
.add_message_handler(user_message_handler(acknowledge_channel_message))
.add_message_handler(user_message_handler(acknowledge_buffer_version))
.add_request_handler(user_handler(get_supermaven_api_key))
.add_request_handler(user_handler(
forward_mutating_project_request::<proto::OpenContext>,
))
.add_request_handler(user_handler(
forward_mutating_project_request::<proto::SynchronizeContexts>,
))
.add_message_handler(broadcast_project_message_from_host::<proto::AdvertiseContexts>)
.add_message_handler(update_context)
.add_streaming_request_handler({
let app_state = app_state.clone();
move |request, response, session| {
@@ -2315,69 +2305,6 @@ async fn join_hosted_project(
join_project_internal(response, session, &mut project, &replica_id)
}
async fn list_remote_directory(
request: proto::ListRemoteDirectory,
response: Response<proto::ListRemoteDirectory>,
session: UserSession,
) -> Result<()> {
let dev_server_id = DevServerId(request.dev_server_id as i32);
let dev_server_connection_id = session
.connection_pool()
.await
.dev_server_connection_id_supporting(dev_server_id, ZedVersion::with_list_directory())?;
session
.db()
.await
.get_dev_server_for_user(dev_server_id, session.user_id())
.await?;
response.send(
session
.peer
.forward_request(session.connection_id, dev_server_connection_id, request)
.await?,
)?;
Ok(())
}
async fn update_dev_server_project(
request: proto::UpdateDevServerProject,
response: Response<proto::UpdateDevServerProject>,
session: UserSession,
) -> Result<()> {
let dev_server_project_id = DevServerProjectId(request.dev_server_project_id as i32);
let (dev_server_project, update) = session
.db()
.await
.update_dev_server_project(dev_server_project_id, &request.paths, session.user_id())
.await?;
let projects = session
.db()
.await
.get_projects_for_dev_server(dev_server_project.dev_server_id)
.await?;
let dev_server_connection_id = session
.connection_pool()
.await
.dev_server_connection_id_supporting(
dev_server_project.dev_server_id,
ZedVersion::with_list_directory(),
)?;
session.peer.send(
dev_server_connection_id,
proto::DevServerInstructions { projects },
)?;
send_dev_server_projects_update(session.user_id(), update, &session).await;
response.send(proto::Ack {})
}
async fn create_dev_server_project(
request: proto::CreateDevServerProject,
response: Response<proto::CreateDevServerProject>,
@@ -3129,53 +3056,6 @@ async fn update_buffer(
Ok(())
}
async fn update_context(message: proto::UpdateContext, session: Session) -> Result<()> {
let project_id = ProjectId::from_proto(message.project_id);
let operation = message.operation.as_ref().context("invalid operation")?;
let capability = match operation.variant.as_ref() {
Some(proto::context_operation::Variant::BufferOperation(buffer_op)) => {
if let Some(buffer_op) = buffer_op.operation.as_ref() {
match buffer_op.variant {
None | Some(proto::operation::Variant::UpdateSelections(_)) => {
Capability::ReadOnly
}
_ => Capability::ReadWrite,
}
} else {
Capability::ReadWrite
}
}
Some(_) => Capability::ReadWrite,
None => Capability::ReadOnly,
};
let guard = session
.db()
.await
.connections_for_buffer_update(
project_id,
session.principal_id(),
session.connection_id,
capability,
)
.await?;
let (host, guests) = &*guard;
broadcast(
Some(session.connection_id),
guests.iter().chain([host]).copied(),
|connection_id| {
session
.peer
.forward_send(session.connection_id, connection_id, message.clone())
},
);
Ok(())
}
/// Notify other participants that a project has been updated.
async fn broadcast_project_message_from_host<T: EntityMessage<Entity = ShareProject>>(
request: T,

View File

@@ -38,10 +38,6 @@ impl ZedVersion {
pub fn with_save_as() -> ZedVersion {
ZedVersion(SemanticVersion::new(0, 134, 0))
}
pub fn with_list_directory() -> ZedVersion {
ZedVersion(SemanticVersion::new(0, 145, 0))
}
}
pub trait VersionedMessage {
@@ -191,18 +187,6 @@ impl ConnectionPool {
self.connected_dev_servers.get(&dev_server_id).copied()
}
pub fn dev_server_connection_id_supporting(
&self,
dev_server_id: DevServerId,
required: ZedVersion,
) -> Result<ConnectionId> {
match self.connected_dev_servers.get(&dev_server_id) {
Some(cid) if self.connections[cid].zed_version >= required => Ok(*cid),
Some(_) => Err(anyhow!(proto::ErrorCode::RemoteUpgradeRequired)),
None => Err(anyhow!(proto::ErrorCode::DevServerOffline)),
}
}
pub fn channel_user_ids(
&self,
channel_id: ChannelId,

View File

@@ -66,7 +66,7 @@ async fn test_dev_server(cx: &mut gpui::TestAppContext, cx2: &mut gpui::TestAppC
.update(cx, |store, cx| {
let projects = store.dev_server_projects();
assert_eq!(projects.len(), 1);
assert_eq!(projects[0].paths, vec!["/remote"]);
assert_eq!(projects[0].path, "/remote");
workspace::join_dev_server_project(
projects[0].id,
projects[0].project_id.unwrap(),
@@ -206,7 +206,7 @@ async fn create_dev_server_project(
.update(cx, |store, cx| {
let projects = store.dev_server_projects();
assert_eq!(projects.len(), 1);
assert_eq!(projects[0].paths, vec!["/remote"]);
assert_eq!(projects[0].path, "/remote");
workspace::join_dev_server_project(
projects[0].id,
projects[0].project_id.unwrap(),

View File

@@ -135,7 +135,7 @@ async fn test_basic_following(
assert_eq!(editor.selections.ranges(cx), vec![2..1]);
});
// When client B starts following client A, only the active view state is replicated to client B.
// When client B starts following client A, all visible view states are replicated to client B.
workspace_b.update(cx_b, |workspace, cx| workspace.follow(peer_id_a, cx));
cx_c.executor().run_until_parked();
@@ -156,7 +156,7 @@ async fn test_basic_following(
);
assert_eq!(
editor_b1.update(cx_b, |editor, cx| editor.selections.ranges(cx)),
vec![3..3]
vec![3..2]
);
executor.run_until_parked();
@@ -194,7 +194,7 @@ async fn test_basic_following(
// Client C unfollows client A.
workspace_c.update(cx_c, |workspace, cx| {
workspace.unfollow(peer_id_a, cx).unwrap();
workspace.unfollow(&workspace.active_pane().clone(), cx);
});
// All clients see that clients B is following client A.
@@ -398,7 +398,7 @@ async fn test_basic_following(
// After unfollowing, client B stops receiving updates from client A.
workspace_b.update(cx_b, |workspace, cx| {
workspace.unfollow(peer_id_a, cx).unwrap()
workspace.unfollow(&workspace.active_pane().clone(), cx)
});
workspace_a.update(cx_a, |workspace, cx| {
workspace.activate_item(&editor_a2, cx)

View File

@@ -6,7 +6,6 @@ use crate::{
},
};
use anyhow::{anyhow, Result};
use assistant::ContextStore;
use call::{room, ActiveCall, ParticipantLocation, Room};
use client::{User, RECEIVE_TIMEOUT};
use collections::{HashMap, HashSet};
@@ -1528,7 +1527,7 @@ async fn test_project_reconnect(
});
let (worktree_a2, _) = project_a1
.update(cx_a, |p, cx| {
p.find_or_create_worktree("/root-1/dir2", true, cx)
p.find_or_create_local_worktree("/root-1/dir2", true, cx)
})
.await
.unwrap();
@@ -1601,7 +1600,7 @@ async fn test_project_reconnect(
});
let (worktree_a3, _) = project_a1
.update(cx_a, |p, cx| {
p.find_or_create_worktree("/root-1/dir3", true, cx)
p.find_or_create_local_worktree("/root-1/dir3", true, cx)
})
.await
.unwrap();
@@ -1725,7 +1724,7 @@ async fn test_project_reconnect(
// While client B is disconnected, add and remove worktrees from client A's project.
let (worktree_a4, _) = project_a1
.update(cx_a, |p, cx| {
p.find_or_create_worktree("/root-1/dir4", true, cx)
p.find_or_create_local_worktree("/root-1/dir4", true, cx)
})
.await
.unwrap();
@@ -3327,7 +3326,7 @@ async fn test_local_settings(
let store = cx.global::<SettingsStore>();
assert_eq!(
store
.local_settings(worktree_b.entity_id().as_u64() as _)
.local_settings(worktree_b.read(cx).id().to_usize())
.collect::<Vec<_>>(),
&[
(Path::new("").into(), r#"{"tab_size":2}"#.to_string()),
@@ -3346,7 +3345,7 @@ async fn test_local_settings(
let store = cx.global::<SettingsStore>();
assert_eq!(
store
.local_settings(worktree_b.entity_id().as_u64() as _)
.local_settings(worktree_b.read(cx).id().to_usize())
.collect::<Vec<_>>(),
&[
(Path::new("").into(), r#"{}"#.to_string()),
@@ -3375,7 +3374,7 @@ async fn test_local_settings(
let store = cx.global::<SettingsStore>();
assert_eq!(
store
.local_settings(worktree_b.entity_id().as_u64() as _)
.local_settings(worktree_b.read(cx).id().to_usize())
.collect::<Vec<_>>(),
&[
(Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
@@ -3407,7 +3406,7 @@ async fn test_local_settings(
let store = cx.global::<SettingsStore>();
assert_eq!(
store
.local_settings(worktree_b.entity_id().as_u64() as _)
.local_settings(worktree_b.read(cx).id().to_usize())
.collect::<Vec<_>>(),
&[(Path::new("a").into(), r#"{"hard_tabs":true}"#.to_string()),]
)
@@ -4887,7 +4886,7 @@ async fn test_project_search(
let (project_a, _) = client_a.build_local_project("/root/dir-1", cx_a).await;
let (worktree_2, _) = project_a
.update(cx_a, |p, cx| {
p.find_or_create_worktree("/root/dir-2", true, cx)
p.find_or_create_local_worktree("/root/dir-2", true, cx)
})
.await
.unwrap();
@@ -6450,123 +6449,3 @@ async fn test_preview_tabs(cx: &mut TestAppContext) {
assert!(!pane.can_navigate_forward());
});
}
#[gpui::test(iterations = 10)]
async fn test_context_collaboration_with_reconnect(
executor: BackgroundExecutor,
cx_a: &mut TestAppContext,
cx_b: &mut TestAppContext,
) {
let mut server = TestServer::start(executor.clone()).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
.create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
.await;
let active_call_a = cx_a.read(ActiveCall::global);
client_a.fs().insert_tree("/a", Default::default()).await;
let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
let project_id = active_call_a
.update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
.await
.unwrap();
let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
// Client A sees that a guest has joined.
executor.run_until_parked();
project_a.read_with(cx_a, |project, _| {
assert_eq!(project.collaborators().len(), 1);
});
project_b.read_with(cx_b, |project, _| {
assert_eq!(project.collaborators().len(), 1);
});
let context_store_a = cx_a
.update(|cx| ContextStore::new(project_a.clone(), cx))
.await
.unwrap();
let context_store_b = cx_b
.update(|cx| ContextStore::new(project_b.clone(), cx))
.await
.unwrap();
// Client A creates a new context.
let context_a = context_store_a.update(cx_a, |store, cx| store.create(cx));
executor.run_until_parked();
// Client B retrieves host's contexts and joins one.
let context_b = context_store_b
.update(cx_b, |store, cx| {
let host_contexts = store.host_contexts().to_vec();
assert_eq!(host_contexts.len(), 1);
store.open_remote_context(host_contexts[0].id.clone(), cx)
})
.await
.unwrap();
// Host and guest make changes
context_a.update(cx_a, |context, cx| {
context.buffer().update(cx, |buffer, cx| {
buffer.edit([(0..0, "Host change\n")], None, cx)
})
});
context_b.update(cx_b, |context, cx| {
context.buffer().update(cx, |buffer, cx| {
buffer.edit([(0..0, "Guest change\n")], None, cx)
})
});
executor.run_until_parked();
assert_eq!(
context_a.read_with(cx_a, |context, cx| context.buffer().read(cx).text()),
"Guest change\nHost change\n"
);
assert_eq!(
context_b.read_with(cx_b, |context, cx| context.buffer().read(cx).text()),
"Guest change\nHost change\n"
);
// Disconnect client A and make some changes while disconnected.
server.disconnect_client(client_a.peer_id().unwrap());
server.forbid_connections();
context_a.update(cx_a, |context, cx| {
context.buffer().update(cx, |buffer, cx| {
buffer.edit([(0..0, "Host offline change\n")], None, cx)
})
});
context_b.update(cx_b, |context, cx| {
context.buffer().update(cx, |buffer, cx| {
buffer.edit([(0..0, "Guest offline change\n")], None, cx)
})
});
executor.run_until_parked();
assert_eq!(
context_a.read_with(cx_a, |context, cx| context.buffer().read(cx).text()),
"Host offline change\nGuest change\nHost change\n"
);
assert_eq!(
context_b.read_with(cx_b, |context, cx| context.buffer().read(cx).text()),
"Guest offline change\nGuest change\nHost change\n"
);
// Allow client A to reconnect and verify that contexts converge.
server.allow_connections();
executor.advance_clock(RECEIVE_TIMEOUT);
assert_eq!(
context_a.read_with(cx_a, |context, cx| context.buffer().read(cx).text()),
"Guest offline change\nHost offline change\nGuest change\nHost change\n"
);
assert_eq!(
context_b.read_with(cx_b, |context, cx| context.buffer().read(cx).text()),
"Guest offline change\nHost offline change\nGuest change\nHost change\n"
);
// Client A disconnects without being able to reconnect. Context B becomes readonly.
server.forbid_connections();
server.disconnect_client(client_a.peer_id().unwrap());
executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
context_b.read_with(cx_b, |context, cx| {
assert!(context.buffer().read(cx).read_only());
});
}

View File

@@ -581,7 +581,7 @@ impl RandomizedTest for ProjectCollaborationTest {
}
project
.update(cx, |project, cx| {
project.find_or_create_worktree(&new_root_path, true, cx)
project.find_or_create_local_worktree(&new_root_path, true, cx)
})
.await
.unwrap();
@@ -1237,7 +1237,7 @@ impl RandomizedTest for ProjectCollaborationTest {
}
}
for buffer in guest_project.opened_buffers(cx) {
for buffer in guest_project.opened_buffers() {
let buffer = buffer.read(cx);
assert_eq!(
buffer.deferred_ops_len(),
@@ -1287,8 +1287,8 @@ impl RandomizedTest for ProjectCollaborationTest {
for guest_buffer in guest_buffers {
let buffer_id =
guest_buffer.read_with(client_cx, |buffer, _| buffer.remote_id());
let host_buffer = host_project.read_with(host_cx, |project, cx| {
project.buffer_for_id(buffer_id, cx).unwrap_or_else(|| {
let host_buffer = host_project.read_with(host_cx, |project, _| {
project.buffer_for_id(buffer_id).unwrap_or_else(|| {
panic!(
"host does not have buffer for guest:{}, peer:{:?}, id:{}",
client.username,

View File

@@ -294,8 +294,6 @@ impl TestServer {
menu::init();
dev_server_projects::init(client.clone(), cx);
settings::KeymapFile::load_asset(os_keymap, cx).unwrap();
assistant::FakeCompletionProvider::setup_test(cx);
assistant::context_store::init(&client);
});
client
@@ -805,7 +803,9 @@ impl TestClient {
) -> (Model<Project>, WorktreeId) {
let project = self.build_empty_local_project(cx);
let (worktree, _) = project
.update(cx, |p, cx| p.find_or_create_worktree(root_path, true, cx))
.update(cx, |p, cx| {
p.find_or_create_local_worktree(root_path, true, cx)
})
.await
.unwrap();
worktree

View File

@@ -22,9 +22,10 @@ use std::{
};
use ui::{prelude::*, Label};
use util::ResultExt;
use workspace::{item::Dedup, notifications::NotificationId};
use workspace::notifications::NotificationId;
use workspace::{
item::{FollowableItem, Item, ItemEvent, ItemHandle, TabContentParams},
register_followable_item,
searchable::SearchableItemHandle,
ItemNavHistory, Pane, SaveIntent, Toast, ViewId, Workspace, WorkspaceId,
};
@@ -32,7 +33,7 @@ use workspace::{
actions!(collab, [CopyLink]);
pub fn init(cx: &mut AppContext) {
workspace::FollowableViewRegistry::register::<ChannelView>(cx)
register_followable_item::<ChannelView>(cx)
}
pub struct ChannelView {
@@ -82,56 +83,6 @@ impl ChannelView {
pane: View<Pane>,
workspace: View<Workspace>,
cx: &mut WindowContext,
) -> Task<Result<View<Self>>> {
let channel_view = Self::load(channel_id, workspace, cx);
cx.spawn(|mut cx| async move {
let channel_view = channel_view.await?;
pane.update(&mut cx, |pane, cx| {
let buffer_id = channel_view.read(cx).channel_buffer.read(cx).remote_id(cx);
let existing_view = pane
.items_of_type::<Self>()
.find(|view| view.read(cx).channel_buffer.read(cx).remote_id(cx) == buffer_id);
// If this channel buffer is already open in this pane, just return it.
if let Some(existing_view) = existing_view.clone() {
if existing_view.read(cx).channel_buffer == channel_view.read(cx).channel_buffer
{
if let Some(link_position) = link_position {
existing_view.update(cx, |channel_view, cx| {
channel_view.focus_position_from_link(link_position, true, cx)
});
}
return existing_view;
}
}
// If the pane contained a disconnected view for this channel buffer,
// replace that.
if let Some(existing_item) = existing_view {
if let Some(ix) = pane.index_for_item(&existing_item) {
pane.close_item_by_id(existing_item.entity_id(), SaveIntent::Skip, cx)
.detach();
pane.add_item(Box::new(channel_view.clone()), true, true, Some(ix), cx);
}
}
if let Some(link_position) = link_position {
channel_view.update(cx, |channel_view, cx| {
channel_view.focus_position_from_link(link_position, true, cx)
});
}
channel_view
})
})
}
pub fn load(
channel_id: ChannelId,
workspace: View<Workspace>,
cx: &mut WindowContext,
) -> Task<Result<View<Self>>> {
let weak_workspace = workspace.downgrade();
let workspace = workspace.read(cx);
@@ -156,11 +107,49 @@ impl ChannelView {
})
})?;
cx.new_view(|cx| {
let mut this =
Self::new(project, weak_workspace, channel_store, channel_buffer, cx);
this.acknowledge_buffer_version(cx);
this
pane.update(&mut cx, |pane, cx| {
let buffer_id = channel_buffer.read(cx).remote_id(cx);
let existing_view = pane
.items_of_type::<Self>()
.find(|view| view.read(cx).channel_buffer.read(cx).remote_id(cx) == buffer_id);
// If this channel buffer is already open in this pane, just return it.
if let Some(existing_view) = existing_view.clone() {
if existing_view.read(cx).channel_buffer == channel_buffer {
if let Some(link_position) = link_position {
existing_view.update(cx, |channel_view, cx| {
channel_view.focus_position_from_link(link_position, true, cx)
});
}
return existing_view;
}
}
let view = cx.new_view(|cx| {
let mut this =
Self::new(project, weak_workspace, channel_store, channel_buffer, cx);
this.acknowledge_buffer_version(cx);
this
});
// If the pane contained a disconnected view for this channel buffer,
// replace that.
if let Some(existing_item) = existing_view {
if let Some(ix) = pane.index_for_item(&existing_item) {
pane.close_item_by_id(existing_item.entity_id(), SaveIntent::Skip, cx)
.detach();
pane.add_item(Box::new(view.clone()), true, true, Some(ix), cx);
}
}
if let Some(link_position) = link_position {
view.update(cx, |channel_view, cx| {
channel_view.focus_position_from_link(link_position, true, cx)
});
}
view
})
})
}
@@ -489,6 +478,7 @@ impl FollowableItem for ChannelView {
}
fn from_state_proto(
pane: View<workspace::Pane>,
workspace: View<workspace::Workspace>,
remote_id: workspace::ViewId,
state: &mut Option<proto::view::Variant>,
@@ -501,7 +491,8 @@ impl FollowableItem for ChannelView {
unreachable!()
};
let open = ChannelView::load(ChannelId(state.channel_id), workspace, cx);
let open =
ChannelView::open_in_pane(ChannelId(state.channel_id), None, pane, workspace, cx);
Some(cx.spawn(|mut cx| async move {
let this = open.await?;
@@ -572,19 +563,6 @@ impl FollowableItem for ChannelView {
fn to_follow_event(event: &Self::Event) -> Option<workspace::item::FollowEvent> {
Editor::to_follow_event(event)
}
fn dedup(&self, existing: &Self, cx: &WindowContext) -> Option<Dedup> {
let existing = existing.channel_buffer.read(cx);
if self.channel_buffer.read(cx).channel_id == existing.channel_id {
if existing.is_connected() {
Some(Dedup::KeepExisting)
} else {
Some(Dedup::ReplaceExisting)
}
} else {
None
}
}
}
struct ChannelBufferCollaborationHub(Model<ChannelBuffer>);

View File

@@ -20,7 +20,7 @@ pub struct Store {
pub struct DevServerProject {
pub id: DevServerProjectId,
pub project_id: Option<ProjectId>,
pub paths: Vec<SharedString>,
pub path: SharedString,
pub dev_server_id: DevServerId,
}
@@ -29,7 +29,7 @@ impl From<proto::DevServerProject> for DevServerProject {
Self {
id: DevServerProjectId(project.id),
project_id: project.project_id.map(|id| ProjectId(id)),
paths: project.paths.into_iter().map(|path| path.into()).collect(),
path: project.path.into(),
dev_server_id: DevServerId(project.dev_server_id),
}
}
@@ -85,7 +85,7 @@ impl Store {
.filter(|project| project.dev_server_id == id)
.cloned()
.collect();
projects.sort_by_key(|p| (p.paths.clone(), p.id));
projects.sort_by_key(|p| (p.path.clone(), p.id));
projects
}
@@ -108,7 +108,7 @@ impl Store {
pub fn dev_server_projects(&self) -> Vec<DevServerProject> {
let mut projects: Vec<DevServerProject> =
self.dev_server_projects.values().cloned().collect();
projects.sort_by_key(|p| (p.paths.clone(), p.id));
projects.sort_by_key(|p| (p.path.clone(), p.id));
projects
}

View File

@@ -18,13 +18,11 @@ collections.workspace = true
ctor.workspace = true
editor.workspace = true
env_logger.workspace = true
feature_flags.workspace = true
futures.workspace = true
gpui.workspace = true
language.workspace = true
log.workspace = true
lsp.workspace = true
multi_buffer.workspace = true
project.workspace = true
rand.workspace = true
schemars.workspace = true

View File

@@ -4,7 +4,6 @@ mod toolbar_controls;
#[cfg(test)]
mod diagnostics_tests;
mod grouped_diagnostics;
use anyhow::Result;
use collections::{BTreeSet, HashSet};
@@ -15,7 +14,6 @@ use editor::{
scroll::Autoscroll,
Editor, EditorEvent, ExcerptId, ExcerptRange, MultiBuffer, ToOffset,
};
use feature_flags::FeatureFlagAppExt;
use futures::{
channel::mpsc::{self, UnboundedSender},
StreamExt as _,
@@ -54,9 +52,6 @@ pub fn init(cx: &mut AppContext) {
ProjectDiagnosticsSettings::register(cx);
cx.observe_new_views(ProjectDiagnosticsEditor::register)
.detach();
if !cx.has_flag::<feature_flags::GroupedDiagnostics>() {
grouped_diagnostics::init(cx);
}
}
struct ProjectDiagnosticsEditor {
@@ -471,9 +466,7 @@ impl ProjectDiagnosticsEditor {
position: (excerpt_id, entry.range.start),
height: diagnostic.message.matches('\n').count() as u8 + 1,
style: BlockStyle::Fixed,
render: diagnostic_block_renderer(
diagnostic, None, true, true,
),
render: diagnostic_block_renderer(diagnostic, true),
disposition: BlockDisposition::Below,
});
}
@@ -805,7 +798,7 @@ impl Item for ProjectDiagnosticsEditor {
const DIAGNOSTIC_HEADER: &'static str = "diagnostic header";
fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
let (message, code_ranges) = highlight_diagnostic_message(&diagnostic, None);
let (message, code_ranges) = highlight_diagnostic_message(&diagnostic);
let message: SharedString = message;
Box::new(move |cx| {
let highlight_style: HighlightStyle = cx.theme().colors().text_accent.into();

View File

@@ -973,8 +973,8 @@ fn editor_blocks(
blocks.extend(
snapshot
.blocks_in_range(DisplayRow(0)..snapshot.max_point().row())
.filter_map(|(row, block)| {
let transform_block_id = block.id();
.enumerate()
.filter_map(|(ix, (row, block))| {
let name: SharedString = match block {
TransformBlock::Custom(block) => {
let mut element = block.render(&mut BlockContext {
@@ -984,7 +984,7 @@ fn editor_blocks(
line_height: px(0.),
em_width: px(0.),
max_width: px(0.),
transform_block_id,
block_id: ix,
editor_style: &editor::EditorStyle::default(),
});
let element = element.downcast_mut::<Stateful<Div>>().unwrap();

File diff suppressed because it is too large Load Diff

View File

@@ -49,7 +49,6 @@ lazy_static.workspace = true
linkify.workspace = true
log.workspace = true
lsp.workspace = true
markdown.workspace = true
multi_buffer.workspace = true
ordered-float.workspace = true
parking_lot.workspace = true

View File

@@ -286,14 +286,12 @@ gpui::actions!(
SelectPageUp,
ShowCharacterPalette,
ShowInlineCompletion,
ShowSignatureHelp,
ShuffleLines,
SortLinesCaseInsensitive,
SortLinesCaseSensitive,
SplitSelectionIntoLines,
Tab,
TabPrev,
ToggleAutoSignatureHelp,
ToggleGitBlame,
ToggleGitBlameInline,
ToggleSelectionMenu,

View File

@@ -30,7 +30,6 @@ use crate::{
pub use block_map::{
BlockBufferRows, BlockChunks as DisplayChunks, BlockContext, BlockDisposition, BlockId,
BlockMap, BlockPoint, BlockProperties, BlockStyle, RenderBlock, TransformBlock,
TransformBlockId,
};
use block_map::{BlockRow, BlockSnapshot};
use collections::{HashMap, HashSet};

View File

@@ -4,7 +4,7 @@ use super::{
};
use crate::{EditorStyle, GutterDimensions};
use collections::{Bound, HashMap, HashSet};
use gpui::{AnyElement, EntityId, Pixels, WindowContext};
use gpui::{AnyElement, Pixels, WindowContext};
use language::{BufferSnapshot, Chunk, Patch, Point};
use multi_buffer::{Anchor, ExcerptId, ExcerptRange, MultiBufferRow, ToPoint as _};
use parking_lot::Mutex;
@@ -20,7 +20,6 @@ use std::{
};
use sum_tree::{Bias, SumTree};
use text::Edit;
use ui::ElementId;
const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize];
@@ -54,12 +53,6 @@ pub struct BlockSnapshot {
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BlockId(usize);
impl Into<ElementId> for BlockId {
fn into(self) -> ElementId {
ElementId::Integer(self.0)
}
}
#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
pub struct BlockPoint(pub Point);
@@ -69,7 +62,7 @@ pub struct BlockRow(pub(super) u32);
#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
struct WrapRow(u32);
pub type RenderBlock = Box<dyn Send + FnMut(&mut BlockContext) -> AnyElement>;
pub type RenderBlock = Box<dyn Send + Fn(&mut BlockContext) -> AnyElement>;
pub struct Block {
id: BlockId,
@@ -84,22 +77,11 @@ pub struct BlockProperties<P> {
pub position: P,
pub height: u8,
pub style: BlockStyle,
pub render: RenderBlock,
pub render: Box<dyn Send + Fn(&mut BlockContext) -> AnyElement>,
pub disposition: BlockDisposition,
}
impl<P: Debug> Debug for BlockProperties<P> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("BlockProperties")
.field("position", &self.position)
.field("height", &self.height)
.field("style", &self.style)
.field("disposition", &self.disposition)
.finish()
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum BlockStyle {
Fixed,
Flex,
@@ -113,47 +95,10 @@ pub struct BlockContext<'a, 'b> {
pub gutter_dimensions: &'b GutterDimensions,
pub em_width: Pixels,
pub line_height: Pixels,
pub transform_block_id: TransformBlockId,
pub block_id: usize,
pub editor_style: &'b EditorStyle,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TransformBlockId {
Block(BlockId),
ExcerptHeader(ExcerptId),
ExcerptFooter(ExcerptId),
}
impl From<TransformBlockId> for EntityId {
fn from(value: TransformBlockId) -> Self {
match value {
TransformBlockId::Block(BlockId(id)) => EntityId::from(id as u64),
TransformBlockId::ExcerptHeader(id) => id.into(),
TransformBlockId::ExcerptFooter(id) => id.into(),
}
}
}
impl Into<ElementId> for TransformBlockId {
fn into(self) -> ElementId {
match self {
Self::Block(BlockId(id)) => ("Block", id).into(),
Self::ExcerptHeader(id) => ("ExcerptHeader", EntityId::from(id)).into(),
Self::ExcerptFooter(id) => ("ExcerptFooter", EntityId::from(id)).into(),
}
}
}
impl std::fmt::Display for TransformBlockId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Block(id) => write!(f, "Block({id:?})"),
Self::ExcerptHeader(id) => write!(f, "ExcerptHeader({id:?})"),
Self::ExcerptFooter(id) => write!(f, "ExcerptFooter({id:?})"),
}
}
}
/// Whether the block should be considered above or below the anchor line
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum BlockDisposition {
@@ -212,14 +157,6 @@ impl BlockLike for TransformBlock {
}
impl TransformBlock {
pub fn id(&self) -> TransformBlockId {
match self {
TransformBlock::Custom(block) => TransformBlockId::Block(block.id),
TransformBlock::ExcerptHeader { id, .. } => TransformBlockId::ExcerptHeader(*id),
TransformBlock::ExcerptFooter { id, .. } => TransformBlockId::ExcerptFooter(*id),
}
}
fn disposition(&self) -> BlockDisposition {
match self {
TransformBlock::Custom(block) => block.disposition,

View File

@@ -164,7 +164,7 @@ impl<'a> FoldMapWriter<'a> {
new_tree
};
let edits = consolidate_inlay_edits(edits);
consolidate_inlay_edits(&mut edits);
let edits = self.0.sync(snapshot.clone(), edits);
(self.0.snapshot.clone(), edits)
}
@@ -212,7 +212,7 @@ impl<'a> FoldMapWriter<'a> {
folds
};
let edits = consolidate_inlay_edits(edits);
consolidate_inlay_edits(&mut edits);
let edits = self.0.sync(snapshot.clone(), edits);
(self.0.snapshot.clone(), edits)
}
@@ -513,7 +513,7 @@ impl FoldMap {
});
}
fold_edits = consolidate_fold_edits(fold_edits);
consolidate_fold_edits(&mut fold_edits);
}
self.snapshot.transforms = new_transforms;
@@ -809,7 +809,7 @@ where
cursor
}
fn consolidate_inlay_edits(mut edits: Vec<InlayEdit>) -> Vec<InlayEdit> {
fn consolidate_inlay_edits(edits: &mut Vec<InlayEdit>) {
edits.sort_unstable_by(|a, b| {
a.old
.start
@@ -817,68 +817,42 @@ fn consolidate_inlay_edits(mut edits: Vec<InlayEdit>) -> Vec<InlayEdit> {
.then_with(|| b.old.end.cmp(&a.old.end))
});
let _old_alloc_ptr = edits.as_ptr();
let mut inlay_edits = edits.into_iter();
let inlay_edits = if let Some(mut first_edit) = inlay_edits.next() {
// This code relies on reusing allocations from the Vec<_> - at the time of writing .flatten() prevents them.
#[allow(clippy::filter_map_identity)]
let mut v: Vec<_> = inlay_edits
.scan(&mut first_edit, |prev_edit, edit| {
if prev_edit.old.end >= edit.old.start {
prev_edit.old.end = prev_edit.old.end.max(edit.old.end);
prev_edit.new.start = prev_edit.new.start.min(edit.new.start);
prev_edit.new.end = prev_edit.new.end.max(edit.new.end);
Some(None) // Skip this edit, it's merged
} else {
let prev = std::mem::replace(*prev_edit, edit);
Some(Some(prev)) // Yield the previous edit
}
})
.filter_map(|x| x)
.collect();
v.push(first_edit.clone());
debug_assert_eq!(_old_alloc_ptr, v.as_ptr(), "Inlay edits were reallocated");
v
} else {
vec![]
};
inlay_edits
let mut i = 1;
while i < edits.len() {
let edit = edits[i].clone();
let prev_edit = &mut edits[i - 1];
if prev_edit.old.end >= edit.old.start {
prev_edit.old.end = prev_edit.old.end.max(edit.old.end);
prev_edit.new.start = prev_edit.new.start.min(edit.new.start);
prev_edit.new.end = prev_edit.new.end.max(edit.new.end);
edits.remove(i);
continue;
}
i += 1;
}
}
fn consolidate_fold_edits(mut edits: Vec<FoldEdit>) -> Vec<FoldEdit> {
fn consolidate_fold_edits(edits: &mut Vec<FoldEdit>) {
edits.sort_unstable_by(|a, b| {
a.old
.start
.cmp(&b.old.start)
.then_with(|| b.old.end.cmp(&a.old.end))
});
let _old_alloc_ptr = edits.as_ptr();
let mut fold_edits = edits.into_iter();
let fold_edits = if let Some(mut first_edit) = fold_edits.next() {
// This code relies on reusing allocations from the Vec<_> - at the time of writing .flatten() prevents them.
#[allow(clippy::filter_map_identity)]
let mut v: Vec<_> = fold_edits
.scan(&mut first_edit, |prev_edit, edit| {
if prev_edit.old.end >= edit.old.start {
prev_edit.old.end = prev_edit.old.end.max(edit.old.end);
prev_edit.new.start = prev_edit.new.start.min(edit.new.start);
prev_edit.new.end = prev_edit.new.end.max(edit.new.end);
Some(None) // Skip this edit, it's merged
} else {
let prev = std::mem::replace(*prev_edit, edit);
Some(Some(prev)) // Yield the previous edit
}
})
.filter_map(|x| x)
.collect();
v.push(first_edit.clone());
v
} else {
vec![]
};
fold_edits
let mut i = 1;
while i < edits.len() {
let edit = edits[i].clone();
let prev_edit = &mut edits[i - 1];
if prev_edit.old.end >= edit.old.start {
prev_edit.old.end = prev_edit.old.end.max(edit.old.end);
prev_edit.new.start = prev_edit.new.start.min(edit.new.start);
prev_edit.new.end = prev_edit.new.end.max(edit.new.end);
edits.remove(i);
continue;
}
i += 1;
}
}
#[derive(Clone, Debug, Default)]

View File

@@ -103,33 +103,20 @@ impl TabMap {
}
}
let _old_alloc_ptr = fold_edits.as_ptr();
// Combine any edits that overlap due to the expansion.
let mut fold_edits = fold_edits.into_iter();
let fold_edits = if let Some(mut first_edit) = fold_edits.next() {
// This code relies on reusing allocations from the Vec<_> - at the time of writing .flatten() prevents them.
#[allow(clippy::filter_map_identity)]
let mut v: Vec<_> = fold_edits
.scan(&mut first_edit, |state, edit| {
if state.old.end >= edit.old.start {
state.old.end = edit.old.end;
state.new.end = edit.new.end;
Some(None) // Skip this edit, it's merged
} else {
let new_state = edit.clone();
let result = Some(Some(state.clone())); // Yield the previous edit
**state = new_state;
result
}
})
.filter_map(|x| x)
.collect();
v.push(first_edit);
debug_assert_eq!(v.as_ptr(), _old_alloc_ptr, "Fold edits were reallocated");
v
} else {
vec![]
};
let mut ix = 1;
while ix < fold_edits.len() {
let (prev_edits, next_edits) = fold_edits.split_at_mut(ix);
let prev_edit = prev_edits.last_mut().unwrap();
let edit = &next_edits[0];
if prev_edit.old.end >= edit.old.start {
prev_edit.old.end = edit.old.end;
prev_edit.new.end = edit.new.end;
fold_edits.remove(ix);
} else {
ix += 1;
}
}
for fold_edit in fold_edits {
let old_start = fold_edit.old.start.to_point(&old_snapshot.fold_snapshot);

View File

@@ -567,7 +567,7 @@ impl WrapSnapshot {
});
}
wrap_edits = consolidate_wrap_edits(wrap_edits);
consolidate_wrap_edits(&mut wrap_edits);
Patch::new(wrap_edits)
}
@@ -1008,33 +1008,19 @@ impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapPoint {
}
}
fn consolidate_wrap_edits(edits: Vec<WrapEdit>) -> Vec<WrapEdit> {
let _old_alloc_ptr = edits.as_ptr();
let mut wrap_edits = edits.into_iter();
let wrap_edits = if let Some(mut first_edit) = wrap_edits.next() {
// This code relies on reusing allocations from the Vec<_> - at the time of writing .flatten() prevents them.
#[allow(clippy::filter_map_identity)]
let mut v: Vec<_> = wrap_edits
.scan(&mut first_edit, |prev_edit, edit| {
if prev_edit.old.end >= edit.old.start {
prev_edit.old.end = edit.old.end;
prev_edit.new.end = edit.new.end;
Some(None) // Skip this edit, it's merged
} else {
let prev = std::mem::replace(*prev_edit, edit);
Some(Some(prev)) // Yield the previous edit
}
})
.filter_map(|x| x)
.collect();
v.push(first_edit.clone());
debug_assert_eq!(v.as_ptr(), _old_alloc_ptr, "Wrap edits were reallocated");
v
} else {
vec![]
};
wrap_edits
fn consolidate_wrap_edits(edits: &mut Vec<WrapEdit>) {
let mut i = 1;
while i < edits.len() {
let edit = edits[i].clone();
let prev_edit = &mut edits[i - 1];
if prev_edit.old.end >= edit.old.start {
prev_edit.old.end = edit.old.end;
prev_edit.new.end = edit.new.end;
edits.remove(i);
continue;
}
i += 1;
}
}
#[cfg(test)]

View File

@@ -39,10 +39,8 @@ pub mod tasks;
#[cfg(test)]
mod editor_tests;
mod signature_help;
#[cfg(any(test, feature = "test-support"))]
pub mod test;
use ::git::diff::{DiffHunk, DiffHunkStatus};
use ::git::{parse_git_remote_url, BuildPermalinkParams, GitHostingProviderRegistry};
pub(crate) use actions::*;
@@ -68,12 +66,12 @@ use git::diff_hunk_to_display;
use gpui::{
div, impl_actions, point, prelude::*, px, relative, size, uniform_list, Action, AnyElement,
AppContext, AsyncWindowContext, AvailableSpace, BackgroundExecutor, Bounds, ClipboardItem,
Context, DispatchPhase, ElementId, EntityId, EventEmitter, FocusHandle, FocusOutEvent,
FocusableView, FontId, FontStyle, FontWeight, HighlightStyle, Hsla, InteractiveText,
KeyContext, ListSizingBehavior, Model, MouseButton, PaintQuad, ParentElement, Pixels, Render,
SharedString, Size, StrikethroughStyle, Styled, StyledText, Subscription, Task, TextStyle,
UnderlineStyle, UniformListScrollHandle, View, ViewContext, ViewInputHandler, VisualContext,
WeakFocusHandle, WeakView, WhiteSpace, WindowContext,
Context, DispatchPhase, ElementId, EventEmitter, FocusHandle, FocusOutEvent, FocusableView,
FontId, FontStyle, FontWeight, HighlightStyle, Hsla, InteractiveText, KeyContext,
ListSizingBehavior, Model, MouseButton, PaintQuad, ParentElement, Pixels, Render, SharedString,
Size, StrikethroughStyle, Styled, StyledText, Subscription, Task, TextStyle, UnderlineStyle,
UniformListScrollHandle, View, ViewContext, ViewInputHandler, VisualContext, WeakFocusHandle,
WeakView, WhiteSpace, WindowContext,
};
use highlight_matching_bracket::refresh_matching_bracket_highlights;
use hover_popover::{hide_hover, HoverState};
@@ -156,7 +154,6 @@ use workspace::{
use workspace::{OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
use crate::hover_links::find_url;
use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
pub const FILE_HEADER_HEIGHT: u8 = 1;
pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u8 = 1;
@@ -271,7 +268,7 @@ pub fn init(cx: &mut AppContext) {
init_settings(cx);
workspace::register_project_item::<Editor>(cx);
workspace::FollowableViewRegistry::register::<Editor>(cx);
workspace::register_followable_item::<Editor>(cx);
workspace::register_deserializable_item::<Editor>(cx);
cx.observe_new_views(
|workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
@@ -504,8 +501,6 @@ pub struct Editor {
context_menu: RwLock<Option<ContextMenu>>,
mouse_context_menu: Option<MouseContextMenu>,
completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
signature_help_state: SignatureHelpState,
auto_signature_help: Option<bool>,
find_all_references_task_sources: Vec<Anchor>,
next_completion_id: CompletionId,
completion_documentation_pre_resolve_debounce: DebouncedDelay,
@@ -1770,8 +1765,6 @@ impl Editor {
);
let focus_handle = cx.focus_handle();
cx.on_focus(&focus_handle, Self::handle_focus).detach();
cx.on_focus_in(&focus_handle, Self::handle_focus_in)
.detach();
cx.on_focus_out(&focus_handle, Self::handle_focus_out)
.detach();
cx.on_blur(&focus_handle, Self::handle_blur).detach();
@@ -1826,8 +1819,6 @@ impl Editor {
context_menu: RwLock::new(None),
mouse_context_menu: None,
completion_tasks: Default::default(),
signature_help_state: SignatureHelpState::default(),
auto_signature_help: None,
find_all_references_task_sources: Vec::new(),
next_completion_id: 0,
completion_documentation_pre_resolve_debounce: DebouncedDelay::new(),
@@ -2165,10 +2156,6 @@ impl Editor {
pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
self.cursor_shape = cursor_shape;
// Disrupt blink for immediate user feedback that the cursor shape has changed
self.blink_manager.update(cx, BlinkManager::show_cursor);
cx.notify();
}
@@ -2424,15 +2411,6 @@ impl Editor {
self.request_autoscroll(autoscroll, cx);
}
self.selections_did_change(true, &old_cursor_position, request_completions, cx);
if self.should_open_signature_help_automatically(
&old_cursor_position,
self.signature_help_state.backspace_pressed(),
cx,
) {
self.show_signature_help(&ShowSignatureHelp, cx);
}
self.signature_help_state.set_backspace_pressed(false);
}
result
@@ -2888,10 +2866,6 @@ impl Editor {
return true;
}
if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
return true;
}
if self.hide_context_menu(cx).is_some() {
return true;
}
@@ -2968,7 +2942,7 @@ impl Editor {
}
let selections = self.selections.all_adjusted(cx);
let mut bracket_inserted = false;
let mut brace_inserted = false;
let mut edits = Vec::new();
let mut linked_edits = HashMap::<_, Vec<_>>::default();
let mut new_selections = Vec::with_capacity(selections.len());
@@ -3030,7 +3004,6 @@ impl Editor {
),
&bracket_pair.start[..prefix_len],
));
if autoclose
&& bracket_pair.close
&& following_text_allows_autoclose
@@ -3048,7 +3021,7 @@ impl Editor {
selection.range(),
format!("{}{}", text, bracket_pair.end).into(),
));
bracket_inserted = true;
brace_inserted = true;
continue;
}
}
@@ -3094,7 +3067,7 @@ impl Editor {
selection.end..selection.end,
bracket_pair.end.as_str().into(),
));
bracket_inserted = true;
brace_inserted = true;
new_selections.push((
Selection {
id: selection.id,
@@ -3251,7 +3224,7 @@ impl Editor {
s.select(new_selections)
});
if !bracket_inserted && EditorSettings::get_global(cx).use_on_type_format {
if !brace_inserted && EditorSettings::get_global(cx).use_on_type_format {
if let Some(on_type_format_task) =
this.trigger_on_type_formatting(text.to_string(), cx)
{
@@ -3259,14 +3232,6 @@ impl Editor {
}
}
let editor_settings = EditorSettings::get_global(cx);
if bracket_inserted
&& (editor_settings.auto_signature_help
|| editor_settings.show_signature_help_after_edits)
{
this.show_signature_help(&ShowSignatureHelp, cx);
}
let trigger_in_words = !had_active_inline_completion;
this.trigger_completion_on_input(&text, trigger_in_words, cx);
linked_editing_ranges::refresh_linked_ranges(this, cx);
@@ -4340,14 +4305,6 @@ impl Editor {
true,
cx,
);
let editor_settings = EditorSettings::get_global(cx);
if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
// After the code completion is finished, users often want to know what signatures are needed.
// so we should automatically call signature_help
self.show_signature_help(&ShowSignatureHelp, cx);
}
Some(cx.foreground_executor().spawn(async move {
apply_edits.await?;
Ok(())
@@ -5371,7 +5328,6 @@ impl Editor {
}
}
this.signature_help_state.set_backspace_pressed(true);
this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
this.insert("", cx);
let empty_str: Arc<str> = Arc::from("");
@@ -8531,7 +8487,7 @@ impl Editor {
) -> Vec<(TaskSourceKind, TaskTemplate)> {
let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
let (worktree_id, file) = project
.buffer_for_id(runnable.buffer, cx)
.buffer_for_id(runnable.buffer)
.and_then(|buffer| buffer.read(cx).file())
.map(|file| (WorktreeId::from_usize(file.worktree_id()), file.clone()))
.unzip();
@@ -9491,11 +9447,6 @@ impl Editor {
}
editor
});
cx.subscribe(&rename_editor, |_, _, e, cx| match e {
EditorEvent::Focused => cx.emit(EditorEvent::FocusedIn),
_ => {}
})
.detach();
let write_highlights =
this.clear_background_highlights::<DocumentHighlightWrite>(cx);
@@ -9769,7 +9720,7 @@ impl Editor {
*block_id,
(
None,
diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
diagnostic_block_renderer(diagnostic.clone(), is_valid),
),
);
}
@@ -9822,7 +9773,7 @@ impl Editor {
style: BlockStyle::Fixed,
position: buffer.anchor_after(entry.range.start),
height: message_height,
render: diagnostic_block_renderer(diagnostic, None, true, true),
render: diagnostic_block_renderer(diagnostic, true),
disposition: BlockDisposition::Below,
}
}),
@@ -11142,7 +11093,6 @@ impl Editor {
if *singleton_buffer_edited {
if let Some(project) = &self.project {
let project = project.read(cx);
#[allow(clippy::mutable_key_type)]
let languages_affected = multibuffer
.read(cx)
.all_buffers()
@@ -11639,10 +11589,6 @@ impl Editor {
}
}
fn handle_focus_in(&mut self, cx: &mut ViewContext<Self>) {
cx.emit(EditorEvent::FocusedIn)
}
fn handle_focus_out(&mut self, event: FocusOutEvent, _cx: &mut ViewContext<Self>) {
if event.blurred != self.focus_handle {
self.last_focused_descendant = Some(event.blurred);
@@ -11657,11 +11603,8 @@ impl Editor {
if let Some(blame) = self.blame.as_ref() {
blame.update(cx, GitBlame::blur)
}
if !self.hover_state.focused(cx) {
hide_hover(self, cx);
}
self.hide_context_menu(cx);
hide_hover(self, cx);
cx.emit(EditorEvent::Blurred);
cx.notify();
}
@@ -11824,7 +11767,7 @@ fn snippet_completions(
let language = buffer.read(cx).language_at(buffer_position);
let language_name = language.as_ref().map(|language| language.lsp_id());
let snippet_store = project.snippets().read(cx);
let snippets = snippet_store.snippets_for(language_name, cx);
let snippets = snippet_store.snippets_for(language_name);
if snippets.is_empty() {
return vec![];
@@ -12247,7 +12190,6 @@ pub enum EditorEvent {
},
Reparsed(BufferId),
Focused,
FocusedIn,
Blurred,
DirtyChanged,
Saved,
@@ -12696,17 +12638,11 @@ impl InvalidationRegion for SnippetState {
}
}
pub fn diagnostic_block_renderer(
diagnostic: Diagnostic,
max_message_rows: Option<u8>,
allow_closing: bool,
_is_valid: bool,
) -> RenderBlock {
let (text_without_backticks, code_ranges) =
highlight_diagnostic_message(&diagnostic, max_message_rows);
pub fn diagnostic_block_renderer(diagnostic: Diagnostic, _is_valid: bool) -> RenderBlock {
let (text_without_backticks, code_ranges) = highlight_diagnostic_message(&diagnostic);
Box::new(move |cx: &mut BlockContext| {
let group_id: SharedString = cx.transform_block_id.to_string().into();
let group_id: SharedString = cx.block_id.to_string().into();
let mut text_style = cx.text_style().clone();
text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
@@ -12718,25 +12654,23 @@ pub fn diagnostic_block_renderer(
let multi_line_diagnostic = diagnostic.message.contains('\n');
let buttons = |diagnostic: &Diagnostic, block_id: TransformBlockId| {
let buttons = |diagnostic: &Diagnostic, block_id: usize| {
if multi_line_diagnostic {
v_flex()
} else {
h_flex()
}
.when(allow_closing, |div| {
div.children(diagnostic.is_primary.then(|| {
IconButton::new(("close-block", EntityId::from(block_id)), IconName::XCircle)
.icon_color(Color::Muted)
.size(ButtonSize::Compact)
.style(ButtonStyle::Transparent)
.visible_on_hover(group_id.clone())
.on_click(move |_click, cx| cx.dispatch_action(Box::new(Cancel)))
.tooltip(|cx| Tooltip::for_action("Close Diagnostics", &Cancel, cx))
}))
})
.children(diagnostic.is_primary.then(|| {
IconButton::new(("close-block", block_id), IconName::XCircle)
.icon_color(Color::Muted)
.size(ButtonSize::Compact)
.style(ButtonStyle::Transparent)
.visible_on_hover(group_id.clone())
.on_click(move |_click, cx| cx.dispatch_action(Box::new(Cancel)))
.tooltip(|cx| Tooltip::for_action("Close Diagnostics", &Cancel, cx))
}))
.child(
IconButton::new(("copy-block", EntityId::from(block_id)), IconName::Copy)
IconButton::new(("copy-block", block_id), IconName::Copy)
.icon_color(Color::Muted)
.size(ButtonSize::Compact)
.style(ButtonStyle::Transparent)
@@ -12749,12 +12683,12 @@ pub fn diagnostic_block_renderer(
)
};
let icon_size = buttons(&diagnostic, cx.transform_block_id)
let icon_size = buttons(&diagnostic, cx.block_id)
.into_any_element()
.layout_as_root(AvailableSpace::min_size(), cx);
h_flex()
.id(cx.transform_block_id)
.id(cx.block_id)
.group(group_id.clone())
.relative()
.size_full()
@@ -12766,7 +12700,7 @@ pub fn diagnostic_block_renderer(
.w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
.flex_shrink(),
)
.child(buttons(&diagnostic, cx.transform_block_id))
.child(buttons(&diagnostic, cx.block_id))
.child(div().flex().flex_shrink_0().child(
StyledText::new(text_without_backticks.clone()).with_highlights(
&text_style,
@@ -12785,10 +12719,7 @@ pub fn diagnostic_block_renderer(
})
}
pub fn highlight_diagnostic_message(
diagnostic: &Diagnostic,
mut max_message_rows: Option<u8>,
) -> (SharedString, Vec<Range<usize>>) {
pub fn highlight_diagnostic_message(diagnostic: &Diagnostic) -> (SharedString, Vec<Range<usize>>) {
let mut text_without_backticks = String::new();
let mut code_ranges = Vec::new();
@@ -12800,45 +12731,18 @@ pub fn highlight_diagnostic_message(
let mut prev_offset = 0;
let mut in_code_block = false;
let mut newline_indices = diagnostic
.message
.match_indices('\n')
.map(|(ix, _)| ix)
.fuse()
.peekable();
for (ix, _) in diagnostic
.message
.match_indices('`')
.chain([(diagnostic.message.len(), "")])
{
let mut trimmed_ix = ix;
while let Some(newline_index) = newline_indices.peek() {
if *newline_index < ix {
if let Some(rows_left) = &mut max_message_rows {
if *rows_left == 0 {
trimmed_ix = newline_index.saturating_sub(1);
break;
} else {
*rows_left -= 1;
}
}
let _ = newline_indices.next();
} else {
break;
}
}
let prev_len = text_without_backticks.len();
let new_text = &diagnostic.message[prev_offset..trimmed_ix];
text_without_backticks.push_str(new_text);
text_without_backticks.push_str(&diagnostic.message[prev_offset..ix]);
prev_offset = ix + 1;
if in_code_block {
code_ranges.push(prev_len..text_without_backticks.len());
}
prev_offset = trimmed_ix + 1;
in_code_block = !in_code_block;
if trimmed_ix != ix {
text_without_backticks.push_str("...");
break;
}
}
(text_without_backticks.into(), code_ranges)

View File

@@ -25,9 +25,6 @@ pub struct EditorSettings {
pub expand_excerpt_lines: u32,
#[serde(default)]
pub double_click_in_multibuffer: DoubleClickInMultibuffer,
pub search_wrap: bool,
pub auto_signature_help: bool,
pub show_signature_help_after_edits: bool,
#[serde(default)]
pub jupyter: Jupyter,
}
@@ -231,20 +228,6 @@ pub struct EditorSettingsContent {
///
/// Default: select
pub double_click_in_multibuffer: Option<DoubleClickInMultibuffer>,
/// Whether the editor search results will loop
///
/// Default: true
pub search_wrap: Option<bool>,
/// Whether to automatically show a signature help pop-up or not.
///
/// Default: false
pub auto_signature_help: Option<bool>,
/// Whether to show the signature help pop-up after completions or bracket pairs inserted.
///
/// Default: true
pub show_signature_help_after_edits: Option<bool>,
/// Jupyter REPL settings.
pub jupyter: Option<Jupyter>,

View File

@@ -21,16 +21,13 @@ use language::{
BracketPairConfig,
Capability::ReadWrite,
FakeLspAdapter, IndentGuide, LanguageConfig, LanguageConfigOverride, LanguageMatcher, Override,
ParsedMarkdown, Point,
Point,
};
use language_settings::IndentGuideSettings;
use multi_buffer::MultiBufferIndentGuide;
use parking_lot::Mutex;
use project::project_settings::{LspSettings, ProjectSettings};
use project::FakeFs;
use project::{
lsp_command::SIGNATURE_HELP_HIGHLIGHT_CURRENT,
project_settings::{LspSettings, ProjectSettings},
};
use serde_json::{self, json};
use std::sync::atomic;
use std::sync::atomic::AtomicUsize;
@@ -6834,626 +6831,6 @@ async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext)
);
}
#[gpui::test]
async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
cx: &mut gpui::TestAppContext,
) {
init_test(cx, |_| {});
cx.update(|cx| {
cx.update_global::<SettingsStore, _>(|settings, cx| {
settings.update_user_settings::<EditorSettings>(cx, |settings| {
settings.auto_signature_help = Some(true);
});
});
});
let mut cx = EditorLspTestContext::new_rust(
lsp::ServerCapabilities {
signature_help_provider: Some(lsp::SignatureHelpOptions {
..Default::default()
}),
..Default::default()
},
cx,
)
.await;
let language = Language::new(
LanguageConfig {
name: "Rust".into(),
brackets: BracketPairConfig {
pairs: vec![
BracketPair {
start: "{".to_string(),
end: "}".to_string(),
close: true,
surround: true,
newline: true,
},
BracketPair {
start: "(".to_string(),
end: ")".to_string(),
close: true,
surround: true,
newline: true,
},
BracketPair {
start: "/*".to_string(),
end: " */".to_string(),
close: true,
surround: true,
newline: true,
},
BracketPair {
start: "[".to_string(),
end: "]".to_string(),
close: false,
surround: false,
newline: true,
},
BracketPair {
start: "\"".to_string(),
end: "\"".to_string(),
close: true,
surround: true,
newline: false,
},
BracketPair {
start: "<".to_string(),
end: ">".to_string(),
close: false,
surround: true,
newline: true,
},
],
..Default::default()
},
autoclose_before: "})]".to_string(),
..Default::default()
},
Some(tree_sitter_rust::language()),
);
let language = Arc::new(language);
cx.language_registry().add(language.clone());
cx.update_buffer(|buffer, cx| {
buffer.set_language(Some(language), cx);
});
cx.set_state(
&r#"
fn main() {
sampleˇ
}
"#
.unindent(),
);
cx.update_editor(|view, cx| {
view.handle_input("(", cx);
});
cx.assert_editor_state(
&"
fn main() {
sample(ˇ)
}
"
.unindent(),
);
let mocked_response = lsp::SignatureHelp {
signatures: vec![lsp::SignatureInformation {
label: "fn sample(param1: u8, param2: u8)".to_string(),
documentation: None,
parameters: Some(vec![
lsp::ParameterInformation {
label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
documentation: None,
},
lsp::ParameterInformation {
label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
documentation: None,
},
]),
active_parameter: None,
}],
active_signature: Some(0),
active_parameter: Some(0),
};
handle_signature_help_request(&mut cx, mocked_response).await;
cx.condition(|editor, _| editor.signature_help_state.is_shown())
.await;
cx.editor(|editor, _| {
let signature_help_state = editor.signature_help_state.popover().cloned();
assert!(signature_help_state.is_some());
let ParsedMarkdown {
text, highlights, ..
} = signature_help_state.unwrap().parsed_content;
assert_eq!(text, "param1: u8, param2: u8");
assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
});
}
#[gpui::test]
async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
cx.update(|cx| {
cx.update_global::<SettingsStore, _>(|settings, cx| {
settings.update_user_settings::<EditorSettings>(cx, |settings| {
settings.auto_signature_help = Some(false);
settings.show_signature_help_after_edits = Some(false);
});
});
});
let mut cx = EditorLspTestContext::new_rust(
lsp::ServerCapabilities {
signature_help_provider: Some(lsp::SignatureHelpOptions {
..Default::default()
}),
..Default::default()
},
cx,
)
.await;
let language = Language::new(
LanguageConfig {
name: "Rust".into(),
brackets: BracketPairConfig {
pairs: vec![
BracketPair {
start: "{".to_string(),
end: "}".to_string(),
close: true,
surround: true,
newline: true,
},
BracketPair {
start: "(".to_string(),
end: ")".to_string(),
close: true,
surround: true,
newline: true,
},
BracketPair {
start: "/*".to_string(),
end: " */".to_string(),
close: true,
surround: true,
newline: true,
},
BracketPair {
start: "[".to_string(),
end: "]".to_string(),
close: false,
surround: false,
newline: true,
},
BracketPair {
start: "\"".to_string(),
end: "\"".to_string(),
close: true,
surround: true,
newline: false,
},
BracketPair {
start: "<".to_string(),
end: ">".to_string(),
close: false,
surround: true,
newline: true,
},
],
..Default::default()
},
autoclose_before: "})]".to_string(),
..Default::default()
},
Some(tree_sitter_rust::language()),
);
let language = Arc::new(language);
cx.language_registry().add(language.clone());
cx.update_buffer(|buffer, cx| {
buffer.set_language(Some(language), cx);
});
// Ensure that signature_help is not called when no signature help is enabled.
cx.set_state(
&r#"
fn main() {
sampleˇ
}
"#
.unindent(),
);
cx.update_editor(|view, cx| {
view.handle_input("(", cx);
});
cx.assert_editor_state(
&"
fn main() {
sample(ˇ)
}
"
.unindent(),
);
cx.editor(|editor, _| {
assert!(editor.signature_help_state.task().is_none());
});
let mocked_response = lsp::SignatureHelp {
signatures: vec![lsp::SignatureInformation {
label: "fn sample(param1: u8, param2: u8)".to_string(),
documentation: None,
parameters: Some(vec![
lsp::ParameterInformation {
label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
documentation: None,
},
lsp::ParameterInformation {
label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
documentation: None,
},
]),
active_parameter: None,
}],
active_signature: Some(0),
active_parameter: Some(0),
};
// Ensure that signature_help is called when enabled afte edits
cx.update(|cx| {
cx.update_global::<SettingsStore, _>(|settings, cx| {
settings.update_user_settings::<EditorSettings>(cx, |settings| {
settings.auto_signature_help = Some(false);
settings.show_signature_help_after_edits = Some(true);
});
});
});
cx.set_state(
&r#"
fn main() {
sampleˇ
}
"#
.unindent(),
);
cx.update_editor(|view, cx| {
view.handle_input("(", cx);
});
cx.assert_editor_state(
&"
fn main() {
sample(ˇ)
}
"
.unindent(),
);
handle_signature_help_request(&mut cx, mocked_response.clone()).await;
cx.condition(|editor, _| editor.signature_help_state.is_shown())
.await;
cx.update_editor(|editor, _| {
let signature_help_state = editor.signature_help_state.popover().cloned();
assert!(signature_help_state.is_some());
let ParsedMarkdown {
text, highlights, ..
} = signature_help_state.unwrap().parsed_content;
assert_eq!(text, "param1: u8, param2: u8");
assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
editor.signature_help_state = SignatureHelpState::default();
});
// Ensure that signature_help is called when auto signature help override is enabled
cx.update(|cx| {
cx.update_global::<SettingsStore, _>(|settings, cx| {
settings.update_user_settings::<EditorSettings>(cx, |settings| {
settings.auto_signature_help = Some(true);
settings.show_signature_help_after_edits = Some(false);
});
});
});
cx.set_state(
&r#"
fn main() {
sampleˇ
}
"#
.unindent(),
);
cx.update_editor(|view, cx| {
view.handle_input("(", cx);
});
cx.assert_editor_state(
&"
fn main() {
sample(ˇ)
}
"
.unindent(),
);
handle_signature_help_request(&mut cx, mocked_response).await;
cx.condition(|editor, _| editor.signature_help_state.is_shown())
.await;
cx.editor(|editor, _| {
let signature_help_state = editor.signature_help_state.popover().cloned();
assert!(signature_help_state.is_some());
let ParsedMarkdown {
text, highlights, ..
} = signature_help_state.unwrap().parsed_content;
assert_eq!(text, "param1: u8, param2: u8");
assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
});
}
#[gpui::test]
async fn test_signature_help(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
cx.update(|cx| {
cx.update_global::<SettingsStore, _>(|settings, cx| {
settings.update_user_settings::<EditorSettings>(cx, |settings| {
settings.auto_signature_help = Some(true);
});
});
});
let mut cx = EditorLspTestContext::new_rust(
lsp::ServerCapabilities {
signature_help_provider: Some(lsp::SignatureHelpOptions {
..Default::default()
}),
..Default::default()
},
cx,
)
.await;
// A test that directly calls `show_signature_help`
cx.update_editor(|editor, cx| {
editor.show_signature_help(&ShowSignatureHelp, cx);
});
let mocked_response = lsp::SignatureHelp {
signatures: vec![lsp::SignatureInformation {
label: "fn sample(param1: u8, param2: u8)".to_string(),
documentation: None,
parameters: Some(vec![
lsp::ParameterInformation {
label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
documentation: None,
},
lsp::ParameterInformation {
label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
documentation: None,
},
]),
active_parameter: None,
}],
active_signature: Some(0),
active_parameter: Some(0),
};
handle_signature_help_request(&mut cx, mocked_response).await;
cx.condition(|editor, _| editor.signature_help_state.is_shown())
.await;
cx.editor(|editor, _| {
let signature_help_state = editor.signature_help_state.popover().cloned();
assert!(signature_help_state.is_some());
let ParsedMarkdown {
text, highlights, ..
} = signature_help_state.unwrap().parsed_content;
assert_eq!(text, "param1: u8, param2: u8");
assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
});
// When exiting outside from inside the brackets, `signature_help` is closed.
cx.set_state(indoc! {"
fn main() {
sample(ˇ);
}
fn sample(param1: u8, param2: u8) {}
"});
cx.update_editor(|editor, cx| {
editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
});
let mocked_response = lsp::SignatureHelp {
signatures: Vec::new(),
active_signature: None,
active_parameter: None,
};
handle_signature_help_request(&mut cx, mocked_response).await;
cx.condition(|editor, _| !editor.signature_help_state.is_shown())
.await;
cx.editor(|editor, _| {
assert!(!editor.signature_help_state.is_shown());
});
// When entering inside the brackets from outside, `show_signature_help` is automatically called.
cx.set_state(indoc! {"
fn main() {
sample(ˇ);
}
fn sample(param1: u8, param2: u8) {}
"});
let mocked_response = lsp::SignatureHelp {
signatures: vec![lsp::SignatureInformation {
label: "fn sample(param1: u8, param2: u8)".to_string(),
documentation: None,
parameters: Some(vec![
lsp::ParameterInformation {
label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
documentation: None,
},
lsp::ParameterInformation {
label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
documentation: None,
},
]),
active_parameter: None,
}],
active_signature: Some(0),
active_parameter: Some(0),
};
handle_signature_help_request(&mut cx, mocked_response.clone()).await;
cx.condition(|editor, _| editor.signature_help_state.is_shown())
.await;
cx.editor(|editor, _| {
assert!(editor.signature_help_state.is_shown());
});
// Restore the popover with more parameter input
cx.set_state(indoc! {"
fn main() {
sample(param1, param2ˇ);
}
fn sample(param1: u8, param2: u8) {}
"});
let mocked_response = lsp::SignatureHelp {
signatures: vec![lsp::SignatureInformation {
label: "fn sample(param1: u8, param2: u8)".to_string(),
documentation: None,
parameters: Some(vec![
lsp::ParameterInformation {
label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
documentation: None,
},
lsp::ParameterInformation {
label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
documentation: None,
},
]),
active_parameter: None,
}],
active_signature: Some(0),
active_parameter: Some(1),
};
handle_signature_help_request(&mut cx, mocked_response.clone()).await;
cx.condition(|editor, _| editor.signature_help_state.is_shown())
.await;
// When selecting a range, the popover is gone.
// Avoid using `cx.set_state` to not actually edit the document, just change its selections.
cx.update_editor(|editor, cx| {
editor.change_selections(None, cx, |s| {
s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
})
});
cx.assert_editor_state(indoc! {"
fn main() {
sample(param1, «ˇparam2»);
}
fn sample(param1: u8, param2: u8) {}
"});
cx.editor(|editor, _| {
assert!(!editor.signature_help_state.is_shown());
});
// When unselecting again, the popover is back if within the brackets.
cx.update_editor(|editor, cx| {
editor.change_selections(None, cx, |s| {
s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
})
});
cx.assert_editor_state(indoc! {"
fn main() {
sample(param1, ˇparam2);
}
fn sample(param1: u8, param2: u8) {}
"});
handle_signature_help_request(&mut cx, mocked_response).await;
cx.condition(|editor, _| editor.signature_help_state.is_shown())
.await;
cx.editor(|editor, _| {
assert!(editor.signature_help_state.is_shown());
});
// Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
cx.update_editor(|editor, cx| {
editor.change_selections(None, cx, |s| {
s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
})
});
cx.assert_editor_state(indoc! {"
fn main() {
sample(param1, ˇparam2);
}
fn sample(param1: u8, param2: u8) {}
"});
let mocked_response = lsp::SignatureHelp {
signatures: vec![lsp::SignatureInformation {
label: "fn sample(param1: u8, param2: u8)".to_string(),
documentation: None,
parameters: Some(vec![
lsp::ParameterInformation {
label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
documentation: None,
},
lsp::ParameterInformation {
label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
documentation: None,
},
]),
active_parameter: None,
}],
active_signature: Some(0),
active_parameter: Some(1),
};
handle_signature_help_request(&mut cx, mocked_response.clone()).await;
cx.condition(|editor, _| editor.signature_help_state.is_shown())
.await;
cx.update_editor(|editor, cx| {
editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
});
cx.condition(|editor, _| !editor.signature_help_state.is_shown())
.await;
cx.update_editor(|editor, cx| {
editor.change_selections(None, cx, |s| {
s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
})
});
cx.assert_editor_state(indoc! {"
fn main() {
sample(param1, «ˇparam2»);
}
fn sample(param1: u8, param2: u8) {}
"});
cx.update_editor(|editor, cx| {
editor.change_selections(None, cx, |s| {
s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
})
});
cx.assert_editor_state(indoc! {"
fn main() {
sample(param1, ˇparam2);
}
fn sample(param1: u8, param2: u8) {}
"});
cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
.await;
}
#[gpui::test]
async fn test_completion(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
@@ -8812,6 +8189,7 @@ async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
let follower_1 = cx
.update_window(*workspace.deref(), |_, cx| {
Editor::from_state_proto(
pane.clone(),
workspace.root_view(cx).unwrap(),
ViewId {
creator: Default::default(),
@@ -8903,6 +8281,7 @@ async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
let follower_2 = cx
.update_window(*workspace.deref(), |_, cx| {
Editor::from_state_proto(
pane.clone(),
workspace.root_view(cx).unwrap().clone(),
ViewId {
creator: Default::default(),
@@ -13071,21 +12450,6 @@ fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewCo
);
}
pub fn handle_signature_help_request(
cx: &mut EditorLspTestContext,
mocked_response: lsp::SignatureHelp,
) -> impl Future<Output = ()> {
let mut request =
cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
let mocked_response = mocked_response.clone();
async move { Ok(Some(mocked_response)) }
});
async move {
request.next().await;
}
}
/// Handle completion request passing a marked string specifying where the completion
/// should be triggered from using '|' character, what range should be replaced, and what completions
/// should be returned using '<' and '>' to delimit the range

View File

@@ -1,5 +1,4 @@
use crate::editor_settings::ScrollBeyondLastLine;
use crate::TransformBlockId;
use crate::{
blame_entry_tooltip::{blame_entry_relative_timestamp, BlameEntryTooltip},
display_map::{
@@ -32,7 +31,7 @@ use gpui::{
anchored, deferred, div, fill, outline, point, px, quad, relative, size, svg,
transparent_black, Action, AnchorCorner, AnyElement, AvailableSpace, Bounds, ClipboardItem,
ContentMask, Corners, CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Entity,
EntityId, FontId, GlobalElementId, Hitbox, Hsla, InteractiveElement, IntoElement, Length,
FontId, GlobalElementId, Hitbox, Hsla, InteractiveElement, IntoElement, Length,
ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad,
ParentElement, Pixels, ScrollDelta, ScrollWheelEvent, ShapedLine, SharedString, Size,
StatefulInteractiveElement, Style, Styled, TextRun, TextStyle, TextStyleRefinement, View,
@@ -383,7 +382,6 @@ impl EditorElement {
cx.propagate();
}
});
register_action(view, cx, Editor::show_signature_help);
register_action(view, cx, Editor::next_inline_completion);
register_action(view, cx, Editor::previous_inline_completion);
register_action(view, cx, Editor::show_inline_completion);
@@ -409,10 +407,8 @@ impl EditorElement {
if phase != DispatchPhase::Bubble {
return;
}
editor.update(cx, |editor, cx| {
if editor.hover_state.focused(cx) {
return;
}
Self::modifiers_changed(editor, event, &position_map, &text_hitbox, cx)
})
}
@@ -713,24 +709,18 @@ impl EditorElement {
let Some(hub) = editor.collaboration_hub() else {
return;
};
let start = snapshot.display_snapshot.clip_point(
DisplayPoint::new(point.row(), point.column().saturating_sub(1)),
Bias::Left,
);
let end = snapshot.display_snapshot.clip_point(
DisplayPoint::new(
let range = DisplayPoint::new(point.row(), point.column().saturating_sub(1))
..DisplayPoint::new(
point.row(),
(point.column() + 1).min(snapshot.line_len(point.row())),
),
Bias::Right,
);
);
let range = snapshot
.buffer_snapshot
.anchor_at(start.to_point(&snapshot.display_snapshot), Bias::Left)
.anchor_at(range.start.to_point(&snapshot.display_snapshot), Bias::Left)
..snapshot
.buffer_snapshot
.anchor_at(end.to_point(&snapshot.display_snapshot), Bias::Right);
.anchor_at(range.end.to_point(&snapshot.display_snapshot), Bias::Right);
let Some(selection) = snapshot.remote_selections_in_range(&range, hub, cx).next() else {
return;
@@ -1940,6 +1930,7 @@ impl EditorElement {
line_layouts: &[LineWithInvisibles],
cx: &mut WindowContext,
) -> Vec<BlockLayout> {
let mut block_id = 0;
let (fixed_blocks, non_fixed_blocks) = snapshot
.blocks_in_range(rows.clone())
.partition::<Vec<_>, _>(|(_, block)| match block {
@@ -1950,7 +1941,7 @@ impl EditorElement {
let render_block = |block: &TransformBlock,
available_space: Size<AvailableSpace>,
block_id: TransformBlockId,
block_id: usize,
block_row_start: DisplayRow,
cx: &mut WindowContext| {
let mut element = match block {
@@ -1974,7 +1965,7 @@ impl EditorElement {
gutter_dimensions,
line_height,
em_width,
transform_block_id: block_id,
block_id,
max_width: text_hitbox.size.width.max(*scroll_width),
editor_style: &self.style,
})
@@ -2058,7 +2049,7 @@ impl EditorElement {
let header_padding = px(6.0);
v_flex()
.id(("path excerpt header", EntityId::from(block_id)))
.id(("path excerpt header", block_id))
.size_full()
.p(header_padding)
.child(
@@ -2166,7 +2157,7 @@ impl EditorElement {
}))
} else {
v_flex()
.id(("excerpt header", EntityId::from(block_id)))
.id(("excerpt header", block_id))
.size_full()
.child(
div()
@@ -2314,54 +2305,49 @@ impl EditorElement {
}
TransformBlock::ExcerptFooter { id, .. } => {
let element = v_flex()
.id(("excerpt footer", EntityId::from(block_id)))
.size_full()
.child(
h_flex()
.justify_end()
.flex_none()
.w(gutter_dimensions.width
- (gutter_dimensions.left_padding + gutter_dimensions.margin))
.h_full()
.child(
ButtonLike::new("expand-icon")
.style(ButtonStyle::Transparent)
.child(
svg()
.path(IconName::ArrowDownFromLine.path())
.size(IconSize::XSmall.rems())
.text_color(cx.theme().colors().editor_line_number)
.group("")
.hover(|style| {
style.text_color(
cx.theme()
.colors()
.editor_active_line_number,
)
}),
)
.on_click(cx.listener_for(&self.editor, {
let id = *id;
move |editor, _, cx| {
editor.expand_excerpt(
id,
multi_buffer::ExpandExcerptDirection::Down,
cx,
);
}
}))
.tooltip({
move |cx| {
Tooltip::for_action(
"Expand Excerpt",
&ExpandExcerpts { lines: 0 },
cx,
let element = v_flex().id(("excerpt footer", block_id)).size_full().child(
h_flex()
.justify_end()
.flex_none()
.w(gutter_dimensions.width
- (gutter_dimensions.left_padding + gutter_dimensions.margin))
.h_full()
.child(
ButtonLike::new("expand-icon")
.style(ButtonStyle::Transparent)
.child(
svg()
.path(IconName::ArrowDownFromLine.path())
.size(IconSize::XSmall.rems())
.text_color(cx.theme().colors().editor_line_number)
.group("")
.hover(|style| {
style.text_color(
cx.theme().colors().editor_active_line_number,
)
}
}),
),
);
}),
)
.on_click(cx.listener_for(&self.editor, {
let id = *id;
move |editor, _, cx| {
editor.expand_excerpt(
id,
multi_buffer::ExpandExcerptDirection::Down,
cx,
);
}
}))
.tooltip({
move |cx| {
Tooltip::for_action(
"Expand Excerpt",
&ExpandExcerpts { lines: 0 },
cx,
)
}
}),
),
);
element.into_any()
}
};
@@ -2377,8 +2363,8 @@ impl EditorElement {
AvailableSpace::MinContent,
AvailableSpace::Definite(block.height() as f32 * line_height),
);
let block_id = block.id();
let (element, element_size) = render_block(block, available_space, block_id, row, cx);
block_id += 1;
fixed_block_max_width = fixed_block_max_width.max(element_size.width + em_width);
blocks.push(BlockLayout {
row,
@@ -2406,8 +2392,8 @@ impl EditorElement {
AvailableSpace::Definite(width),
AvailableSpace::Definite(block.height() as f32 * line_height),
);
let block_id = block.id();
let (element, _) = render_block(block, available_space, block_id, row, cx);
block_id += 1;
blocks.push(BlockLayout {
row,
element,
@@ -2643,73 +2629,6 @@ impl EditorElement {
}
}
#[allow(clippy::too_many_arguments)]
fn layout_signature_help(
&self,
hitbox: &Hitbox,
content_origin: gpui::Point<Pixels>,
scroll_pixel_position: gpui::Point<Pixels>,
newest_selection_head: Option<DisplayPoint>,
start_row: DisplayRow,
line_layouts: &[LineWithInvisibles],
line_height: Pixels,
em_width: Pixels,
cx: &mut WindowContext,
) {
let Some(newest_selection_head) = newest_selection_head else {
return;
};
let selection_row = newest_selection_head.row();
if selection_row < start_row {
return;
}
let Some(cursor_row_layout) = line_layouts.get(selection_row.minus(start_row) as usize)
else {
return;
};
let start_x = cursor_row_layout.x_for_index(newest_selection_head.column() as usize)
- scroll_pixel_position.x
+ content_origin.x;
let start_y =
selection_row.as_f32() * line_height + content_origin.y - scroll_pixel_position.y;
let max_size = size(
(120. * em_width) // Default size
.min(hitbox.size.width / 2.) // Shrink to half of the editor width
.max(MIN_POPOVER_CHARACTER_WIDTH * em_width), // Apply minimum width of 20 characters
(16. * line_height) // Default size
.min(hitbox.size.height / 2.) // Shrink to half of the editor height
.max(MIN_POPOVER_LINE_HEIGHT * line_height), // Apply minimum height of 4 lines
);
let maybe_element = self.editor.update(cx, |editor, cx| {
if let Some(popover) = editor.signature_help_state.popover_mut() {
let element = popover.render(
&self.style,
max_size,
editor.workspace.as_ref().map(|(w, _)| w.clone()),
cx,
);
Some(element)
} else {
None
}
});
if let Some(mut element) = maybe_element {
let window_size = cx.viewport_size();
let size = element.layout_as_root(Size::<AvailableSpace>::default(), cx);
let mut point = point(start_x, start_y - size.height);
// Adjusting to ensure the popover does not overflow in the X-axis direction.
if point.x + size.width >= window_size.width {
point.x = window_size.width - size.width;
}
cx.defer_draw(element, point, 1)
}
}
fn paint_background(&self, layout: &EditorLayout, cx: &mut WindowContext) {
cx.paint_layer(layout.hitbox.bounds, |cx| {
let scroll_top = layout.position_map.snapshot.scroll_position().y;
@@ -3815,9 +3734,6 @@ impl EditorElement {
move |event: &MouseMoveEvent, phase, cx| {
if phase == DispatchPhase::Bubble {
editor.update(cx, |editor, cx| {
if editor.hover_state.focused(cx) {
return;
}
if event.pressed_button == Some(MouseButton::Left)
|| event.pressed_button == Some(MouseButton::Middle)
{
@@ -5147,18 +5063,6 @@ impl Element for EditorElement {
vec![]
};
self.layout_signature_help(
&hitbox,
content_origin,
scroll_pixel_position,
newest_selection_head,
start_row,
&line_layouts,
line_height,
em_width,
cx,
);
if !cx.has_active_drag() {
self.layout_hover_popovers(
&snapshot,

View File

@@ -5,26 +5,24 @@ use crate::{
Anchor, AnchorRangeExt, DisplayPoint, DisplayRow, Editor, EditorSettings, EditorSnapshot,
EditorStyle, Hover, RangeToAnchorExt,
};
use futures::{stream::FuturesUnordered, FutureExt};
use gpui::{
div, px, AnyElement, AsyncWindowContext, CursorStyle, FontWeight, Hsla, InteractiveElement,
IntoElement, MouseButton, ParentElement, Pixels, ScrollHandle, SharedString, Size,
StatefulInteractiveElement, StyleRefinement, Styled, Task, TextStyleRefinement, View,
ViewContext, WeakView,
div, px, AnyElement, CursorStyle, Hsla, InteractiveElement, IntoElement, MouseButton,
ParentElement, Pixels, ScrollHandle, SharedString, Size, StatefulInteractiveElement, Styled,
Task, ViewContext, WeakView,
};
use itertools::Itertools;
use language::{DiagnosticEntry, Language, LanguageRegistry};
use language::{markdown, DiagnosticEntry, Language, LanguageRegistry, ParsedMarkdown};
use lsp::DiagnosticSeverity;
use markdown::{Markdown, MarkdownStyle};
use multi_buffer::ToOffset;
use project::{HoverBlock, InlayHintLabelPart};
use project::{HoverBlock, HoverBlockKind, InlayHintLabelPart};
use settings::Settings;
use std::rc::Rc;
use std::{borrow::Cow, cell::RefCell};
use smol::stream::StreamExt;
use std::{ops::Range, sync::Arc, time::Duration};
use theme::ThemeSettings;
use ui::{prelude::*, window_is_transparent, Tooltip};
use util::TryFutureExt;
use workspace::Workspace;
pub const HOVER_DELAY_MILLIS: u64 = 350;
pub const HOVER_REQUEST_DELAY_MILLIS: u64 = 200;
@@ -42,9 +40,6 @@ pub fn hover(editor: &mut Editor, _: &Hover, cx: &mut ViewContext<Editor>) {
/// depending on whether a point to hover over is provided.
pub fn hover_at(editor: &mut Editor, anchor: Option<Anchor>, cx: &mut ViewContext<Editor>) {
if EditorSettings::get_global(cx).hover_popover_enabled {
if show_keyboard_hover(editor, cx) {
return;
}
if let Some(anchor) = anchor {
show_hover(editor, anchor, false, cx);
} else {
@@ -53,20 +48,6 @@ pub fn hover_at(editor: &mut Editor, anchor: Option<Anchor>, cx: &mut ViewContex
}
}
pub fn show_keyboard_hover(editor: &mut Editor, cx: &mut ViewContext<Editor>) -> bool {
let info_popovers = editor.hover_state.info_popovers.clone();
for p in info_popovers {
let keyboard_grace = p.keyboard_grace.borrow();
if *keyboard_grace {
if let Some(anchor) = p.anchor {
show_hover(editor, anchor, false, cx);
return true;
}
}
}
return false;
}
pub struct InlayHover {
pub range: InlayHighlight,
pub tooltip: HoverBlock,
@@ -132,14 +113,12 @@ pub fn hover_at_inlay(editor: &mut Editor, inlay_hover: InlayHover, cx: &mut Vie
let language_registry = project.update(&mut cx, |p, _| p.languages().clone())?;
let blocks = vec![inlay_hover.tooltip];
let parsed_content = parse_blocks(&blocks, &language_registry, None, &mut cx).await;
let parsed_content = parse_blocks(&blocks, &language_registry, None).await;
let hover_popover = InfoPopover {
symbol_range: RangeInEditor::Inlay(inlay_hover.range.clone()),
parsed_content,
scroll_handle: ScrollHandle::new(),
keyboard_grace: Rc::new(RefCell::new(false)),
anchor: None,
};
this.update(&mut cx, |this, cx| {
@@ -312,40 +291,39 @@ fn show_hover(
let snapshot = this.update(&mut cx, |this, cx| this.snapshot(cx))?;
let mut hover_highlights = Vec::with_capacity(hovers_response.len());
let mut info_popovers = Vec::with_capacity(hovers_response.len());
let mut info_popover_tasks = Vec::with_capacity(hovers_response.len());
let mut info_popover_tasks = hovers_response
.into_iter()
.map(|hover_result| async {
// Create symbol range of anchors for highlighting and filtering of future requests.
let range = hover_result
.range
.and_then(|range| {
let start = snapshot
.buffer_snapshot
.anchor_in_excerpt(excerpt_id, range.start)?;
let end = snapshot
.buffer_snapshot
.anchor_in_excerpt(excerpt_id, range.end)?;
for hover_result in hovers_response {
// Create symbol range of anchors for highlighting and filtering of future requests.
let range = hover_result
.range
.and_then(|range| {
let start = snapshot
.buffer_snapshot
.anchor_in_excerpt(excerpt_id, range.start)?;
let end = snapshot
.buffer_snapshot
.anchor_in_excerpt(excerpt_id, range.end)?;
Some(start..end)
})
.unwrap_or_else(|| anchor..anchor);
Some(start..end)
})
.unwrap_or_else(|| anchor..anchor);
let blocks = hover_result.contents;
let language = hover_result.language;
let parsed_content = parse_blocks(&blocks, &language_registry, language).await;
let blocks = hover_result.contents;
let language = hover_result.language;
let parsed_content =
parse_blocks(&blocks, &language_registry, language, &mut cx).await;
info_popover_tasks.push((
range.clone(),
InfoPopover {
symbol_range: RangeInEditor::Text(range),
parsed_content,
scroll_handle: ScrollHandle::new(),
keyboard_grace: Rc::new(RefCell::new(ignore_timeout)),
anchor: Some(anchor),
},
));
}
for (highlight_range, info_popover) in info_popover_tasks {
(
range.clone(),
InfoPopover {
symbol_range: RangeInEditor::Text(range),
parsed_content,
scroll_handle: ScrollHandle::new(),
},
)
})
.collect::<FuturesUnordered<_>>();
while let Some((highlight_range, info_popover)) = info_popover_tasks.next().await {
hover_highlights.push(highlight_range);
info_popovers.push(info_popover);
}
@@ -379,81 +357,72 @@ async fn parse_blocks(
blocks: &[HoverBlock],
language_registry: &Arc<LanguageRegistry>,
language: Option<Arc<Language>>,
cx: &mut AsyncWindowContext,
) -> Option<View<Markdown>> {
let fallback_language_name = if let Some(ref l) = language {
let l = Arc::clone(l);
Some(l.lsp_id().clone())
} else {
None
};
) -> markdown::ParsedMarkdown {
let mut text = String::new();
let mut highlights = Vec::new();
let mut region_ranges = Vec::new();
let mut regions = Vec::new();
let combined_text = blocks
.iter()
.map(|block| match &block.kind {
project::HoverBlockKind::PlainText | project::HoverBlockKind::Markdown => {
Cow::Borrowed(block.text.trim())
for block in blocks {
match &block.kind {
HoverBlockKind::PlainText => {
markdown::new_paragraph(&mut text, &mut Vec::new());
text.push_str(&block.text.replace("\\n", "\n"));
}
project::HoverBlockKind::Code { language } => {
Cow::Owned(format!("```{}\n{}\n```", language, block.text.trim()))
HoverBlockKind::Markdown => {
markdown::parse_markdown_block(
&block.text.replace("\\n", "\n"),
language_registry,
language.clone(),
&mut text,
&mut highlights,
&mut region_ranges,
&mut regions,
)
.await
}
})
.join("\n\n");
let rendered_block = cx
.new_view(|cx| {
let settings = ThemeSettings::get_global(cx);
let buffer_font_family = settings.buffer_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()
});
HoverBlockKind::Code { language } => {
if let Some(language) = language_registry
.language_for_name(language)
.now_or_never()
.and_then(Result::ok)
{
markdown::highlight_code(&mut text, &mut highlights, &block.text, &language);
} else {
text.push_str(&block.text);
}
}
}
}
let markdown_style = MarkdownStyle {
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),
..Default::default()
},
rule_color: Color::Muted.color(cx),
block_quote_border_color: Color::Muted.color(cx),
block_quote: TextStyleRefinement {
color: Some(Color::Muted.color(cx)),
..Default::default()
},
link: TextStyleRefinement {
color: Some(cx.theme().colors().editor_foreground),
underline: Some(gpui::UnderlineStyle {
thickness: px(1.),
color: Some(cx.theme().colors().editor_foreground),
wavy: false,
}),
..Default::default()
},
syntax: cx.theme().syntax().clone(),
selection_background_color: { cx.theme().players().local().selection },
break_style: Default::default(),
heading: StyleRefinement::default()
.font_weight(FontWeight::BOLD)
.text_base()
.mt(rems(1.))
.mb_0(),
};
let leading_space = text.chars().take_while(|c| c.is_whitespace()).count();
if leading_space > 0 {
highlights = highlights
.into_iter()
.map(|(range, style)| {
(
range.start.saturating_sub(leading_space)
..range.end.saturating_sub(leading_space),
style,
)
})
.collect();
region_ranges = region_ranges
.into_iter()
.map(|range| {
range.start.saturating_sub(leading_space)..range.end.saturating_sub(leading_space)
})
.collect();
}
Markdown::new(
combined_text,
markdown_style.clone(),
Some(language_registry.clone()),
cx,
fallback_language_name,
)
})
.ok();
rendered_block
ParsedMarkdown {
text: text.trim().to_string(),
highlights,
region_ranges,
regions,
}
}
#[derive(Default, Debug)]
@@ -475,7 +444,7 @@ impl HoverState {
style: &EditorStyle,
visible_rows: Range<DisplayRow>,
max_size: Size<Pixels>,
_workspace: Option<WeakView<Workspace>>,
workspace: Option<WeakView<Workspace>>,
cx: &mut ViewContext<Editor>,
) -> Option<(DisplayPoint, Vec<AnyElement>)> {
// If there is a diagnostic, position the popovers based on that.
@@ -513,39 +482,29 @@ impl HoverState {
elements.push(diagnostic_popover.render(style, max_size, cx));
}
for info_popover in &mut self.info_popovers {
elements.push(info_popover.render(max_size, cx));
elements.push(info_popover.render(style, max_size, workspace.clone(), cx));
}
Some((point, elements))
}
pub fn focused(&self, cx: &mut ViewContext<Editor>) -> bool {
let mut hover_popover_is_focused = false;
for info_popover in &self.info_popovers {
for markdown_view in &info_popover.parsed_content {
if markdown_view.focus_handle(cx).is_focused(cx) {
hover_popover_is_focused = true;
}
}
}
return hover_popover_is_focused;
}
}
#[derive(Debug, Clone)]
#[derive(Clone, Debug)]
pub struct InfoPopover {
pub symbol_range: RangeInEditor,
pub parsed_content: Option<View<Markdown>>,
pub parsed_content: ParsedMarkdown,
pub scroll_handle: ScrollHandle,
pub keyboard_grace: Rc<RefCell<bool>>,
pub anchor: Option<Anchor>,
}
impl InfoPopover {
pub fn render(&mut self, max_size: Size<Pixels>, cx: &mut ViewContext<Editor>) -> AnyElement {
let keyboard_grace = Rc::clone(&self.keyboard_grace);
let mut d = div()
pub fn render(
&mut self,
style: &EditorStyle,
max_size: Size<Pixels>,
workspace: Option<WeakView<Workspace>>,
cx: &mut ViewContext<Editor>,
) -> AnyElement {
div()
.id("info_popover")
.elevation_2(cx)
.overflow_y_scroll()
@@ -555,17 +514,15 @@ impl InfoPopover {
// Prevent a mouse down/move on the popover from being propagated to the editor,
// because that would dismiss the popover.
.on_mouse_move(|_, cx| cx.stop_propagation())
.on_mouse_down(MouseButton::Left, move |_, cx| {
let mut keyboard_grace = keyboard_grace.borrow_mut();
*keyboard_grace = false;
cx.stop_propagation();
})
.p_2();
if let Some(markdown) = &self.parsed_content {
d = d.child(markdown.clone());
}
d.into_any_element()
.on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
.child(div().p_2().child(crate::render_parsed_markdown(
"content",
&self.parsed_content,
style,
workspace,
cx,
)))
.into_any_element()
}
pub fn scroll(&self, amount: &ScrollAmount, cx: &mut ViewContext<Editor>) {
@@ -636,6 +593,8 @@ impl DiagnosticPopover {
.when(window_is_transparent(cx), |this| {
this.bg(gpui::transparent_black())
})
.max_w(max_size.width)
.max_h(max_size.height)
.cursor(CursorStyle::PointingHand)
.tooltip(move |cx| Tooltip::for_action("Go To Diagnostic", &crate::GoToDiagnostic, cx))
// Prevent a mouse move on the popover from being propagated to the editor,
@@ -649,8 +608,6 @@ impl DiagnosticPopover {
div()
.id("diagnostic-inner")
.overflow_y_scroll()
.max_w(max_size.width)
.max_h(max_size.height)
.px_2()
.py_1()
.bg(diagnostic_colors.background)
@@ -685,33 +642,17 @@ mod tests {
InlayId, PointForPosition,
};
use collections::BTreeSet;
use gpui::{FontWeight, HighlightStyle, UnderlineStyle};
use indoc::indoc;
use language::{language_settings::InlayHintSettings, Diagnostic, DiagnosticSet};
use lsp::LanguageServerId;
use markdown::parser::MarkdownEvent;
use project::{HoverBlock, HoverBlockKind};
use smol::stream::StreamExt;
use std::sync::atomic;
use std::sync::atomic::AtomicUsize;
use text::Bias;
impl InfoPopover {
fn get_rendered_text(&self, cx: &gpui::AppContext) -> String {
let mut rendered_text = String::new();
if let Some(parsed_content) = self.parsed_content.clone() {
let markdown = parsed_content.read(cx);
let text = markdown.parsed_markdown().source().to_string();
let data = markdown.parsed_markdown().events();
let slice = data;
for (range, event) in slice.iter() {
if [MarkdownEvent::Text, MarkdownEvent::Code].contains(event) {
rendered_text.push_str(&text[range.clone()])
}
}
}
rendered_text
}
}
use unindent::Unindent;
use util::test::marked_text_ranges;
#[gpui::test]
async fn test_mouse_hover_info_popover_with_autocomplete_popover(
@@ -795,7 +736,7 @@ mod tests {
.advance_clock(Duration::from_millis(HOVER_DELAY_MILLIS + 100));
requests.next().await;
cx.editor(|editor, cx| {
cx.editor(|editor, _| {
assert!(editor.hover_state.visible());
assert_eq!(
editor.hover_state.info_popovers.len(),
@@ -803,13 +744,14 @@ mod tests {
"Expected exactly one hover but got: {:?}",
editor.hover_state.info_popovers
);
let rendered_text = editor
let rendered = editor
.hover_state
.info_popovers
.first()
.cloned()
.unwrap()
.get_rendered_text(cx);
assert_eq!(rendered_text, "some basic docs".to_string())
.parsed_content;
assert_eq!(rendered.text, "some basic docs".to_string())
});
// check that the completion menu is still visible and that there still has only been 1 completion request
@@ -835,7 +777,7 @@ mod tests {
assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
//verify the information popover is still visible and unchanged
cx.editor(|editor, cx| {
cx.editor(|editor, _| {
assert!(editor.hover_state.visible());
assert_eq!(
editor.hover_state.info_popovers.len(),
@@ -843,14 +785,14 @@ mod tests {
"Expected exactly one hover but got: {:?}",
editor.hover_state.info_popovers
);
let rendered_text = editor
let rendered = editor
.hover_state
.info_popovers
.first()
.cloned()
.unwrap()
.get_rendered_text(cx);
assert_eq!(rendered_text, "some basic docs".to_string())
.parsed_content;
assert_eq!(rendered.text, "some basic docs".to_string())
});
// Mouse moved with no hover response dismisses
@@ -928,7 +870,7 @@ mod tests {
.advance_clock(Duration::from_millis(HOVER_DELAY_MILLIS + 100));
requests.next().await;
cx.editor(|editor, cx| {
cx.editor(|editor, _| {
assert!(editor.hover_state.visible());
assert_eq!(
editor.hover_state.info_popovers.len(),
@@ -936,14 +878,14 @@ mod tests {
"Expected exactly one hover but got: {:?}",
editor.hover_state.info_popovers
);
let rendered_text = editor
let rendered = editor
.hover_state
.info_popovers
.first()
.cloned()
.unwrap()
.get_rendered_text(cx);
assert_eq!(rendered_text, "some basic docs".to_string())
.parsed_content;
assert_eq!(rendered.text, "some basic docs".to_string())
});
// Mouse moved with no hover response dismisses
@@ -989,49 +931,34 @@ mod tests {
let symbol_range = cx.lsp_range(indoc! {"
«fn» test() { println!(); }
"});
cx.editor(|editor, _cx| {
assert!(!editor.hover_state.visible());
assert_eq!(
editor.hover_state.info_popovers.len(),
0,
"Expected no hovers but got but got: {:?}",
editor.hover_state.info_popovers
);
});
let mut requests =
cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
Ok(Some(lsp::Hover {
contents: lsp::HoverContents::Markup(lsp::MarkupContent {
kind: lsp::MarkupKind::Markdown,
value: "some other basic docs".to_string(),
}),
range: Some(symbol_range),
}))
});
requests.next().await;
cx.dispatch_action(Hover);
cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
Ok(Some(lsp::Hover {
contents: lsp::HoverContents::Markup(lsp::MarkupContent {
kind: lsp::MarkupKind::Markdown,
value: "some other basic docs".to_string(),
}),
range: Some(symbol_range),
}))
})
.next()
.await;
cx.condition(|editor, _| editor.hover_state.visible()).await;
cx.editor(|editor, cx| {
cx.editor(|editor, _| {
assert_eq!(
editor.hover_state.info_popovers.len(),
1,
"Expected exactly one hover but got: {:?}",
editor.hover_state.info_popovers
);
let rendered_text = editor
let rendered = editor
.hover_state
.info_popovers
.first()
.cloned()
.unwrap()
.get_rendered_text(cx);
assert_eq!(rendered_text, "some other basic docs".to_string())
.parsed_content;
assert_eq!(rendered.text, "some other basic docs".to_string())
});
}
@@ -1071,25 +998,24 @@ mod tests {
})
.next()
.await;
cx.dispatch_action(Hover);
cx.condition(|editor, _| editor.hover_state.visible()).await;
cx.editor(|editor, cx| {
cx.editor(|editor, _| {
assert_eq!(
editor.hover_state.info_popovers.len(),
1,
"Expected exactly one hover but got: {:?}",
editor.hover_state.info_popovers
);
let rendered_text = editor
let rendered = editor
.hover_state
.info_popovers
.first()
.cloned()
.unwrap()
.get_rendered_text(cx);
.parsed_content;
assert_eq!(
rendered_text,
rendered.text,
"regular text for hover to show".to_string(),
"No empty string hovers should be shown"
);
@@ -1137,25 +1063,24 @@ mod tests {
.next()
.await;
cx.dispatch_action(Hover);
cx.condition(|editor, _| editor.hover_state.visible()).await;
cx.editor(|editor, cx| {
cx.editor(|editor, _| {
assert_eq!(
editor.hover_state.info_popovers.len(),
1,
"Expected exactly one hover but got: {:?}",
editor.hover_state.info_popovers
);
let rendered_text = editor
let rendered = editor
.hover_state
.info_popovers
.first()
.cloned()
.unwrap()
.get_rendered_text(cx);
.parsed_content;
assert_eq!(
rendered_text, code_str,
rendered.text,
code_str.trim(),
"Should not have extra line breaks at end of rendered hover"
);
});
@@ -1231,6 +1156,153 @@ mod tests {
});
}
#[gpui::test]
fn test_render_blocks(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
let languages = Arc::new(LanguageRegistry::test(cx.executor()));
let editor = cx.add_window(|cx| Editor::single_line(cx));
editor
.update(cx, |editor, _cx| {
let style = editor.style.clone().unwrap();
struct Row {
blocks: Vec<HoverBlock>,
expected_marked_text: String,
expected_styles: Vec<HighlightStyle>,
}
let rows = &[
// Strong emphasis
Row {
blocks: vec![HoverBlock {
text: "one **two** three".to_string(),
kind: HoverBlockKind::Markdown,
}],
expected_marked_text: "one «two» three".to_string(),
expected_styles: vec![HighlightStyle {
font_weight: Some(FontWeight::BOLD),
..Default::default()
}],
},
// Links
Row {
blocks: vec![HoverBlock {
text: "one [two](https://the-url) three".to_string(),
kind: HoverBlockKind::Markdown,
}],
expected_marked_text: "one «two» three".to_string(),
expected_styles: vec![HighlightStyle {
underline: Some(UnderlineStyle {
thickness: 1.0.into(),
..Default::default()
}),
..Default::default()
}],
},
// Lists
Row {
blocks: vec![HoverBlock {
text: "
lists:
* one
- a
- b
* two
- [c](https://the-url)
- d"
.unindent(),
kind: HoverBlockKind::Markdown,
}],
expected_marked_text: "
lists:
- one
- a
- b
- two
- «c»
- d"
.unindent(),
expected_styles: vec![HighlightStyle {
underline: Some(UnderlineStyle {
thickness: 1.0.into(),
..Default::default()
}),
..Default::default()
}],
},
// Multi-paragraph list items
Row {
blocks: vec![HoverBlock {
text: "
* one two
three
* four five
* six seven
eight
nine
* ten
* six"
.unindent(),
kind: HoverBlockKind::Markdown,
}],
expected_marked_text: "
- one two three
- four five
- six seven eight
nine
- ten
- six"
.unindent(),
expected_styles: vec![HighlightStyle {
underline: Some(UnderlineStyle {
thickness: 1.0.into(),
..Default::default()
}),
..Default::default()
}],
},
];
for Row {
blocks,
expected_marked_text,
expected_styles,
} in &rows[0..]
{
let rendered = smol::block_on(parse_blocks(&blocks, &languages, None));
let (expected_text, ranges) = marked_text_ranges(expected_marked_text, false);
let expected_highlights = ranges
.into_iter()
.zip(expected_styles.iter().cloned())
.collect::<Vec<_>>();
assert_eq!(
rendered.text, expected_text,
"wrong text for input {blocks:?}"
);
let rendered_highlights: Vec<_> = rendered
.highlights
.iter()
.filter_map(|(range, highlight)| {
let highlight = highlight.to_highlight_style(&style.syntax)?;
Some((range.clone(), highlight))
})
.collect();
assert_eq!(
rendered_highlights, expected_highlights,
"wrong highlights for input {blocks:?}"
);
}
})
.unwrap();
}
#[gpui::test]
async fn test_hover_inlay_label_parts(cx: &mut gpui::TestAppContext) {
init_test(cx, |settings| {
@@ -1474,8 +1546,9 @@ mod tests {
"Popover range should match the new type label part"
);
assert_eq!(
popover.get_rendered_text(cx),
format!("A tooltip for {new_type_label}"),
popover.parsed_content.text,
format!("A tooltip for `{new_type_label}`"),
"Rendered text should not anyhow alter backticks"
);
});
@@ -1529,7 +1602,7 @@ mod tests {
"Popover range should match the struct label part"
);
assert_eq!(
popover.get_rendered_text(cx),
popover.parsed_content.text,
format!("A tooltip for {struct_label}"),
"Rendered markdown element should remove backticks from text"
);

View File

@@ -19,7 +19,7 @@ use multi_buffer::AnchorRangeExt;
use project::{search::SearchQuery, FormatTrigger, Item as _, Project, ProjectPath};
use rpc::proto::{self, update_view, PeerId};
use settings::Settings;
use workspace::item::{Dedup, ItemSettings, TabContentParams};
use workspace::item::{ItemSettings, TabContentParams};
use std::{
any::TypeId,
@@ -34,7 +34,7 @@ use text::{BufferId, Selection};
use theme::{Theme, ThemeSettings};
use ui::{h_flex, prelude::*, Label};
use util::{paths::PathExt, ResultExt, TryFutureExt};
use workspace::item::{BreadcrumbText, FollowEvent};
use workspace::item::{BreadcrumbText, FollowEvent, FollowableItemHandle};
use workspace::{
item::{FollowableItem, Item, ItemEvent, ItemHandle, ProjectItem},
searchable::{Direction, SearchEvent, SearchableItem, SearchableItemHandle},
@@ -49,6 +49,7 @@ impl FollowableItem for Editor {
}
fn from_state_proto(
pane: View<workspace::Pane>,
workspace: View<Workspace>,
remote_id: ViewId,
state: &mut Option<proto::view::Variant>,
@@ -62,6 +63,7 @@ impl FollowableItem for Editor {
unreachable!()
};
let client = project.read(cx).client();
let replica_id = project.read(cx).replica_id();
let buffer_ids = state
.excerpts
@@ -75,55 +77,72 @@ impl FollowableItem for Editor {
.collect::<Result<Vec<_>>>()
});
let pane = pane.downgrade();
Some(cx.spawn(|mut cx| async move {
let mut buffers = futures::future::try_join_all(buffers?)
.await
.debug_assert_ok("leaders don't share views for unshared buffers")?;
let editor = cx.update(|cx| {
let multibuffer = cx.new_model(|cx| {
let mut multibuffer;
if state.singleton && buffers.len() == 1 {
multibuffer = MultiBuffer::singleton(buffers.pop().unwrap(), cx)
} else {
multibuffer = MultiBuffer::new(replica_id, project.read(cx).capability());
let mut excerpts = state.excerpts.into_iter().peekable();
while let Some(excerpt) = excerpts.peek() {
let Ok(buffer_id) = BufferId::new(excerpt.buffer_id) else {
continue;
};
let buffer_excerpts = iter::from_fn(|| {
let excerpt = excerpts.peek()?;
(excerpt.buffer_id == u64::from(buffer_id))
.then(|| excerpts.next().unwrap())
});
let buffer =
buffers.iter().find(|b| b.read(cx).remote_id() == buffer_id);
if let Some(buffer) = buffer {
multibuffer.push_excerpts(
buffer.clone(),
buffer_excerpts.filter_map(deserialize_excerpt_range),
cx,
);
}
}
};
if let Some(title) = &state.title {
multibuffer = multibuffer.with_title(title.clone())
}
multibuffer
});
cx.new_view(|cx| {
let mut editor =
Editor::for_multibuffer(multibuffer, Some(project.clone()), true, cx);
editor.remote_id = Some(remote_id);
editor
let editor = pane.update(&mut cx, |pane, cx| {
let mut editors = pane.items_of_type::<Self>();
editors.find(|editor| {
let ids_match = editor.remote_id(&client, cx) == Some(remote_id);
let singleton_buffer_matches = state.singleton
&& buffers.first()
== editor.read(cx).buffer.read(cx).as_singleton().as_ref();
ids_match || singleton_buffer_matches
})
})?;
let editor = if let Some(editor) = editor {
editor
} else {
pane.update(&mut cx, |_, cx| {
let multibuffer = cx.new_model(|cx| {
let mut multibuffer;
if state.singleton && buffers.len() == 1 {
multibuffer = MultiBuffer::singleton(buffers.pop().unwrap(), cx)
} else {
multibuffer =
MultiBuffer::new(replica_id, project.read(cx).capability());
let mut excerpts = state.excerpts.into_iter().peekable();
while let Some(excerpt) = excerpts.peek() {
let Ok(buffer_id) = BufferId::new(excerpt.buffer_id) else {
continue;
};
let buffer_excerpts = iter::from_fn(|| {
let excerpt = excerpts.peek()?;
(excerpt.buffer_id == u64::from(buffer_id))
.then(|| excerpts.next().unwrap())
});
let buffer =
buffers.iter().find(|b| b.read(cx).remote_id() == buffer_id);
if let Some(buffer) = buffer {
multibuffer.push_excerpts(
buffer.clone(),
buffer_excerpts.filter_map(deserialize_excerpt_range),
cx,
);
}
}
};
if let Some(title) = &state.title {
multibuffer = multibuffer.with_title(title.clone())
}
multibuffer
});
cx.new_view(|cx| {
let mut editor =
Editor::for_multibuffer(multibuffer, Some(project.clone()), true, cx);
editor.remote_id = Some(remote_id);
editor
})
})?
};
update_editor_from_message(
editor.downgrade(),
project,
@@ -308,16 +327,6 @@ impl FollowableItem for Editor {
fn is_project_item(&self, _cx: &WindowContext) -> bool {
true
}
fn dedup(&self, existing: &Self, cx: &WindowContext) -> Option<Dedup> {
let self_singleton = self.buffer.read(cx).as_singleton()?;
let other_singleton = existing.buffer.read(cx).as_singleton()?;
if self_singleton == other_singleton {
Some(Dedup::KeepExisting)
} else {
None
}
}
}
async fn update_editor_from_message(
@@ -362,7 +371,7 @@ async fn update_editor_from_message(
continue;
};
let buffer_id = BufferId::new(excerpt.buffer_id)?;
let Some(buffer) = project.read(cx).buffer_for_id(buffer_id, cx) else {
let Some(buffer) = project.read(cx).buffer_for_id(buffer_id) else {
continue;
};
@@ -935,7 +944,7 @@ impl Item for Editor {
.context("No path stored for this editor")?;
let (worktree, path) = project
.find_worktree(&path, cx)
.find_local_worktree(&path, cx)
.with_context(|| format!("No worktree for path: {path:?}"))?;
let project_path = ProjectPath {
worktree_id: worktree.read(cx).id(),

View File

@@ -49,11 +49,13 @@ fn display_ranges<'a>(
.pending
.as_ref()
.map(|pending| &pending.selection);
selections
.disjoint
.iter()
.chain(pending)
.map(move |s| s.start.to_display_point(&display_map)..s.end.to_display_point(&display_map))
selections.disjoint.iter().chain(pending).map(move |s| {
if s.reversed {
s.end.to_display_point(&display_map)..s.start.to_display_point(&display_map)
} else {
s.start.to_display_point(&display_map)..s.end.to_display_point(&display_map)
}
})
}
pub fn deploy_context_menu(

View File

@@ -476,10 +476,7 @@ impl Editor {
}
let cur_position = self.scroll_position(cx);
let Some(visible_line_count) = self.visible_line_count() else {
return;
};
let new_pos = cur_position + point(0., amount.lines(visible_line_count));
let new_pos = cur_position + point(0., amount.lines(self));
self.set_scroll_position(new_pos, cx);
}

View File

@@ -1,3 +1,4 @@
use crate::Editor;
use serde::Deserialize;
use ui::{px, Pixels};
@@ -10,16 +11,19 @@ pub enum ScrollAmount {
}
impl ScrollAmount {
pub fn lines(&self, mut visible_line_count: f32) -> f32 {
pub fn lines(&self, editor: &mut Editor) -> f32 {
match self {
Self::Line(count) => *count,
Self::Page(count) => {
// for full pages subtract one to leave an anchor line
if count.abs() == 1.0 {
visible_line_count -= 1.0
}
(visible_line_count * count).trunc()
}
Self::Page(count) => editor
.visible_line_count()
.map(|mut l| {
// for full pages subtract one to leave an anchor line
if count.abs() == 1.0 {
l -= 1.0
}
(l * count).trunc()
})
.unwrap_or(0.),
}
}

View File

@@ -1,225 +0,0 @@
mod popover;
mod state;
use crate::actions::ShowSignatureHelp;
use crate::{Editor, EditorSettings, ToggleAutoSignatureHelp};
use gpui::{AppContext, ViewContext};
use language::markdown::parse_markdown;
use multi_buffer::{Anchor, ToOffset};
use settings::Settings;
use std::ops::Range;
pub use popover::SignatureHelpPopover;
pub use state::SignatureHelpState;
// Language-specific settings may define quotes as "brackets", so filter them out separately.
const QUOTE_PAIRS: [(&'static str, &'static str); 3] = [("'", "'"), ("\"", "\""), ("`", "`")];
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum SignatureHelpHiddenBy {
AutoClose,
Escape,
Selection,
}
impl Editor {
pub fn toggle_auto_signature_help_menu(
&mut self,
_: &ToggleAutoSignatureHelp,
cx: &mut ViewContext<Self>,
) {
self.auto_signature_help = self
.auto_signature_help
.map(|auto_signature_help| !auto_signature_help)
.or_else(|| Some(!EditorSettings::get_global(cx).auto_signature_help));
match self.auto_signature_help {
Some(auto_signature_help) if auto_signature_help => {
self.show_signature_help(&ShowSignatureHelp, cx);
}
Some(_) => {
self.hide_signature_help(cx, SignatureHelpHiddenBy::AutoClose);
}
None => {}
}
cx.notify();
}
pub(super) fn hide_signature_help(
&mut self,
cx: &mut ViewContext<Self>,
signature_help_hidden_by: SignatureHelpHiddenBy,
) -> bool {
if self.signature_help_state.is_shown() {
self.signature_help_state.kill_task();
self.signature_help_state.hide(signature_help_hidden_by);
cx.notify();
true
} else {
false
}
}
pub fn auto_signature_help_enabled(&self, cx: &AppContext) -> bool {
if let Some(auto_signature_help) = self.auto_signature_help {
auto_signature_help
} else {
EditorSettings::get_global(cx).auto_signature_help
}
}
pub(super) fn should_open_signature_help_automatically(
&mut self,
old_cursor_position: &Anchor,
backspace_pressed: bool,
cx: &mut ViewContext<Self>,
) -> bool {
if !(self.signature_help_state.is_shown() || self.auto_signature_help_enabled(cx)) {
return false;
}
let newest_selection = self.selections.newest::<usize>(cx);
let head = newest_selection.head();
// There are two cases where the head and tail of a selection are different: selecting multiple ranges and using backspace.
// If we dont exclude the backspace case, signature_help will blink every time backspace is pressed, so we need to prevent this.
if !newest_selection.is_empty() && !backspace_pressed && head != newest_selection.tail() {
self.signature_help_state
.hide(SignatureHelpHiddenBy::Selection);
return false;
}
let buffer_snapshot = self.buffer().read(cx).snapshot(cx);
let bracket_range = |position: usize| match (position, position + 1) {
(0, b) if b <= buffer_snapshot.len() => 0..b,
(0, b) => 0..b - 1,
(a, b) if b <= buffer_snapshot.len() => a - 1..b,
(a, b) => a - 1..b - 1,
};
let not_quote_like_brackets = |start: Range<usize>, end: Range<usize>| {
let text = buffer_snapshot.text();
let (text_start, text_end) = (text.get(start), text.get(end));
QUOTE_PAIRS
.into_iter()
.all(|(start, end)| text_start != Some(start) && text_end != Some(end))
};
let previous_position = old_cursor_position.to_offset(&buffer_snapshot);
let previous_brackets_range = bracket_range(previous_position);
let previous_brackets_surround = buffer_snapshot
.innermost_enclosing_bracket_ranges(
previous_brackets_range,
Some(&not_quote_like_brackets),
)
.filter(|(start_bracket_range, end_bracket_range)| {
start_bracket_range.start != previous_position
&& end_bracket_range.end != previous_position
});
let current_brackets_range = bracket_range(head);
let current_brackets_surround = buffer_snapshot
.innermost_enclosing_bracket_ranges(
current_brackets_range,
Some(&not_quote_like_brackets),
)
.filter(|(start_bracket_range, end_bracket_range)| {
start_bracket_range.start != head && end_bracket_range.end != head
});
match (previous_brackets_surround, current_brackets_surround) {
(None, None) => {
self.signature_help_state
.hide(SignatureHelpHiddenBy::AutoClose);
false
}
(Some(_), None) => {
self.signature_help_state
.hide(SignatureHelpHiddenBy::AutoClose);
false
}
(None, Some(_)) => true,
(Some(previous), Some(current)) => {
let condition = self.signature_help_state.hidden_by_selection()
|| previous != current
|| (previous == current && self.signature_help_state.is_shown());
if !condition {
self.signature_help_state
.hide(SignatureHelpHiddenBy::AutoClose);
}
condition
}
}
}
pub fn show_signature_help(&mut self, _: &ShowSignatureHelp, cx: &mut ViewContext<Self>) {
if self.pending_rename.is_some() {
return;
}
let position = self.selections.newest_anchor().head();
let Some((buffer, buffer_position)) =
self.buffer.read(cx).text_anchor_for_position(position, cx)
else {
return;
};
self.signature_help_state
.set_task(cx.spawn(move |editor, mut cx| async move {
let signature_help = editor
.update(&mut cx, |editor, cx| {
let language = editor.language_at(position, cx);
let project = editor.project.clone()?;
let (markdown, language_registry) = {
project.update(cx, |project, mut cx| {
let language_registry = project.languages().clone();
(
project.signature_help(&buffer, buffer_position, &mut cx),
language_registry,
)
})
};
Some((markdown, language_registry, language))
})
.ok()
.flatten();
let signature_help_popover = if let Some((
signature_help_task,
language_registry,
language,
)) = signature_help
{
// TODO allow multiple signature helps inside the same popover
if let Some(mut signature_help) = signature_help_task.await.into_iter().next() {
let mut parsed_content = parse_markdown(
signature_help.markdown.as_str(),
&language_registry,
language,
)
.await;
parsed_content
.highlights
.append(&mut signature_help.highlights);
Some(SignatureHelpPopover { parsed_content })
} else {
None
}
} else {
None
};
editor
.update(&mut cx, |editor, cx| {
let previous_popover = editor.signature_help_state.popover();
if previous_popover != signature_help_popover.as_ref() {
if let Some(signature_help_popover) = signature_help_popover {
editor
.signature_help_state
.set_popover(signature_help_popover);
} else {
editor
.signature_help_state
.hide(SignatureHelpHiddenBy::AutoClose);
}
cx.notify();
}
})
.ok();
}));
}
}

View File

@@ -1,48 +0,0 @@
use crate::{Editor, EditorStyle};
use gpui::{
div, AnyElement, InteractiveElement, IntoElement, MouseButton, ParentElement, Pixels, Size,
StatefulInteractiveElement, Styled, ViewContext, WeakView,
};
use language::ParsedMarkdown;
use ui::StyledExt;
use workspace::Workspace;
#[derive(Clone, Debug)]
pub struct SignatureHelpPopover {
pub parsed_content: ParsedMarkdown,
}
impl PartialEq for SignatureHelpPopover {
fn eq(&self, other: &Self) -> bool {
let str_equality = self.parsed_content.text.as_str() == other.parsed_content.text.as_str();
let highlight_equality = self.parsed_content.highlights == other.parsed_content.highlights;
str_equality && highlight_equality
}
}
impl SignatureHelpPopover {
pub fn render(
&mut self,
style: &EditorStyle,
max_size: Size<Pixels>,
workspace: Option<WeakView<Workspace>>,
cx: &mut ViewContext<Editor>,
) -> AnyElement {
div()
.id("signature_help_popover")
.elevation_2(cx)
.overflow_y_scroll()
.max_w(max_size.width)
.max_h(max_size.height)
.on_mouse_move(|_, cx| cx.stop_propagation())
.on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
.child(div().p_2().child(crate::render_parsed_markdown(
"signature_help_popover_content",
&self.parsed_content,
style,
workspace,
cx,
)))
.into_any_element()
}
}

View File

@@ -1,65 +0,0 @@
use crate::signature_help::popover::SignatureHelpPopover;
use crate::signature_help::SignatureHelpHiddenBy;
use gpui::Task;
#[derive(Default, Debug)]
pub struct SignatureHelpState {
task: Option<Task<()>>,
popover: Option<SignatureHelpPopover>,
hidden_by: Option<SignatureHelpHiddenBy>,
backspace_pressed: bool,
}
impl SignatureHelpState {
pub fn set_task(&mut self, task: Task<()>) {
self.task = Some(task);
self.hidden_by = None;
}
pub fn kill_task(&mut self) {
self.task = None;
}
pub fn popover(&self) -> Option<&SignatureHelpPopover> {
self.popover.as_ref()
}
pub fn popover_mut(&mut self) -> Option<&mut SignatureHelpPopover> {
self.popover.as_mut()
}
pub fn backspace_pressed(&self) -> bool {
self.backspace_pressed
}
pub fn set_backspace_pressed(&mut self, backspace_pressed: bool) {
self.backspace_pressed = backspace_pressed;
}
pub fn set_popover(&mut self, popover: SignatureHelpPopover) {
self.popover = Some(popover);
self.hidden_by = None;
}
pub fn hide(&mut self, hidden_by: SignatureHelpHiddenBy) {
if self.hidden_by.is_none() {
self.popover = None;
self.hidden_by = Some(hidden_by);
}
}
pub fn hidden_by_selection(&self) -> bool {
self.hidden_by == Some(SignatureHelpHiddenBy::Selection)
}
pub fn is_shown(&self) -> bool {
self.popover.is_some()
}
}
#[cfg(test)]
impl SignatureHelpState {
pub fn task(&self) -> Option<&Task<()>> {
self.task.as_ref()
}
}

View File

@@ -79,7 +79,7 @@ impl EditorLspTestContext {
let mut cx = VisualTestContext::from_window(*window.deref(), cx);
project
.update(&mut cx, |project, cx| {
project.find_or_create_worktree("/root", true, cx)
project.find_or_create_local_worktree("/root", true, cx)
})
.await
.unwrap();

View File

@@ -42,7 +42,6 @@ semantic_version.workspace = true
serde.workspace = true
serde_json.workspace = true
settings.workspace = true
snippet_provider.workspace = true
theme.workspace = true
toml.workspace = true
ui.workspace = true

View File

@@ -526,11 +526,6 @@ fn populate_defaults(manifest: &mut ExtensionManifest, extension_path: &Path) ->
}
}
let snippets_json_path = extension_path.join("snippets.json");
if snippets_json_path.exists() {
manifest.snippets = Some(snippets_json_path);
}
// For legacy extensions on the v0 schema (aka, using `extension.json`), we want to populate the grammars in
// the manifest using the contents of the `grammars` directory.
if manifest.schema_version.is_v0() {

View File

@@ -78,8 +78,6 @@ pub struct ExtensionManifest {
pub slash_commands: BTreeMap<Arc<str>, SlashCommandManifestEntry>,
#[serde(default)]
pub indexed_docs_providers: BTreeMap<Arc<str>, IndexedDocsProviderEntry>,
#[serde(default)]
pub snippets: Option<PathBuf>,
}
#[derive(Clone, Default, PartialEq, Eq, Debug, Deserialize, Serialize)]
@@ -208,6 +206,5 @@ fn manifest_from_old_manifest(
language_servers: Default::default(),
slash_commands: BTreeMap::default(),
indexed_docs_providers: BTreeMap::default(),
snippets: None,
}
}

View File

@@ -44,7 +44,6 @@ use release_channel::ReleaseChannel;
use semantic_version::SemanticVersion;
use serde::{Deserialize, Serialize};
use settings::Settings;
use snippet_provider::SnippetRegistry;
use std::ops::RangeInclusive;
use std::str::FromStr;
use std::{
@@ -116,7 +115,6 @@ pub struct ExtensionStore {
theme_registry: Arc<ThemeRegistry>,
slash_command_registry: Arc<SlashCommandRegistry>,
indexed_docs_registry: Arc<IndexedDocsRegistry>,
snippet_registry: Arc<SnippetRegistry>,
modified_extensions: HashSet<Arc<str>>,
wasm_host: Arc<WasmHost>,
wasm_extensions: Vec<(Arc<ExtensionManifest>, WasmExtension)>,
@@ -195,7 +193,6 @@ pub fn init(
theme_registry,
SlashCommandRegistry::global(cx),
IndexedDocsRegistry::global(cx),
SnippetRegistry::global(cx),
cx,
)
});
@@ -230,7 +227,6 @@ impl ExtensionStore {
theme_registry: Arc<ThemeRegistry>,
slash_command_registry: Arc<SlashCommandRegistry>,
indexed_docs_registry: Arc<IndexedDocsRegistry>,
snippet_registry: Arc<SnippetRegistry>,
cx: &mut ModelContext<Self>,
) -> Self {
let work_dir = extensions_dir.join("work");
@@ -263,7 +259,6 @@ impl ExtensionStore {
theme_registry,
slash_command_registry,
indexed_docs_registry,
snippet_registry,
reload_tx,
tasks: Vec::new(),
};
@@ -1050,7 +1045,6 @@ impl ExtensionStore {
.collect::<Vec<_>>();
let mut grammars_to_add = Vec::new();
let mut themes_to_add = Vec::new();
let mut snippets_to_add = Vec::new();
for extension_id in &extensions_to_load {
let Some(extension) = new_index.extensions.get(extension_id) else {
continue;
@@ -1068,11 +1062,6 @@ impl ExtensionStore {
path.extend([Path::new(extension_id.as_ref()), theme_path.as_path()]);
path
}));
snippets_to_add.extend(extension.manifest.snippets.iter().map(|snippets_path| {
let mut path = self.installed_dir.clone();
path.extend([Path::new(extension_id.as_ref()), snippets_path.as_path()]);
path
}));
}
self.language_registry
@@ -1108,7 +1097,6 @@ impl ExtensionStore {
let wasm_host = self.wasm_host.clone();
let root_dir = self.installed_dir.clone();
let theme_registry = self.theme_registry.clone();
let snippet_registry = self.snippet_registry.clone();
let extension_entries = extensions_to_load
.iter()
.filter_map(|name| new_index.extensions.get(name).cloned())
@@ -1129,15 +1117,6 @@ impl ExtensionStore {
.await
.log_err();
}
for snippets_path in &snippets_to_add {
if let Some(snippets_contents) = fs.load(snippets_path).await.log_err()
{
snippet_registry
.register_snippets(snippets_path, &snippets_contents)
.log_err();
}
}
}
})
.await;

View File

@@ -19,7 +19,6 @@ use parking_lot::Mutex;
use project::{Project, DEFAULT_COMPLETION_CONTEXT};
use serde_json::json;
use settings::{Settings as _, SettingsStore};
use snippet_provider::SnippetRegistry;
use std::{
ffi::OsString,
path::{Path, PathBuf},
@@ -161,7 +160,6 @@ async fn test_extension_store(cx: &mut TestAppContext) {
language_servers: BTreeMap::default(),
slash_commands: BTreeMap::default(),
indexed_docs_providers: BTreeMap::default(),
snippets: None,
}),
dev: false,
},
@@ -187,7 +185,6 @@ async fn test_extension_store(cx: &mut TestAppContext) {
language_servers: BTreeMap::default(),
slash_commands: BTreeMap::default(),
indexed_docs_providers: BTreeMap::default(),
snippets: None,
}),
dev: false,
},
@@ -261,7 +258,6 @@ async fn test_extension_store(cx: &mut TestAppContext) {
let theme_registry = Arc::new(ThemeRegistry::new(Box::new(())));
let slash_command_registry = SlashCommandRegistry::new();
let indexed_docs_registry = Arc::new(IndexedDocsRegistry::new(cx.executor()));
let snippet_registry = Arc::new(SnippetRegistry::new());
let node_runtime = FakeNodeRuntime::new();
let store = cx.new_model(|cx| {
@@ -276,7 +272,6 @@ async fn test_extension_store(cx: &mut TestAppContext) {
theme_registry.clone(),
slash_command_registry.clone(),
indexed_docs_registry.clone(),
snippet_registry.clone(),
cx,
)
});
@@ -350,7 +345,6 @@ async fn test_extension_store(cx: &mut TestAppContext) {
language_servers: BTreeMap::default(),
slash_commands: BTreeMap::default(),
indexed_docs_providers: BTreeMap::default(),
snippets: None,
}),
dev: false,
},
@@ -402,7 +396,6 @@ async fn test_extension_store(cx: &mut TestAppContext) {
theme_registry.clone(),
slash_command_registry,
indexed_docs_registry,
snippet_registry,
cx,
)
});
@@ -484,7 +477,6 @@ async fn test_extension_store_with_test_extension(cx: &mut TestAppContext) {
let theme_registry = Arc::new(ThemeRegistry::new(Box::new(())));
let slash_command_registry = SlashCommandRegistry::new();
let indexed_docs_registry = Arc::new(IndexedDocsRegistry::new(cx.executor()));
let snippet_registry = Arc::new(SnippetRegistry::new());
let node_runtime = FakeNodeRuntime::new();
let mut status_updates = language_registry.language_server_binary_statuses();
@@ -576,7 +568,6 @@ async fn test_extension_store_with_test_extension(cx: &mut TestAppContext) {
theme_registry.clone(),
slash_command_registry,
indexed_docs_registry,
snippet_registry,
cx,
)
});

View File

@@ -18,6 +18,7 @@ use language::LanguageRegistry;
use node_runtime::NodeRuntime;
use release_channel::ReleaseChannel;
use semantic_version::SemanticVersion;
use std::sync::atomic::AtomicBool;
use std::{
path::{Path, PathBuf},
sync::{Arc, OnceLock},
@@ -39,6 +40,7 @@ pub(crate) struct WasmHost {
pub(crate) work_dir: PathBuf,
_main_thread_message_task: Task<()>,
main_thread_message_tx: mpsc::UnboundedSender<MainThreadCall>,
enable_binary_downloads: bool,
}
#[derive(Clone)]
@@ -91,7 +93,11 @@ impl WasmHost {
message(&mut cx).await;
}
});
Arc::new(Self {
let enable_binary_downloads =
AtomicBool::new(DownloadConfiguration::get_global(cx).enable_binary_downloads);
let this = Arc::new(Self {
engine: wasm_engine(),
fs,
work_dir,
@@ -101,7 +107,18 @@ impl WasmHost {
release_channel: ReleaseChannel::global(cx),
_main_thread_message_task: task,
main_thread_message_tx: tx,
enable_binary_downloads: enable_binary_downloads.clone(),
});
cx.observe_global::<SettingsStore>(move |cx| {
enable_binary_downloads.store(
DownloadConfiguration::get_global(cx).enable_binary_downloads,
Ordering::SeqCst,
)
})
.detach();
this
}
pub fn load_extension(

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