Compare commits
1 Commits
assistant-
...
more-compl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f4766be1a |
2
.github/workflows/bump_nightly_tag.yml
vendored
2
.github/workflows/bump_nightly_tag.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
3
.github/workflows/bump_patch_version.yml
vendored
3
.github/workflows/bump_patch_version.yml
vendored
@@ -15,7 +15,8 @@ concurrency:
|
||||
jobs:
|
||||
bump_patch_version:
|
||||
runs-on:
|
||||
- buildjet-16vcpu-ubuntu-2204
|
||||
- self-hosted
|
||||
- test
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
||||
|
||||
30
.github/workflows/ci.yml
vendored
30
.github/workflows/ci.yml
vendored
@@ -39,7 +39,16 @@ jobs:
|
||||
run: git clean -df
|
||||
|
||||
- name: Check spelling
|
||||
run: script/check-spelling
|
||||
run: |
|
||||
if ! cargo install --list | grep "typos-cli v$TYPOS_CLI_VERSION" > /dev/null; then
|
||||
echo "Installing typos-cli@$TYPOS_CLI_VERSION..."
|
||||
cargo install "typos-cli@$TYPOS_CLI_VERSION"
|
||||
else
|
||||
echo "typos-cli@$TYPOS_CLI_VERSION is already installed."
|
||||
fi
|
||||
typos
|
||||
env:
|
||||
TYPOS_CLI_VERSION: "1.23.3"
|
||||
|
||||
- name: Run style checks
|
||||
uses: ./.github/actions/check_style
|
||||
@@ -101,7 +110,7 @@ jobs:
|
||||
timeout-minutes: 60
|
||||
name: (Linux) Run Clippy and tests
|
||||
runs-on:
|
||||
- buildjet-16vcpu-ubuntu-2204
|
||||
- hosted-linux-x86-1
|
||||
steps:
|
||||
- name: Add Rust to the PATH
|
||||
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
|
||||
@@ -111,12 +120,6 @@ jobs:
|
||||
with:
|
||||
clean: false
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2
|
||||
with:
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
cache-provider: "buildjet"
|
||||
|
||||
- name: Install Linux dependencies
|
||||
run: ./script/linux
|
||||
|
||||
@@ -144,7 +147,6 @@ jobs:
|
||||
uses: swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2
|
||||
with:
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
cache-provider: "github"
|
||||
|
||||
- name: cargo clippy
|
||||
# Windows can't run shell scripts, so we need to use `cargo xtask`.
|
||||
@@ -271,7 +273,7 @@ jobs:
|
||||
timeout-minutes: 60
|
||||
name: Create a Linux bundle
|
||||
runs-on:
|
||||
- buildjet-16vcpu-ubuntu-2204
|
||||
- hosted-linux-x86-1
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
|
||||
needs: [linux_tests]
|
||||
env:
|
||||
@@ -286,6 +288,9 @@ jobs:
|
||||
- name: Install Linux dependencies
|
||||
run: ./script/linux
|
||||
|
||||
- name: Limit target directory size
|
||||
run: script/clear-target-dir-if-larger-than 100
|
||||
|
||||
- name: Determine version and release channel
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||
run: |
|
||||
@@ -339,7 +344,7 @@ jobs:
|
||||
timeout-minutes: 60
|
||||
name: Create arm64 Linux bundle
|
||||
runs-on:
|
||||
- buildjet-16vcpu-ubuntu-2204-arm
|
||||
- hosted-linux-arm-1
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
|
||||
needs: [linux_tests]
|
||||
env:
|
||||
@@ -354,6 +359,9 @@ jobs:
|
||||
- name: Install Linux dependencies
|
||||
run: ./script/linux
|
||||
|
||||
- name: Limit target directory size
|
||||
run: script/clear-target-dir-if-larger-than 100
|
||||
|
||||
- name: Determine version and release channel
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||
run: |
|
||||
|
||||
6
.github/workflows/deploy_collab.yml
vendored
6
.github/workflows/deploy_collab.yml
vendored
@@ -61,7 +61,8 @@ jobs:
|
||||
- style
|
||||
- tests
|
||||
runs-on:
|
||||
- buildjet-16vcpu-ubuntu-2204
|
||||
- self-hosted
|
||||
- deploy
|
||||
steps:
|
||||
- name: Add Rust to the PATH
|
||||
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
|
||||
@@ -88,7 +89,8 @@ jobs:
|
||||
needs:
|
||||
- publish
|
||||
runs-on:
|
||||
- buildjet-16vcpu-ubuntu-2204
|
||||
- self-hosted
|
||||
- deploy
|
||||
|
||||
steps:
|
||||
- name: Sign into Kubernetes
|
||||
|
||||
1
.github/workflows/publish_extension_cli.yml
vendored
1
.github/workflows/publish_extension_cli.yml
vendored
@@ -24,7 +24,6 @@ jobs:
|
||||
uses: swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2
|
||||
with:
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
cache-provider: "github"
|
||||
|
||||
- name: Configure linux
|
||||
shell: bash -euxo pipefail {0}
|
||||
|
||||
3
.github/workflows/randomized_tests.yml
vendored
3
.github/workflows/randomized_tests.yml
vendored
@@ -19,7 +19,8 @@ jobs:
|
||||
tests:
|
||||
name: Run randomized tests
|
||||
runs-on:
|
||||
- buildjet-16vcpu-ubuntu-2204
|
||||
- self-hosted
|
||||
- randomized-tests
|
||||
steps:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4
|
||||
|
||||
9
.github/workflows/release_nightly.yml
vendored
9
.github/workflows/release_nightly.yml
vendored
@@ -97,7 +97,8 @@ jobs:
|
||||
name: Create a Linux *.tar.gz bundle for x86
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on:
|
||||
- buildjet-16vcpu-ubuntu-2204
|
||||
- self-hosted
|
||||
- deploy
|
||||
needs: tests
|
||||
env:
|
||||
DIGITALOCEAN_SPACES_ACCESS_KEY: ${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }}
|
||||
@@ -113,12 +114,6 @@ jobs:
|
||||
- name: Add Rust to the PATH
|
||||
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Install Linux dependencies
|
||||
run: ./script/linux
|
||||
|
||||
- name: Limit target directory size
|
||||
run: script/clear-target-dir-if-larger-than 100
|
||||
|
||||
- name: Set release channel to nightly
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
235
Cargo.lock
generated
235
Cargo.lock
generated
@@ -263,9 +263,9 @@ checksum = "34cd60c5e3152cef0a592f1b296f1cc93715d89d2551d85315828c3a09575ff4"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.89"
|
||||
version = "1.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6"
|
||||
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
||||
|
||||
[[package]]
|
||||
name = "approx"
|
||||
@@ -402,7 +402,6 @@ dependencies = [
|
||||
"indoc",
|
||||
"language",
|
||||
"language_model",
|
||||
"languages",
|
||||
"log",
|
||||
"markdown",
|
||||
"menu",
|
||||
@@ -437,7 +436,6 @@ dependencies = [
|
||||
"text",
|
||||
"theme",
|
||||
"toml 0.8.19",
|
||||
"tree-sitter-md",
|
||||
"ui",
|
||||
"unindent",
|
||||
"util",
|
||||
@@ -457,7 +455,6 @@ dependencies = [
|
||||
"language",
|
||||
"parking_lot",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"workspace",
|
||||
]
|
||||
|
||||
@@ -878,20 +875,6 @@ version = "4.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
|
||||
|
||||
[[package]]
|
||||
name = "async-tls"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfeefd0ca297cbbb3bd34fd6b228401c2a5177038257afd751bc29f0a2da4795"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"rustls 0.20.9",
|
||||
"rustls-pemfile 1.0.4",
|
||||
"webpki",
|
||||
"webpki-roots 0.22.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.81"
|
||||
@@ -909,8 +892,8 @@ version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1e9efbe14612da0a19fb983059a0b621e9cf6225d7018ecab4f9988215540dc"
|
||||
dependencies = [
|
||||
"async-native-tls",
|
||||
"async-std",
|
||||
"async-tls",
|
||||
"futures-io",
|
||||
"futures-util",
|
||||
"log",
|
||||
@@ -997,6 +980,7 @@ dependencies = [
|
||||
"editor",
|
||||
"gpui",
|
||||
"http_client",
|
||||
"isahc",
|
||||
"log",
|
||||
"markdown_preview",
|
||||
"menu",
|
||||
@@ -1064,7 +1048,7 @@ dependencies = [
|
||||
"fastrand 2.1.1",
|
||||
"hex",
|
||||
"http 0.2.12",
|
||||
"ring 0.17.8",
|
||||
"ring",
|
||||
"time",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@@ -1233,7 +1217,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"p256",
|
||||
"percent-encoding",
|
||||
"ring 0.17.8",
|
||||
"ring",
|
||||
"sha2",
|
||||
"subtle",
|
||||
"time",
|
||||
@@ -1346,7 +1330,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"rustls 0.21.12",
|
||||
"rustls",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
@@ -2420,8 +2404,6 @@ dependencies = [
|
||||
"rand 0.8.5",
|
||||
"release_channel",
|
||||
"rpc",
|
||||
"rustls 0.20.9",
|
||||
"rustls-native-certs 0.8.0",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -2570,7 +2552,6 @@ dependencies = [
|
||||
"http_client",
|
||||
"hyper",
|
||||
"indoc",
|
||||
"isahc_http_client",
|
||||
"jsonwebtoken",
|
||||
"language",
|
||||
"language_model",
|
||||
@@ -4018,34 +3999,6 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "evals"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"client",
|
||||
"clock",
|
||||
"collections",
|
||||
"env_logger",
|
||||
"feature_flags",
|
||||
"fs",
|
||||
"git",
|
||||
"gpui",
|
||||
"http_client",
|
||||
"isahc_http_client",
|
||||
"language",
|
||||
"languages",
|
||||
"node_runtime",
|
||||
"open_ai",
|
||||
"project",
|
||||
"semantic_index",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"settings",
|
||||
"smol",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "2.5.3"
|
||||
@@ -4129,7 +4082,6 @@ dependencies = [
|
||||
"http_client",
|
||||
"indexed_docs",
|
||||
"isahc",
|
||||
"isahc_http_client",
|
||||
"language",
|
||||
"log",
|
||||
"lsp",
|
||||
@@ -4168,7 +4120,7 @@ dependencies = [
|
||||
"env_logger",
|
||||
"extension",
|
||||
"fs",
|
||||
"isahc_http_client",
|
||||
"http_client",
|
||||
"language",
|
||||
"log",
|
||||
"rpc",
|
||||
@@ -4415,7 +4367,7 @@ dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"nanorand",
|
||||
"spin 0.9.8",
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4924,6 +4876,7 @@ dependencies = [
|
||||
"git",
|
||||
"gpui",
|
||||
"http_client",
|
||||
"isahc",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"serde",
|
||||
@@ -5556,11 +5509,12 @@ dependencies = [
|
||||
"anyhow",
|
||||
"derive_more",
|
||||
"futures 0.3.30",
|
||||
"http 0.2.12",
|
||||
"futures-lite 1.13.0",
|
||||
"http 1.1.0",
|
||||
"isahc",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smol",
|
||||
"url",
|
||||
]
|
||||
|
||||
@@ -5622,8 +5576,8 @@ dependencies = [
|
||||
"http 0.2.12",
|
||||
"hyper",
|
||||
"log",
|
||||
"rustls 0.21.12",
|
||||
"rustls-native-certs 0.6.3",
|
||||
"rustls",
|
||||
"rustls-native-certs",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
]
|
||||
@@ -6035,17 +5989,6 @@ dependencies = [
|
||||
"waker-fn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "isahc_http_client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures 0.3.30",
|
||||
"http_client",
|
||||
"isahc",
|
||||
"util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.5"
|
||||
@@ -6150,7 +6093,7 @@ dependencies = [
|
||||
"base64 0.21.7",
|
||||
"js-sys",
|
||||
"pem",
|
||||
"ring 0.17.8",
|
||||
"ring",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"simple_asn1",
|
||||
@@ -6401,7 +6344,7 @@ version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
dependencies = [
|
||||
"spin 0.9.8",
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7512,6 +7455,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"futures 0.3.30",
|
||||
"http_client",
|
||||
"isahc",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -9203,7 +9147,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rustls-pemfile 1.0.4",
|
||||
"rustls-pemfile",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
@@ -9267,21 +9211,6 @@ dependencies = [
|
||||
"util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.16.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"spin 0.5.2",
|
||||
"untrusted 0.7.1",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.8"
|
||||
@@ -9292,8 +9221,8 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"getrandom 0.2.15",
|
||||
"libc",
|
||||
"spin 0.9.8",
|
||||
"untrusted 0.9.0",
|
||||
"spin",
|
||||
"untrusted",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
@@ -9449,7 +9378,7 @@ dependencies = [
|
||||
"futures 0.3.30",
|
||||
"glob",
|
||||
"rand 0.8.5",
|
||||
"ring 0.17.8",
|
||||
"ring",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"shellexpand 3.1.0",
|
||||
@@ -9570,18 +9499,6 @@ dependencies = [
|
||||
"rustix 0.38.35",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.20.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring 0.16.20",
|
||||
"sct",
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.21.12"
|
||||
@@ -9589,7 +9506,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring 0.17.8",
|
||||
"ring",
|
||||
"rustls-webpki",
|
||||
"sct",
|
||||
]
|
||||
@@ -9601,20 +9518,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00"
|
||||
dependencies = [
|
||||
"openssl-probe",
|
||||
"rustls-pemfile 1.0.4",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-native-certs"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a"
|
||||
dependencies = [
|
||||
"openssl-probe",
|
||||
"rustls-pemfile 2.1.3",
|
||||
"rustls-pki-types",
|
||||
"rustls-pemfile",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
]
|
||||
@@ -9628,30 +9532,14 @@ dependencies = [
|
||||
"base64 0.21.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0"
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.101.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
|
||||
dependencies = [
|
||||
"ring 0.17.8",
|
||||
"untrusted 0.9.0",
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9765,8 +9653,8 @@ version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
|
||||
dependencies = [
|
||||
"ring 0.17.8",
|
||||
"untrusted 0.9.0",
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9962,7 +9850,6 @@ dependencies = [
|
||||
"gpui",
|
||||
"heed",
|
||||
"http_client",
|
||||
"isahc_http_client",
|
||||
"language",
|
||||
"language_model",
|
||||
"languages",
|
||||
@@ -10522,12 +10409,6 @@ dependencies = [
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
@@ -10650,8 +10531,8 @@ dependencies = [
|
||||
"paste",
|
||||
"percent-encoding",
|
||||
"rust_decimal",
|
||||
"rustls 0.21.12",
|
||||
"rustls-pemfile 1.0.4",
|
||||
"rustls",
|
||||
"rustls-pemfile",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
@@ -10664,7 +10545,7 @@ dependencies = [
|
||||
"tracing",
|
||||
"url",
|
||||
"uuid",
|
||||
"webpki-roots 0.25.4",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11176,16 +11057,17 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.31.4"
|
||||
version = "0.30.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "355dbe4f8799b304b05e1b0f05fc59b2a18d36645cf169607da45bde2f69a1be"
|
||||
checksum = "0a5b4ddaee55fb2bea2bf0e5000747e5f5c0de765e5a5ff87f4cd106439f4bb3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"memchr",
|
||||
"ntapi",
|
||||
"once_cell",
|
||||
"rayon",
|
||||
"windows 0.54.0",
|
||||
"windows 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11796,7 +11678,7 @@ version = "0.24.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
|
||||
dependencies = [
|
||||
"rustls 0.21.12",
|
||||
"rustls",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
@@ -12323,6 +12205,7 @@ dependencies = [
|
||||
"http 0.2.12",
|
||||
"httparse",
|
||||
"log",
|
||||
"native-tls",
|
||||
"rand 0.8.5",
|
||||
"sha1",
|
||||
"thiserror",
|
||||
@@ -12507,12 +12390,6 @@ version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
@@ -13367,25 +13244,6 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki"
|
||||
version = "0.22.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53"
|
||||
dependencies = [
|
||||
"ring 0.17.8",
|
||||
"untrusted 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.22.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87"
|
||||
dependencies = [
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.25.4"
|
||||
@@ -13557,6 +13415,16 @@ dependencies = [
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
|
||||
dependencies = [
|
||||
"windows-core 0.52.0",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.54.0"
|
||||
@@ -14420,7 +14288,6 @@ dependencies = [
|
||||
"inline_completion_button",
|
||||
"install_cli",
|
||||
"isahc",
|
||||
"isahc_http_client",
|
||||
"journal",
|
||||
"language",
|
||||
"language_model",
|
||||
@@ -14613,7 +14480,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed_ocaml"
|
||||
version = "0.1.0"
|
||||
version = "0.0.2"
|
||||
dependencies = [
|
||||
"zed_extension_api 0.1.0",
|
||||
]
|
||||
@@ -14648,7 +14515,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed_ruff"
|
||||
version = "0.1.0"
|
||||
version = "0.0.2"
|
||||
dependencies = [
|
||||
"zed_extension_api 0.1.0",
|
||||
]
|
||||
@@ -14663,7 +14530,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed_svelte"
|
||||
version = "0.2.0"
|
||||
version = "0.1.1"
|
||||
dependencies = [
|
||||
"zed_extension_api 0.1.0",
|
||||
]
|
||||
|
||||
@@ -27,7 +27,6 @@ members = [
|
||||
"crates/diagnostics",
|
||||
"crates/docs_preprocessor",
|
||||
"crates/editor",
|
||||
"crates/evals",
|
||||
"crates/extension",
|
||||
"crates/extension_api",
|
||||
"crates/extension_cli",
|
||||
@@ -52,7 +51,6 @@ members = [
|
||||
"crates/indexed_docs",
|
||||
"crates/inline_completion_button",
|
||||
"crates/install_cli",
|
||||
"crates/isahc_http_client",
|
||||
"crates/journal",
|
||||
"crates/language",
|
||||
"crates/language_model",
|
||||
@@ -227,7 +225,6 @@ image_viewer = { path = "crates/image_viewer" }
|
||||
indexed_docs = { path = "crates/indexed_docs" }
|
||||
inline_completion_button = { path = "crates/inline_completion_button" }
|
||||
install_cli = { path = "crates/install_cli" }
|
||||
isahc_http_client = { path = "crates/isahc_http_client" }
|
||||
journal = { path = "crates/journal" }
|
||||
language = { path = "crates/language" }
|
||||
language_model = { path = "crates/language_model" }
|
||||
@@ -396,8 +393,6 @@ runtimelib = { version = "0.15", default-features = false, features = [
|
||||
] }
|
||||
rustc-demangle = "0.1.23"
|
||||
rust-embed = { version = "8.4", features = ["include-exclude"] }
|
||||
rustls = "0.20.3"
|
||||
rustls-native-certs = "0.8.0"
|
||||
schemars = { version = "0.8", features = ["impl_json_schema"] }
|
||||
semver = "1.0"
|
||||
serde = { version = "1.0", features = ["derive", "rc"] }
|
||||
@@ -420,7 +415,7 @@ strsim = "0.11"
|
||||
strum = { version = "0.25.0", features = ["derive"] }
|
||||
subtle = "2.5.0"
|
||||
sys-locale = "0.3.1"
|
||||
sysinfo = "0.31.0"
|
||||
sysinfo = "0.30.7"
|
||||
tempfile = "3.9.0"
|
||||
thiserror = "1.0.29"
|
||||
tiktoken-rs = "0.5.9"
|
||||
@@ -494,6 +489,7 @@ features = [
|
||||
"implement",
|
||||
"Foundation_Numerics",
|
||||
"Storage",
|
||||
"System",
|
||||
"System_Threading",
|
||||
"UI_ViewManagement",
|
||||
"Wdk_System_SystemServices",
|
||||
@@ -524,7 +520,6 @@ features = [
|
||||
"Win32_UI_Input_Ime",
|
||||
"Win32_UI_Input_KeyboardAndMouse",
|
||||
"Win32_UI_Shell",
|
||||
"Win32_UI_Shell_Common",
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
]
|
||||
|
||||
|
||||
@@ -56,7 +56,6 @@
|
||||
"shift-tab": "editor::TabPrev",
|
||||
"ctrl-k": "editor::CutToEndOfLine",
|
||||
// "ctrl-t": "editor::Transpose",
|
||||
"alt-q": "editor::Rewrap",
|
||||
"ctrl-backspace": "editor::DeleteToPreviousWordStart",
|
||||
"ctrl-delete": "editor::DeleteToNextWordEnd",
|
||||
"shift-delete": "editor::Cut",
|
||||
@@ -166,7 +165,6 @@
|
||||
{
|
||||
"context": "AssistantPanel",
|
||||
"bindings": {
|
||||
"ctrl-k c": "assistant::CopyCode",
|
||||
"ctrl-g": "search::SelectNextMatch",
|
||||
"ctrl-shift-g": "search::SelectPrevMatch",
|
||||
"alt-m": "assistant::ToggleModelSelector",
|
||||
|
||||
@@ -51,7 +51,6 @@
|
||||
"shift-tab": "editor::TabPrev",
|
||||
"ctrl-k": "editor::CutToEndOfLine",
|
||||
"ctrl-t": "editor::Transpose",
|
||||
"alt-q": "editor::Rewrap",
|
||||
"cmd-backspace": "editor::DeleteToBeginningOfLine",
|
||||
"cmd-delete": "editor::DeleteToEndOfLine",
|
||||
"alt-backspace": "editor::DeleteToPreviousWordStart",
|
||||
@@ -188,7 +187,6 @@
|
||||
{
|
||||
"context": "AssistantPanel",
|
||||
"bindings": {
|
||||
"cmd-k c": "assistant::CopyCode",
|
||||
"cmd-g": "search::SelectNextMatch",
|
||||
"cmd-shift-g": "search::SelectPrevMatch",
|
||||
"alt-m": "assistant::ToggleModelSelector",
|
||||
|
||||
@@ -124,7 +124,6 @@
|
||||
"g i": "vim::InsertAtPrevious",
|
||||
"g ,": "vim::ChangeListNewer",
|
||||
"g ;": "vim::ChangeListOlder",
|
||||
"g q": "editor::Rewrap",
|
||||
"shift-h": "vim::WindowTop",
|
||||
"shift-m": "vim::WindowMiddle",
|
||||
"shift-l": "vim::WindowBottom",
|
||||
|
||||
@@ -318,10 +318,6 @@
|
||||
"show_parameter_hints": true,
|
||||
// Corresponds to null/None LSP hint type value.
|
||||
"show_other_hints": true,
|
||||
// Whether to show a background for inlay hints.
|
||||
//
|
||||
// If set to `true`, the background will use the `hint.background` color from the current theme.
|
||||
"show_background": false,
|
||||
// Time to wait after editing the buffer, before requesting the hints,
|
||||
// set to 0 to disable debouncing.
|
||||
"edit_debounce_ms": 700,
|
||||
|
||||
@@ -94,11 +94,9 @@ editor = { workspace = true, features = ["test-support"] }
|
||||
env_logger.workspace = true
|
||||
language = { workspace = true, features = ["test-support"] }
|
||||
language_model = { workspace = true, features = ["test-support"] }
|
||||
languages = { workspace = true, features = ["test-support"] }
|
||||
log.workspace = true
|
||||
project = { workspace = true, features = ["test-support"] }
|
||||
rand.workspace = true
|
||||
serde_json_lenient.workspace = true
|
||||
text = { workspace = true, features = ["test-support"] }
|
||||
tree-sitter-md.workspace = true
|
||||
unindent.workspace = true
|
||||
|
||||
@@ -41,9 +41,9 @@ use semantic_index::{CloudEmbeddingProvider, SemanticDb};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::{update_settings_file, Settings, SettingsStore};
|
||||
use slash_command::{
|
||||
auto_command, context_server_command, default_command, delta_command, diagnostics_command,
|
||||
docs_command, fetch_command, file_command, now_command, project_command, prompt_command,
|
||||
search_command, symbols_command, tab_command, terminal_command, workflow_command,
|
||||
auto_command, context_server_command, default_command, diagnostics_command, docs_command,
|
||||
fetch_command, file_command, now_command, project_command, prompt_command, search_command,
|
||||
symbols_command, tab_command, terminal_command, workflow_command,
|
||||
};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
@@ -58,7 +58,6 @@ actions!(
|
||||
[
|
||||
Assist,
|
||||
Split,
|
||||
CopyCode,
|
||||
CycleMessageRole,
|
||||
QuoteSelection,
|
||||
InsertIntoEditor,
|
||||
@@ -368,7 +367,6 @@ fn register_slash_commands(prompt_builder: Option<Arc<PromptBuilder>>, cx: &mut
|
||||
let slash_command_registry = SlashCommandRegistry::global(cx);
|
||||
|
||||
slash_command_registry.register_command(file_command::FileSlashCommand, true);
|
||||
slash_command_registry.register_command(delta_command::DeltaSlashCommand, true);
|
||||
slash_command_registry.register_command(symbols_command::OutlineSlashCommand, true);
|
||||
slash_command_registry.register_command(tab_command::TabSlashCommand, true);
|
||||
slash_command_registry.register_command(project_command::ProjectSlashCommand, true);
|
||||
|
||||
@@ -12,11 +12,11 @@ use crate::{
|
||||
slash_command_picker,
|
||||
terminal_inline_assistant::TerminalInlineAssistant,
|
||||
Assist, CacheStatus, ConfirmCommand, Content, Context, ContextEvent, ContextId, ContextStore,
|
||||
ContextStoreEvent, CopyCode, CycleMessageRole, DeployHistory, DeployPromptLibrary,
|
||||
InlineAssistId, InlineAssistant, InsertDraggedFiles, InsertIntoEditor, Message, MessageId,
|
||||
MessageMetadata, MessageStatus, ModelPickerDelegate, ModelSelector, NewContext,
|
||||
PendingSlashCommand, PendingSlashCommandStatus, QuoteSelection, RemoteContextMetadata,
|
||||
SavedContextMetadata, Split, ToggleFocus, ToggleModelSelector, WorkflowStepResolution,
|
||||
ContextStoreEvent, CycleMessageRole, DeployHistory, DeployPromptLibrary, InlineAssistId,
|
||||
InlineAssistant, InsertDraggedFiles, InsertIntoEditor, Message, MessageId, MessageMetadata,
|
||||
MessageStatus, ModelPickerDelegate, ModelSelector, NewContext, PendingSlashCommand,
|
||||
PendingSlashCommandStatus, QuoteSelection, RemoteContextMetadata, SavedContextMetadata, Split,
|
||||
ToggleFocus, ToggleModelSelector, WorkflowStepResolution,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use assistant_slash_command::{SlashCommand, SlashCommandOutputSection};
|
||||
@@ -45,8 +45,7 @@ use gpui::{
|
||||
};
|
||||
use indexed_docs::IndexedDocsStore;
|
||||
use language::{
|
||||
language_settings::SoftWrap, BufferSnapshot, Capability, LanguageRegistry, LspAdapterDelegate,
|
||||
ToOffset,
|
||||
language_settings::SoftWrap, Capability, LanguageRegistry, LspAdapterDelegate, Point, ToOffset,
|
||||
};
|
||||
use language_model::{
|
||||
provider::cloud::PROVIDER_ID, LanguageModelProvider, LanguageModelProviderId,
|
||||
@@ -55,9 +54,8 @@ use language_model::{
|
||||
use language_model::{LanguageModelImage, LanguageModelToolUse};
|
||||
use multi_buffer::MultiBufferRow;
|
||||
use picker::{Picker, PickerDelegate};
|
||||
use project::lsp_store::LocalLspAdapterDelegate;
|
||||
use project::lsp_store::ProjectLspAdapterDelegate;
|
||||
use project::{Project, Worktree};
|
||||
use rope::Point;
|
||||
use search::{buffer_search::DivRegistrar, BufferSearchBar};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::{update_settings_file, Settings};
|
||||
@@ -83,10 +81,9 @@ use util::{maybe, ResultExt};
|
||||
use workspace::{
|
||||
dock::{DockPosition, Panel, PanelEvent},
|
||||
item::{self, FollowableItem, Item, ItemHandle},
|
||||
notifications::NotificationId,
|
||||
pane::{self, SaveIntent},
|
||||
searchable::{SearchEvent, SearchableItem},
|
||||
DraggedSelection, Pane, Save, ShowConfiguration, Toast, ToggleZoom, ToolbarItemEvent,
|
||||
DraggedSelection, Pane, Save, ShowConfiguration, ToggleZoom, ToolbarItemEvent,
|
||||
ToolbarItemLocation, ToolbarItemView, Workspace,
|
||||
};
|
||||
use workspace::{searchable::SearchableItemHandle, DraggedTab};
|
||||
@@ -108,7 +105,6 @@ pub fn init(cx: &mut AppContext) {
|
||||
.register_action(AssistantPanel::inline_assist)
|
||||
.register_action(ContextEditor::quote_selection)
|
||||
.register_action(ContextEditor::insert_selection)
|
||||
.register_action(ContextEditor::copy_code)
|
||||
.register_action(ContextEditor::insert_dragged_files)
|
||||
.register_action(AssistantPanel::show_configuration)
|
||||
.register_action(AssistantPanel::create_new_context);
|
||||
@@ -1076,13 +1072,6 @@ impl AssistantPanel {
|
||||
self.show_updated_summary(&context_editor, cx);
|
||||
cx.notify()
|
||||
}
|
||||
EditorEvent::SelectionsChanged { local } => {
|
||||
if *local {
|
||||
context_editor.update(cx, |this, cx| {
|
||||
this.update_code_fence_blocks(cx);
|
||||
})
|
||||
}
|
||||
}
|
||||
EditorEvent::Edited { .. } => cx.emit(AssistantPanelEvent::ContextEdited),
|
||||
_ => {}
|
||||
}
|
||||
@@ -1512,7 +1501,6 @@ pub struct ContextEditor {
|
||||
editor: View<Editor>,
|
||||
blocks: HashMap<MessageId, (MessageHeader, CustomBlockId)>,
|
||||
image_blocks: HashSet<CustomBlockId>,
|
||||
code_fence_blocks: HashSet<CustomBlockId>,
|
||||
scroll_position: Option<ScrollPosition>,
|
||||
remote_id: Option<workspace::ViewId>,
|
||||
pending_slash_command_creases: HashMap<Range<language::Anchor>, CreaseId>,
|
||||
@@ -1581,7 +1569,6 @@ impl ContextEditor {
|
||||
lsp_adapter_delegate,
|
||||
blocks: Default::default(),
|
||||
image_blocks: Default::default(),
|
||||
code_fence_blocks: Default::default(),
|
||||
scroll_position: None,
|
||||
remote_id: None,
|
||||
fs,
|
||||
@@ -1919,22 +1906,7 @@ impl ContextEditor {
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
if let Some(command) = SlashCommandRegistry::global(cx).command(name) {
|
||||
let context = self.context.read(cx);
|
||||
let sections = context
|
||||
.slash_command_output_sections()
|
||||
.into_iter()
|
||||
.filter(|section| section.is_valid(context.buffer().read(cx)))
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
let snapshot = context.buffer().read(cx).snapshot();
|
||||
let output = command.run(
|
||||
arguments,
|
||||
§ions,
|
||||
snapshot,
|
||||
workspace,
|
||||
self.lsp_adapter_delegate.clone(),
|
||||
cx,
|
||||
);
|
||||
let output = command.run(arguments, workspace, self.lsp_adapter_delegate.clone(), cx);
|
||||
self.context.update(cx, |context, cx| {
|
||||
context.insert_command_output(
|
||||
command_range,
|
||||
@@ -3113,40 +3085,6 @@ impl ContextEditor {
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns either the selected text, or the content of the Markdown code
|
||||
/// block surrounding the cursor.
|
||||
fn get_selection_or_code_block(
|
||||
context_editor_view: &View<ContextEditor>,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) -> Option<(String, bool)> {
|
||||
let context_editor = context_editor_view.read(cx).editor.read(cx);
|
||||
|
||||
if context_editor.selections.newest::<Point>(cx).is_empty() {
|
||||
let snapshot = context_editor.buffer().read(cx).snapshot(cx);
|
||||
let (_, _, snapshot) = snapshot.as_singleton()?;
|
||||
|
||||
let head = context_editor.selections.newest::<Point>(cx).head();
|
||||
let offset = snapshot.point_to_offset(head);
|
||||
|
||||
let surrounding_code_block_range = find_surrounding_code_block(snapshot, offset)?;
|
||||
let text = snapshot
|
||||
.text_for_range(surrounding_code_block_range)
|
||||
.collect::<String>();
|
||||
|
||||
(!text.is_empty()).then_some((text, true))
|
||||
} else {
|
||||
let anchor = context_editor.selections.newest_anchor();
|
||||
let text = context_editor
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.read(cx)
|
||||
.text_for_range(anchor.range())
|
||||
.collect::<String>();
|
||||
|
||||
(!text.is_empty()).then_some((text, false))
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_selection(
|
||||
workspace: &mut Workspace,
|
||||
_: &InsertIntoEditor,
|
||||
@@ -3165,7 +3103,17 @@ impl ContextEditor {
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some((text, _)) = Self::get_selection_or_code_block(&context_editor_view, cx) {
|
||||
let context_editor = context_editor_view.read(cx).editor.read(cx);
|
||||
let anchor = context_editor.selections.newest_anchor();
|
||||
let text = context_editor
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.read(cx)
|
||||
.text_for_range(anchor.range())
|
||||
.collect::<String>();
|
||||
|
||||
// If nothing is selected, don't delete the current selection; instead, be a no-op.
|
||||
if !text.is_empty() {
|
||||
active_editor_view.update(cx, |editor, cx| {
|
||||
editor.insert(&text, cx);
|
||||
editor.focus(cx);
|
||||
@@ -3173,36 +3121,6 @@ impl ContextEditor {
|
||||
}
|
||||
}
|
||||
|
||||
fn copy_code(workspace: &mut Workspace, _: &CopyCode, cx: &mut ViewContext<Workspace>) {
|
||||
let result = maybe!({
|
||||
let panel = workspace.panel::<AssistantPanel>(cx)?;
|
||||
let context_editor_view = panel.read(cx).active_context_editor(cx)?;
|
||||
Self::get_selection_or_code_block(&context_editor_view, cx)
|
||||
});
|
||||
let Some((text, is_code_block)) = result else {
|
||||
return;
|
||||
};
|
||||
|
||||
cx.write_to_clipboard(ClipboardItem::new_string(text));
|
||||
|
||||
struct CopyToClipboardToast;
|
||||
workspace.show_toast(
|
||||
Toast::new(
|
||||
NotificationId::unique::<CopyToClipboardToast>(),
|
||||
format!(
|
||||
"{} copied to clipboard.",
|
||||
if is_code_block {
|
||||
"Code block"
|
||||
} else {
|
||||
"Selection"
|
||||
}
|
||||
),
|
||||
)
|
||||
.autohide(),
|
||||
cx,
|
||||
);
|
||||
}
|
||||
|
||||
fn insert_dragged_files(
|
||||
workspace: &mut Workspace,
|
||||
action: &InsertDraggedFiles,
|
||||
@@ -3349,7 +3267,7 @@ impl ContextEditor {
|
||||
|
||||
let fence = codeblock_fence_for_path(
|
||||
filename.as_deref(),
|
||||
Some(selection.start.row..=selection.end.row),
|
||||
Some(selection.start.row..selection.end.row),
|
||||
);
|
||||
|
||||
if let Some((line_comment_prefix, outline_text)) =
|
||||
@@ -3716,54 +3634,6 @@ impl ContextEditor {
|
||||
});
|
||||
}
|
||||
|
||||
fn update_code_fence_blocks(&mut self, cx: &mut ViewContext<Self>) {
|
||||
let workspace = self.workspace.clone();
|
||||
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
let buffer = editor.buffer().read(cx).snapshot(cx);
|
||||
let Some((_, _, snapshot)) = buffer.as_singleton() else {
|
||||
return;
|
||||
};
|
||||
let old_blocks = std::mem::take(&mut self.code_fence_blocks);
|
||||
editor.remove_blocks(old_blocks, None, cx);
|
||||
|
||||
let selection_head = editor.selections.newest::<usize>(cx).head();
|
||||
if let Some(range) = find_surrounding_code_block(snapshot, selection_head) {
|
||||
let start_row = snapshot.offset_to_point(range.start).row.saturating_sub(1);
|
||||
let position = buffer.anchor_after(Point::new(start_row, 0));
|
||||
let block = BlockProperties {
|
||||
position,
|
||||
height: 0,
|
||||
style: BlockStyle::Sticky,
|
||||
render: Box::new(move |_| {
|
||||
h_flex()
|
||||
.justify_end()
|
||||
.child(
|
||||
IconButton::new("copy-code", IconName::Copy)
|
||||
.shape(ui::IconButtonShape::Square)
|
||||
.style(ButtonStyle::Filled)
|
||||
.on_click({
|
||||
let workspace = workspace.clone();
|
||||
move |_, cx| {
|
||||
workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
Self::copy_code(workspace, &CopyCode, cx);
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
}),
|
||||
)
|
||||
.into_any_element()
|
||||
}),
|
||||
disposition: BlockDisposition::Above,
|
||||
priority: 0,
|
||||
};
|
||||
let ids = editor.insert_blocks(vec![block], None, cx);
|
||||
self.code_fence_blocks = HashSet::from_iter(ids);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn split(&mut self, _: &Split, cx: &mut ViewContext<Self>) {
|
||||
self.context.update(cx, |context, cx| {
|
||||
let selections = self.editor.read(cx).selections.disjoint_anchors();
|
||||
@@ -4330,48 +4200,6 @@ impl ContextEditor {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the contents of the *outermost* fenced code block that contains the given offset.
|
||||
fn find_surrounding_code_block(snapshot: &BufferSnapshot, offset: usize) -> Option<Range<usize>> {
|
||||
const CODE_BLOCK_NODE: &'static str = "fenced_code_block";
|
||||
const CODE_BLOCK_CONTENT: &'static str = "code_fence_content";
|
||||
|
||||
let layer = snapshot.syntax_layers().next()?;
|
||||
|
||||
let root_node = layer.node();
|
||||
let mut cursor = root_node.walk();
|
||||
|
||||
// Go to the first child for the given offset
|
||||
while cursor.goto_first_child_for_byte(offset).is_some() {
|
||||
// If we're at the end of the node, go to the next one.
|
||||
// Example: if you have a fenced-code-block, and you're on the start of the line
|
||||
// right after the closing ```, you want to skip the fenced-code-block and
|
||||
// go to the next sibling.
|
||||
if cursor.node().end_byte() == offset {
|
||||
cursor.goto_next_sibling();
|
||||
}
|
||||
|
||||
if cursor.node().start_byte() > offset {
|
||||
break;
|
||||
}
|
||||
|
||||
// We found the fenced code block.
|
||||
if cursor.node().kind() == CODE_BLOCK_NODE {
|
||||
// Now we need to find the child node that contains the code.
|
||||
cursor.goto_first_child();
|
||||
loop {
|
||||
if cursor.node().kind() == CODE_BLOCK_CONTENT {
|
||||
return Some(cursor.node().byte_range());
|
||||
}
|
||||
if !cursor.goto_next_sibling() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn render_fold_icon_button(
|
||||
editor: WeakView<Editor>,
|
||||
icon: IconName,
|
||||
@@ -5541,16 +5369,18 @@ fn make_lsp_adapter_delegate(
|
||||
let worktree = project
|
||||
.worktrees(cx)
|
||||
.next()
|
||||
.ok_or_else(|| anyhow!("no worktrees when constructing LocalLspAdapterDelegate"))?;
|
||||
.ok_or_else(|| anyhow!("no worktrees when constructing ProjectLspAdapterDelegate"))?;
|
||||
let fs = if project.is_local() {
|
||||
Some(project.fs().clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let http_client = project.client().http_client().clone();
|
||||
project.lsp_store().update(cx, |lsp_store, cx| {
|
||||
Ok(LocalLspAdapterDelegate::new(
|
||||
lsp_store,
|
||||
&worktree,
|
||||
http_client,
|
||||
project.fs().clone(),
|
||||
cx,
|
||||
) as Arc<dyn LspAdapterDelegate>)
|
||||
Ok(
|
||||
ProjectLspAdapterDelegate::new(lsp_store, &worktree, http_client, fs, None, cx)
|
||||
as Arc<dyn LspAdapterDelegate>,
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -5654,85 +5484,3 @@ fn configuration_error(cx: &AppContext) -> Option<ConfigurationError> {
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use gpui::{AppContext, Context};
|
||||
use language::Buffer;
|
||||
use unindent::Unindent;
|
||||
|
||||
#[gpui::test]
|
||||
fn test_find_code_blocks(cx: &mut AppContext) {
|
||||
let markdown = languages::language("markdown", tree_sitter_md::LANGUAGE.into());
|
||||
|
||||
let buffer = cx.new_model(|cx| {
|
||||
let text = r#"
|
||||
line 0
|
||||
line 1
|
||||
```rust
|
||||
fn main() {}
|
||||
```
|
||||
line 5
|
||||
line 6
|
||||
line 7
|
||||
```go
|
||||
func main() {}
|
||||
```
|
||||
line 11
|
||||
```
|
||||
this is plain text code block
|
||||
```
|
||||
|
||||
```go
|
||||
func another() {}
|
||||
```
|
||||
line 19
|
||||
"#
|
||||
.unindent();
|
||||
let mut buffer = Buffer::local(text, cx);
|
||||
buffer.set_language(Some(markdown.clone()), cx);
|
||||
buffer
|
||||
});
|
||||
let snapshot = buffer.read(cx).snapshot();
|
||||
|
||||
let code_blocks = vec![
|
||||
Point::new(3, 0)..Point::new(4, 0),
|
||||
Point::new(9, 0)..Point::new(10, 0),
|
||||
Point::new(13, 0)..Point::new(14, 0),
|
||||
Point::new(17, 0)..Point::new(18, 0),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|range| snapshot.point_to_offset(range.start)..snapshot.point_to_offset(range.end))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let expected_results = vec![
|
||||
(0, None),
|
||||
(1, None),
|
||||
(2, Some(code_blocks[0].clone())),
|
||||
(3, Some(code_blocks[0].clone())),
|
||||
(4, Some(code_blocks[0].clone())),
|
||||
(5, None),
|
||||
(6, None),
|
||||
(7, None),
|
||||
(8, Some(code_blocks[1].clone())),
|
||||
(9, Some(code_blocks[1].clone())),
|
||||
(10, Some(code_blocks[1].clone())),
|
||||
(11, None),
|
||||
(12, Some(code_blocks[2].clone())),
|
||||
(13, Some(code_blocks[2].clone())),
|
||||
(14, Some(code_blocks[2].clone())),
|
||||
(15, None),
|
||||
(16, Some(code_blocks[3].clone())),
|
||||
(17, Some(code_blocks[3].clone())),
|
||||
(18, Some(code_blocks[3].clone())),
|
||||
(19, None),
|
||||
];
|
||||
|
||||
for (row, expected) in expected_results {
|
||||
let offset = snapshot.point_to_offset(Point::new(row, 0));
|
||||
let range = find_surrounding_code_block(&snapshot, offset);
|
||||
assert_eq!(range, expected, "unexpected result on row {:?}", row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,9 +46,9 @@ use std::{
|
||||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use telemetry_events::{AssistantKind, AssistantPhase};
|
||||
use telemetry_events::AssistantKind;
|
||||
use text::BufferSnapshot;
|
||||
use util::{post_inc, ResultExt, TryFutureExt};
|
||||
use util::{post_inc, TryFutureExt};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
@@ -162,9 +162,6 @@ impl ContextOperation {
|
||||
)?,
|
||||
icon: section.icon_name.parse()?,
|
||||
label: section.label.into(),
|
||||
metadata: section
|
||||
.metadata
|
||||
.and_then(|metadata| serde_json::from_str(&metadata).log_err()),
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?,
|
||||
@@ -245,9 +242,6 @@ impl ContextOperation {
|
||||
)),
|
||||
icon_name: icon_name.to_string(),
|
||||
label: section.label.to_string(),
|
||||
metadata: section.metadata.as_ref().and_then(|metadata| {
|
||||
serde_json::to_string(metadata).log_err()
|
||||
}),
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
@@ -641,13 +635,12 @@ impl Context {
|
||||
.slash_command_output_sections
|
||||
.iter()
|
||||
.filter_map(|section| {
|
||||
if section.is_valid(buffer) {
|
||||
let range = section.range.to_offset(buffer);
|
||||
let range = section.range.to_offset(buffer);
|
||||
if section.range.start.is_valid(buffer) && !range.is_empty() {
|
||||
Some(assistant_slash_command::SlashCommandOutputSection {
|
||||
range,
|
||||
icon: section.icon,
|
||||
label: section.label.clone(),
|
||||
metadata: section.metadata.clone(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
@@ -1832,7 +1825,6 @@ impl Context {
|
||||
..buffer.anchor_before(start + section.range.end),
|
||||
icon: section.icon,
|
||||
label: section.label,
|
||||
metadata: section.metadata,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
sections.sort_by(|a, b| a.range.cmp(&b.range, buffer));
|
||||
@@ -2134,7 +2126,6 @@ impl Context {
|
||||
telemetry.report_assistant_event(
|
||||
Some(this.id.0.clone()),
|
||||
AssistantKind::Panel,
|
||||
AssistantPhase::Response,
|
||||
model.telemetry_id(),
|
||||
response_latency,
|
||||
error_message,
|
||||
@@ -2986,7 +2977,6 @@ impl SavedContext {
|
||||
..buffer.anchor_before(section.range.end),
|
||||
icon: section.icon,
|
||||
label: section.label,
|
||||
metadata: section.metadata,
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
|
||||
@@ -12,7 +12,7 @@ use assistant_slash_command::{
|
||||
use collections::HashSet;
|
||||
use fs::FakeFs;
|
||||
use gpui::{AppContext, Model, SharedString, Task, TestAppContext, WeakView};
|
||||
use language::{Buffer, BufferSnapshot, LanguageRegistry, LspAdapterDelegate};
|
||||
use language::{Buffer, LanguageRegistry, LspAdapterDelegate};
|
||||
use language_model::{LanguageModelCacheConfiguration, LanguageModelRegistry, Role};
|
||||
use parking_lot::Mutex;
|
||||
use project::Project;
|
||||
@@ -1089,7 +1089,6 @@ async fn test_random_context_collaboration(cx: &mut TestAppContext, mut rng: Std
|
||||
range: section_start..section_end,
|
||||
icon: ui::IconName::Ai,
|
||||
label: "section".into(),
|
||||
metadata: None,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1426,8 +1425,6 @@ impl SlashCommand for FakeSlashCommand {
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
_arguments: &[String],
|
||||
_context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
|
||||
_context_buffer: BufferSnapshot,
|
||||
_workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
_cx: &mut WindowContext,
|
||||
|
||||
@@ -174,18 +174,6 @@ impl InlineAssistant {
|
||||
initial_prompt: Option<String>,
|
||||
cx: &mut WindowContext,
|
||||
) {
|
||||
if let Some(telemetry) = self.telemetry.as_ref() {
|
||||
if let Some(model) = LanguageModelRegistry::read_global(cx).active_model() {
|
||||
telemetry.report_assistant_event(
|
||||
None,
|
||||
telemetry_events::AssistantKind::Inline,
|
||||
telemetry_events::AssistantPhase::Invoked,
|
||||
model.telemetry_id(),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
let snapshot = editor.read(cx).buffer().read(cx).snapshot(cx);
|
||||
|
||||
let mut selections = Vec::<Selection<Point>>::new();
|
||||
@@ -720,22 +708,6 @@ impl InlineAssistant {
|
||||
}
|
||||
|
||||
pub fn finish_assist(&mut self, assist_id: InlineAssistId, undo: bool, cx: &mut WindowContext) {
|
||||
if let Some(telemetry) = self.telemetry.as_ref() {
|
||||
if let Some(model) = LanguageModelRegistry::read_global(cx).active_model() {
|
||||
telemetry.report_assistant_event(
|
||||
None,
|
||||
telemetry_events::AssistantKind::Inline,
|
||||
if undo {
|
||||
telemetry_events::AssistantPhase::Rejected
|
||||
} else {
|
||||
telemetry_events::AssistantPhase::Accepted
|
||||
},
|
||||
model.telemetry_id(),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
if let Some(assist) = self.assists.get(&assist_id) {
|
||||
let assist_group_id = assist.group_id;
|
||||
if self.assist_groups[&assist_group_id].linked {
|
||||
@@ -2586,7 +2558,6 @@ impl Codegen {
|
||||
telemetry.report_assistant_event(
|
||||
None,
|
||||
telemetry_events::AssistantKind::Inline,
|
||||
telemetry_events::AssistantPhase::Response,
|
||||
model_telemetry_id,
|
||||
response_latency,
|
||||
error_message,
|
||||
|
||||
@@ -921,8 +921,10 @@ impl PromptLibrary {
|
||||
scrollbar_width: Pixels::ZERO,
|
||||
syntax: cx.theme().syntax().clone(),
|
||||
status: cx.theme().status().clone(),
|
||||
inlay_hints_style:
|
||||
editor::make_inlay_hints_style(cx),
|
||||
inlay_hints_style: HighlightStyle {
|
||||
color: Some(cx.theme().status().hint),
|
||||
..HighlightStyle::default()
|
||||
},
|
||||
suggestions_style: HighlightStyle {
|
||||
color: Some(cx.theme().status().predictive),
|
||||
..HighlightStyle::default()
|
||||
|
||||
@@ -22,7 +22,6 @@ use workspace::Workspace;
|
||||
pub mod auto_command;
|
||||
pub mod context_server_command;
|
||||
pub mod default_command;
|
||||
pub mod delta_command;
|
||||
pub mod diagnostics_command;
|
||||
pub mod docs_command;
|
||||
pub mod fetch_command;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::create_label_for_command;
|
||||
use super::{SlashCommand, SlashCommandOutput};
|
||||
use anyhow::{anyhow, Result};
|
||||
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
|
||||
use assistant_slash_command::ArgumentCompletion;
|
||||
use feature_flags::FeatureFlag;
|
||||
use futures::StreamExt;
|
||||
use gpui::{AppContext, AsyncAppContext, Task, WeakView};
|
||||
@@ -87,8 +87,6 @@ impl SlashCommand for AutoCommand {
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
arguments: &[String],
|
||||
_context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
|
||||
_context_buffer: language::BufferSnapshot,
|
||||
workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
|
||||
@@ -9,7 +9,7 @@ use context_servers::{
|
||||
protocol::PromptInfo,
|
||||
};
|
||||
use gpui::{Task, WeakView, WindowContext};
|
||||
use language::{BufferSnapshot, CodeLabel, LspAdapterDelegate};
|
||||
use language::{CodeLabel, LspAdapterDelegate};
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::Arc;
|
||||
use text::LineEnding;
|
||||
@@ -96,6 +96,7 @@ impl SlashCommand for ContextServerSlashCommand {
|
||||
replace_previous_arguments: false,
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(completions)
|
||||
})
|
||||
} else {
|
||||
@@ -106,8 +107,6 @@ impl SlashCommand for ContextServerSlashCommand {
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
arguments: &[String],
|
||||
_context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
|
||||
_context_buffer: BufferSnapshot,
|
||||
_workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
@@ -142,7 +141,6 @@ impl SlashCommand for ContextServerSlashCommand {
|
||||
.description
|
||||
.unwrap_or(format!("Result from {}", prompt_name)),
|
||||
),
|
||||
metadata: None,
|
||||
}],
|
||||
text: prompt,
|
||||
run_commands_in_text: false,
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::prompt_library::PromptStore;
|
||||
use anyhow::{anyhow, Result};
|
||||
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
|
||||
use gpui::{Task, WeakView};
|
||||
use language::{BufferSnapshot, LspAdapterDelegate};
|
||||
use language::LspAdapterDelegate;
|
||||
use std::{
|
||||
fmt::Write,
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
@@ -43,8 +43,6 @@ impl SlashCommand for DefaultSlashCommand {
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
_arguments: &[String],
|
||||
_context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
|
||||
_context_buffer: BufferSnapshot,
|
||||
_workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
@@ -72,7 +70,6 @@ impl SlashCommand for DefaultSlashCommand {
|
||||
range: 0..text.len(),
|
||||
icon: IconName::Library,
|
||||
label: "Default".into(),
|
||||
metadata: None,
|
||||
}],
|
||||
text,
|
||||
run_commands_in_text: true,
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
use crate::slash_command::file_command::{FileCommandMetadata, FileSlashCommand};
|
||||
use anyhow::Result;
|
||||
use assistant_slash_command::{
|
||||
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
|
||||
};
|
||||
use collections::HashSet;
|
||||
use futures::future;
|
||||
use gpui::{Task, WeakView, WindowContext};
|
||||
use language::{BufferSnapshot, LspAdapterDelegate};
|
||||
use std::sync::{atomic::AtomicBool, Arc};
|
||||
use text::OffsetRangeExt;
|
||||
use workspace::Workspace;
|
||||
|
||||
pub(crate) struct DeltaSlashCommand;
|
||||
|
||||
impl SlashCommand for DeltaSlashCommand {
|
||||
fn name(&self) -> String {
|
||||
"delta".into()
|
||||
}
|
||||
|
||||
fn description(&self) -> String {
|
||||
"re-insert changed files".into()
|
||||
}
|
||||
|
||||
fn menu_text(&self) -> String {
|
||||
"Re-insert Changed Files".into()
|
||||
}
|
||||
|
||||
fn requires_argument(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn complete_argument(
|
||||
self: Arc<Self>,
|
||||
_arguments: &[String],
|
||||
_cancellation_flag: Arc<AtomicBool>,
|
||||
_workspace: Option<WeakView<Workspace>>,
|
||||
_cx: &mut WindowContext,
|
||||
) -> Task<Result<Vec<ArgumentCompletion>>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
_arguments: &[String],
|
||||
context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
|
||||
context_buffer: BufferSnapshot,
|
||||
workspace: WeakView<Workspace>,
|
||||
delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Task<Result<SlashCommandOutput>> {
|
||||
let mut paths = HashSet::default();
|
||||
let mut file_command_old_outputs = Vec::new();
|
||||
let mut file_command_new_outputs = Vec::new();
|
||||
for section in context_slash_command_output_sections.iter().rev() {
|
||||
if let Some(metadata) = section
|
||||
.metadata
|
||||
.as_ref()
|
||||
.and_then(|value| serde_json::from_value::<FileCommandMetadata>(value.clone()).ok())
|
||||
{
|
||||
if paths.insert(metadata.path.clone()) {
|
||||
file_command_old_outputs.push(
|
||||
context_buffer
|
||||
.as_rope()
|
||||
.slice(section.range.to_offset(&context_buffer)),
|
||||
);
|
||||
file_command_new_outputs.push(Arc::new(FileSlashCommand).run(
|
||||
&[metadata.path.clone()],
|
||||
context_slash_command_output_sections,
|
||||
context_buffer.clone(),
|
||||
workspace.clone(),
|
||||
delegate.clone(),
|
||||
cx,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cx.background_executor().spawn(async move {
|
||||
let mut output = SlashCommandOutput::default();
|
||||
|
||||
let file_command_new_outputs = future::join_all(file_command_new_outputs).await;
|
||||
for (old_text, new_output) in file_command_old_outputs
|
||||
.into_iter()
|
||||
.zip(file_command_new_outputs)
|
||||
{
|
||||
if let Ok(new_output) = new_output {
|
||||
if let Some(file_command_range) = new_output.sections.first() {
|
||||
let new_text = &new_output.text[file_command_range.range.clone()];
|
||||
if old_text.chars().ne(new_text.chars()) {
|
||||
output.sections.extend(new_output.sections.into_iter().map(
|
||||
|section| SlashCommandOutputSection {
|
||||
range: output.text.len() + section.range.start
|
||||
..output.text.len() + section.range.end,
|
||||
icon: section.icon,
|
||||
label: section.label,
|
||||
metadata: section.metadata,
|
||||
},
|
||||
));
|
||||
output.text.push_str(&new_output.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(output)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -9,9 +9,10 @@ use language::{
|
||||
};
|
||||
use project::{DiagnosticSummary, PathMatchCandidateSet, Project};
|
||||
use rope::Point;
|
||||
use std::fmt::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{
|
||||
fmt::Write,
|
||||
path::{Path, PathBuf},
|
||||
ops::Range,
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
};
|
||||
use ui::prelude::*;
|
||||
@@ -162,8 +163,6 @@ impl SlashCommand for DiagnosticsSlashCommand {
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
arguments: &[String],
|
||||
_context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
|
||||
_context_buffer: BufferSnapshot,
|
||||
workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
@@ -176,7 +175,68 @@ impl SlashCommand for DiagnosticsSlashCommand {
|
||||
|
||||
let task = collect_diagnostics(workspace.read(cx).project().clone(), options, cx);
|
||||
|
||||
cx.spawn(move |_| async move { task.await?.ok_or_else(|| anyhow!("No diagnostics found")) })
|
||||
cx.spawn(move |_| async move {
|
||||
let Some((text, sections)) = task.await? else {
|
||||
return Ok(SlashCommandOutput {
|
||||
sections: vec![SlashCommandOutputSection {
|
||||
range: 0..1,
|
||||
icon: IconName::Library,
|
||||
label: "No Diagnostics".into(),
|
||||
}],
|
||||
text: "\n".to_string(),
|
||||
run_commands_in_text: true,
|
||||
});
|
||||
};
|
||||
|
||||
let sections = sections
|
||||
.into_iter()
|
||||
.map(|(range, placeholder_type)| SlashCommandOutputSection {
|
||||
range,
|
||||
icon: match placeholder_type {
|
||||
PlaceholderType::Root(_, _) => IconName::Warning,
|
||||
PlaceholderType::File(_) => IconName::File,
|
||||
PlaceholderType::Diagnostic(DiagnosticType::Error, _) => IconName::XCircle,
|
||||
PlaceholderType::Diagnostic(DiagnosticType::Warning, _) => {
|
||||
IconName::Warning
|
||||
}
|
||||
},
|
||||
label: match placeholder_type {
|
||||
PlaceholderType::Root(summary, source) => {
|
||||
let mut label = String::new();
|
||||
label.push_str("Diagnostics");
|
||||
if let Some(source) = source {
|
||||
write!(label, " ({})", source).unwrap();
|
||||
}
|
||||
|
||||
if summary.error_count > 0 || summary.warning_count > 0 {
|
||||
label.push(':');
|
||||
|
||||
if summary.error_count > 0 {
|
||||
write!(label, " {} errors", summary.error_count).unwrap();
|
||||
if summary.warning_count > 0 {
|
||||
label.push_str(",");
|
||||
}
|
||||
}
|
||||
|
||||
if summary.warning_count > 0 {
|
||||
write!(label, " {} warnings", summary.warning_count).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
label.into()
|
||||
}
|
||||
PlaceholderType::File(file_path) => file_path.into(),
|
||||
PlaceholderType::Diagnostic(_, message) => message.into(),
|
||||
},
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(SlashCommandOutput {
|
||||
text,
|
||||
sections,
|
||||
run_commands_in_text: false,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +277,7 @@ fn collect_diagnostics(
|
||||
project: Model<Project>,
|
||||
options: Options,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<Option<SlashCommandOutput>>> {
|
||||
) -> Task<Result<Option<(String, Vec<(Range<usize>, PlaceholderType)>)>>> {
|
||||
let error_source = if let Some(path_matcher) = &options.path_matcher {
|
||||
debug_assert_eq!(path_matcher.sources().len(), 1);
|
||||
Some(path_matcher.sources().first().cloned().unwrap_or_default())
|
||||
@@ -258,13 +318,13 @@ fn collect_diagnostics(
|
||||
.collect();
|
||||
|
||||
cx.spawn(|mut cx| async move {
|
||||
let mut output = SlashCommandOutput::default();
|
||||
|
||||
let mut text = String::new();
|
||||
if let Some(error_source) = error_source.as_ref() {
|
||||
writeln!(output.text, "diagnostics: {}", error_source).unwrap();
|
||||
writeln!(text, "diagnostics: {}", error_source).unwrap();
|
||||
} else {
|
||||
writeln!(output.text, "diagnostics").unwrap();
|
||||
writeln!(text, "diagnostics").unwrap();
|
||||
}
|
||||
let mut sections: Vec<(Range<usize>, PlaceholderType)> = Vec::new();
|
||||
|
||||
let mut project_summary = DiagnosticSummary::default();
|
||||
for (project_path, path, summary) in diagnostic_summaries {
|
||||
@@ -281,10 +341,10 @@ fn collect_diagnostics(
|
||||
continue;
|
||||
}
|
||||
|
||||
let last_end = output.text.len();
|
||||
let last_end = text.len();
|
||||
let file_path = path.to_string_lossy().to_string();
|
||||
if !glob_is_exact_file_match {
|
||||
writeln!(&mut output.text, "{file_path}").unwrap();
|
||||
writeln!(&mut text, "{file_path}").unwrap();
|
||||
}
|
||||
|
||||
if let Some(buffer) = project_handle
|
||||
@@ -292,73 +352,75 @@ fn collect_diagnostics(
|
||||
.await
|
||||
.log_err()
|
||||
{
|
||||
let snapshot = cx.read_model(&buffer, |buffer, _| buffer.snapshot())?;
|
||||
collect_buffer_diagnostics(&mut output, &snapshot, options.include_warnings);
|
||||
collect_buffer_diagnostics(
|
||||
&mut text,
|
||||
&mut sections,
|
||||
cx.read_model(&buffer, |buffer, _| buffer.snapshot())?,
|
||||
options.include_warnings,
|
||||
);
|
||||
}
|
||||
|
||||
if !glob_is_exact_file_match {
|
||||
output.sections.push(SlashCommandOutputSection {
|
||||
range: last_end..output.text.len().saturating_sub(1),
|
||||
icon: IconName::File,
|
||||
label: file_path.into(),
|
||||
metadata: None,
|
||||
});
|
||||
sections.push((
|
||||
last_end..text.len().saturating_sub(1),
|
||||
PlaceholderType::File(file_path),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// No diagnostics found
|
||||
if output.sections.is_empty() {
|
||||
if sections.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let mut label = String::new();
|
||||
label.push_str("Diagnostics");
|
||||
if let Some(source) = error_source {
|
||||
write!(label, " ({})", source).unwrap();
|
||||
}
|
||||
|
||||
if project_summary.error_count > 0 || project_summary.warning_count > 0 {
|
||||
label.push(':');
|
||||
|
||||
if project_summary.error_count > 0 {
|
||||
write!(label, " {} errors", project_summary.error_count).unwrap();
|
||||
if project_summary.warning_count > 0 {
|
||||
label.push_str(",");
|
||||
}
|
||||
}
|
||||
|
||||
if project_summary.warning_count > 0 {
|
||||
write!(label, " {} warnings", project_summary.warning_count).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
output.sections.insert(
|
||||
0,
|
||||
SlashCommandOutputSection {
|
||||
range: 0..output.text.len(),
|
||||
icon: IconName::Warning,
|
||||
label: label.into(),
|
||||
metadata: None,
|
||||
},
|
||||
);
|
||||
|
||||
Ok(Some(output))
|
||||
sections.push((
|
||||
0..text.len(),
|
||||
PlaceholderType::Root(project_summary, error_source),
|
||||
));
|
||||
Ok(Some((text, sections)))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn collect_buffer_diagnostics(
|
||||
output: &mut SlashCommandOutput,
|
||||
pub fn buffer_has_error_diagnostics(snapshot: &BufferSnapshot) -> bool {
|
||||
for (_, group) in snapshot.diagnostic_groups(None) {
|
||||
let entry = &group.entries[group.primary_ix];
|
||||
if entry.diagnostic.severity == DiagnosticSeverity::ERROR {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn write_single_file_diagnostics(
|
||||
output: &mut String,
|
||||
path: Option<&Path>,
|
||||
snapshot: &BufferSnapshot,
|
||||
) -> bool {
|
||||
if let Some(path) = path {
|
||||
if buffer_has_error_diagnostics(&snapshot) {
|
||||
output.push_str("/diagnostics ");
|
||||
output.push_str(&path.to_string_lossy());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn collect_buffer_diagnostics(
|
||||
text: &mut String,
|
||||
sections: &mut Vec<(Range<usize>, PlaceholderType)>,
|
||||
snapshot: BufferSnapshot,
|
||||
include_warnings: bool,
|
||||
) {
|
||||
for (_, group) in snapshot.diagnostic_groups(None) {
|
||||
let entry = &group.entries[group.primary_ix];
|
||||
collect_diagnostic(output, entry, &snapshot, include_warnings)
|
||||
collect_diagnostic(text, sections, entry, &snapshot, include_warnings)
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_diagnostic(
|
||||
output: &mut SlashCommandOutput,
|
||||
text: &mut String,
|
||||
sections: &mut Vec<(Range<usize>, PlaceholderType)>,
|
||||
entry: &DiagnosticEntry<Anchor>,
|
||||
snapshot: &BufferSnapshot,
|
||||
include_warnings: bool,
|
||||
@@ -366,17 +428,17 @@ fn collect_diagnostic(
|
||||
const EXCERPT_EXPANSION_SIZE: u32 = 2;
|
||||
const MAX_MESSAGE_LENGTH: usize = 2000;
|
||||
|
||||
let (ty, icon) = match entry.diagnostic.severity {
|
||||
let ty = match entry.diagnostic.severity {
|
||||
DiagnosticSeverity::WARNING => {
|
||||
if !include_warnings {
|
||||
return;
|
||||
}
|
||||
("warning", IconName::Warning)
|
||||
DiagnosticType::Warning
|
||||
}
|
||||
DiagnosticSeverity::ERROR => ("error", IconName::XCircle),
|
||||
DiagnosticSeverity::ERROR => DiagnosticType::Error,
|
||||
_ => return,
|
||||
};
|
||||
let prev_len = output.text.len();
|
||||
let prev_len = text.len();
|
||||
|
||||
let range = entry.range.to_point(snapshot);
|
||||
let diagnostic_row_number = range.start.row + 1;
|
||||
@@ -386,11 +448,11 @@ fn collect_diagnostic(
|
||||
let excerpt_range =
|
||||
Point::new(start_row, 0).to_offset(&snapshot)..Point::new(end_row, 0).to_offset(&snapshot);
|
||||
|
||||
output.text.push_str("```");
|
||||
text.push_str("```");
|
||||
if let Some(language_name) = snapshot.language().map(|l| l.code_fence_block_name()) {
|
||||
output.text.push_str(&language_name);
|
||||
text.push_str(&language_name);
|
||||
}
|
||||
output.text.push('\n');
|
||||
text.push('\n');
|
||||
|
||||
let mut buffer_text = String::new();
|
||||
for chunk in snapshot.text_for_range(excerpt_range) {
|
||||
@@ -399,26 +461,46 @@ fn collect_diagnostic(
|
||||
|
||||
for (i, line) in buffer_text.lines().enumerate() {
|
||||
let line_number = start_row + i as u32 + 1;
|
||||
writeln!(output.text, "{}", line).unwrap();
|
||||
writeln!(text, "{}", line).unwrap();
|
||||
|
||||
if line_number == diagnostic_row_number {
|
||||
output.text.push_str("//");
|
||||
let prev_len = output.text.len();
|
||||
write!(output.text, " {}: ", ty).unwrap();
|
||||
let padding = output.text.len() - prev_len;
|
||||
text.push_str("//");
|
||||
let prev_len = text.len();
|
||||
write!(text, " {}: ", ty.as_str()).unwrap();
|
||||
let padding = text.len() - prev_len;
|
||||
|
||||
let message = util::truncate(&entry.diagnostic.message, MAX_MESSAGE_LENGTH)
|
||||
.replace('\n', format!("\n//{:padding$}", "").as_str());
|
||||
|
||||
writeln!(output.text, "{message}").unwrap();
|
||||
writeln!(text, "{message}").unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
writeln!(output.text, "```").unwrap();
|
||||
output.sections.push(SlashCommandOutputSection {
|
||||
range: prev_len..output.text.len().saturating_sub(1),
|
||||
icon,
|
||||
label: entry.diagnostic.message.clone().into(),
|
||||
metadata: None,
|
||||
});
|
||||
writeln!(text, "```").unwrap();
|
||||
sections.push((
|
||||
prev_len..text.len().saturating_sub(1),
|
||||
PlaceholderType::Diagnostic(ty, entry.diagnostic.message.clone()),
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum PlaceholderType {
|
||||
Root(DiagnosticSummary, Option<String>),
|
||||
File(String),
|
||||
Diagnostic(DiagnosticType, String),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum DiagnosticType {
|
||||
Warning,
|
||||
Error,
|
||||
}
|
||||
|
||||
impl DiagnosticType {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
DiagnosticType::Warning => "warning",
|
||||
DiagnosticType::Error => "error",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ use indexed_docs::{
|
||||
DocsDotRsProvider, IndexedDocsRegistry, IndexedDocsStore, LocalRustdocProvider, PackageName,
|
||||
ProviderId,
|
||||
};
|
||||
use language::{BufferSnapshot, LspAdapterDelegate};
|
||||
use language::LspAdapterDelegate;
|
||||
use project::{Project, ProjectPath};
|
||||
use ui::prelude::*;
|
||||
use util::{maybe, ResultExt};
|
||||
@@ -269,8 +269,6 @@ impl SlashCommand for DocsSlashCommand {
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
arguments: &[String],
|
||||
_context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
|
||||
_context_buffer: BufferSnapshot,
|
||||
_workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
@@ -351,7 +349,6 @@ impl SlashCommand for DocsSlashCommand {
|
||||
range,
|
||||
icon: IconName::FileDoc,
|
||||
label: format!("docs ({provider}): {key}",).into(),
|
||||
metadata: None,
|
||||
})
|
||||
.collect(),
|
||||
run_commands_in_text: false,
|
||||
|
||||
@@ -11,7 +11,7 @@ use futures::AsyncReadExt;
|
||||
use gpui::{Task, WeakView};
|
||||
use html_to_markdown::{convert_html_to_markdown, markdown, TagHandler};
|
||||
use http_client::{AsyncBody, HttpClient, HttpClientWithUrl};
|
||||
use language::{BufferSnapshot, LspAdapterDelegate};
|
||||
use language::LspAdapterDelegate;
|
||||
use ui::prelude::*;
|
||||
use workspace::Workspace;
|
||||
|
||||
@@ -128,8 +128,6 @@ impl SlashCommand for FetchSlashCommand {
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
arguments: &[String],
|
||||
_context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
|
||||
_context_buffer: BufferSnapshot,
|
||||
workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
@@ -163,7 +161,6 @@ impl SlashCommand for FetchSlashCommand {
|
||||
range,
|
||||
icon: IconName::AtSign,
|
||||
label: format!("fetch {}", url).into(),
|
||||
metadata: None,
|
||||
}],
|
||||
run_commands_in_text: false,
|
||||
})
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
use super::{diagnostics_command::collect_buffer_diagnostics, SlashCommand, SlashCommandOutput};
|
||||
use super::{diagnostics_command::write_single_file_diagnostics, SlashCommand, SlashCommandOutput};
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use assistant_slash_command::{AfterCompletion, ArgumentCompletion, SlashCommandOutputSection};
|
||||
use fuzzy::PathMatch;
|
||||
use gpui::{AppContext, Model, Task, View, WeakView};
|
||||
use language::{BufferSnapshot, CodeLabel, HighlightId, LineEnding, LspAdapterDelegate};
|
||||
use project::{PathMatchCandidateSet, Project};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
fmt::Write,
|
||||
ops::{Range, RangeInclusive},
|
||||
ops::Range,
|
||||
path::{Path, PathBuf},
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
};
|
||||
@@ -176,8 +175,6 @@ impl SlashCommand for FileSlashCommand {
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
arguments: &[String],
|
||||
_context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
|
||||
_context_buffer: BufferSnapshot,
|
||||
workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
@@ -190,15 +187,54 @@ impl SlashCommand for FileSlashCommand {
|
||||
return Task::ready(Err(anyhow!("missing path")));
|
||||
};
|
||||
|
||||
collect_files(workspace.read(cx).project().clone(), arguments, cx)
|
||||
let task = collect_files(workspace.read(cx).project().clone(), arguments, cx);
|
||||
|
||||
cx.foreground_executor().spawn(async move {
|
||||
let output = task.await?;
|
||||
Ok(SlashCommandOutput {
|
||||
text: output.completion_text,
|
||||
sections: output
|
||||
.files
|
||||
.into_iter()
|
||||
.map(|file| {
|
||||
build_entry_output_section(
|
||||
file.range_in_text,
|
||||
Some(&file.path),
|
||||
file.entry_type == EntryType::Directory,
|
||||
None,
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
run_commands_in_text: true,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
enum EntryType {
|
||||
File,
|
||||
Directory,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
struct FileCommandOutput {
|
||||
completion_text: String,
|
||||
files: Vec<OutputFile>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
struct OutputFile {
|
||||
range_in_text: Range<usize>,
|
||||
path: PathBuf,
|
||||
entry_type: EntryType,
|
||||
}
|
||||
|
||||
fn collect_files(
|
||||
project: Model<Project>,
|
||||
glob_inputs: &[String],
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<SlashCommandOutput>> {
|
||||
) -> Task<Result<FileCommandOutput>> {
|
||||
let Ok(matchers) = glob_inputs
|
||||
.into_iter()
|
||||
.map(|glob_input| {
|
||||
@@ -218,7 +254,8 @@ fn collect_files(
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
cx.spawn(|mut cx| async move {
|
||||
let mut output = SlashCommandOutput::default();
|
||||
let mut text = String::new();
|
||||
let mut ranges = Vec::new();
|
||||
for snapshot in snapshots {
|
||||
let worktree_id = snapshot.id();
|
||||
let mut directory_stack: Vec<(Arc<Path>, String, usize)> = Vec::new();
|
||||
@@ -242,12 +279,11 @@ fn collect_files(
|
||||
break;
|
||||
}
|
||||
let (_, entry_name, start) = directory_stack.pop().unwrap();
|
||||
output.sections.push(build_entry_output_section(
|
||||
start..output.text.len().saturating_sub(1),
|
||||
Some(&PathBuf::from(entry_name)),
|
||||
true,
|
||||
None,
|
||||
));
|
||||
ranges.push(OutputFile {
|
||||
range_in_text: start..text.len().saturating_sub(1),
|
||||
path: PathBuf::from(entry_name),
|
||||
entry_type: EntryType::Directory,
|
||||
});
|
||||
}
|
||||
|
||||
let filename = entry
|
||||
@@ -279,23 +315,21 @@ fn collect_files(
|
||||
continue;
|
||||
}
|
||||
let prefix_paths = folded_directory_names_stack.drain(..).as_slice().join("/");
|
||||
let entry_start = output.text.len();
|
||||
let entry_start = text.len();
|
||||
if prefix_paths.is_empty() {
|
||||
if is_top_level_directory {
|
||||
output
|
||||
.text
|
||||
.push_str(&path_including_worktree_name.to_string_lossy());
|
||||
text.push_str(&path_including_worktree_name.to_string_lossy());
|
||||
is_top_level_directory = false;
|
||||
} else {
|
||||
output.text.push_str(&filename);
|
||||
text.push_str(&filename);
|
||||
}
|
||||
directory_stack.push((entry.path.clone(), filename, entry_start));
|
||||
} else {
|
||||
let entry_name = format!("{}/{}", prefix_paths, &filename);
|
||||
output.text.push_str(&entry_name);
|
||||
text.push_str(&entry_name);
|
||||
directory_stack.push((entry.path.clone(), entry_name, entry_start));
|
||||
}
|
||||
output.text.push('\n');
|
||||
text.push('\n');
|
||||
} else if entry.is_file() {
|
||||
let Some(open_buffer_task) = project_handle
|
||||
.update(&mut cx, |project, cx| {
|
||||
@@ -306,13 +340,28 @@ fn collect_files(
|
||||
continue;
|
||||
};
|
||||
if let Some(buffer) = open_buffer_task.await.log_err() {
|
||||
let snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot())?;
|
||||
append_buffer_to_output(
|
||||
&snapshot,
|
||||
let buffer_snapshot =
|
||||
cx.read_model(&buffer, |buffer, _| buffer.snapshot())?;
|
||||
let prev_len = text.len();
|
||||
collect_file_content(
|
||||
&mut text,
|
||||
&buffer_snapshot,
|
||||
path_including_worktree_name.to_string_lossy().to_string(),
|
||||
);
|
||||
text.push('\n');
|
||||
if !write_single_file_diagnostics(
|
||||
&mut text,
|
||||
Some(&path_including_worktree_name),
|
||||
&mut output,
|
||||
)
|
||||
.log_err();
|
||||
&buffer_snapshot,
|
||||
) {
|
||||
text.pop();
|
||||
}
|
||||
ranges.push(OutputFile {
|
||||
range_in_text: prev_len..text.len(),
|
||||
path: path_including_worktree_name,
|
||||
entry_type: EntryType::File,
|
||||
});
|
||||
text.push('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -322,30 +371,43 @@ fn collect_files(
|
||||
let mut root_path = PathBuf::new();
|
||||
root_path.push(snapshot.root_name());
|
||||
root_path.push(&dir);
|
||||
output.sections.push(build_entry_output_section(
|
||||
start..output.text.len(),
|
||||
Some(&root_path),
|
||||
true,
|
||||
None,
|
||||
));
|
||||
ranges.push(OutputFile {
|
||||
range_in_text: start..text.len(),
|
||||
path: root_path,
|
||||
entry_type: EntryType::Directory,
|
||||
});
|
||||
} else {
|
||||
output.sections.push(build_entry_output_section(
|
||||
start..output.text.len(),
|
||||
Some(&PathBuf::from(entry.as_str())),
|
||||
true,
|
||||
None,
|
||||
));
|
||||
ranges.push(OutputFile {
|
||||
range_in_text: start..text.len(),
|
||||
path: PathBuf::from(entry.as_str()),
|
||||
entry_type: EntryType::Directory,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(output)
|
||||
Ok(FileCommandOutput {
|
||||
completion_text: text,
|
||||
files: ranges,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn codeblock_fence_for_path(
|
||||
path: Option<&Path>,
|
||||
row_range: Option<RangeInclusive<u32>>,
|
||||
) -> String {
|
||||
fn collect_file_content(buffer: &mut String, snapshot: &BufferSnapshot, filename: String) {
|
||||
let mut content = snapshot.text();
|
||||
LineEnding::normalize(&mut content);
|
||||
buffer.reserve(filename.len() + content.len() + 9);
|
||||
buffer.push_str(&codeblock_fence_for_path(
|
||||
Some(&PathBuf::from(filename)),
|
||||
None,
|
||||
));
|
||||
buffer.push_str(&content);
|
||||
if !buffer.ends_with('\n') {
|
||||
buffer.push('\n');
|
||||
}
|
||||
buffer.push_str("```");
|
||||
}
|
||||
|
||||
pub fn codeblock_fence_for_path(path: Option<&Path>, row_range: Option<Range<u32>>) -> String {
|
||||
let mut text = String::new();
|
||||
write!(text, "```").unwrap();
|
||||
|
||||
@@ -360,18 +422,13 @@ pub fn codeblock_fence_for_path(
|
||||
}
|
||||
|
||||
if let Some(row_range) = row_range {
|
||||
write!(text, ":{}-{}", row_range.start() + 1, row_range.end() + 1).unwrap();
|
||||
write!(text, ":{}-{}", row_range.start + 1, row_range.end + 1).unwrap();
|
||||
}
|
||||
|
||||
text.push('\n');
|
||||
text
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct FileCommandMetadata {
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
pub fn build_entry_output_section(
|
||||
range: Range<usize>,
|
||||
path: Option<&Path>,
|
||||
@@ -397,16 +454,6 @@ pub fn build_entry_output_section(
|
||||
range,
|
||||
icon,
|
||||
label: label.into(),
|
||||
metadata: if is_directory {
|
||||
None
|
||||
} else {
|
||||
path.and_then(|path| {
|
||||
serde_json::to_value(FileCommandMetadata {
|
||||
path: path.to_string_lossy().to_string(),
|
||||
})
|
||||
.ok()
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,36 +539,6 @@ mod custom_path_matcher {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn append_buffer_to_output(
|
||||
buffer: &BufferSnapshot,
|
||||
path: Option<&Path>,
|
||||
output: &mut SlashCommandOutput,
|
||||
) -> Result<()> {
|
||||
let prev_len = output.text.len();
|
||||
|
||||
let mut content = buffer.text();
|
||||
LineEnding::normalize(&mut content);
|
||||
output.text.push_str(&codeblock_fence_for_path(path, None));
|
||||
output.text.push_str(&content);
|
||||
if !output.text.ends_with('\n') {
|
||||
output.text.push('\n');
|
||||
}
|
||||
output.text.push_str("```");
|
||||
output.text.push('\n');
|
||||
|
||||
let section_ix = output.sections.len();
|
||||
collect_buffer_diagnostics(output, buffer, false);
|
||||
|
||||
output.sections.insert(
|
||||
section_ix,
|
||||
build_entry_output_section(prev_len..output.text.len(), path, false, None),
|
||||
);
|
||||
|
||||
output.text.push('\n');
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use fs::FakeFs;
|
||||
@@ -574,9 +591,9 @@ mod test {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(result_1.text.starts_with("root/dir"));
|
||||
assert!(result_1.completion_text.starts_with("root/dir"));
|
||||
// 4 files + 2 directories
|
||||
assert_eq!(result_1.sections.len(), 6);
|
||||
assert_eq!(6, result_1.files.len());
|
||||
|
||||
let result_2 = cx
|
||||
.update(|cx| collect_files(project.clone(), &["root/dir/".to_string()], cx))
|
||||
@@ -590,9 +607,9 @@ mod test {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(result.text.starts_with("root/dir"));
|
||||
assert!(result.completion_text.starts_with("root/dir"));
|
||||
// 5 files + 2 directories
|
||||
assert_eq!(result.sections.len(), 7);
|
||||
assert_eq!(7, result.files.len());
|
||||
|
||||
// Ensure that the project lasts until after the last await
|
||||
drop(project);
|
||||
@@ -637,27 +654,36 @@ mod test {
|
||||
.unwrap();
|
||||
|
||||
// Sanity check
|
||||
assert!(result.text.starts_with("zed/assets/themes\n"));
|
||||
assert_eq!(result.sections.len(), 7);
|
||||
assert!(result.completion_text.starts_with("zed/assets/themes\n"));
|
||||
assert_eq!(7, result.files.len());
|
||||
|
||||
// Ensure that full file paths are included in the real output
|
||||
assert!(result.text.contains("zed/assets/themes/andromeda/LICENSE"));
|
||||
assert!(result.text.contains("zed/assets/themes/ayu/LICENSE"));
|
||||
assert!(result.text.contains("zed/assets/themes/summercamp/LICENSE"));
|
||||
assert!(result
|
||||
.completion_text
|
||||
.contains("zed/assets/themes/andromeda/LICENSE"));
|
||||
assert!(result
|
||||
.completion_text
|
||||
.contains("zed/assets/themes/ayu/LICENSE"));
|
||||
assert!(result
|
||||
.completion_text
|
||||
.contains("zed/assets/themes/summercamp/LICENSE"));
|
||||
|
||||
assert_eq!(result.sections[5].label, "summercamp");
|
||||
assert_eq!("summercamp", result.files[5].path.to_string_lossy());
|
||||
|
||||
// Ensure that things are in descending order, with properly relativized paths
|
||||
assert_eq!(
|
||||
result.sections[0].label,
|
||||
"zed/assets/themes/andromeda/LICENSE"
|
||||
"zed/assets/themes/andromeda/LICENSE",
|
||||
result.files[0].path.to_string_lossy()
|
||||
);
|
||||
assert_eq!(result.sections[1].label, "andromeda");
|
||||
assert_eq!(result.sections[2].label, "zed/assets/themes/ayu/LICENSE");
|
||||
assert_eq!(result.sections[3].label, "ayu");
|
||||
assert_eq!("andromeda", result.files[1].path.to_string_lossy());
|
||||
assert_eq!(
|
||||
result.sections[4].label,
|
||||
"zed/assets/themes/summercamp/LICENSE"
|
||||
"zed/assets/themes/ayu/LICENSE",
|
||||
result.files[2].path.to_string_lossy()
|
||||
);
|
||||
assert_eq!("ayu", result.files[3].path.to_string_lossy());
|
||||
assert_eq!(
|
||||
"zed/assets/themes/summercamp/LICENSE",
|
||||
result.files[4].path.to_string_lossy()
|
||||
);
|
||||
|
||||
// Ensure that the project lasts until after the last await
|
||||
@@ -697,24 +723,27 @@ mod test {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(result.text.starts_with("zed/assets/themes\n"));
|
||||
assert_eq!(result.sections[0].label, "zed/assets/themes/LICENSE");
|
||||
assert!(result.completion_text.starts_with("zed/assets/themes\n"));
|
||||
assert_eq!(
|
||||
result.sections[1].label,
|
||||
"zed/assets/themes/summercamp/LICENSE"
|
||||
"zed/assets/themes/LICENSE",
|
||||
result.files[0].path.to_string_lossy()
|
||||
);
|
||||
assert_eq!(
|
||||
result.sections[2].label,
|
||||
"zed/assets/themes/summercamp/subdir/LICENSE"
|
||||
"zed/assets/themes/summercamp/LICENSE",
|
||||
result.files[1].path.to_string_lossy()
|
||||
);
|
||||
assert_eq!(
|
||||
result.sections[3].label,
|
||||
"zed/assets/themes/summercamp/subdir/subsubdir/LICENSE"
|
||||
"zed/assets/themes/summercamp/subdir/LICENSE",
|
||||
result.files[2].path.to_string_lossy()
|
||||
);
|
||||
assert_eq!(result.sections[4].label, "subsubdir");
|
||||
assert_eq!(result.sections[5].label, "subdir");
|
||||
assert_eq!(result.sections[6].label, "summercamp");
|
||||
assert_eq!(result.sections[7].label, "zed/assets/themes");
|
||||
assert_eq!(
|
||||
"zed/assets/themes/summercamp/subdir/subsubdir/LICENSE",
|
||||
result.files[3].path.to_string_lossy()
|
||||
);
|
||||
assert_eq!("subsubdir", result.files[4].path.to_string_lossy());
|
||||
assert_eq!("subdir", result.files[5].path.to_string_lossy());
|
||||
assert_eq!("summercamp", result.files[6].path.to_string_lossy());
|
||||
assert_eq!("zed/assets/themes", result.files[7].path.to_string_lossy());
|
||||
|
||||
// Ensure that the project lasts until after the last await
|
||||
drop(project);
|
||||
|
||||
@@ -7,7 +7,7 @@ use assistant_slash_command::{
|
||||
};
|
||||
use chrono::Local;
|
||||
use gpui::{Task, WeakView};
|
||||
use language::{BufferSnapshot, LspAdapterDelegate};
|
||||
use language::LspAdapterDelegate;
|
||||
use ui::prelude::*;
|
||||
use workspace::Workspace;
|
||||
|
||||
@@ -43,8 +43,6 @@ impl SlashCommand for NowSlashCommand {
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
_arguments: &[String],
|
||||
_context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
|
||||
_context_buffer: BufferSnapshot,
|
||||
_workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
_cx: &mut WindowContext,
|
||||
@@ -59,7 +57,6 @@ impl SlashCommand for NowSlashCommand {
|
||||
range,
|
||||
icon: IconName::CountdownTimer,
|
||||
label: now.to_rfc2822().into(),
|
||||
metadata: None,
|
||||
}],
|
||||
run_commands_in_text: false,
|
||||
}))
|
||||
|
||||
@@ -3,7 +3,7 @@ use anyhow::{anyhow, Context, Result};
|
||||
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
|
||||
use fs::Fs;
|
||||
use gpui::{AppContext, Model, Task, WeakView};
|
||||
use language::{BufferSnapshot, LspAdapterDelegate};
|
||||
use language::LspAdapterDelegate;
|
||||
use project::{Project, ProjectPath};
|
||||
use std::{
|
||||
fmt::Write,
|
||||
@@ -118,8 +118,6 @@ impl SlashCommand for ProjectSlashCommand {
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
_arguments: &[String],
|
||||
_context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
|
||||
_context_buffer: BufferSnapshot,
|
||||
workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
@@ -142,7 +140,6 @@ impl SlashCommand for ProjectSlashCommand {
|
||||
range,
|
||||
icon: IconName::FileTree,
|
||||
label: "Project".into(),
|
||||
metadata: None,
|
||||
}],
|
||||
run_commands_in_text: false,
|
||||
})
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::prompt_library::PromptStore;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
|
||||
use gpui::{Task, WeakView};
|
||||
use language::{BufferSnapshot, LspAdapterDelegate};
|
||||
use language::LspAdapterDelegate;
|
||||
use std::sync::{atomic::AtomicBool, Arc};
|
||||
use ui::prelude::*;
|
||||
use workspace::Workspace;
|
||||
@@ -56,8 +56,6 @@ impl SlashCommand for PromptSlashCommand {
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
arguments: &[String],
|
||||
_context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
|
||||
_context_buffer: BufferSnapshot,
|
||||
_workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
@@ -97,7 +95,6 @@ impl SlashCommand for PromptSlashCommand {
|
||||
range,
|
||||
icon: IconName::Library,
|
||||
label: title,
|
||||
metadata: None,
|
||||
}],
|
||||
run_commands_in_text: true,
|
||||
})
|
||||
|
||||
@@ -8,12 +8,14 @@ use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
|
||||
use feature_flags::FeatureFlag;
|
||||
use gpui::{AppContext, Task, WeakView};
|
||||
use language::{CodeLabel, LineEnding, LspAdapterDelegate};
|
||||
use semantic_index::{LoadedSearchResult, SemanticDb};
|
||||
use semantic_index::SemanticDb;
|
||||
use std::{
|
||||
fmt::Write,
|
||||
path::PathBuf,
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
};
|
||||
use ui::{prelude::*, IconName};
|
||||
use util::ResultExt;
|
||||
use workspace::Workspace;
|
||||
|
||||
pub(crate) struct SearchSlashCommandFeatureFlag;
|
||||
@@ -58,8 +60,6 @@ impl SlashCommand for SearchSlashCommand {
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
arguments: &[String],
|
||||
_context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
|
||||
_context_buffer: language::BufferSnapshot,
|
||||
workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
@@ -105,28 +105,52 @@ impl SlashCommand for SearchSlashCommand {
|
||||
})?
|
||||
.await?;
|
||||
|
||||
let loaded_results = SemanticDb::load_results(results, &fs, &cx).await?;
|
||||
let mut loaded_results = Vec::new();
|
||||
for result in results {
|
||||
let (full_path, file_content) =
|
||||
result.worktree.read_with(&cx, |worktree, _cx| {
|
||||
let entry_abs_path = worktree.abs_path().join(&result.path);
|
||||
let mut entry_full_path = PathBuf::from(worktree.root_name());
|
||||
entry_full_path.push(&result.path);
|
||||
let file_content = async {
|
||||
let entry_abs_path = entry_abs_path;
|
||||
fs.load(&entry_abs_path).await
|
||||
};
|
||||
(entry_full_path, file_content)
|
||||
})?;
|
||||
if let Some(file_content) = file_content.await.log_err() {
|
||||
loaded_results.push((result, full_path, file_content));
|
||||
}
|
||||
}
|
||||
|
||||
let output = cx
|
||||
.background_executor()
|
||||
.spawn(async move {
|
||||
let mut text = format!("Search results for {query}:\n");
|
||||
let mut sections = Vec::new();
|
||||
for LoadedSearchResult {
|
||||
path,
|
||||
range,
|
||||
full_path,
|
||||
file_content,
|
||||
row_range,
|
||||
} in loaded_results
|
||||
{
|
||||
for (result, full_path, file_content) in loaded_results {
|
||||
let range_start = result.range.start.min(file_content.len());
|
||||
let range_end = result.range.end.min(file_content.len());
|
||||
|
||||
let start_row = file_content[0..range_start].matches('\n').count() as u32;
|
||||
let end_row = file_content[0..range_end].matches('\n').count() as u32;
|
||||
let start_line_byte_offset = file_content[0..range_start]
|
||||
.rfind('\n')
|
||||
.map(|pos| pos + 1)
|
||||
.unwrap_or_default();
|
||||
let end_line_byte_offset = file_content[range_end..]
|
||||
.find('\n')
|
||||
.map(|pos| range_end + pos)
|
||||
.unwrap_or_else(|| file_content.len());
|
||||
|
||||
let section_start_ix = text.len();
|
||||
text.push_str(&codeblock_fence_for_path(
|
||||
Some(&path),
|
||||
Some(row_range.clone()),
|
||||
Some(&result.path),
|
||||
Some(start_row..end_row),
|
||||
));
|
||||
|
||||
let mut excerpt = file_content[range].to_string();
|
||||
let mut excerpt =
|
||||
file_content[start_line_byte_offset..end_line_byte_offset].to_string();
|
||||
LineEnding::normalize(&mut excerpt);
|
||||
text.push_str(&excerpt);
|
||||
writeln!(text, "\n```\n").unwrap();
|
||||
@@ -135,7 +159,7 @@ impl SlashCommand for SearchSlashCommand {
|
||||
section_start_ix..section_end_ix,
|
||||
Some(&full_path),
|
||||
false,
|
||||
Some(row_range.start() + 1..row_range.end() + 1),
|
||||
Some(start_row + 1..end_row + 1),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -144,7 +168,6 @@ impl SlashCommand for SearchSlashCommand {
|
||||
range: 0..text.len(),
|
||||
icon: IconName::MagnifyingGlass,
|
||||
label: query,
|
||||
metadata: None,
|
||||
});
|
||||
|
||||
SlashCommandOutput {
|
||||
|
||||
@@ -3,7 +3,7 @@ use anyhow::{anyhow, Context as _, Result};
|
||||
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
|
||||
use editor::Editor;
|
||||
use gpui::{Task, WeakView};
|
||||
use language::{BufferSnapshot, LspAdapterDelegate};
|
||||
use language::LspAdapterDelegate;
|
||||
use std::sync::Arc;
|
||||
use std::{path::Path, sync::atomic::AtomicBool};
|
||||
use ui::{IconName, WindowContext};
|
||||
@@ -41,8 +41,6 @@ impl SlashCommand for OutlineSlashCommand {
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
_arguments: &[String],
|
||||
_context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
|
||||
_context_buffer: BufferSnapshot,
|
||||
workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
@@ -79,7 +77,6 @@ impl SlashCommand for OutlineSlashCommand {
|
||||
range: 0..outline_text.len(),
|
||||
icon: IconName::ListTree,
|
||||
label: path.to_string_lossy().to_string().into(),
|
||||
metadata: None,
|
||||
}],
|
||||
text: outline_text,
|
||||
run_commands_in_text: false,
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
use super::{file_command::append_buffer_to_output, SlashCommand, SlashCommandOutput};
|
||||
use super::{
|
||||
diagnostics_command::write_single_file_diagnostics,
|
||||
file_command::{build_entry_output_section, codeblock_fence_for_path},
|
||||
SlashCommand, SlashCommandOutput,
|
||||
};
|
||||
use anyhow::{Context, Result};
|
||||
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
|
||||
use assistant_slash_command::ArgumentCompletion;
|
||||
use collections::{HashMap, HashSet};
|
||||
use editor::Editor;
|
||||
use futures::future::join_all;
|
||||
use gpui::{Entity, Task, WeakView};
|
||||
use language::{BufferSnapshot, CodeLabel, HighlightId, LspAdapterDelegate};
|
||||
use std::{
|
||||
fmt::Write,
|
||||
path::PathBuf,
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
};
|
||||
use ui::{ActiveTheme, WindowContext};
|
||||
use util::ResultExt;
|
||||
use workspace::Workspace;
|
||||
|
||||
pub(crate) struct TabSlashCommand;
|
||||
@@ -127,8 +131,6 @@ impl SlashCommand for TabSlashCommand {
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
arguments: &[String],
|
||||
_context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
|
||||
_context_buffer: BufferSnapshot,
|
||||
workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
@@ -142,11 +144,40 @@ impl SlashCommand for TabSlashCommand {
|
||||
);
|
||||
|
||||
cx.background_executor().spawn(async move {
|
||||
let mut output = SlashCommandOutput::default();
|
||||
let mut sections = Vec::new();
|
||||
let mut text = String::new();
|
||||
let mut has_diagnostics = false;
|
||||
for (full_path, buffer, _) in tab_items_search.await? {
|
||||
append_buffer_to_output(&buffer, full_path.as_deref(), &mut output).log_err();
|
||||
let section_start_ix = text.len();
|
||||
text.push_str(&codeblock_fence_for_path(full_path.as_deref(), None));
|
||||
for chunk in buffer.as_rope().chunks() {
|
||||
text.push_str(chunk);
|
||||
}
|
||||
if !text.ends_with('\n') {
|
||||
text.push('\n');
|
||||
}
|
||||
writeln!(text, "```").unwrap();
|
||||
if write_single_file_diagnostics(&mut text, full_path.as_deref(), &buffer) {
|
||||
has_diagnostics = true;
|
||||
}
|
||||
if !text.ends_with('\n') {
|
||||
text.push('\n');
|
||||
}
|
||||
|
||||
let section_end_ix = text.len() - 1;
|
||||
sections.push(build_entry_output_section(
|
||||
section_start_ix..section_end_ix,
|
||||
full_path.as_deref(),
|
||||
false,
|
||||
None,
|
||||
));
|
||||
}
|
||||
Ok(output)
|
||||
|
||||
Ok(SlashCommandOutput {
|
||||
text,
|
||||
sections,
|
||||
run_commands_in_text: has_diagnostics,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use assistant_slash_command::{
|
||||
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
|
||||
};
|
||||
use gpui::{AppContext, Task, View, WeakView};
|
||||
use language::{BufferSnapshot, CodeLabel, LspAdapterDelegate};
|
||||
use language::{CodeLabel, LspAdapterDelegate};
|
||||
use terminal_view::{terminal_panel::TerminalPanel, TerminalView};
|
||||
use ui::prelude::*;
|
||||
use workspace::{dock::Panel, Workspace};
|
||||
@@ -57,8 +57,6 @@ impl SlashCommand for TerminalSlashCommand {
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
arguments: &[String],
|
||||
_context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
|
||||
_context_buffer: BufferSnapshot,
|
||||
workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
@@ -93,7 +91,6 @@ impl SlashCommand for TerminalSlashCommand {
|
||||
range,
|
||||
icon: IconName::Terminal,
|
||||
label: "Terminal".into(),
|
||||
metadata: None,
|
||||
}],
|
||||
run_commands_in_text: false,
|
||||
}))
|
||||
|
||||
@@ -8,7 +8,7 @@ use assistant_slash_command::{
|
||||
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
|
||||
};
|
||||
use gpui::{Task, WeakView};
|
||||
use language::{BufferSnapshot, LspAdapterDelegate};
|
||||
use language::LspAdapterDelegate;
|
||||
use ui::prelude::*;
|
||||
|
||||
use workspace::Workspace;
|
||||
@@ -53,8 +53,6 @@ impl SlashCommand for WorkflowSlashCommand {
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
_arguments: &[String],
|
||||
_context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
|
||||
_context_buffer: BufferSnapshot,
|
||||
_workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
@@ -70,7 +68,6 @@ impl SlashCommand for WorkflowSlashCommand {
|
||||
range,
|
||||
icon: IconName::Route,
|
||||
label: "Workflow".into(),
|
||||
metadata: None,
|
||||
}],
|
||||
run_commands_in_text: false,
|
||||
})
|
||||
|
||||
@@ -570,7 +570,7 @@ impl Render for PromptEditor {
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.border_y_1()
|
||||
.border_color(cx.theme().status().info_border)
|
||||
.py_2()
|
||||
.py_1p5()
|
||||
.h_full()
|
||||
.w_full()
|
||||
.on_action(cx.listener(Self::confirm))
|
||||
@@ -949,11 +949,12 @@ impl PromptEditor {
|
||||
} else {
|
||||
cx.theme().colors().text
|
||||
},
|
||||
font_family: settings.buffer_font.family.clone(),
|
||||
font_fallbacks: settings.buffer_font.fallbacks.clone(),
|
||||
font_size: settings.buffer_font_size.into(),
|
||||
font_weight: settings.buffer_font.weight,
|
||||
line_height: relative(settings.buffer_line_height.value()),
|
||||
font_family: settings.ui_font.family.clone(),
|
||||
font_features: settings.ui_font.features.clone(),
|
||||
font_fallbacks: settings.ui_font.fallbacks.clone(),
|
||||
font_size: rems(0.875).into(),
|
||||
font_weight: settings.ui_font.weight,
|
||||
line_height: relative(1.3),
|
||||
..Default::default()
|
||||
};
|
||||
EditorElement::new(
|
||||
@@ -1066,7 +1067,6 @@ impl Codegen {
|
||||
telemetry.report_assistant_event(
|
||||
None,
|
||||
telemetry_events::AssistantKind::Inline,
|
||||
telemetry_events::AssistantPhase::Response,
|
||||
model_telemetry_id,
|
||||
response_latency,
|
||||
error_message,
|
||||
|
||||
@@ -19,5 +19,4 @@ gpui.workspace = true
|
||||
language.workspace = true
|
||||
parking_lot.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
workspace.workspace = true
|
||||
|
||||
@@ -2,7 +2,7 @@ mod slash_command_registry;
|
||||
|
||||
use anyhow::Result;
|
||||
use gpui::{AnyElement, AppContext, ElementId, SharedString, Task, WeakView, WindowContext};
|
||||
use language::{BufferSnapshot, CodeLabel, LspAdapterDelegate, OffsetRangeExt};
|
||||
use language::{CodeLabel, LspAdapterDelegate};
|
||||
use serde::{Deserialize, Serialize};
|
||||
pub use slash_command_registry::*;
|
||||
use std::{
|
||||
@@ -77,8 +77,6 @@ pub trait SlashCommand: 'static + Send + Sync {
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
arguments: &[String],
|
||||
context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
|
||||
context_buffer: BufferSnapshot,
|
||||
workspace: WeakView<Workspace>,
|
||||
// TODO: We're just using the `LspAdapterDelegate` here because that is
|
||||
// what the extension API is already expecting.
|
||||
@@ -96,7 +94,7 @@ pub type RenderFoldPlaceholder = Arc<
|
||||
+ Fn(ElementId, Arc<dyn Fn(&mut WindowContext)>, &mut WindowContext) -> AnyElement,
|
||||
>;
|
||||
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SlashCommandOutput {
|
||||
pub text: String,
|
||||
pub sections: Vec<SlashCommandOutputSection<usize>>,
|
||||
@@ -108,11 +106,4 @@ pub struct SlashCommandOutputSection<T> {
|
||||
pub range: Range<T>,
|
||||
pub icon: IconName,
|
||||
pub label: SharedString,
|
||||
pub metadata: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
impl SlashCommandOutputSection<language::Anchor> {
|
||||
pub fn is_valid(&self, buffer: &language::TextBuffer) -> bool {
|
||||
self.range.start.is_valid(buffer) && !self.range.to_offset(buffer).is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ db.workspace = true
|
||||
editor.workspace = true
|
||||
gpui.workspace = true
|
||||
http_client.workspace = true
|
||||
isahc.workspace = true
|
||||
log.workspace = true
|
||||
markdown_preview.workspace = true
|
||||
menu.workspace = true
|
||||
|
||||
@@ -9,6 +9,7 @@ use gpui::{
|
||||
actions, AppContext, AsyncAppContext, Context as _, Global, Model, ModelContext,
|
||||
SemanticVersion, SharedString, Task, View, ViewContext, VisualContext, WindowContext,
|
||||
};
|
||||
use isahc::AsyncBody;
|
||||
|
||||
use markdown_preview::markdown_preview_view::{MarkdownPreviewMode, MarkdownPreviewView};
|
||||
use schemars::JsonSchema;
|
||||
@@ -19,7 +20,7 @@ use smol::{fs, io::AsyncReadExt};
|
||||
use settings::{Settings, SettingsSources, SettingsStore};
|
||||
use smol::{fs::File, process::Command};
|
||||
|
||||
use http_client::{AsyncBody, HttpClient, HttpClientWithUrl};
|
||||
use http_client::{HttpClient, HttpClientWithUrl};
|
||||
use release_channel::{AppCommitSha, AppVersion, ReleaseChannel};
|
||||
use std::{
|
||||
env::{
|
||||
@@ -243,22 +244,19 @@ pub fn view_release_notes(_: &ViewReleaseNotes, cx: &mut AppContext) -> Option<(
|
||||
let auto_updater = AutoUpdater::get(cx)?;
|
||||
let release_channel = ReleaseChannel::try_global(cx)?;
|
||||
|
||||
match release_channel {
|
||||
ReleaseChannel::Stable | ReleaseChannel::Preview => {
|
||||
let auto_updater = auto_updater.read(cx);
|
||||
let current_version = auto_updater.current_version;
|
||||
let release_channel = release_channel.dev_name();
|
||||
let path = format!("/releases/{release_channel}/{current_version}");
|
||||
let url = &auto_updater.http_client.build_url(&path);
|
||||
cx.open_url(url);
|
||||
}
|
||||
ReleaseChannel::Nightly => {
|
||||
cx.open_url("https://github.com/zed-industries/zed/commits/nightly/");
|
||||
}
|
||||
ReleaseChannel::Dev => {
|
||||
cx.open_url("https://github.com/zed-industries/zed/commits/main/");
|
||||
}
|
||||
if matches!(
|
||||
release_channel,
|
||||
ReleaseChannel::Stable | ReleaseChannel::Preview
|
||||
) {
|
||||
let auto_updater = auto_updater.read(cx);
|
||||
let release_channel = release_channel.dev_name();
|
||||
let current_version = auto_updater.current_version;
|
||||
let url = &auto_updater
|
||||
.http_client
|
||||
.build_url(&format!("/releases/{release_channel}/{current_version}"));
|
||||
cx.open_url(url);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
|
||||
@@ -332,7 +332,7 @@ impl ChannelChat {
|
||||
.update(&mut cx, |chat, cx| {
|
||||
if let Some(first_id) = chat.first_loaded_message_id() {
|
||||
if first_id <= message_id {
|
||||
let mut cursor = chat.messages.cursor::<(ChannelMessageId, Count)>(&());
|
||||
let mut cursor = chat.messages.cursor::<(ChannelMessageId, Count)>();
|
||||
let message_id = ChannelMessageId::Saved(message_id);
|
||||
cursor.seek(&message_id, Bias::Left, &());
|
||||
return ControlFlow::Break(
|
||||
@@ -498,7 +498,7 @@ impl ChannelChat {
|
||||
}
|
||||
|
||||
pub fn message(&self, ix: usize) -> &ChannelMessage {
|
||||
let mut cursor = self.messages.cursor::<Count>(&());
|
||||
let mut cursor = self.messages.cursor::<Count>();
|
||||
cursor.seek(&Count(ix), Bias::Right, &());
|
||||
cursor.item().unwrap()
|
||||
}
|
||||
@@ -515,13 +515,13 @@ impl ChannelChat {
|
||||
}
|
||||
|
||||
pub fn messages_in_range(&self, range: Range<usize>) -> impl Iterator<Item = &ChannelMessage> {
|
||||
let mut cursor = self.messages.cursor::<Count>(&());
|
||||
let mut cursor = self.messages.cursor::<Count>();
|
||||
cursor.seek(&Count(range.start), Bias::Right, &());
|
||||
cursor.take(range.len())
|
||||
}
|
||||
|
||||
pub fn pending_messages(&self) -> impl Iterator<Item = &ChannelMessage> {
|
||||
let mut cursor = self.messages.cursor::<ChannelMessageId>(&());
|
||||
let mut cursor = self.messages.cursor::<ChannelMessageId>();
|
||||
cursor.seek(&ChannelMessageId::Pending(0), Bias::Left, &());
|
||||
cursor
|
||||
}
|
||||
@@ -589,11 +589,11 @@ impl ChannelChat {
|
||||
fn insert_messages(&mut self, messages: SumTree<ChannelMessage>, cx: &mut ModelContext<Self>) {
|
||||
if let Some((first_message, last_message)) = messages.first().zip(messages.last()) {
|
||||
let nonces = messages
|
||||
.cursor::<()>(&())
|
||||
.cursor::<()>()
|
||||
.map(|m| m.nonce)
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
let mut old_cursor = self.messages.cursor::<(ChannelMessageId, Count)>(&());
|
||||
let mut old_cursor = self.messages.cursor::<(ChannelMessageId, Count)>();
|
||||
let mut new_messages = old_cursor.slice(&first_message.id, Bias::Left, &());
|
||||
let start_ix = old_cursor.start().1 .0;
|
||||
let removed_messages = old_cursor.slice(&last_message.id, Bias::Right, &());
|
||||
@@ -646,7 +646,7 @@ impl ChannelChat {
|
||||
}
|
||||
|
||||
fn message_removed(&mut self, id: u64, cx: &mut ModelContext<Self>) {
|
||||
let mut cursor = self.messages.cursor::<ChannelMessageId>(&());
|
||||
let mut cursor = self.messages.cursor::<ChannelMessageId>();
|
||||
let mut messages = cursor.slice(&ChannelMessageId::Saved(id), Bias::Left, &());
|
||||
if let Some(item) = cursor.item() {
|
||||
if item.id == ChannelMessageId::Saved(id) {
|
||||
@@ -685,7 +685,7 @@ impl ChannelChat {
|
||||
edited_at: Option<OffsetDateTime>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
let mut cursor = self.messages.cursor::<ChannelMessageId>(&());
|
||||
let mut cursor = self.messages.cursor::<ChannelMessageId>();
|
||||
let mut messages = cursor.slice(&id, Bias::Left, &());
|
||||
let ix = messages.summary().count;
|
||||
|
||||
@@ -716,7 +716,7 @@ async fn messages_from_proto(
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Result<SumTree<ChannelMessage>> {
|
||||
let messages = ChannelMessage::from_proto_vec(proto_messages, user_store, cx).await?;
|
||||
let mut result = SumTree::default();
|
||||
let mut result = SumTree::new();
|
||||
result.extend(messages, &());
|
||||
Ok(result)
|
||||
}
|
||||
@@ -825,10 +825,6 @@ impl Default for ChannelMessageId {
|
||||
impl sum_tree::Summary for ChannelMessageSummary {
|
||||
type Context = ();
|
||||
|
||||
fn zero(_cx: &Self::Context) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, summary: &Self, _: &()) {
|
||||
self.max_id = summary.max_id;
|
||||
self.count += summary.count;
|
||||
@@ -836,10 +832,6 @@ impl sum_tree::Summary for ChannelMessageSummary {
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, ChannelMessageSummary> for ChannelMessageId {
|
||||
fn zero(_cx: &()) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, summary: &'a ChannelMessageSummary, _: &()) {
|
||||
debug_assert!(summary.max_id > *self);
|
||||
*self = summary.max_id;
|
||||
@@ -847,10 +839,6 @@ impl<'a> sum_tree::Dimension<'a, ChannelMessageSummary> for ChannelMessageId {
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, ChannelMessageSummary> for Count {
|
||||
fn zero(_cx: &()) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, summary: &'a ChannelMessageSummary, _: &()) {
|
||||
self.0 += summary.count;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ test-support = ["clock/test-support", "collections/test-support", "gpui/test-sup
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
async-recursion = "0.3"
|
||||
async-tungstenite = { workspace = true, features = ["async-std", "async-tls"] }
|
||||
async-tungstenite = { workspace = true, features = ["async-std", "async-native-tls"] }
|
||||
chrono = { workspace = true, features = ["serde"] }
|
||||
clock.workspace = true
|
||||
collections.workspace = true
|
||||
@@ -35,8 +35,6 @@ postage.workspace = true
|
||||
rand.workspace = true
|
||||
release_channel.workspace = true
|
||||
rpc = { workspace = true, features = ["gpui"] }
|
||||
rustls.workspace = true
|
||||
rustls-native-certs.workspace = true
|
||||
schemars.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
@@ -240,6 +240,8 @@ pub enum EstablishConnectionError {
|
||||
#[error("{0}")]
|
||||
Other(#[from] anyhow::Error),
|
||||
#[error("{0}")]
|
||||
Http(#[from] http_client::Error),
|
||||
#[error("{0}")]
|
||||
InvalidHeaderValue(#[from] async_tungstenite::tungstenite::http::header::InvalidHeaderValue),
|
||||
#[error("{0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
@@ -527,13 +529,19 @@ impl Client {
|
||||
}
|
||||
|
||||
pub fn production(cx: &mut AppContext) -> Arc<Self> {
|
||||
let user_agent = format!(
|
||||
"Zed/{} ({}; {})",
|
||||
AppVersion::global(cx),
|
||||
std::env::consts::OS,
|
||||
std::env::consts::ARCH
|
||||
);
|
||||
let clock = Arc::new(clock::RealSystemClock);
|
||||
let http = Arc::new(HttpClientWithUrl::new_uri(
|
||||
cx.http_client(),
|
||||
let http = Arc::new(HttpClientWithUrl::new(
|
||||
&ClientSettings::get_global(cx).server_url,
|
||||
cx.http_client().proxy().cloned(),
|
||||
Some(user_agent),
|
||||
ProxySettings::get_global(cx).proxy.clone(),
|
||||
));
|
||||
Self::new(clock, http, cx)
|
||||
Self::new(clock, http.clone(), cx)
|
||||
}
|
||||
|
||||
pub fn id(&self) -> u64 {
|
||||
@@ -1137,32 +1145,8 @@ impl Client {
|
||||
|
||||
match url_scheme {
|
||||
Https => {
|
||||
let client_config = {
|
||||
let mut root_store = rustls::RootCertStore::empty();
|
||||
|
||||
let root_certs = rustls_native_certs::load_native_certs();
|
||||
for error in root_certs.errors {
|
||||
log::warn!("error loading native certs: {:?}", error);
|
||||
}
|
||||
root_store.add_parsable_certificates(
|
||||
&root_certs
|
||||
.certs
|
||||
.into_iter()
|
||||
.map(|cert| cert.as_ref().to_owned())
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
rustls::ClientConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_root_certificates(root_store)
|
||||
.with_no_client_auth()
|
||||
};
|
||||
let (stream, _) =
|
||||
async_tungstenite::async_tls::client_async_tls_with_connector(
|
||||
request,
|
||||
stream,
|
||||
Some(client_config.into()),
|
||||
)
|
||||
.await?;
|
||||
async_tungstenite::async_std::client_async_tls(request, stream).await?;
|
||||
Ok(Connection::new(
|
||||
stream
|
||||
.map_err(|error| anyhow!(error))
|
||||
|
||||
@@ -16,9 +16,9 @@ use std::io::Write;
|
||||
use std::{env, mem, path::PathBuf, sync::Arc, time::Duration};
|
||||
use sysinfo::{CpuRefreshKind, Pid, ProcessRefreshKind, RefreshKind, System};
|
||||
use telemetry_events::{
|
||||
ActionEvent, AppEvent, AssistantEvent, AssistantKind, AssistantPhase, CallEvent, CpuEvent,
|
||||
EditEvent, EditorEvent, Event, EventRequestBody, EventWrapper, ExtensionEvent,
|
||||
InlineCompletionEvent, MemoryEvent, ReplEvent, SettingEvent,
|
||||
ActionEvent, AppEvent, AssistantEvent, AssistantKind, CallEvent, CpuEvent, EditEvent,
|
||||
EditorEvent, Event, EventRequestBody, EventWrapper, ExtensionEvent, InlineCompletionEvent,
|
||||
MemoryEvent, ReplEvent, SettingEvent,
|
||||
};
|
||||
use tempfile::NamedTempFile;
|
||||
#[cfg(not(debug_assertions))]
|
||||
@@ -37,10 +37,9 @@ pub struct Telemetry {
|
||||
|
||||
struct TelemetryState {
|
||||
settings: TelemetrySettings,
|
||||
system_id: Option<Arc<str>>, // Per system
|
||||
metrics_id: Option<Arc<str>>, // Per logged-in user
|
||||
installation_id: Option<Arc<str>>, // Per app installation (different for dev, nightly, preview, and stable)
|
||||
session_id: Option<String>, // Per app launch
|
||||
metrics_id: Option<Arc<str>>, // Per logged-in user
|
||||
release_channel: Option<&'static str>,
|
||||
architecture: &'static str,
|
||||
events_queue: Vec<EventWrapper>,
|
||||
@@ -192,10 +191,9 @@ impl Telemetry {
|
||||
settings: *TelemetrySettings::get_global(cx),
|
||||
architecture: env::consts::ARCH,
|
||||
release_channel,
|
||||
system_id: None,
|
||||
installation_id: None,
|
||||
session_id: None,
|
||||
metrics_id: None,
|
||||
session_id: None,
|
||||
events_queue: Vec::new(),
|
||||
flush_events_task: None,
|
||||
log_file: None,
|
||||
@@ -285,13 +283,11 @@ impl Telemetry {
|
||||
|
||||
pub fn start(
|
||||
self: &Arc<Self>,
|
||||
system_id: Option<String>,
|
||||
installation_id: Option<String>,
|
||||
session_id: String,
|
||||
cx: &mut AppContext,
|
||||
) {
|
||||
let mut state = self.state.lock();
|
||||
state.system_id = system_id.map(|id| id.into());
|
||||
state.installation_id = installation_id.map(|id| id.into());
|
||||
state.session_id = Some(session_id);
|
||||
state.app_version = release_channel::AppVersion::global(cx).to_string();
|
||||
@@ -308,10 +304,7 @@ impl Telemetry {
|
||||
|
||||
let refresh_kind = ProcessRefreshKind::new().with_cpu().with_memory();
|
||||
let current_process = Pid::from_u32(std::process::id());
|
||||
system.refresh_processes_specifics(
|
||||
sysinfo::ProcessesToUpdate::Some(&[current_process]),
|
||||
refresh_kind,
|
||||
);
|
||||
system.refresh_process_specifics(current_process, refresh_kind);
|
||||
|
||||
// Waiting some amount of time before the first query is important to get a reasonable value
|
||||
// https://docs.rs/sysinfo/0.29.10/sysinfo/trait.ProcessExt.html#tymethod.cpu_usage
|
||||
@@ -321,10 +314,7 @@ impl Telemetry {
|
||||
smol::Timer::after(DURATION_BETWEEN_SYSTEM_EVENTS).await;
|
||||
|
||||
let current_process = Pid::from_u32(std::process::id());
|
||||
system.refresh_processes_specifics(
|
||||
sysinfo::ProcessesToUpdate::Some(&[current_process]),
|
||||
refresh_kind,
|
||||
);
|
||||
system.refresh_process_specifics(current_process, refresh_kind);
|
||||
let Some(process) = system.process(current_process) else {
|
||||
log::error!(
|
||||
"Failed to find own process {current_process:?} in system process table"
|
||||
@@ -395,7 +385,6 @@ impl Telemetry {
|
||||
self: &Arc<Self>,
|
||||
conversation_id: Option<String>,
|
||||
kind: AssistantKind,
|
||||
phase: AssistantPhase,
|
||||
model: String,
|
||||
response_latency: Option<Duration>,
|
||||
error_message: Option<String>,
|
||||
@@ -403,7 +392,6 @@ impl Telemetry {
|
||||
let event = Event::Assistant(AssistantEvent {
|
||||
conversation_id,
|
||||
kind,
|
||||
phase,
|
||||
model: model.to_string(),
|
||||
response_latency,
|
||||
error_message,
|
||||
@@ -641,10 +629,9 @@ impl Telemetry {
|
||||
let state = this.state.lock();
|
||||
|
||||
let request_body = EventRequestBody {
|
||||
system_id: state.system_id.as_deref().map(Into::into),
|
||||
installation_id: state.installation_id.as_deref().map(Into::into),
|
||||
session_id: state.session_id.clone(),
|
||||
metrics_id: state.metrics_id.as_deref().map(Into::into),
|
||||
session_id: state.session_id.clone(),
|
||||
is_staff: state.is_staff,
|
||||
app_version: state.app_version.clone(),
|
||||
os_name: state.os_name.clone(),
|
||||
@@ -716,7 +703,6 @@ mod tests {
|
||||
Utc.with_ymd_and_hms(1990, 4, 12, 12, 0, 0).unwrap(),
|
||||
));
|
||||
let http = FakeHttpClient::with_200_response();
|
||||
let system_id = Some("system_id".to_string());
|
||||
let installation_id = Some("installation_id".to_string());
|
||||
let session_id = "session_id".to_string();
|
||||
|
||||
@@ -724,7 +710,7 @@ mod tests {
|
||||
let telemetry = Telemetry::new(clock.clone(), http, cx);
|
||||
|
||||
telemetry.state.lock().max_queue_size = 4;
|
||||
telemetry.start(system_id, installation_id, session_id, cx);
|
||||
telemetry.start(installation_id, session_id, cx);
|
||||
|
||||
assert!(is_empty_state(&telemetry));
|
||||
|
||||
@@ -802,14 +788,13 @@ mod tests {
|
||||
Utc.with_ymd_and_hms(1990, 4, 12, 12, 0, 0).unwrap(),
|
||||
));
|
||||
let http = FakeHttpClient::with_200_response();
|
||||
let system_id = Some("system_id".to_string());
|
||||
let installation_id = Some("installation_id".to_string());
|
||||
let session_id = "session_id".to_string();
|
||||
|
||||
cx.update(|cx| {
|
||||
let telemetry = Telemetry::new(clock.clone(), http, cx);
|
||||
telemetry.state.lock().max_queue_size = 4;
|
||||
telemetry.start(system_id, installation_id, session_id, cx);
|
||||
telemetry.start(installation_id, session_id, cx);
|
||||
|
||||
assert!(is_empty_state(&telemetry));
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ futures.workspace = true
|
||||
google_ai.workspace = true
|
||||
hex.workspace = true
|
||||
http_client.workspace = true
|
||||
isahc_http_client.workspace = true
|
||||
jsonwebtoken.workspace = true
|
||||
live_kit_server.workspace = true
|
||||
log.workspace = true
|
||||
|
||||
@@ -149,8 +149,7 @@ pub async fn post_crash(
|
||||
installation_id = %installation_id,
|
||||
description = %description,
|
||||
backtrace = %summary,
|
||||
"crash report"
|
||||
);
|
||||
"crash report");
|
||||
|
||||
if let Some(slack_panics_webhook) = app.config.slack_panics_webhook.clone() {
|
||||
let payload = slack::WebhookBody::new(|w| {
|
||||
@@ -628,9 +627,7 @@ where
|
||||
|
||||
#[derive(Serialize, Debug, clickhouse::Row)]
|
||||
pub struct EditorEventRow {
|
||||
system_id: String,
|
||||
installation_id: String,
|
||||
session_id: Option<String>,
|
||||
metrics_id: String,
|
||||
operation: String,
|
||||
app_version: String,
|
||||
@@ -650,6 +647,7 @@ pub struct EditorEventRow {
|
||||
historical_event: bool,
|
||||
architecture: String,
|
||||
is_staff: Option<bool>,
|
||||
session_id: Option<String>,
|
||||
major: Option<i32>,
|
||||
minor: Option<i32>,
|
||||
patch: Option<i32>,
|
||||
@@ -679,10 +677,9 @@ impl EditorEventRow {
|
||||
os_name: body.os_name.clone(),
|
||||
os_version: body.os_version.clone().unwrap_or_default(),
|
||||
architecture: body.architecture.clone(),
|
||||
system_id: body.system_id.clone().unwrap_or_default(),
|
||||
installation_id: body.installation_id.clone().unwrap_or_default(),
|
||||
session_id: body.session_id.clone(),
|
||||
metrics_id: body.metrics_id.clone().unwrap_or_default(),
|
||||
session_id: body.session_id.clone(),
|
||||
is_staff: body.is_staff,
|
||||
time: time.timestamp_millis(),
|
||||
operation: event.operation,
|
||||
@@ -702,7 +699,6 @@ impl EditorEventRow {
|
||||
#[derive(Serialize, Debug, clickhouse::Row)]
|
||||
pub struct InlineCompletionEventRow {
|
||||
installation_id: String,
|
||||
session_id: Option<String>,
|
||||
provider: String,
|
||||
suggestion_accepted: bool,
|
||||
app_version: String,
|
||||
@@ -717,6 +713,7 @@ pub struct InlineCompletionEventRow {
|
||||
city: String,
|
||||
time: i64,
|
||||
is_staff: Option<bool>,
|
||||
session_id: Option<String>,
|
||||
major: Option<i32>,
|
||||
minor: Option<i32>,
|
||||
patch: Option<i32>,
|
||||
@@ -837,7 +834,6 @@ pub struct AssistantEventRow {
|
||||
// AssistantEventRow
|
||||
conversation_id: String,
|
||||
kind: String,
|
||||
phase: String,
|
||||
model: String,
|
||||
response_latency_in_ms: Option<i64>,
|
||||
error_message: Option<String>,
|
||||
@@ -870,7 +866,6 @@ impl AssistantEventRow {
|
||||
time: time.timestamp_millis(),
|
||||
conversation_id: event.conversation_id.unwrap_or_default(),
|
||||
kind: event.kind.to_string(),
|
||||
phase: event.phase.to_string(),
|
||||
model: event.model,
|
||||
response_latency_in_ms: event
|
||||
.response_latency
|
||||
@@ -882,9 +877,7 @@ impl AssistantEventRow {
|
||||
|
||||
#[derive(Debug, clickhouse::Row, Serialize)]
|
||||
pub struct CpuEventRow {
|
||||
system_id: Option<String>,
|
||||
installation_id: Option<String>,
|
||||
session_id: Option<String>,
|
||||
is_staff: Option<bool>,
|
||||
usage_as_percentage: f32,
|
||||
core_count: u32,
|
||||
@@ -893,6 +886,7 @@ pub struct CpuEventRow {
|
||||
os_name: String,
|
||||
os_version: String,
|
||||
time: i64,
|
||||
session_id: Option<String>,
|
||||
// pub normalized_cpu_usage: f64, MATERIALIZED
|
||||
major: Option<i32>,
|
||||
minor: Option<i32>,
|
||||
@@ -921,7 +915,6 @@ impl CpuEventRow {
|
||||
release_channel: body.release_channel.clone().unwrap_or_default(),
|
||||
os_name: body.os_name.clone(),
|
||||
os_version: body.os_version.clone().unwrap_or_default(),
|
||||
system_id: body.system_id.clone(),
|
||||
installation_id: body.installation_id.clone(),
|
||||
session_id: body.session_id.clone(),
|
||||
is_staff: body.is_staff,
|
||||
@@ -945,7 +938,6 @@ pub struct MemoryEventRow {
|
||||
os_version: String,
|
||||
|
||||
// ClientEventBase
|
||||
system_id: Option<String>,
|
||||
installation_id: Option<String>,
|
||||
session_id: Option<String>,
|
||||
is_staff: Option<bool>,
|
||||
@@ -977,7 +969,6 @@ impl MemoryEventRow {
|
||||
release_channel: body.release_channel.clone().unwrap_or_default(),
|
||||
os_name: body.os_name.clone(),
|
||||
os_version: body.os_version.clone().unwrap_or_default(),
|
||||
system_id: body.system_id.clone(),
|
||||
installation_id: body.installation_id.clone(),
|
||||
session_id: body.session_id.clone(),
|
||||
is_staff: body.is_staff,
|
||||
@@ -1001,7 +992,6 @@ pub struct AppEventRow {
|
||||
os_version: String,
|
||||
|
||||
// ClientEventBase
|
||||
system_id: Option<String>,
|
||||
installation_id: Option<String>,
|
||||
session_id: Option<String>,
|
||||
is_staff: Option<bool>,
|
||||
@@ -1032,7 +1022,6 @@ impl AppEventRow {
|
||||
release_channel: body.release_channel.clone().unwrap_or_default(),
|
||||
os_name: body.os_name.clone(),
|
||||
os_version: body.os_version.clone().unwrap_or_default(),
|
||||
system_id: body.system_id.clone(),
|
||||
installation_id: body.installation_id.clone(),
|
||||
session_id: body.session_id.clone(),
|
||||
is_staff: body.is_staff,
|
||||
@@ -1055,7 +1044,6 @@ pub struct SettingEventRow {
|
||||
os_version: String,
|
||||
|
||||
// ClientEventBase
|
||||
system_id: Option<String>,
|
||||
installation_id: Option<String>,
|
||||
session_id: Option<String>,
|
||||
is_staff: Option<bool>,
|
||||
@@ -1086,7 +1074,6 @@ impl SettingEventRow {
|
||||
release_channel: body.release_channel.clone().unwrap_or_default(),
|
||||
os_name: body.os_name.clone(),
|
||||
os_version: body.os_version.clone().unwrap_or_default(),
|
||||
system_id: body.system_id.clone(),
|
||||
installation_id: body.installation_id.clone(),
|
||||
session_id: body.session_id.clone(),
|
||||
is_staff: body.is_staff,
|
||||
@@ -1110,7 +1097,6 @@ pub struct ExtensionEventRow {
|
||||
os_version: String,
|
||||
|
||||
// ClientEventBase
|
||||
system_id: Option<String>,
|
||||
installation_id: Option<String>,
|
||||
session_id: Option<String>,
|
||||
is_staff: Option<bool>,
|
||||
@@ -1146,7 +1132,6 @@ impl ExtensionEventRow {
|
||||
release_channel: body.release_channel.clone().unwrap_or_default(),
|
||||
os_name: body.os_name.clone(),
|
||||
os_version: body.os_version.clone().unwrap_or_default(),
|
||||
system_id: body.system_id.clone(),
|
||||
installation_id: body.installation_id.clone(),
|
||||
session_id: body.session_id.clone(),
|
||||
is_staff: body.is_staff,
|
||||
@@ -1237,7 +1222,6 @@ pub struct EditEventRow {
|
||||
os_version: String,
|
||||
|
||||
// ClientEventBase
|
||||
system_id: Option<String>,
|
||||
installation_id: Option<String>,
|
||||
// Note: This column name has a typo in the ClickHouse table.
|
||||
#[serde(rename = "sesssion_id")]
|
||||
@@ -1275,7 +1259,6 @@ impl EditEventRow {
|
||||
release_channel: body.release_channel.clone().unwrap_or_default(),
|
||||
os_name: body.os_name.clone(),
|
||||
os_version: body.os_version.clone().unwrap_or_default(),
|
||||
system_id: body.system_id.clone(),
|
||||
installation_id: body.installation_id.clone(),
|
||||
session_id: body.session_id.clone(),
|
||||
is_staff: body.is_staff,
|
||||
|
||||
@@ -104,7 +104,7 @@ pub enum ChannelRole {
|
||||
/// Admin can read/write and change permissions.
|
||||
#[sea_orm(string_value = "admin")]
|
||||
Admin,
|
||||
/// Member can read/write, but not change permissions.
|
||||
/// Member can read/write, but not change pemissions.
|
||||
#[sea_orm(string_value = "member")]
|
||||
#[default]
|
||||
Member,
|
||||
|
||||
@@ -22,7 +22,7 @@ use chrono::{DateTime, Duration, Utc};
|
||||
use collections::HashMap;
|
||||
use db::{usage_measure::UsageMeasure, ActiveUserCount, LlmDatabase};
|
||||
use futures::{Stream, StreamExt as _};
|
||||
use isahc_http_client::IsahcHttpClient;
|
||||
use http_client::IsahcHttpClient;
|
||||
use rpc::ListModelsResponse;
|
||||
use rpc::{
|
||||
proto::Plan, LanguageModelProvider, PerformCompletionParams, EXPIRED_LLM_TOKEN_HEADER_NAME,
|
||||
@@ -72,7 +72,6 @@ impl LlmState {
|
||||
let http_client = IsahcHttpClient::builder()
|
||||
.default_header("User-Agent", user_agent)
|
||||
.build()
|
||||
.map(IsahcHttpClient::from)
|
||||
.context("failed to construct http client")?;
|
||||
|
||||
let this = Self {
|
||||
|
||||
@@ -35,8 +35,6 @@ use chrono::Utc;
|
||||
use collections::{HashMap, HashSet};
|
||||
pub use connection_pool::{ConnectionPool, ZedVersion};
|
||||
use core::fmt::{self, Debug, Formatter};
|
||||
use http_client::HttpClient;
|
||||
use isahc_http_client::IsahcHttpClient;
|
||||
use open_ai::{OpenAiEmbeddingModel, OPEN_AI_API_URL};
|
||||
use sha2::Digest;
|
||||
use supermaven_api::{CreateExternalUserRequest, SupermavenAdminApi};
|
||||
@@ -47,6 +45,7 @@ use futures::{
|
||||
stream::FuturesUnordered,
|
||||
FutureExt, SinkExt, StreamExt, TryStreamExt,
|
||||
};
|
||||
use http_client::IsahcHttpClient;
|
||||
use prometheus::{register_int_gauge, IntGauge};
|
||||
use rpc::{
|
||||
proto::{
|
||||
@@ -140,7 +139,7 @@ struct Session {
|
||||
connection_pool: Arc<parking_lot::Mutex<ConnectionPool>>,
|
||||
app_state: Arc<AppState>,
|
||||
supermaven_client: Option<Arc<SupermavenAdminApi>>,
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
http_client: Arc<IsahcHttpClient>,
|
||||
/// The GeoIP country code for the user.
|
||||
#[allow(unused)]
|
||||
geoip_country_code: Option<String>,
|
||||
@@ -958,7 +957,7 @@ impl Server {
|
||||
|
||||
let user_agent = format!("Zed Server/{}", env!("CARGO_PKG_VERSION"));
|
||||
let http_client = match IsahcHttpClient::builder().default_header("User-Agent", user_agent).build() {
|
||||
Ok(http_client) => Arc::new(IsahcHttpClient::from(http_client)),
|
||||
Ok(http_client) => Arc::new(http_client),
|
||||
Err(error) => {
|
||||
tracing::error!(?error, "failed to create HTTP client");
|
||||
return;
|
||||
|
||||
@@ -1524,7 +1524,6 @@ async fn test_mutual_editor_inlay_hint_cache_update(
|
||||
show_type_hints: true,
|
||||
show_parameter_hints: false,
|
||||
show_other_hints: true,
|
||||
show_background: false,
|
||||
})
|
||||
});
|
||||
});
|
||||
@@ -1539,7 +1538,6 @@ async fn test_mutual_editor_inlay_hint_cache_update(
|
||||
show_type_hints: true,
|
||||
show_parameter_hints: false,
|
||||
show_other_hints: true,
|
||||
show_background: false,
|
||||
})
|
||||
});
|
||||
});
|
||||
@@ -1788,7 +1786,6 @@ async fn test_inlay_hint_refresh_is_forwarded(
|
||||
show_type_hints: false,
|
||||
show_parameter_hints: false,
|
||||
show_other_hints: false,
|
||||
show_background: false,
|
||||
})
|
||||
});
|
||||
});
|
||||
@@ -1803,7 +1800,6 @@ async fn test_inlay_hint_refresh_is_forwarded(
|
||||
show_type_hints: true,
|
||||
show_parameter_hints: true,
|
||||
show_other_hints: true,
|
||||
show_background: false,
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,14 +11,16 @@ pub use smol;
|
||||
pub use sqlez;
|
||||
pub use sqlez_macros;
|
||||
|
||||
use release_channel::ReleaseChannel;
|
||||
pub use release_channel::RELEASE_CHANNEL;
|
||||
use sqlez::domain::Migrator;
|
||||
use sqlez::thread_safe_connection::ThreadSafeConnection;
|
||||
use sqlez_macros::sql;
|
||||
use std::env;
|
||||
use std::future::Future;
|
||||
use std::path::Path;
|
||||
use std::sync::{atomic::Ordering, LazyLock};
|
||||
use std::{env, sync::atomic::AtomicBool};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::LazyLock;
|
||||
use util::{maybe, ResultExt};
|
||||
|
||||
const CONNECTION_INITIALIZE_QUERY: &str = sql!(
|
||||
@@ -45,12 +47,16 @@ pub static ALL_FILE_DB_FAILED: LazyLock<AtomicBool> = LazyLock::new(|| AtomicBoo
|
||||
/// This will retry a couple times if there are failures. If opening fails once, the db directory
|
||||
/// is moved to a backup folder and a new one is created. If that fails, a shared in memory db is created.
|
||||
/// In either case, static variables are set so that the user can be notified.
|
||||
pub async fn open_db<M: Migrator + 'static>(db_dir: &Path, scope: &str) -> ThreadSafeConnection<M> {
|
||||
pub async fn open_db<M: Migrator + 'static>(
|
||||
db_dir: &Path,
|
||||
release_channel: &ReleaseChannel,
|
||||
) -> ThreadSafeConnection<M> {
|
||||
if *ZED_STATELESS {
|
||||
return open_fallback_db().await;
|
||||
}
|
||||
|
||||
let main_db_dir = db_dir.join(format!("0-{}", scope));
|
||||
let release_channel_name = release_channel.dev_name();
|
||||
let main_db_dir = db_dir.join(Path::new(&format!("0-{}", release_channel_name)));
|
||||
|
||||
let connection = maybe!(async {
|
||||
smol::fs::create_dir_all(&main_db_dir)
|
||||
@@ -112,7 +118,7 @@ pub async fn open_test_db<M: Migrator>(db_name: &str) -> ThreadSafeConnection<M>
|
||||
/// Implements a basic DB wrapper for a given domain
|
||||
#[macro_export]
|
||||
macro_rules! define_connection {
|
||||
(pub static ref $id:ident: $t:ident<()> = $migrations:expr; $($global:ident)?) => {
|
||||
(pub static ref $id:ident: $t:ident<()> = $migrations:expr;) => {
|
||||
pub struct $t($crate::sqlez::thread_safe_connection::ThreadSafeConnection<$t>);
|
||||
|
||||
impl ::std::ops::Deref for $t {
|
||||
@@ -133,23 +139,18 @@ macro_rules! define_connection {
|
||||
}
|
||||
}
|
||||
|
||||
use std::sync::LazyLock;
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub static $id: std::sync::LazyLock<$t> = std::sync::LazyLock::new(|| {
|
||||
pub static $id: LazyLock<$t> = LazyLock::new(|| {
|
||||
$t($crate::smol::block_on($crate::open_test_db(stringify!($id))))
|
||||
});
|
||||
|
||||
#[cfg(not(any(test, feature = "test-support")))]
|
||||
pub static $id: std::sync::LazyLock<$t> = std::sync::LazyLock::new(|| {
|
||||
let db_dir = $crate::database_dir();
|
||||
let scope = if false $(|| stringify!($global) == "global")? {
|
||||
"global"
|
||||
} else {
|
||||
$crate::RELEASE_CHANNEL.dev_name()
|
||||
};
|
||||
$t($crate::smol::block_on($crate::open_db(db_dir, scope)))
|
||||
pub static $id: LazyLock<$t> = LazyLock::new(|| {
|
||||
$t($crate::smol::block_on($crate::open_db($crate::database_dir(), &$crate::RELEASE_CHANNEL)))
|
||||
});
|
||||
};
|
||||
(pub static ref $id:ident: $t:ident<$($d:ty),+> = $migrations:expr; $($global:ident)?) => {
|
||||
(pub static ref $id:ident: $t:ident<$($d:ty),+> = $migrations:expr;) => {
|
||||
pub struct $t($crate::sqlez::thread_safe_connection::ThreadSafeConnection<( $($d),+, $t )>);
|
||||
|
||||
impl ::std::ops::Deref for $t {
|
||||
@@ -177,13 +178,7 @@ macro_rules! define_connection {
|
||||
|
||||
#[cfg(not(any(test, feature = "test-support")))]
|
||||
pub static $id: std::sync::LazyLock<$t> = std::sync::LazyLock::new(|| {
|
||||
let db_dir = $crate::database_dir();
|
||||
let scope = if false $(|| stringify!($global) == "global")? {
|
||||
"global"
|
||||
} else {
|
||||
$crate::RELEASE_CHANNEL.dev_name()
|
||||
};
|
||||
$t($crate::smol::block_on($crate::open_db(db_dir, scope)))
|
||||
$t($crate::smol::block_on($crate::open_db($crate::database_dir(), &$crate::RELEASE_CHANNEL)))
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -230,11 +225,7 @@ mod tests {
|
||||
.prefix("DbTests")
|
||||
.tempdir()
|
||||
.unwrap();
|
||||
let _bad_db = open_db::<BadDB>(
|
||||
tempdir.path(),
|
||||
&release_channel::ReleaseChannel::Dev.dev_name(),
|
||||
)
|
||||
.await;
|
||||
let _bad_db = open_db::<BadDB>(tempdir.path(), &release_channel::ReleaseChannel::Dev).await;
|
||||
}
|
||||
|
||||
/// Test that DB exists but corrupted (causing recreate)
|
||||
@@ -271,19 +262,13 @@ mod tests {
|
||||
.tempdir()
|
||||
.unwrap();
|
||||
{
|
||||
let corrupt_db = open_db::<CorruptedDB>(
|
||||
tempdir.path(),
|
||||
&release_channel::ReleaseChannel::Dev.dev_name(),
|
||||
)
|
||||
.await;
|
||||
let corrupt_db =
|
||||
open_db::<CorruptedDB>(tempdir.path(), &release_channel::ReleaseChannel::Dev).await;
|
||||
assert!(corrupt_db.persistent());
|
||||
}
|
||||
|
||||
let good_db = open_db::<GoodDB>(
|
||||
tempdir.path(),
|
||||
&release_channel::ReleaseChannel::Dev.dev_name(),
|
||||
)
|
||||
.await;
|
||||
let good_db =
|
||||
open_db::<GoodDB>(tempdir.path(), &release_channel::ReleaseChannel::Dev).await;
|
||||
assert!(
|
||||
good_db.select_row::<usize>("SELECT * FROM test2").unwrap()()
|
||||
.unwrap()
|
||||
@@ -326,11 +311,8 @@ mod tests {
|
||||
.unwrap();
|
||||
{
|
||||
// Setup the bad database
|
||||
let corrupt_db = open_db::<CorruptedDB>(
|
||||
tempdir.path(),
|
||||
&release_channel::ReleaseChannel::Dev.dev_name(),
|
||||
)
|
||||
.await;
|
||||
let corrupt_db =
|
||||
open_db::<CorruptedDB>(tempdir.path(), &release_channel::ReleaseChannel::Dev).await;
|
||||
assert!(corrupt_db.persistent());
|
||||
}
|
||||
|
||||
@@ -341,7 +323,7 @@ mod tests {
|
||||
let guard = thread::spawn(move || {
|
||||
let good_db = smol::block_on(open_db::<GoodDB>(
|
||||
tmp_path.as_path(),
|
||||
&release_channel::ReleaseChannel::Dev.dev_name(),
|
||||
&release_channel::ReleaseChannel::Dev,
|
||||
));
|
||||
assert!(
|
||||
good_db.select_row::<usize>("SELECT * FROM test2").unwrap()()
|
||||
|
||||
@@ -60,33 +60,3 @@ mod tests {
|
||||
assert_eq!(db.read_kvp("key-1").unwrap(), None);
|
||||
}
|
||||
}
|
||||
|
||||
define_connection!(pub static ref GLOBAL_KEY_VALUE_STORE: GlobalKeyValueStore<()> =
|
||||
&[sql!(
|
||||
CREATE TABLE IF NOT EXISTS kv_store(
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL
|
||||
) STRICT;
|
||||
)];
|
||||
global
|
||||
);
|
||||
|
||||
impl GlobalKeyValueStore {
|
||||
query! {
|
||||
pub fn read_kvp(key: &str) -> Result<Option<String>> {
|
||||
SELECT value FROM kv_store WHERE key = (?)
|
||||
}
|
||||
}
|
||||
|
||||
query! {
|
||||
pub async fn write_kvp(key: String, value: String) -> Result<()> {
|
||||
INSERT OR REPLACE INTO kv_store(key, value) VALUES ((?), (?))
|
||||
}
|
||||
}
|
||||
|
||||
query! {
|
||||
pub async fn delete_kvp(key: String) -> Result<()> {
|
||||
DELETE FROM kv_store WHERE key = (?)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,14 +156,14 @@ pub struct DeleteToPreviousWordStart {
|
||||
impl_actions!(
|
||||
editor,
|
||||
[
|
||||
ComposeCompletion,
|
||||
ConfirmCodeAction,
|
||||
ConfirmCompletion,
|
||||
ComposeCompletion,
|
||||
DeleteToNextWordEnd,
|
||||
DeleteToPreviousWordStart,
|
||||
ExpandExcerpts,
|
||||
ExpandExcerptsDown,
|
||||
ExpandExcerptsUp,
|
||||
ExpandExcerptsDown,
|
||||
FoldAt,
|
||||
HandleInput,
|
||||
MoveDownByLines,
|
||||
@@ -188,8 +188,8 @@ impl_actions!(
|
||||
gpui::actions!(
|
||||
editor,
|
||||
[
|
||||
AcceptInlineCompletion,
|
||||
AcceptPartialCopilotSuggestion,
|
||||
AcceptInlineCompletion,
|
||||
AcceptPartialInlineCompletion,
|
||||
AddSelectionAbove,
|
||||
AddSelectionBelow,
|
||||
@@ -210,10 +210,10 @@ gpui::actions!(
|
||||
ConvertToUpperCamelCase,
|
||||
ConvertToUpperCase,
|
||||
Copy,
|
||||
CopyFileLocation,
|
||||
CopyHighlightJson,
|
||||
CopyPath,
|
||||
CopyPermalinkToLine,
|
||||
CopyFileLocation,
|
||||
CopyRelativePath,
|
||||
Cut,
|
||||
CutToEndOfLine,
|
||||
@@ -232,10 +232,10 @@ gpui::actions!(
|
||||
Fold,
|
||||
FoldSelectedRanges,
|
||||
Format,
|
||||
GoToDeclaration,
|
||||
GoToDeclarationSplit,
|
||||
GoToDefinition,
|
||||
GoToDefinitionSplit,
|
||||
GoToDeclaration,
|
||||
GoToDeclarationSplit,
|
||||
GoToDiagnostic,
|
||||
GoToHunk,
|
||||
GoToImplementation,
|
||||
@@ -273,9 +273,9 @@ gpui::actions!(
|
||||
NextScreen,
|
||||
OpenExcerpts,
|
||||
OpenExcerptsSplit,
|
||||
OpenFile,
|
||||
OpenPermalinkToLine,
|
||||
OpenUrl,
|
||||
OpenFile,
|
||||
Outdent,
|
||||
PageDown,
|
||||
PageUp,
|
||||
@@ -284,25 +284,23 @@ gpui::actions!(
|
||||
Redo,
|
||||
RedoSelection,
|
||||
Rename,
|
||||
Rewrap,
|
||||
RestartLanguageServer,
|
||||
RevealInFileManager,
|
||||
ReverseLines,
|
||||
RevertFile,
|
||||
RevertSelectedHunks,
|
||||
Rewrap,
|
||||
ScrollCursorBottom,
|
||||
ScrollCursorCenter,
|
||||
ScrollCursorCenterTopBottom,
|
||||
ScrollCursorTop,
|
||||
ScrollCursorCenterTopBottom,
|
||||
SelectAll,
|
||||
SelectAllMatches,
|
||||
SelectDown,
|
||||
SelectEnclosingSymbol,
|
||||
SelectLargerSyntaxNode,
|
||||
SelectEnclosingSymbol,
|
||||
SelectLeft,
|
||||
SelectLine,
|
||||
SelectPageDown,
|
||||
SelectPageUp,
|
||||
SelectRight,
|
||||
SelectSmallerSyntaxNode,
|
||||
SelectToBeginning,
|
||||
@@ -314,6 +312,8 @@ gpui::actions!(
|
||||
SelectToPreviousWordStart,
|
||||
SelectToStartOfParagraph,
|
||||
SelectUp,
|
||||
SelectPageDown,
|
||||
SelectPageUp,
|
||||
ShowCharacterPalette,
|
||||
ShowInlineCompletion,
|
||||
ShowSignatureHelp,
|
||||
@@ -327,13 +327,13 @@ gpui::actions!(
|
||||
ToggleAutoSignatureHelp,
|
||||
ToggleGitBlame,
|
||||
ToggleGitBlameInline,
|
||||
ToggleSelectionMenu,
|
||||
ToggleHunkDiff,
|
||||
ToggleIndentGuides,
|
||||
ToggleInlayHints,
|
||||
ToggleInlineCompletions,
|
||||
ToggleLineNumbers,
|
||||
ToggleRelativeLineNumbers,
|
||||
ToggleSelectionMenu,
|
||||
ToggleIndentGuides,
|
||||
ToggleSoftWrap,
|
||||
ToggleTabBar,
|
||||
Transpose,
|
||||
|
||||
@@ -127,9 +127,7 @@ impl DisplayMap {
|
||||
let buffer_subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
|
||||
|
||||
let tab_size = Self::tab_size(&buffer, cx);
|
||||
let buffer_snapshot = buffer.read(cx).snapshot(cx);
|
||||
let crease_map = CreaseMap::new(&buffer_snapshot);
|
||||
let (inlay_map, snapshot) = InlayMap::new(buffer_snapshot);
|
||||
let (inlay_map, snapshot) = InlayMap::new(buffer.read(cx).snapshot(cx));
|
||||
let (fold_map, snapshot) = FoldMap::new(snapshot);
|
||||
let (tab_map, snapshot) = TabMap::new(snapshot, tab_size);
|
||||
let (wrap_map, snapshot) = WrapMap::new(snapshot, font, font_size, wrap_width, cx);
|
||||
@@ -140,6 +138,7 @@ impl DisplayMap {
|
||||
excerpt_header_height,
|
||||
excerpt_footer_height,
|
||||
);
|
||||
let crease_map = CreaseMap::default();
|
||||
|
||||
cx.observe(&wrap_map, |_, _, cx| cx.notify()).detach();
|
||||
|
||||
|
||||
@@ -389,10 +389,10 @@ impl BlockMap {
|
||||
}
|
||||
|
||||
let mut transforms = self.transforms.borrow_mut();
|
||||
let mut new_transforms = SumTree::default();
|
||||
let mut new_transforms = SumTree::new();
|
||||
let old_row_count = transforms.summary().input_rows;
|
||||
let new_row_count = wrap_snapshot.max_point().row() + 1;
|
||||
let mut cursor = transforms.cursor::<WrapRow>(&());
|
||||
let mut cursor = transforms.cursor::<WrapRow>();
|
||||
let mut last_block_ix = 0;
|
||||
let mut blocks_in_edit = Vec::new();
|
||||
let mut edits = edits.into_iter().peekable();
|
||||
@@ -757,7 +757,7 @@ impl<'a> BlockMapReader<'a> {
|
||||
.unwrap_or(self.wrap_snapshot.max_point().row() + 1),
|
||||
);
|
||||
|
||||
let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>(&());
|
||||
let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>();
|
||||
cursor.seek(&start_wrap_row, Bias::Left, &());
|
||||
while let Some(transform) = cursor.item() {
|
||||
if cursor.start().0 > end_wrap_row {
|
||||
@@ -950,7 +950,7 @@ impl BlockSnapshot {
|
||||
highlights: Highlights<'a>,
|
||||
) -> BlockChunks<'a> {
|
||||
let max_output_row = cmp::min(rows.end, self.transforms.summary().output_rows);
|
||||
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
|
||||
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
|
||||
let input_end = {
|
||||
cursor.seek(&BlockRow(rows.end), Bias::Right, &());
|
||||
let overshoot = if cursor
|
||||
@@ -990,7 +990,7 @@ impl BlockSnapshot {
|
||||
}
|
||||
|
||||
pub(super) fn buffer_rows(&self, start_row: BlockRow) -> BlockBufferRows {
|
||||
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
|
||||
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
|
||||
cursor.seek(&start_row, Bias::Right, &());
|
||||
let (output_start, input_start) = cursor.start();
|
||||
let overshoot = if cursor.item().map_or(false, |t| t.is_isomorphic()) {
|
||||
@@ -1008,7 +1008,7 @@ impl BlockSnapshot {
|
||||
}
|
||||
|
||||
pub fn blocks_in_range(&self, rows: Range<u32>) -> impl Iterator<Item = (u32, &Block)> {
|
||||
let mut cursor = self.transforms.cursor::<BlockRow>(&());
|
||||
let mut cursor = self.transforms.cursor::<BlockRow>();
|
||||
cursor.seek(&BlockRow(rows.start), Bias::Left, &());
|
||||
while cursor.start().0 < rows.start && cursor.end(&()).0 <= rows.start {
|
||||
cursor.next(&());
|
||||
@@ -1050,7 +1050,7 @@ impl BlockSnapshot {
|
||||
let wrap_point = self
|
||||
.wrap_snapshot
|
||||
.make_wrap_point(excerpt_range.start, Bias::Left);
|
||||
let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>(&());
|
||||
let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>();
|
||||
cursor.seek(&WrapRow(wrap_point.row()), Bias::Left, &());
|
||||
while let Some(transform) = cursor.item() {
|
||||
if let Some(block) = transform.block.as_ref() {
|
||||
@@ -1072,7 +1072,7 @@ impl BlockSnapshot {
|
||||
.wrap_snapshot
|
||||
.make_wrap_point(excerpt_range.end, Bias::Left);
|
||||
|
||||
let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>(&());
|
||||
let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>();
|
||||
cursor.seek(&WrapRow(wrap_point.row()), Bias::Left, &());
|
||||
while let Some(transform) = cursor.item() {
|
||||
if let Some(block) = transform.block.as_ref() {
|
||||
@@ -1102,7 +1102,7 @@ impl BlockSnapshot {
|
||||
}
|
||||
|
||||
pub(super) fn line_len(&self, row: BlockRow) -> u32 {
|
||||
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
|
||||
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
|
||||
cursor.seek(&BlockRow(row.0), Bias::Right, &());
|
||||
if let Some(transform) = cursor.item() {
|
||||
let (output_start, input_start) = cursor.start();
|
||||
@@ -1118,13 +1118,13 @@ impl BlockSnapshot {
|
||||
}
|
||||
|
||||
pub(super) fn is_block_line(&self, row: BlockRow) -> bool {
|
||||
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
|
||||
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
|
||||
cursor.seek(&row, Bias::Right, &());
|
||||
cursor.item().map_or(false, |t| t.block.is_some())
|
||||
}
|
||||
|
||||
pub fn clip_point(&self, point: BlockPoint, bias: Bias) -> BlockPoint {
|
||||
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
|
||||
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
|
||||
cursor.seek(&BlockRow(point.row), Bias::Right, &());
|
||||
|
||||
let max_input_row = WrapRow(self.transforms.summary().input_rows);
|
||||
@@ -1172,7 +1172,7 @@ impl BlockSnapshot {
|
||||
}
|
||||
|
||||
pub fn to_block_point(&self, wrap_point: WrapPoint) -> BlockPoint {
|
||||
let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>(&());
|
||||
let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>();
|
||||
cursor.seek(&WrapRow(wrap_point.row()), Bias::Right, &());
|
||||
if let Some(transform) = cursor.item() {
|
||||
debug_assert!(transform.is_isomorphic());
|
||||
@@ -1188,7 +1188,7 @@ impl BlockSnapshot {
|
||||
}
|
||||
|
||||
pub fn to_wrap_point(&self, block_point: BlockPoint) -> WrapPoint {
|
||||
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
|
||||
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
|
||||
cursor.seek(&BlockRow(block_point.row), Bias::Right, &());
|
||||
if let Some(transform) = cursor.item() {
|
||||
match transform.block.as_ref().map(|b| b.disposition()) {
|
||||
@@ -1368,10 +1368,6 @@ impl sum_tree::Item for Transform {
|
||||
impl sum_tree::Summary for TransformSummary {
|
||||
type Context = ();
|
||||
|
||||
fn zero(_cx: &()) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, summary: &Self, _: &()) {
|
||||
self.input_rows += summary.input_rows;
|
||||
self.output_rows += summary.output_rows;
|
||||
@@ -1379,20 +1375,12 @@ impl sum_tree::Summary for TransformSummary {
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapRow {
|
||||
fn zero(_cx: &()) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
|
||||
self.0 += summary.input_rows;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, TransformSummary> for BlockRow {
|
||||
fn zero(_cx: &()) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
|
||||
self.0 += summary.output_rows;
|
||||
}
|
||||
|
||||
@@ -12,34 +12,19 @@ use crate::FoldPlaceholder;
|
||||
#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
|
||||
pub struct CreaseId(usize);
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct CreaseMap {
|
||||
snapshot: CreaseSnapshot,
|
||||
next_id: CreaseId,
|
||||
id_to_range: HashMap<CreaseId, Range<Anchor>>,
|
||||
}
|
||||
|
||||
impl CreaseMap {
|
||||
pub fn new(snapshot: &MultiBufferSnapshot) -> Self {
|
||||
CreaseMap {
|
||||
snapshot: CreaseSnapshot::new(snapshot),
|
||||
next_id: CreaseId::default(),
|
||||
id_to_range: HashMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Default)]
|
||||
pub struct CreaseSnapshot {
|
||||
creases: SumTree<CreaseItem>,
|
||||
}
|
||||
|
||||
impl CreaseSnapshot {
|
||||
pub fn new(snapshot: &MultiBufferSnapshot) -> Self {
|
||||
CreaseSnapshot {
|
||||
creases: SumTree::new(snapshot),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the first Crease starting on the specified buffer row.
|
||||
pub fn query_row<'a>(
|
||||
&'a self,
|
||||
@@ -47,7 +32,7 @@ impl CreaseSnapshot {
|
||||
snapshot: &'a MultiBufferSnapshot,
|
||||
) -> Option<&'a Crease> {
|
||||
let start = snapshot.anchor_before(Point::new(row.0, 0));
|
||||
let mut cursor = self.creases.cursor::<ItemSummary>(snapshot);
|
||||
let mut cursor = self.creases.cursor::<ItemSummary>();
|
||||
cursor.seek(&start, Bias::Left, snapshot);
|
||||
while let Some(item) = cursor.item() {
|
||||
match Ord::cmp(&item.crease.range.start.to_point(snapshot).row, &row.0) {
|
||||
@@ -71,7 +56,7 @@ impl CreaseSnapshot {
|
||||
snapshot: &'a MultiBufferSnapshot,
|
||||
) -> impl '_ + Iterator<Item = &'a Crease> {
|
||||
let start = snapshot.anchor_before(Point::new(range.start.0, 0));
|
||||
let mut cursor = self.creases.cursor::<ItemSummary>(snapshot);
|
||||
let mut cursor = self.creases.cursor::<ItemSummary>();
|
||||
cursor.seek(&start, Bias::Left, snapshot);
|
||||
|
||||
std::iter::from_fn(move || {
|
||||
@@ -94,7 +79,7 @@ impl CreaseSnapshot {
|
||||
&self,
|
||||
snapshot: &MultiBufferSnapshot,
|
||||
) -> Vec<(CreaseId, Range<Point>)> {
|
||||
let mut cursor = self.creases.cursor::<ItemSummary>(snapshot);
|
||||
let mut cursor = self.creases.cursor::<ItemSummary>();
|
||||
let mut results = Vec::new();
|
||||
|
||||
cursor.next(snapshot);
|
||||
@@ -209,8 +194,8 @@ impl CreaseMap {
|
||||
) -> Vec<CreaseId> {
|
||||
let mut new_ids = Vec::new();
|
||||
self.snapshot.creases = {
|
||||
let mut new_creases = SumTree::new(snapshot);
|
||||
let mut cursor = self.snapshot.creases.cursor::<ItemSummary>(snapshot);
|
||||
let mut new_creases = SumTree::new();
|
||||
let mut cursor = self.snapshot.creases.cursor::<ItemSummary>();
|
||||
for crease in creases {
|
||||
new_creases.append(cursor.slice(&crease.range, Bias::Left, snapshot), snapshot);
|
||||
|
||||
@@ -242,8 +227,8 @@ impl CreaseMap {
|
||||
});
|
||||
|
||||
self.snapshot.creases = {
|
||||
let mut new_creases = SumTree::new(snapshot);
|
||||
let mut cursor = self.snapshot.creases.cursor::<ItemSummary>(snapshot);
|
||||
let mut new_creases = SumTree::new();
|
||||
let mut cursor = self.snapshot.creases.cursor::<ItemSummary>();
|
||||
|
||||
for (id, range) in removals {
|
||||
new_creases.append(cursor.slice(&range, Bias::Left, snapshot), snapshot);
|
||||
@@ -279,10 +264,6 @@ impl Default for ItemSummary {
|
||||
impl sum_tree::Summary for ItemSummary {
|
||||
type Context = MultiBufferSnapshot;
|
||||
|
||||
fn zero(_cx: &Self::Context) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, other: &Self, _snapshot: &MultiBufferSnapshot) {
|
||||
self.range = other.range.clone();
|
||||
}
|
||||
@@ -322,7 +303,7 @@ mod test {
|
||||
let text = "line1\nline2\nline3\nline4\nline5";
|
||||
let buffer = MultiBuffer::build_simple(text, cx);
|
||||
let snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
|
||||
let mut crease_map = CreaseMap::new(&buffer.read(cx).read(cx));
|
||||
let mut crease_map = CreaseMap::default();
|
||||
|
||||
// Insert creases
|
||||
let creases = [
|
||||
@@ -369,7 +350,7 @@ mod test {
|
||||
let text = "line1\nline2\nline3\nline4\nline5\nline6\nline7";
|
||||
let buffer = MultiBuffer::build_simple(text, cx);
|
||||
let snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
|
||||
let mut crease_map = CreaseMap::new(&snapshot);
|
||||
let mut crease_map = CreaseMap::default();
|
||||
|
||||
let creases = [
|
||||
Crease::new(
|
||||
|
||||
@@ -79,7 +79,7 @@ impl FoldPoint {
|
||||
}
|
||||
|
||||
pub fn to_inlay_point(self, snapshot: &FoldSnapshot) -> InlayPoint {
|
||||
let mut cursor = snapshot.transforms.cursor::<(FoldPoint, InlayPoint)>(&());
|
||||
let mut cursor = snapshot.transforms.cursor::<(FoldPoint, InlayPoint)>();
|
||||
cursor.seek(&self, Bias::Right, &());
|
||||
let overshoot = self.0 - cursor.start().0 .0;
|
||||
InlayPoint(cursor.start().1 .0 + overshoot)
|
||||
@@ -88,7 +88,7 @@ impl FoldPoint {
|
||||
pub fn to_offset(self, snapshot: &FoldSnapshot) -> FoldOffset {
|
||||
let mut cursor = snapshot
|
||||
.transforms
|
||||
.cursor::<(FoldPoint, TransformSummary)>(&());
|
||||
.cursor::<(FoldPoint, TransformSummary)>();
|
||||
cursor.seek(&self, Bias::Right, &());
|
||||
let overshoot = self.0 - cursor.start().1.output.lines;
|
||||
let mut offset = cursor.start().1.output.len;
|
||||
@@ -105,10 +105,6 @@ impl FoldPoint {
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, TransformSummary> for FoldPoint {
|
||||
fn zero(_cx: &()) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
|
||||
self.0 += &summary.output.lines;
|
||||
}
|
||||
@@ -158,8 +154,8 @@ impl<'a> FoldMapWriter<'a> {
|
||||
folds.sort_unstable_by(|a, b| sum_tree::SeekTarget::cmp(&a.range, &b.range, buffer));
|
||||
|
||||
self.0.snapshot.folds = {
|
||||
let mut new_tree = SumTree::new(buffer);
|
||||
let mut cursor = self.0.snapshot.folds.cursor::<FoldRange>(buffer);
|
||||
let mut new_tree = SumTree::new();
|
||||
let mut cursor = self.0.snapshot.folds.cursor::<FoldRange>();
|
||||
for fold in folds {
|
||||
new_tree.append(cursor.slice(&fold.range, Bias::Right, buffer), buffer);
|
||||
new_tree.push(fold, buffer);
|
||||
@@ -206,8 +202,8 @@ impl<'a> FoldMapWriter<'a> {
|
||||
fold_ixs_to_delete.dedup();
|
||||
|
||||
self.0.snapshot.folds = {
|
||||
let mut cursor = self.0.snapshot.folds.cursor::<usize>(buffer);
|
||||
let mut folds = SumTree::new(buffer);
|
||||
let mut cursor = self.0.snapshot.folds.cursor::<usize>();
|
||||
let mut folds = SumTree::new();
|
||||
for fold_ix in fold_ixs_to_delete {
|
||||
folds.append(cursor.slice(&fold_ix, Bias::Right, buffer), buffer);
|
||||
cursor.next(buffer);
|
||||
@@ -234,7 +230,7 @@ impl FoldMap {
|
||||
pub(crate) fn new(inlay_snapshot: InlaySnapshot) -> (Self, FoldSnapshot) {
|
||||
let this = Self {
|
||||
snapshot: FoldSnapshot {
|
||||
folds: SumTree::new(&inlay_snapshot.buffer),
|
||||
folds: Default::default(),
|
||||
transforms: SumTree::from_item(
|
||||
Transform {
|
||||
summary: TransformSummary {
|
||||
@@ -318,8 +314,8 @@ impl FoldMap {
|
||||
} else {
|
||||
let mut inlay_edits_iter = inlay_edits.iter().cloned().peekable();
|
||||
|
||||
let mut new_transforms = SumTree::<Transform>::default();
|
||||
let mut cursor = self.snapshot.transforms.cursor::<InlayOffset>(&());
|
||||
let mut new_transforms = SumTree::<Transform>::new();
|
||||
let mut cursor = self.snapshot.transforms.cursor::<InlayOffset>();
|
||||
cursor.seek(&InlayOffset(0), Bias::Right, &());
|
||||
|
||||
while let Some(mut edit) = inlay_edits_iter.next() {
|
||||
@@ -371,10 +367,7 @@ impl FoldMap {
|
||||
let anchor = inlay_snapshot
|
||||
.buffer
|
||||
.anchor_before(inlay_snapshot.to_buffer_offset(edit.new.start));
|
||||
let mut folds_cursor = self
|
||||
.snapshot
|
||||
.folds
|
||||
.cursor::<FoldRange>(&inlay_snapshot.buffer);
|
||||
let mut folds_cursor = self.snapshot.folds.cursor::<FoldRange>();
|
||||
folds_cursor.seek(
|
||||
&FoldRange(anchor..Anchor::max()),
|
||||
Bias::Left,
|
||||
@@ -477,8 +470,8 @@ impl FoldMap {
|
||||
let mut old_transforms = self
|
||||
.snapshot
|
||||
.transforms
|
||||
.cursor::<(InlayOffset, FoldOffset)>(&());
|
||||
let mut new_transforms = new_transforms.cursor::<(InlayOffset, FoldOffset)>(&());
|
||||
.cursor::<(InlayOffset, FoldOffset)>();
|
||||
let mut new_transforms = new_transforms.cursor::<(InlayOffset, FoldOffset)>();
|
||||
|
||||
for mut edit in inlay_edits {
|
||||
old_transforms.seek(&edit.old.start, Bias::Left, &());
|
||||
@@ -552,7 +545,7 @@ impl FoldSnapshot {
|
||||
pub fn text_summary_for_range(&self, range: Range<FoldPoint>) -> TextSummary {
|
||||
let mut summary = TextSummary::default();
|
||||
|
||||
let mut cursor = self.transforms.cursor::<(FoldPoint, InlayPoint)>(&());
|
||||
let mut cursor = self.transforms.cursor::<(FoldPoint, InlayPoint)>();
|
||||
cursor.seek(&range.start, Bias::Right, &());
|
||||
if let Some(transform) = cursor.item() {
|
||||
let start_in_transform = range.start.0 - cursor.start().0 .0;
|
||||
@@ -601,7 +594,7 @@ impl FoldSnapshot {
|
||||
}
|
||||
|
||||
pub fn to_fold_point(&self, point: InlayPoint, bias: Bias) -> FoldPoint {
|
||||
let mut cursor = self.transforms.cursor::<(InlayPoint, FoldPoint)>(&());
|
||||
let mut cursor = self.transforms.cursor::<(InlayPoint, FoldPoint)>();
|
||||
cursor.seek(&point, Bias::Right, &());
|
||||
if cursor.item().map_or(false, |t| t.is_fold()) {
|
||||
if bias == Bias::Left || point == cursor.start().0 {
|
||||
@@ -638,7 +631,7 @@ impl FoldSnapshot {
|
||||
}
|
||||
|
||||
let fold_point = FoldPoint::new(start_row, 0);
|
||||
let mut cursor = self.transforms.cursor::<(FoldPoint, InlayPoint)>(&());
|
||||
let mut cursor = self.transforms.cursor::<(FoldPoint, InlayPoint)>();
|
||||
cursor.seek(&fold_point, Bias::Left, &());
|
||||
|
||||
let overshoot = fold_point.0 - cursor.start().0 .0;
|
||||
@@ -679,7 +672,7 @@ impl FoldSnapshot {
|
||||
{
|
||||
let buffer_offset = offset.to_offset(&self.inlay_snapshot.buffer);
|
||||
let inlay_offset = self.inlay_snapshot.to_inlay_offset(buffer_offset);
|
||||
let mut cursor = self.transforms.cursor::<InlayOffset>(&());
|
||||
let mut cursor = self.transforms.cursor::<InlayOffset>();
|
||||
cursor.seek(&inlay_offset, Bias::Right, &());
|
||||
cursor.item().map_or(false, |t| t.placeholder.is_some())
|
||||
}
|
||||
@@ -688,7 +681,7 @@ impl FoldSnapshot {
|
||||
let mut inlay_point = self
|
||||
.inlay_snapshot
|
||||
.to_inlay_point(Point::new(buffer_row.0, 0));
|
||||
let mut cursor = self.transforms.cursor::<InlayPoint>(&());
|
||||
let mut cursor = self.transforms.cursor::<InlayPoint>();
|
||||
cursor.seek(&inlay_point, Bias::Right, &());
|
||||
loop {
|
||||
match cursor.item() {
|
||||
@@ -718,7 +711,7 @@ impl FoldSnapshot {
|
||||
language_aware: bool,
|
||||
highlights: Highlights<'a>,
|
||||
) -> FoldChunks<'a> {
|
||||
let mut transform_cursor = self.transforms.cursor::<(FoldOffset, InlayOffset)>(&());
|
||||
let mut transform_cursor = self.transforms.cursor::<(FoldOffset, InlayOffset)>();
|
||||
transform_cursor.seek(&range.start, Bias::Right, &());
|
||||
|
||||
let inlay_start = {
|
||||
@@ -773,7 +766,7 @@ impl FoldSnapshot {
|
||||
}
|
||||
|
||||
pub fn clip_point(&self, point: FoldPoint, bias: Bias) -> FoldPoint {
|
||||
let mut cursor = self.transforms.cursor::<(FoldPoint, InlayPoint)>(&());
|
||||
let mut cursor = self.transforms.cursor::<(FoldPoint, InlayPoint)>();
|
||||
cursor.seek(&point, Bias::Right, &());
|
||||
if let Some(transform) = cursor.item() {
|
||||
let transform_start = cursor.start().0 .0;
|
||||
@@ -833,7 +826,7 @@ where
|
||||
let buffer = &inlay_snapshot.buffer;
|
||||
let start = buffer.anchor_before(range.start.to_offset(buffer));
|
||||
let end = buffer.anchor_after(range.end.to_offset(buffer));
|
||||
let mut cursor = folds.filter::<_, usize>(buffer, move |summary| {
|
||||
let mut cursor = folds.filter::<_, usize>(move |summary| {
|
||||
let start_cmp = start.cmp(&summary.max_end, buffer);
|
||||
let end_cmp = end.cmp(&summary.min_start, buffer);
|
||||
|
||||
@@ -952,10 +945,6 @@ impl sum_tree::Item for Transform {
|
||||
impl sum_tree::Summary for TransformSummary {
|
||||
type Context = ();
|
||||
|
||||
fn zero(_cx: &()) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, other: &Self, _: &()) {
|
||||
self.input += &other.input;
|
||||
self.output += &other.output;
|
||||
@@ -1039,10 +1028,6 @@ impl Default for FoldSummary {
|
||||
impl sum_tree::Summary for FoldSummary {
|
||||
type Context = MultiBufferSnapshot;
|
||||
|
||||
fn zero(_cx: &MultiBufferSnapshot) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, other: &Self, buffer: &Self::Context) {
|
||||
if other.min_start.cmp(&self.min_start, buffer) == Ordering::Less {
|
||||
self.min_start = other.min_start;
|
||||
@@ -1067,10 +1052,6 @@ impl sum_tree::Summary for FoldSummary {
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, FoldSummary> for FoldRange {
|
||||
fn zero(_cx: &MultiBufferSnapshot) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, summary: &'a FoldSummary, _: &MultiBufferSnapshot) {
|
||||
self.0.start = summary.start;
|
||||
self.0.end = summary.end;
|
||||
@@ -1084,10 +1065,6 @@ impl<'a> sum_tree::SeekTarget<'a, FoldSummary, FoldRange> for FoldRange {
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, FoldSummary> for usize {
|
||||
fn zero(_cx: &MultiBufferSnapshot) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, summary: &'a FoldSummary, _: &MultiBufferSnapshot) {
|
||||
*self += summary.count;
|
||||
}
|
||||
@@ -1219,7 +1196,7 @@ impl FoldOffset {
|
||||
pub fn to_point(self, snapshot: &FoldSnapshot) -> FoldPoint {
|
||||
let mut cursor = snapshot
|
||||
.transforms
|
||||
.cursor::<(FoldOffset, TransformSummary)>(&());
|
||||
.cursor::<(FoldOffset, TransformSummary)>();
|
||||
cursor.seek(&self, Bias::Right, &());
|
||||
let overshoot = if cursor.item().map_or(true, |t| t.is_fold()) {
|
||||
Point::new(0, (self.0 - cursor.start().0 .0) as u32)
|
||||
@@ -1233,7 +1210,7 @@ impl FoldOffset {
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn to_inlay_offset(self, snapshot: &FoldSnapshot) -> InlayOffset {
|
||||
let mut cursor = snapshot.transforms.cursor::<(FoldOffset, InlayOffset)>(&());
|
||||
let mut cursor = snapshot.transforms.cursor::<(FoldOffset, InlayOffset)>();
|
||||
cursor.seek(&self, Bias::Right, &());
|
||||
let overshoot = self.0 - cursor.start().0 .0;
|
||||
InlayOffset(cursor.start().1 .0 + overshoot)
|
||||
@@ -1263,30 +1240,18 @@ impl Sub for FoldOffset {
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, TransformSummary> for FoldOffset {
|
||||
fn zero(_cx: &()) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
|
||||
self.0 += &summary.output.len;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, TransformSummary> for InlayPoint {
|
||||
fn zero(_cx: &()) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
|
||||
self.0 += &summary.input.lines;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, TransformSummary> for InlayOffset {
|
||||
fn zero(_cx: &()) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
|
||||
self.0 += &summary.input.len;
|
||||
}
|
||||
|
||||
@@ -97,10 +97,6 @@ struct TransformSummary {
|
||||
impl sum_tree::Summary for TransformSummary {
|
||||
type Context = ();
|
||||
|
||||
fn zero(_cx: &()) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, other: &Self, _: &()) {
|
||||
self.input += &other.input;
|
||||
self.output += &other.output;
|
||||
@@ -141,10 +137,6 @@ impl SubAssign for InlayOffset {
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, TransformSummary> for InlayOffset {
|
||||
fn zero(_cx: &()) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
|
||||
self.0 += &summary.output.len;
|
||||
}
|
||||
@@ -170,30 +162,18 @@ impl Sub for InlayPoint {
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, TransformSummary> for InlayPoint {
|
||||
fn zero(_cx: &()) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
|
||||
self.0 += &summary.output.lines;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, TransformSummary> for usize {
|
||||
fn zero(_cx: &()) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
|
||||
*self += &summary.input.len;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, TransformSummary> for Point {
|
||||
fn zero(_cx: &()) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
|
||||
*self += &summary.input.lines;
|
||||
}
|
||||
@@ -495,8 +475,8 @@ impl InlayMap {
|
||||
(snapshot.clone(), Vec::new())
|
||||
} else {
|
||||
let mut inlay_edits = Patch::default();
|
||||
let mut new_transforms = SumTree::default();
|
||||
let mut cursor = snapshot.transforms.cursor::<(usize, InlayOffset)>(&());
|
||||
let mut new_transforms = SumTree::new();
|
||||
let mut cursor = snapshot.transforms.cursor::<(usize, InlayOffset)>();
|
||||
let mut buffer_edits_iter = buffer_edits.iter().peekable();
|
||||
while let Some(buffer_edit) = buffer_edits_iter.next() {
|
||||
new_transforms.append(cursor.slice(&buffer_edit.old.start, Bias::Left, &()), &());
|
||||
@@ -713,7 +693,7 @@ impl InlaySnapshot {
|
||||
pub fn to_point(&self, offset: InlayOffset) -> InlayPoint {
|
||||
let mut cursor = self
|
||||
.transforms
|
||||
.cursor::<(InlayOffset, (InlayPoint, usize))>(&());
|
||||
.cursor::<(InlayOffset, (InlayPoint, usize))>();
|
||||
cursor.seek(&offset, Bias::Right, &());
|
||||
let overshoot = offset.0 - cursor.start().0 .0;
|
||||
match cursor.item() {
|
||||
@@ -743,7 +723,7 @@ impl InlaySnapshot {
|
||||
pub fn to_offset(&self, point: InlayPoint) -> InlayOffset {
|
||||
let mut cursor = self
|
||||
.transforms
|
||||
.cursor::<(InlayPoint, (InlayOffset, Point))>(&());
|
||||
.cursor::<(InlayPoint, (InlayOffset, Point))>();
|
||||
cursor.seek(&point, Bias::Right, &());
|
||||
let overshoot = point.0 - cursor.start().0 .0;
|
||||
match cursor.item() {
|
||||
@@ -761,8 +741,9 @@ impl InlaySnapshot {
|
||||
None => self.len(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_buffer_point(&self, point: InlayPoint) -> Point {
|
||||
let mut cursor = self.transforms.cursor::<(InlayPoint, Point)>(&());
|
||||
let mut cursor = self.transforms.cursor::<(InlayPoint, Point)>();
|
||||
cursor.seek(&point, Bias::Right, &());
|
||||
match cursor.item() {
|
||||
Some(Transform::Isomorphic(_)) => {
|
||||
@@ -773,8 +754,9 @@ impl InlaySnapshot {
|
||||
None => self.buffer.max_point(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_buffer_offset(&self, offset: InlayOffset) -> usize {
|
||||
let mut cursor = self.transforms.cursor::<(InlayOffset, usize)>(&());
|
||||
let mut cursor = self.transforms.cursor::<(InlayOffset, usize)>();
|
||||
cursor.seek(&offset, Bias::Right, &());
|
||||
match cursor.item() {
|
||||
Some(Transform::Isomorphic(_)) => {
|
||||
@@ -787,7 +769,7 @@ impl InlaySnapshot {
|
||||
}
|
||||
|
||||
pub fn to_inlay_offset(&self, offset: usize) -> InlayOffset {
|
||||
let mut cursor = self.transforms.cursor::<(usize, InlayOffset)>(&());
|
||||
let mut cursor = self.transforms.cursor::<(usize, InlayOffset)>();
|
||||
cursor.seek(&offset, Bias::Left, &());
|
||||
loop {
|
||||
match cursor.item() {
|
||||
@@ -819,8 +801,9 @@ impl InlaySnapshot {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_inlay_point(&self, point: Point) -> InlayPoint {
|
||||
let mut cursor = self.transforms.cursor::<(Point, InlayPoint)>(&());
|
||||
let mut cursor = self.transforms.cursor::<(Point, InlayPoint)>();
|
||||
cursor.seek(&point, Bias::Left, &());
|
||||
loop {
|
||||
match cursor.item() {
|
||||
@@ -854,7 +837,7 @@ impl InlaySnapshot {
|
||||
}
|
||||
|
||||
pub fn clip_point(&self, mut point: InlayPoint, mut bias: Bias) -> InlayPoint {
|
||||
let mut cursor = self.transforms.cursor::<(InlayPoint, Point)>(&());
|
||||
let mut cursor = self.transforms.cursor::<(InlayPoint, Point)>();
|
||||
cursor.seek(&point, Bias::Left, &());
|
||||
loop {
|
||||
match cursor.item() {
|
||||
@@ -951,7 +934,7 @@ impl InlaySnapshot {
|
||||
pub fn text_summary_for_range(&self, range: Range<InlayOffset>) -> TextSummary {
|
||||
let mut summary = TextSummary::default();
|
||||
|
||||
let mut cursor = self.transforms.cursor::<(InlayOffset, usize)>(&());
|
||||
let mut cursor = self.transforms.cursor::<(InlayOffset, usize)>();
|
||||
cursor.seek(&range.start, Bias::Right, &());
|
||||
|
||||
let overshoot = range.start.0 - cursor.start().0 .0;
|
||||
@@ -999,7 +982,7 @@ impl InlaySnapshot {
|
||||
}
|
||||
|
||||
pub fn buffer_rows(&self, row: u32) -> InlayBufferRows<'_> {
|
||||
let mut cursor = self.transforms.cursor::<(InlayPoint, Point)>(&());
|
||||
let mut cursor = self.transforms.cursor::<(InlayPoint, Point)>();
|
||||
let inlay_point = InlayPoint::new(row, 0);
|
||||
cursor.seek(&inlay_point, Bias::Left, &());
|
||||
|
||||
@@ -1041,7 +1024,7 @@ impl InlaySnapshot {
|
||||
language_aware: bool,
|
||||
highlights: Highlights<'a>,
|
||||
) -> InlayChunks<'a> {
|
||||
let mut cursor = self.transforms.cursor::<(InlayOffset, usize)>(&());
|
||||
let mut cursor = self.transforms.cursor::<(InlayOffset, usize)>();
|
||||
cursor.seek(&range.start, Bias::Right, &());
|
||||
|
||||
let mut highlight_endpoints = Vec::new();
|
||||
|
||||
@@ -204,7 +204,7 @@ impl WrapMap {
|
||||
}
|
||||
} else {
|
||||
let old_rows = self.snapshot.transforms.summary().output.lines.row + 1;
|
||||
self.snapshot.transforms = SumTree::default();
|
||||
self.snapshot.transforms = SumTree::new();
|
||||
let summary = self.snapshot.tab_snapshot.text_summary();
|
||||
if !summary.lines.is_zero() {
|
||||
self.snapshot
|
||||
@@ -303,7 +303,7 @@ impl WrapMap {
|
||||
|
||||
impl WrapSnapshot {
|
||||
fn new(tab_snapshot: TabSnapshot) -> Self {
|
||||
let mut transforms = SumTree::default();
|
||||
let mut transforms = SumTree::new();
|
||||
let extent = tab_snapshot.text_summary();
|
||||
if !extent.lines.is_zero() {
|
||||
transforms.push(Transform::isomorphic(extent), &());
|
||||
@@ -324,7 +324,7 @@ impl WrapSnapshot {
|
||||
if tab_edits.is_empty() {
|
||||
new_transforms = self.transforms.clone();
|
||||
} else {
|
||||
let mut old_cursor = self.transforms.cursor::<TabPoint>(&());
|
||||
let mut old_cursor = self.transforms.cursor::<TabPoint>();
|
||||
|
||||
let mut tab_edits_iter = tab_edits.iter().peekable();
|
||||
new_transforms =
|
||||
@@ -424,7 +424,7 @@ impl WrapSnapshot {
|
||||
new_transforms = self.transforms.clone();
|
||||
} else {
|
||||
let mut row_edits = row_edits.into_iter().peekable();
|
||||
let mut old_cursor = self.transforms.cursor::<TabPoint>(&());
|
||||
let mut old_cursor = self.transforms.cursor::<TabPoint>();
|
||||
|
||||
new_transforms = old_cursor.slice(
|
||||
&TabPoint::new(row_edits.peek().unwrap().old_rows.start, 0),
|
||||
@@ -537,8 +537,8 @@ impl WrapSnapshot {
|
||||
|
||||
fn compute_edits(&self, tab_edits: &[TabEdit], new_snapshot: &WrapSnapshot) -> Patch<u32> {
|
||||
let mut wrap_edits = Vec::new();
|
||||
let mut old_cursor = self.transforms.cursor::<TransformSummary>(&());
|
||||
let mut new_cursor = new_snapshot.transforms.cursor::<TransformSummary>(&());
|
||||
let mut old_cursor = self.transforms.cursor::<TransformSummary>();
|
||||
let mut new_cursor = new_snapshot.transforms.cursor::<TransformSummary>();
|
||||
for mut tab_edit in tab_edits.iter().cloned() {
|
||||
tab_edit.old.start.0.column = 0;
|
||||
tab_edit.old.end.0 += Point::new(1, 0);
|
||||
@@ -579,7 +579,7 @@ impl WrapSnapshot {
|
||||
) -> WrapChunks<'a> {
|
||||
let output_start = WrapPoint::new(rows.start, 0);
|
||||
let output_end = WrapPoint::new(rows.end, 0);
|
||||
let mut transforms = self.transforms.cursor::<(WrapPoint, TabPoint)>(&());
|
||||
let mut transforms = self.transforms.cursor::<(WrapPoint, TabPoint)>();
|
||||
transforms.seek(&output_start, Bias::Right, &());
|
||||
let mut input_start = TabPoint(transforms.start().1 .0);
|
||||
if transforms.item().map_or(false, |t| t.is_isomorphic()) {
|
||||
@@ -606,7 +606,7 @@ impl WrapSnapshot {
|
||||
}
|
||||
|
||||
pub fn line_len(&self, row: u32) -> u32 {
|
||||
let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>(&());
|
||||
let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>();
|
||||
cursor.seek(&WrapPoint::new(row + 1, 0), Bias::Left, &());
|
||||
if cursor
|
||||
.item()
|
||||
@@ -626,7 +626,7 @@ impl WrapSnapshot {
|
||||
}
|
||||
|
||||
pub fn soft_wrap_indent(&self, row: u32) -> Option<u32> {
|
||||
let mut cursor = self.transforms.cursor::<WrapPoint>(&());
|
||||
let mut cursor = self.transforms.cursor::<WrapPoint>();
|
||||
cursor.seek(&WrapPoint::new(row + 1, 0), Bias::Right, &());
|
||||
cursor.item().and_then(|transform| {
|
||||
if transform.is_isomorphic() {
|
||||
@@ -642,7 +642,7 @@ impl WrapSnapshot {
|
||||
}
|
||||
|
||||
pub fn buffer_rows(&self, start_row: u32) -> WrapBufferRows {
|
||||
let mut transforms = self.transforms.cursor::<(WrapPoint, TabPoint)>(&());
|
||||
let mut transforms = self.transforms.cursor::<(WrapPoint, TabPoint)>();
|
||||
transforms.seek(&WrapPoint::new(start_row, 0), Bias::Left, &());
|
||||
let mut input_row = transforms.start().1.row();
|
||||
if transforms.item().map_or(false, |t| t.is_isomorphic()) {
|
||||
@@ -662,7 +662,7 @@ impl WrapSnapshot {
|
||||
}
|
||||
|
||||
pub fn to_tab_point(&self, point: WrapPoint) -> TabPoint {
|
||||
let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>(&());
|
||||
let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>();
|
||||
cursor.seek(&point, Bias::Right, &());
|
||||
let mut tab_point = cursor.start().1 .0;
|
||||
if cursor.item().map_or(false, |t| t.is_isomorphic()) {
|
||||
@@ -680,14 +680,14 @@ impl WrapSnapshot {
|
||||
}
|
||||
|
||||
pub fn tab_point_to_wrap_point(&self, point: TabPoint) -> WrapPoint {
|
||||
let mut cursor = self.transforms.cursor::<(TabPoint, WrapPoint)>(&());
|
||||
let mut cursor = self.transforms.cursor::<(TabPoint, WrapPoint)>();
|
||||
cursor.seek(&point, Bias::Right, &());
|
||||
WrapPoint(cursor.start().1 .0 + (point.0 - cursor.start().0 .0))
|
||||
}
|
||||
|
||||
pub fn clip_point(&self, mut point: WrapPoint, bias: Bias) -> WrapPoint {
|
||||
if bias == Bias::Left {
|
||||
let mut cursor = self.transforms.cursor::<WrapPoint>(&());
|
||||
let mut cursor = self.transforms.cursor::<WrapPoint>();
|
||||
cursor.seek(&point, Bias::Right, &());
|
||||
if cursor.item().map_or(false, |t| !t.is_isomorphic()) {
|
||||
point = *cursor.start();
|
||||
@@ -705,7 +705,7 @@ impl WrapSnapshot {
|
||||
|
||||
*point.column_mut() = 0;
|
||||
|
||||
let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>(&());
|
||||
let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>();
|
||||
cursor.seek(&point, Bias::Right, &());
|
||||
if cursor.item().is_none() {
|
||||
cursor.prev(&());
|
||||
@@ -725,7 +725,7 @@ impl WrapSnapshot {
|
||||
pub fn next_row_boundary(&self, mut point: WrapPoint) -> Option<u32> {
|
||||
point.0 += Point::new(1, 0);
|
||||
|
||||
let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>(&());
|
||||
let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>();
|
||||
cursor.seek(&point, Bias::Right, &());
|
||||
while let Some(transform) = cursor.item() {
|
||||
if transform.is_isomorphic() && cursor.start().1.column() == 0 {
|
||||
@@ -747,7 +747,7 @@ impl WrapSnapshot {
|
||||
);
|
||||
|
||||
{
|
||||
let mut transforms = self.transforms.cursor::<()>(&()).peekable();
|
||||
let mut transforms = self.transforms.cursor::<()>().peekable();
|
||||
while let Some(transform) = transforms.next() {
|
||||
if let Some(next_transform) = transforms.peek() {
|
||||
assert!(transform.is_isomorphic() != next_transform.is_isomorphic());
|
||||
@@ -982,10 +982,6 @@ impl WrapPoint {
|
||||
impl sum_tree::Summary for TransformSummary {
|
||||
type Context = ();
|
||||
|
||||
fn zero(_cx: &()) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, other: &Self, _: &()) {
|
||||
self.input += &other.input;
|
||||
self.output += &other.output;
|
||||
@@ -993,10 +989,6 @@ impl sum_tree::Summary for TransformSummary {
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, TransformSummary> for TabPoint {
|
||||
fn zero(_cx: &()) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
|
||||
self.0 += summary.input.lines;
|
||||
}
|
||||
@@ -1009,10 +1001,6 @@ impl<'a> sum_tree::SeekTarget<'a, TransformSummary, TransformSummary> for TabPoi
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapPoint {
|
||||
fn zero(_cx: &()) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
|
||||
self.0 += summary.output.lines;
|
||||
}
|
||||
|
||||
@@ -412,19 +412,6 @@ impl Default for EditorStyle {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_inlay_hints_style(cx: &WindowContext) -> HighlightStyle {
|
||||
let show_background = all_language_settings(None, cx)
|
||||
.language(None)
|
||||
.inlay_hints
|
||||
.show_background;
|
||||
|
||||
HighlightStyle {
|
||||
color: Some(cx.theme().status().hint),
|
||||
background_color: show_background.then(|| cx.theme().status().hint_background),
|
||||
..HighlightStyle::default()
|
||||
}
|
||||
}
|
||||
|
||||
type CompletionId = usize;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -563,6 +550,7 @@ pub struct Editor {
|
||||
signature_help_state: SignatureHelpState,
|
||||
auto_signature_help: Option<bool>,
|
||||
find_all_references_task_sources: Vec<Anchor>,
|
||||
backup_completion_task: CompletionId,
|
||||
next_completion_id: CompletionId,
|
||||
completion_documentation_pre_resolve_debounce: DebouncedDelay,
|
||||
available_code_actions: Option<(Location, Arc<[CodeAction]>)>,
|
||||
@@ -1908,6 +1896,7 @@ impl Editor {
|
||||
signature_help_state: SignatureHelpState::default(),
|
||||
auto_signature_help: None,
|
||||
find_all_references_task_sources: Vec::new(),
|
||||
backup_completion_task: 0,
|
||||
next_completion_id: 0,
|
||||
completion_documentation_pre_resolve_debounce: DebouncedDelay::new(),
|
||||
next_inlay_id: 0,
|
||||
@@ -4231,16 +4220,42 @@ impl Editor {
|
||||
}),
|
||||
trigger_kind,
|
||||
};
|
||||
let snapshot = buffer.read(cx).snapshot();
|
||||
let classifier = snapshot.char_classifier_at(&buffer_position);
|
||||
let first_char = options
|
||||
.trigger
|
||||
.as_ref()
|
||||
.and_then(|trigger| trigger.chars().next());
|
||||
let should_cancel_backup = first_char.is_some_and(|char| !classifier.is_word(char));
|
||||
let completions = provider.completions(&buffer, buffer_position, completion_context, cx);
|
||||
let sort_completions = provider.sort_completions();
|
||||
|
||||
let id = post_inc(&mut self.next_completion_id);
|
||||
if should_cancel_backup {
|
||||
self.backup_completion_task = id;
|
||||
}
|
||||
dbg!(
|
||||
id,
|
||||
&options.trigger,
|
||||
&should_cancel_backup,
|
||||
self.backup_completion_task
|
||||
);
|
||||
let task = cx.spawn(|this, mut cx| {
|
||||
async move {
|
||||
this.update(&mut cx, |this, _| {
|
||||
this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
|
||||
this.completion_tasks.retain(|(task_id, _)| {
|
||||
if *task_id >= id || *task_id == this.backup_completion_task {
|
||||
true
|
||||
} else {
|
||||
dbg!("dropping", id);
|
||||
false
|
||||
}
|
||||
});
|
||||
})?;
|
||||
let completions = completions.await.log_err();
|
||||
cx.background_executor()
|
||||
.timer(Duration::from_millis(1000))
|
||||
.await;
|
||||
let menu = if let Some(completions) = completions {
|
||||
let mut menu = CompletionsMenu {
|
||||
id,
|
||||
@@ -4266,13 +4281,24 @@ impl Editor {
|
||||
DebouncedDelay::new(),
|
||||
)),
|
||||
};
|
||||
menu.filter(query.as_deref(), cx.background_executor().clone())
|
||||
let completion_query = this.update(&mut cx, |this, cx| {
|
||||
Self::completion_query(&this.buffer.read(cx).read(cx), position)
|
||||
})?;
|
||||
let query = completion_query.or(query);
|
||||
menu.filter(dbg!(query.as_deref()), cx.background_executor().clone())
|
||||
.await;
|
||||
|
||||
this.update(&mut cx, |editor, cx| {
|
||||
dbg!("got here!");
|
||||
editor.backup_completion_task = editor.next_completion_id.saturating_sub(1);
|
||||
})?;
|
||||
|
||||
if menu.matches.is_empty() {
|
||||
None
|
||||
} else {
|
||||
this.update(&mut cx, |editor, cx| {
|
||||
editor.backup_completion_task =
|
||||
editor.next_completion_id.saturating_sub(1);
|
||||
let completions = menu.completions.clone();
|
||||
let matches = menu.matches.clone();
|
||||
|
||||
@@ -6749,22 +6775,22 @@ impl Editor {
|
||||
|
||||
let mut line_prefix = indent_size.chars().collect::<String>();
|
||||
|
||||
if let Some(comment_prefix) =
|
||||
buffer
|
||||
.language_scope_at(selection.head())
|
||||
.and_then(|language| {
|
||||
language
|
||||
.line_comment_prefixes()
|
||||
.iter()
|
||||
.find(|prefix| buffer.contains_str_at(indent_end, prefix))
|
||||
.cloned()
|
||||
})
|
||||
{
|
||||
line_prefix.push_str(&comment_prefix);
|
||||
should_rewrap = true;
|
||||
}
|
||||
|
||||
if selection.is_empty() {
|
||||
if let Some(comment_prefix) =
|
||||
buffer
|
||||
.language_scope_at(selection.head())
|
||||
.and_then(|language| {
|
||||
language
|
||||
.line_comment_prefixes()
|
||||
.iter()
|
||||
.find(|prefix| buffer.contains_str_at(indent_end, prefix))
|
||||
.cloned()
|
||||
})
|
||||
{
|
||||
line_prefix.push_str(&comment_prefix);
|
||||
should_rewrap = true;
|
||||
}
|
||||
|
||||
'expand_upwards: while start_row > 0 {
|
||||
let prev_row = start_row - 1;
|
||||
if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
|
||||
@@ -10047,8 +10073,9 @@ impl Editor {
|
||||
syntax: cx.editor_style.syntax.clone(),
|
||||
status: cx.editor_style.status.clone(),
|
||||
inlay_hints_style: HighlightStyle {
|
||||
color: Some(cx.theme().status().hint),
|
||||
font_weight: Some(FontWeight::BOLD),
|
||||
..make_inlay_hints_style(cx)
|
||||
..HighlightStyle::default()
|
||||
},
|
||||
suggestions_style: HighlightStyle {
|
||||
color: Some(cx.theme().status().predictive),
|
||||
@@ -13004,7 +13031,10 @@ impl Render for Editor {
|
||||
scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
|
||||
syntax: cx.theme().syntax().clone(),
|
||||
status: cx.theme().status().clone(),
|
||||
inlay_hints_style: make_inlay_hints_style(cx),
|
||||
inlay_hints_style: HighlightStyle {
|
||||
color: Some(cx.theme().status().hint),
|
||||
..HighlightStyle::default()
|
||||
},
|
||||
suggestions_style: HighlightStyle {
|
||||
color: Some(cx.theme().status().predictive),
|
||||
..HighlightStyle::default()
|
||||
|
||||
@@ -4017,39 +4017,6 @@ async fn test_rewrap(cx: &mut TestAppContext) {
|
||||
cx.assert_editor_state(wrapped_text);
|
||||
}
|
||||
|
||||
// Test that rewrapping works inside of a selection
|
||||
{
|
||||
let language = Arc::new(Language::new(
|
||||
LanguageConfig {
|
||||
line_comments: vec!["// ".into()],
|
||||
..LanguageConfig::default()
|
||||
},
|
||||
None,
|
||||
));
|
||||
cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
|
||||
|
||||
let unwrapped_text = indoc! {"
|
||||
«// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.ˇ»
|
||||
"};
|
||||
|
||||
let wrapped_text = indoc! {"
|
||||
// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
|
||||
// purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
|
||||
// auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
|
||||
// tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
|
||||
// Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
|
||||
// vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
|
||||
// et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
|
||||
// dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
|
||||
// viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
|
||||
// porttitor id. Aliquam id accumsan eros.ˇ
|
||||
"};
|
||||
|
||||
cx.set_state(unwrapped_text);
|
||||
cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
|
||||
cx.assert_editor_state(wrapped_text);
|
||||
}
|
||||
|
||||
// Test that cursors that expand to the same region are collapsed.
|
||||
{
|
||||
let language = Arc::new(Language::new(
|
||||
|
||||
@@ -2079,13 +2079,13 @@ impl EditorElement {
|
||||
.id(("path excerpt header", EntityId::from(block_id)))
|
||||
.w_full()
|
||||
.px(header_padding)
|
||||
.pt(header_padding)
|
||||
.child(
|
||||
h_flex()
|
||||
.flex_basis(Length::Definite(DefiniteLength::Fraction(0.667)))
|
||||
.id("path header block")
|
||||
.h(2. * cx.line_height())
|
||||
.px(gpui::px(12.))
|
||||
.pl(gpui::px(12.))
|
||||
.pr(gpui::px(8.))
|
||||
.rounded_md()
|
||||
.shadow_md()
|
||||
.border_1()
|
||||
|
||||
@@ -37,20 +37,12 @@ impl sum_tree::Item for GitBlameEntry {
|
||||
impl sum_tree::Summary for GitBlameEntrySummary {
|
||||
type Context = ();
|
||||
|
||||
fn zero(_cx: &()) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, summary: &Self, _cx: &()) {
|
||||
self.rows += summary.rows;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, GitBlameEntrySummary> for u32 {
|
||||
fn zero(_cx: &()) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, summary: &'a GitBlameEntrySummary, _cx: &()) {
|
||||
*self += summary.rows;
|
||||
}
|
||||
@@ -199,7 +191,7 @@ impl GitBlame {
|
||||
) -> impl 'a + Iterator<Item = Option<BlameEntry>> {
|
||||
self.sync(cx);
|
||||
|
||||
let mut cursor = self.entries.cursor::<u32>(&());
|
||||
let mut cursor = self.entries.cursor::<u32>();
|
||||
rows.into_iter().map(move |row| {
|
||||
let row = row?;
|
||||
cursor.seek_forward(&row.0, Bias::Right, &());
|
||||
@@ -257,8 +249,8 @@ impl GitBlame {
|
||||
})
|
||||
.peekable();
|
||||
|
||||
let mut new_entries = SumTree::default();
|
||||
let mut cursor = self.entries.cursor::<u32>(&());
|
||||
let mut new_entries = SumTree::new();
|
||||
let mut cursor = self.entries.cursor::<u32>();
|
||||
|
||||
while let Some(mut edit) = row_edits.next() {
|
||||
while let Some(next_edit) = row_edits.peek() {
|
||||
|
||||
@@ -1205,7 +1205,6 @@ mod tests {
|
||||
show_type_hints: true,
|
||||
show_parameter_hints: true,
|
||||
show_other_hints: true,
|
||||
show_background: false,
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
@@ -1337,7 +1337,6 @@ mod tests {
|
||||
show_type_hints: true,
|
||||
show_parameter_hints: true,
|
||||
show_other_hints: true,
|
||||
show_background: false,
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
@@ -337,7 +337,7 @@ impl InlayHintCache {
|
||||
/// If needed, queries LSP for new inlay hints, using the invalidation strategy given.
|
||||
/// To reduce inlay hint jumping, attempts to query a visible range of the editor(s) first,
|
||||
/// followed by the delayed queries of the same range above and below the visible one.
|
||||
/// This way, subsequent refresh invocations are less likely to trigger LSP queries for the invisible ranges.
|
||||
/// This way, concequent refresh invocations are less likely to trigger LSP queries for the invisible ranges.
|
||||
pub(super) fn spawn_hint_refresh(
|
||||
&mut self,
|
||||
reason_description: &'static str,
|
||||
@@ -1296,7 +1296,6 @@ pub mod tests {
|
||||
show_type_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
|
||||
show_parameter_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Parameter)),
|
||||
show_other_hints: allowed_hint_kinds.contains(&None),
|
||||
show_background: false,
|
||||
})
|
||||
});
|
||||
|
||||
@@ -1429,7 +1428,6 @@ pub mod tests {
|
||||
show_type_hints: true,
|
||||
show_parameter_hints: true,
|
||||
show_other_hints: true,
|
||||
show_background: false,
|
||||
})
|
||||
});
|
||||
|
||||
@@ -1549,7 +1547,6 @@ pub mod tests {
|
||||
show_type_hints: true,
|
||||
show_parameter_hints: true,
|
||||
show_other_hints: true,
|
||||
show_background: false,
|
||||
})
|
||||
});
|
||||
|
||||
@@ -1780,7 +1777,6 @@ pub mod tests {
|
||||
show_type_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
|
||||
show_parameter_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Parameter)),
|
||||
show_other_hints: allowed_hint_kinds.contains(&None),
|
||||
show_background: false,
|
||||
})
|
||||
});
|
||||
|
||||
@@ -1945,7 +1941,6 @@ pub mod tests {
|
||||
show_parameter_hints: new_allowed_hint_kinds
|
||||
.contains(&Some(InlayHintKind::Parameter)),
|
||||
show_other_hints: new_allowed_hint_kinds.contains(&None),
|
||||
show_background: false,
|
||||
})
|
||||
});
|
||||
cx.executor().run_until_parked();
|
||||
@@ -1992,7 +1987,6 @@ pub mod tests {
|
||||
show_parameter_hints: another_allowed_hint_kinds
|
||||
.contains(&Some(InlayHintKind::Parameter)),
|
||||
show_other_hints: another_allowed_hint_kinds.contains(&None),
|
||||
show_background: false,
|
||||
})
|
||||
});
|
||||
cx.executor().run_until_parked();
|
||||
@@ -2053,7 +2047,6 @@ pub mod tests {
|
||||
show_parameter_hints: final_allowed_hint_kinds
|
||||
.contains(&Some(InlayHintKind::Parameter)),
|
||||
show_other_hints: final_allowed_hint_kinds.contains(&None),
|
||||
show_background: false,
|
||||
})
|
||||
});
|
||||
cx.executor().run_until_parked();
|
||||
@@ -2129,7 +2122,6 @@ pub mod tests {
|
||||
show_type_hints: true,
|
||||
show_parameter_hints: true,
|
||||
show_other_hints: true,
|
||||
show_background: false,
|
||||
})
|
||||
});
|
||||
|
||||
@@ -2264,7 +2256,6 @@ pub mod tests {
|
||||
show_type_hints: true,
|
||||
show_parameter_hints: true,
|
||||
show_other_hints: true,
|
||||
show_background: false,
|
||||
})
|
||||
});
|
||||
|
||||
@@ -2560,7 +2551,6 @@ pub mod tests {
|
||||
show_type_hints: true,
|
||||
show_parameter_hints: true,
|
||||
show_other_hints: true,
|
||||
show_background: false,
|
||||
})
|
||||
});
|
||||
|
||||
@@ -2912,7 +2902,6 @@ pub mod tests {
|
||||
show_type_hints: false,
|
||||
show_parameter_hints: false,
|
||||
show_other_hints: false,
|
||||
show_background: false,
|
||||
})
|
||||
});
|
||||
|
||||
@@ -3107,7 +3096,6 @@ pub mod tests {
|
||||
show_type_hints: true,
|
||||
show_parameter_hints: true,
|
||||
show_other_hints: true,
|
||||
show_background: false,
|
||||
})
|
||||
});
|
||||
cx.executor().run_until_parked();
|
||||
@@ -3143,7 +3131,6 @@ pub mod tests {
|
||||
show_type_hints: true,
|
||||
show_parameter_hints: true,
|
||||
show_other_hints: true,
|
||||
show_background: false,
|
||||
})
|
||||
});
|
||||
|
||||
@@ -3238,7 +3225,6 @@ pub mod tests {
|
||||
show_type_hints: true,
|
||||
show_parameter_hints: true,
|
||||
show_other_hints: true,
|
||||
show_background: false,
|
||||
})
|
||||
});
|
||||
|
||||
@@ -3319,7 +3305,6 @@ pub mod tests {
|
||||
show_type_hints: true,
|
||||
show_parameter_hints: true,
|
||||
show_other_hints: true,
|
||||
show_background: false,
|
||||
})
|
||||
});
|
||||
cx.executor().run_until_parked();
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
[package]
|
||||
name = "evals"
|
||||
description = "Evaluations for Zed's AI features"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[[bin]]
|
||||
name = "eval"
|
||||
path = "src/eval.rs"
|
||||
|
||||
[dependencies]
|
||||
clap.workspace = true
|
||||
anyhow.workspace = true
|
||||
client.workspace = true
|
||||
clock.workspace = true
|
||||
collections.workspace = true
|
||||
env_logger.workspace = true
|
||||
feature_flags.workspace = true
|
||||
fs.workspace = true
|
||||
git.workspace = true
|
||||
gpui.workspace = true
|
||||
isahc_http_client.workspace = true
|
||||
language.workspace = true
|
||||
languages.workspace = true
|
||||
http_client.workspace = true
|
||||
open_ai.workspace = true
|
||||
project.workspace = true
|
||||
settings.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
smol.workspace = true
|
||||
semantic_index.workspace = true
|
||||
node_runtime.workspace = true
|
||||
@@ -1 +0,0 @@
|
||||
../../LICENSE-GPL
|
||||
@@ -1,14 +0,0 @@
|
||||
fn main() {
|
||||
if cfg!(target_os = "macos") {
|
||||
println!("cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET=10.15.7");
|
||||
|
||||
println!("cargo:rerun-if-env-changed=ZED_BUNDLE");
|
||||
if std::env::var("ZED_BUNDLE").ok().as_deref() == Some("true") {
|
||||
// Find WebRTC.framework in the Frameworks folder when running as part of an application bundle.
|
||||
println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path/../Frameworks");
|
||||
} else {
|
||||
// Find WebRTC.framework as a sibling of the executable when running outside of an application bundle.
|
||||
println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,644 +0,0 @@
|
||||
use ::fs::{Fs, RealFs};
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use client::{Client, UserStore};
|
||||
use clock::RealSystemClock;
|
||||
use collections::BTreeMap;
|
||||
use feature_flags::FeatureFlagAppExt as _;
|
||||
use git::GitHostingProviderRegistry;
|
||||
use gpui::{AsyncAppContext, BackgroundExecutor, Context, Model};
|
||||
use http_client::{HttpClient, Method};
|
||||
use language::LanguageRegistry;
|
||||
use node_runtime::FakeNodeRuntime;
|
||||
use open_ai::OpenAiEmbeddingModel;
|
||||
use project::Project;
|
||||
use semantic_index::{OpenAiEmbeddingProvider, ProjectIndex, SemanticDb, Status};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::SettingsStore;
|
||||
use smol::channel::bounded;
|
||||
use smol::io::AsyncReadExt;
|
||||
use smol::Timer;
|
||||
use std::ops::RangeInclusive;
|
||||
use std::time::Duration;
|
||||
use std::{
|
||||
fs,
|
||||
path::Path,
|
||||
process::{exit, Command, Stdio},
|
||||
sync::{
|
||||
atomic::{AtomicUsize, Ordering::SeqCst},
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
|
||||
const CODESEARCH_NET_DIR: &'static str = "target/datasets/code-search-net";
|
||||
const EVAL_REPOS_DIR: &'static str = "target/datasets/eval-repos";
|
||||
const EVAL_DB_PATH: &'static str = "target/eval_db";
|
||||
const SEARCH_RESULT_LIMIT: usize = 8;
|
||||
const SKIP_EVAL_PATH: &'static str = ".skip_eval";
|
||||
|
||||
#[derive(clap::Parser)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
struct Cli {
|
||||
#[command(subcommand)]
|
||||
command: Commands,
|
||||
}
|
||||
|
||||
#[derive(clap::Subcommand)]
|
||||
enum Commands {
|
||||
Fetch {},
|
||||
Run {
|
||||
#[arg(long)]
|
||||
repo: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
struct EvaluationProject {
|
||||
repo: String,
|
||||
sha: String,
|
||||
queries: Vec<EvaluationQuery>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
struct EvaluationQuery {
|
||||
query: String,
|
||||
expected_results: Vec<EvaluationSearchResult>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
|
||||
struct EvaluationSearchResult {
|
||||
file: String,
|
||||
lines: RangeInclusive<u32>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
struct EvaluationProjectOutcome {
|
||||
repo: String,
|
||||
sha: String,
|
||||
queries: Vec<EvaluationQueryOutcome>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
struct EvaluationQueryOutcome {
|
||||
repo: String,
|
||||
query: String,
|
||||
expected_results: Vec<EvaluationSearchResult>,
|
||||
actual_results: Vec<EvaluationSearchResult>,
|
||||
covered_file_count: usize,
|
||||
overlapped_result_count: usize,
|
||||
covered_result_count: usize,
|
||||
total_result_count: usize,
|
||||
covered_result_indices: Vec<usize>,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let cli = Cli::parse();
|
||||
env_logger::init();
|
||||
|
||||
gpui::App::headless().run(move |cx| {
|
||||
let executor = cx.background_executor().clone();
|
||||
let client = isahc_http_client::IsahcHttpClient::new(None, None);
|
||||
cx.set_http_client(client.clone());
|
||||
match cli.command {
|
||||
Commands::Fetch {} => {
|
||||
executor
|
||||
.clone()
|
||||
.spawn(async move {
|
||||
if let Err(err) = fetch_evaluation_resources(client, &executor).await {
|
||||
eprintln!("Error: {}", err);
|
||||
exit(1);
|
||||
}
|
||||
exit(0);
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
Commands::Run { repo } => {
|
||||
cx.spawn(|mut cx| async move {
|
||||
if let Err(err) = run_evaluation(repo, &executor, &mut cx).await {
|
||||
eprintln!("Error: {}", err);
|
||||
exit(1);
|
||||
}
|
||||
exit(0);
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn fetch_evaluation_resources(
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
executor: &BackgroundExecutor,
|
||||
) -> Result<()> {
|
||||
fetch_code_search_net_resources(&*http_client).await?;
|
||||
fetch_eval_repos(executor, &*http_client).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn fetch_code_search_net_resources(http_client: &dyn HttpClient) -> Result<()> {
|
||||
eprintln!("Fetching CodeSearchNet evaluations...");
|
||||
|
||||
let annotations_url = "https://raw.githubusercontent.com/github/CodeSearchNet/master/resources/annotationStore.csv";
|
||||
|
||||
let dataset_dir = Path::new(CODESEARCH_NET_DIR);
|
||||
fs::create_dir_all(&dataset_dir).expect("failed to create CodeSearchNet directory");
|
||||
|
||||
// Fetch the annotations CSV, which contains the human-annotated search relevances
|
||||
let annotations_path = dataset_dir.join("annotations.csv");
|
||||
let annotations_csv_content = if annotations_path.exists() {
|
||||
fs::read_to_string(&annotations_path).expect("failed to read annotations")
|
||||
} else {
|
||||
let response = http_client
|
||||
.get(annotations_url, Default::default(), true)
|
||||
.await
|
||||
.expect("failed to fetch annotations csv");
|
||||
let mut body = String::new();
|
||||
response
|
||||
.into_body()
|
||||
.read_to_string(&mut body)
|
||||
.await
|
||||
.expect("failed to read annotations.csv response");
|
||||
fs::write(annotations_path, &body).expect("failed to write annotations.csv");
|
||||
body
|
||||
};
|
||||
|
||||
// Parse the annotations CSV. Skip over queries with zero relevance.
|
||||
let rows = annotations_csv_content.lines().filter_map(|line| {
|
||||
let mut values = line.split(',');
|
||||
let _language = values.next()?;
|
||||
let query = values.next()?;
|
||||
let github_url = values.next()?;
|
||||
let score = values.next()?;
|
||||
|
||||
if score == "0" {
|
||||
return None;
|
||||
}
|
||||
|
||||
let url_path = github_url.strip_prefix("https://github.com/")?;
|
||||
let (url_path, hash) = url_path.split_once('#')?;
|
||||
let (repo_name, url_path) = url_path.split_once("/blob/")?;
|
||||
let (sha, file_path) = url_path.split_once('/')?;
|
||||
let line_range = if let Some((start, end)) = hash.split_once('-') {
|
||||
start.strip_prefix("L")?.parse::<u32>().ok()?..=end.strip_prefix("L")?.parse().ok()?
|
||||
} else {
|
||||
let row = hash.strip_prefix("L")?.parse().ok()?;
|
||||
row..=row
|
||||
};
|
||||
Some((repo_name, sha, query, file_path, line_range))
|
||||
});
|
||||
|
||||
// Group the annotations by repo and sha.
|
||||
let mut evaluations_by_repo = BTreeMap::new();
|
||||
for (repo_name, sha, query, file_path, lines) in rows {
|
||||
let evaluation_project = evaluations_by_repo
|
||||
.entry((repo_name, sha))
|
||||
.or_insert_with(|| EvaluationProject {
|
||||
repo: repo_name.to_string(),
|
||||
sha: sha.to_string(),
|
||||
queries: Vec::new(),
|
||||
});
|
||||
|
||||
let ix = evaluation_project
|
||||
.queries
|
||||
.iter()
|
||||
.position(|entry| entry.query == query)
|
||||
.unwrap_or_else(|| {
|
||||
evaluation_project.queries.push(EvaluationQuery {
|
||||
query: query.to_string(),
|
||||
expected_results: Vec::new(),
|
||||
});
|
||||
evaluation_project.queries.len() - 1
|
||||
});
|
||||
let results = &mut evaluation_project.queries[ix].expected_results;
|
||||
let result = EvaluationSearchResult {
|
||||
file: file_path.to_string(),
|
||||
lines,
|
||||
};
|
||||
if !results.contains(&result) {
|
||||
results.push(result);
|
||||
}
|
||||
}
|
||||
|
||||
let evaluations = evaluations_by_repo.into_values().collect::<Vec<_>>();
|
||||
let evaluations_path = dataset_dir.join("evaluations.json");
|
||||
fs::write(
|
||||
&evaluations_path,
|
||||
serde_json::to_vec_pretty(&evaluations).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
eprintln!(
|
||||
"Fetched CodeSearchNet evaluations into {}",
|
||||
evaluations_path.display()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn run_evaluation(
|
||||
only_repo: Option<String>,
|
||||
executor: &BackgroundExecutor,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Result<()> {
|
||||
let mut http_client = None;
|
||||
cx.update(|cx| {
|
||||
let mut store = SettingsStore::new(cx);
|
||||
store
|
||||
.set_default_settings(settings::default_settings().as_ref(), cx)
|
||||
.unwrap();
|
||||
cx.set_global(store);
|
||||
client::init_settings(cx);
|
||||
language::init(cx);
|
||||
Project::init_settings(cx);
|
||||
http_client = Some(cx.http_client());
|
||||
cx.update_flags(false, vec![]);
|
||||
})
|
||||
.unwrap();
|
||||
let http_client = http_client.unwrap();
|
||||
let dataset_dir = Path::new(CODESEARCH_NET_DIR);
|
||||
let evaluations_path = dataset_dir.join("evaluations.json");
|
||||
let repos_dir = Path::new(EVAL_REPOS_DIR);
|
||||
let db_path = Path::new(EVAL_DB_PATH);
|
||||
let api_key = std::env::var("OPENAI_API_KEY").unwrap();
|
||||
let git_hosting_provider_registry = Arc::new(GitHostingProviderRegistry::new());
|
||||
let fs = Arc::new(RealFs::new(git_hosting_provider_registry, None)) as Arc<dyn Fs>;
|
||||
let clock = Arc::new(RealSystemClock);
|
||||
let client = cx
|
||||
.update(|cx| {
|
||||
Client::new(
|
||||
clock,
|
||||
Arc::new(http_client::HttpClientWithUrl::new(
|
||||
http_client.clone(),
|
||||
"https://zed.dev",
|
||||
None,
|
||||
)),
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.unwrap();
|
||||
let user_store = cx
|
||||
.new_model(|cx| UserStore::new(client.clone(), cx))
|
||||
.unwrap();
|
||||
let node_runtime = Arc::new(FakeNodeRuntime {});
|
||||
|
||||
let evaluations = fs::read(&evaluations_path).expect("failed to read evaluations.json");
|
||||
let evaluations: Vec<EvaluationProject> = serde_json::from_slice(&evaluations).unwrap();
|
||||
|
||||
let embedding_provider = Arc::new(OpenAiEmbeddingProvider::new(
|
||||
http_client.clone(),
|
||||
OpenAiEmbeddingModel::TextEmbedding3Small,
|
||||
open_ai::OPEN_AI_API_URL.to_string(),
|
||||
api_key,
|
||||
));
|
||||
|
||||
let language_registry = Arc::new(LanguageRegistry::new(executor.clone()));
|
||||
cx.update(|cx| languages::init(language_registry.clone(), node_runtime.clone(), cx))
|
||||
.unwrap();
|
||||
|
||||
let mut covered_result_count = 0;
|
||||
let mut overlapped_result_count = 0;
|
||||
let mut covered_file_count = 0;
|
||||
let mut total_result_count = 0;
|
||||
eprint!("Running evals.");
|
||||
|
||||
for evaluation_project in evaluations {
|
||||
if only_repo
|
||||
.as_ref()
|
||||
.map_or(false, |only_repo| only_repo != &evaluation_project.repo)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
eprint!("\r\x1B[2K");
|
||||
eprint!(
|
||||
"Running evals. {}/{} covered. {}/{} overlapped. {}/{} files captured. Project: {}...",
|
||||
covered_result_count,
|
||||
total_result_count,
|
||||
overlapped_result_count,
|
||||
total_result_count,
|
||||
covered_file_count,
|
||||
total_result_count,
|
||||
evaluation_project.repo
|
||||
);
|
||||
|
||||
let repo_db_path =
|
||||
db_path.join(format!("{}.db", evaluation_project.repo.replace('/', "_")));
|
||||
let mut semantic_index = SemanticDb::new(repo_db_path, embedding_provider.clone(), cx)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let repo_dir = repos_dir.join(&evaluation_project.repo);
|
||||
if !repo_dir.exists() || repo_dir.join(SKIP_EVAL_PATH).exists() {
|
||||
eprintln!("Skipping {}: directory not found", evaluation_project.repo);
|
||||
continue;
|
||||
}
|
||||
|
||||
let project = cx
|
||||
.update(|cx| {
|
||||
Project::local(
|
||||
client.clone(),
|
||||
node_runtime.clone(),
|
||||
user_store.clone(),
|
||||
language_registry.clone(),
|
||||
fs.clone(),
|
||||
None,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let (worktree, _) = project
|
||||
.update(cx, |project, cx| {
|
||||
project.find_or_create_worktree(repo_dir, true, cx)
|
||||
})?
|
||||
.await?;
|
||||
|
||||
worktree
|
||||
.update(cx, |worktree, _| {
|
||||
worktree.as_local().unwrap().scan_complete()
|
||||
})
|
||||
.unwrap()
|
||||
.await;
|
||||
|
||||
let project_index = cx
|
||||
.update(|cx| semantic_index.create_project_index(project.clone(), cx))
|
||||
.unwrap();
|
||||
wait_for_indexing_complete(&project_index, cx, Some(Duration::from_secs(120))).await;
|
||||
|
||||
for query in evaluation_project.queries {
|
||||
let results = cx
|
||||
.update(|cx| {
|
||||
let project_index = project_index.read(cx);
|
||||
project_index.search(query.query.clone(), SEARCH_RESULT_LIMIT, cx)
|
||||
})
|
||||
.unwrap()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let results = SemanticDb::load_results(results, &fs.clone(), &cx)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let mut project_covered_result_count = 0;
|
||||
let mut project_overlapped_result_count = 0;
|
||||
let mut project_covered_file_count = 0;
|
||||
let mut covered_result_indices = Vec::new();
|
||||
for expected_result in &query.expected_results {
|
||||
let mut file_matched = false;
|
||||
let mut range_overlapped = false;
|
||||
let mut range_covered = false;
|
||||
|
||||
for (ix, result) in results.iter().enumerate() {
|
||||
if result.path.as_ref() == Path::new(&expected_result.file) {
|
||||
file_matched = true;
|
||||
let start_matched =
|
||||
result.row_range.contains(&expected_result.lines.start());
|
||||
let end_matched = result.row_range.contains(&expected_result.lines.end());
|
||||
|
||||
if start_matched || end_matched {
|
||||
range_overlapped = true;
|
||||
}
|
||||
|
||||
if start_matched && end_matched {
|
||||
range_covered = true;
|
||||
covered_result_indices.push(ix);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if range_covered {
|
||||
project_covered_result_count += 1
|
||||
};
|
||||
if range_overlapped {
|
||||
project_overlapped_result_count += 1
|
||||
};
|
||||
if file_matched {
|
||||
project_covered_file_count += 1
|
||||
};
|
||||
}
|
||||
let outcome_repo = evaluation_project.repo.clone();
|
||||
|
||||
let query_results = EvaluationQueryOutcome {
|
||||
repo: outcome_repo,
|
||||
query: query.query,
|
||||
total_result_count: query.expected_results.len(),
|
||||
covered_result_count: project_covered_result_count,
|
||||
overlapped_result_count: project_overlapped_result_count,
|
||||
covered_file_count: project_covered_file_count,
|
||||
expected_results: query.expected_results,
|
||||
actual_results: results
|
||||
.iter()
|
||||
.map(|result| EvaluationSearchResult {
|
||||
file: result.path.to_string_lossy().to_string(),
|
||||
lines: result.row_range.clone(),
|
||||
})
|
||||
.collect(),
|
||||
covered_result_indices,
|
||||
};
|
||||
|
||||
overlapped_result_count += query_results.overlapped_result_count;
|
||||
covered_result_count += query_results.covered_result_count;
|
||||
covered_file_count += query_results.covered_file_count;
|
||||
total_result_count += query_results.total_result_count;
|
||||
|
||||
println!("{}", serde_json::to_string(&query_results).unwrap());
|
||||
}
|
||||
|
||||
user_store
|
||||
.update(cx, |_, _| {
|
||||
drop(semantic_index);
|
||||
drop(project);
|
||||
drop(worktree);
|
||||
drop(project_index);
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
eprint!(
|
||||
"Running evals. {}/{} covered. {}/{} overlapped. {}/{} files captured.",
|
||||
covered_result_count,
|
||||
total_result_count,
|
||||
overlapped_result_count,
|
||||
total_result_count,
|
||||
covered_file_count,
|
||||
total_result_count,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_indexing_complete(
|
||||
project_index: &Model<ProjectIndex>,
|
||||
cx: &mut AsyncAppContext,
|
||||
timeout: Option<Duration>,
|
||||
) {
|
||||
let (tx, rx) = bounded(1);
|
||||
let subscription = cx.update(|cx| {
|
||||
cx.subscribe(project_index, move |_, event, _| {
|
||||
if let Status::Idle = event {
|
||||
let _ = tx.try_send(*event);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
let result = match timeout {
|
||||
Some(timeout_duration) => {
|
||||
smol::future::or(
|
||||
async {
|
||||
rx.recv().await.map_err(|_| ())?;
|
||||
Ok(())
|
||||
},
|
||||
async {
|
||||
Timer::after(timeout_duration).await;
|
||||
Err(())
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
None => rx.recv().await.map(|_| ()).map_err(|_| ()),
|
||||
};
|
||||
|
||||
match result {
|
||||
Ok(_) => (),
|
||||
Err(_) => {
|
||||
if let Some(timeout) = timeout {
|
||||
eprintln!("Timeout: Indexing did not complete within {:?}", timeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drop(subscription);
|
||||
}
|
||||
|
||||
async fn fetch_eval_repos(
|
||||
executor: &BackgroundExecutor,
|
||||
http_client: &dyn HttpClient,
|
||||
) -> Result<()> {
|
||||
let dataset_dir = Path::new(CODESEARCH_NET_DIR);
|
||||
let evaluations_path = dataset_dir.join("evaluations.json");
|
||||
let repos_dir = Path::new(EVAL_REPOS_DIR);
|
||||
|
||||
let evaluations = fs::read(&evaluations_path).expect("failed to read evaluations.json");
|
||||
let evaluations: Vec<EvaluationProject> = serde_json::from_slice(&evaluations).unwrap();
|
||||
|
||||
eprint!("Fetching evaluation repositories...");
|
||||
|
||||
executor
|
||||
.scoped(move |scope| {
|
||||
let done_count = Arc::new(AtomicUsize::new(0));
|
||||
let len = evaluations.len();
|
||||
for chunk in evaluations.chunks(evaluations.len() / 8) {
|
||||
let chunk = chunk.to_vec();
|
||||
let done_count = done_count.clone();
|
||||
scope.spawn(async move {
|
||||
for EvaluationProject { repo, sha, .. } in chunk {
|
||||
eprint!(
|
||||
"\rFetching evaluation repositories ({}/{})...",
|
||||
done_count.load(SeqCst),
|
||||
len,
|
||||
);
|
||||
|
||||
fetch_eval_repo(repo, sha, repos_dir, http_client).await;
|
||||
done_count.fetch_add(1, SeqCst);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn fetch_eval_repo(
|
||||
repo: String,
|
||||
sha: String,
|
||||
repos_dir: &Path,
|
||||
http_client: &dyn HttpClient,
|
||||
) {
|
||||
let Some((owner, repo_name)) = repo.split_once('/') else {
|
||||
return;
|
||||
};
|
||||
let repo_dir = repos_dir.join(owner).join(repo_name);
|
||||
fs::create_dir_all(&repo_dir).unwrap();
|
||||
let skip_eval_path = repo_dir.join(SKIP_EVAL_PATH);
|
||||
if skip_eval_path.exists() {
|
||||
return;
|
||||
}
|
||||
if let Ok(head_content) = fs::read_to_string(&repo_dir.join(".git").join("HEAD")) {
|
||||
if head_content.trim() == sha {
|
||||
return;
|
||||
}
|
||||
}
|
||||
let repo_response = http_client
|
||||
.send(
|
||||
http_client::Request::builder()
|
||||
.method(Method::HEAD)
|
||||
.uri(format!("https://github.com/{}", repo))
|
||||
.body(Default::default())
|
||||
.expect(""),
|
||||
)
|
||||
.await
|
||||
.expect("failed to check github repo");
|
||||
if !repo_response.status().is_success() && !repo_response.status().is_redirection() {
|
||||
fs::write(&skip_eval_path, "").unwrap();
|
||||
eprintln!(
|
||||
"Repo {repo} is no longer public ({:?}). Skipping",
|
||||
repo_response.status()
|
||||
);
|
||||
return;
|
||||
}
|
||||
if !repo_dir.join(".git").exists() {
|
||||
let init_output = Command::new("git")
|
||||
.current_dir(&repo_dir)
|
||||
.args(&["init"])
|
||||
.output()
|
||||
.unwrap();
|
||||
if !init_output.status.success() {
|
||||
eprintln!(
|
||||
"Failed to initialize git repository for {}: {}",
|
||||
repo,
|
||||
String::from_utf8_lossy(&init_output.stderr)
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
let url = format!("https://github.com/{}.git", repo);
|
||||
Command::new("git")
|
||||
.current_dir(&repo_dir)
|
||||
.args(&["remote", "add", "-f", "origin", &url])
|
||||
.stdin(Stdio::null())
|
||||
.output()
|
||||
.unwrap();
|
||||
let fetch_output = Command::new("git")
|
||||
.current_dir(&repo_dir)
|
||||
.args(&["fetch", "--depth", "1", "origin", &sha])
|
||||
.stdin(Stdio::null())
|
||||
.output()
|
||||
.unwrap();
|
||||
if !fetch_output.status.success() {
|
||||
eprintln!(
|
||||
"Failed to fetch {} for {}: {}",
|
||||
sha,
|
||||
repo,
|
||||
String::from_utf8_lossy(&fetch_output.stderr)
|
||||
);
|
||||
return;
|
||||
}
|
||||
let checkout_output = Command::new("git")
|
||||
.current_dir(&repo_dir)
|
||||
.args(&["checkout", &sha])
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
if !checkout_output.status.success() {
|
||||
eprintln!(
|
||||
"Failed to checkout {} for {}: {}",
|
||||
sha,
|
||||
repo,
|
||||
String::from_utf8_lossy(&checkout_output.stderr)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,6 @@ task.workspace = true
|
||||
serde_json_lenient.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
isahc_http_client.workspace = true
|
||||
ctor.workspace = true
|
||||
env_logger.workspace = true
|
||||
parking_lot.workspace = true
|
||||
|
||||
@@ -246,7 +246,6 @@ impl ExtensionBuilder {
|
||||
.args(scanner_path.exists().then_some(scanner_path))
|
||||
.output()
|
||||
.context("failed to run clang")?;
|
||||
|
||||
if !clang_output.status.success() {
|
||||
bail!(
|
||||
"failed to compile {} parser with clang: {}",
|
||||
@@ -432,7 +431,6 @@ impl ExtensionBuilder {
|
||||
let body = BufReader::new(response.body_mut());
|
||||
let body = GzipDecoder::new(body);
|
||||
let tar = Archive::new(body);
|
||||
|
||||
tar.unpack(&tar_out_dir)
|
||||
.await
|
||||
.context("failed to unpack wasi-sdk archive")?;
|
||||
|
||||
@@ -38,7 +38,7 @@ impl LspAdapter for ExtensionLspAdapter {
|
||||
|
||||
fn get_language_server_command<'a>(
|
||||
self: Arc<Self>,
|
||||
_: Option<Arc<Path>>,
|
||||
_: Arc<Path>,
|
||||
delegate: Arc<dyn LspAdapterDelegate>,
|
||||
_: futures::lock::MutexGuard<'a, Option<LanguageServerBinary>>,
|
||||
_: &'a mut AsyncAppContext,
|
||||
|
||||
@@ -6,7 +6,7 @@ use assistant_slash_command::{
|
||||
};
|
||||
use futures::FutureExt;
|
||||
use gpui::{Task, WeakView, WindowContext};
|
||||
use language::{BufferSnapshot, LspAdapterDelegate};
|
||||
use language::LspAdapterDelegate;
|
||||
use ui::prelude::*;
|
||||
use wasmtime_wasi::WasiView;
|
||||
use workspace::Workspace;
|
||||
@@ -82,8 +82,6 @@ impl SlashCommand for ExtensionSlashCommand {
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
arguments: &[String],
|
||||
_context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
|
||||
_context_buffer: BufferSnapshot,
|
||||
_workspace: WeakView<Workspace>,
|
||||
delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
@@ -123,7 +121,6 @@ impl SlashCommand for ExtensionSlashCommand {
|
||||
range: section.range.into(),
|
||||
icon: IconName::Code,
|
||||
label: section.label.into(),
|
||||
metadata: None,
|
||||
})
|
||||
.collect(),
|
||||
run_commands_in_text: false,
|
||||
|
||||
@@ -190,7 +190,6 @@ pub fn init(
|
||||
None,
|
||||
fs,
|
||||
client.http_client().clone(),
|
||||
client.http_client().clone(),
|
||||
Some(client.telemetry().clone()),
|
||||
node_runtime,
|
||||
language_registry,
|
||||
@@ -226,7 +225,6 @@ impl ExtensionStore {
|
||||
build_dir: Option<PathBuf>,
|
||||
fs: Arc<dyn Fs>,
|
||||
http_client: Arc<HttpClientWithUrl>,
|
||||
builder_client: Arc<dyn HttpClient>,
|
||||
telemetry: Option<Arc<Telemetry>>,
|
||||
node_runtime: Arc<dyn NodeRuntime>,
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
@@ -246,7 +244,12 @@ impl ExtensionStore {
|
||||
extension_index: Default::default(),
|
||||
installed_dir,
|
||||
index_path,
|
||||
builder: Arc::new(ExtensionBuilder::new(builder_client, build_dir)),
|
||||
builder: Arc::new(ExtensionBuilder::new(
|
||||
// Construct a real HTTP client for the extension builder, as we
|
||||
// don't want to use a fake one in the tests.
|
||||
::http_client::client(None, http_client.proxy().cloned()),
|
||||
build_dir,
|
||||
)),
|
||||
outstanding_operations: Default::default(),
|
||||
modified_extensions: Default::default(),
|
||||
reload_complete_senders: Vec::new(),
|
||||
|
||||
@@ -13,12 +13,10 @@ use futures::{io::BufReader, AsyncReadExt, StreamExt};
|
||||
use gpui::{Context, SemanticVersion, TestAppContext};
|
||||
use http_client::{FakeHttpClient, Response};
|
||||
use indexed_docs::IndexedDocsRegistry;
|
||||
use isahc_http_client::IsahcHttpClient;
|
||||
use language::{LanguageMatcher, LanguageRegistry, LanguageServerBinaryStatus, LanguageServerName};
|
||||
use node_runtime::FakeNodeRuntime;
|
||||
use parking_lot::Mutex;
|
||||
use project::{Project, DEFAULT_COMPLETION_CONTEXT};
|
||||
use release_channel::AppVersion;
|
||||
use serde_json::json;
|
||||
use settings::{Settings as _, SettingsStore};
|
||||
use snippet_provider::SnippetRegistry;
|
||||
@@ -272,7 +270,6 @@ async fn test_extension_store(cx: &mut TestAppContext) {
|
||||
None,
|
||||
fs.clone(),
|
||||
http_client.clone(),
|
||||
http_client.clone(),
|
||||
None,
|
||||
node_runtime.clone(),
|
||||
language_registry.clone(),
|
||||
@@ -400,7 +397,6 @@ async fn test_extension_store(cx: &mut TestAppContext) {
|
||||
None,
|
||||
fs.clone(),
|
||||
http_client.clone(),
|
||||
http_client.clone(),
|
||||
None,
|
||||
node_runtime.clone(),
|
||||
language_registry.clone(),
|
||||
@@ -506,7 +502,7 @@ async fn test_extension_store_with_test_extension(cx: &mut TestAppContext) {
|
||||
http_request_count: 0,
|
||||
}));
|
||||
|
||||
let extension_client = FakeHttpClient::create({
|
||||
let http_client = FakeHttpClient::create({
|
||||
let language_server_version = language_server_version.clone();
|
||||
move |request| {
|
||||
let language_server_version = language_server_version.clone();
|
||||
@@ -568,23 +564,13 @@ async fn test_extension_store_with_test_extension(cx: &mut TestAppContext) {
|
||||
}
|
||||
}
|
||||
});
|
||||
let user_agent = cx.update(|cx| {
|
||||
format!(
|
||||
"Zed/{} ({}; {})",
|
||||
AppVersion::global(cx),
|
||||
std::env::consts::OS,
|
||||
std::env::consts::ARCH
|
||||
)
|
||||
});
|
||||
let builder_client = IsahcHttpClient::new(None, Some(user_agent));
|
||||
|
||||
let extension_store = cx.new_model(|cx| {
|
||||
ExtensionStore::new(
|
||||
extensions_dir.clone(),
|
||||
Some(cache_dir),
|
||||
fs.clone(),
|
||||
extension_client.clone(),
|
||||
builder_client,
|
||||
http_client.clone(),
|
||||
None,
|
||||
node_runtime,
|
||||
language_registry.clone(),
|
||||
|
||||
@@ -18,7 +18,7 @@ clap = { workspace = true, features = ["derive"] }
|
||||
env_logger.workspace = true
|
||||
extension = { workspace = true, features = ["no-webrtc"] }
|
||||
fs.workspace = true
|
||||
isahc_http_client.workspace = true
|
||||
http_client.workspace = true
|
||||
language.workspace = true
|
||||
log.workspace = true
|
||||
rpc.workspace = true
|
||||
|
||||
@@ -7,13 +7,13 @@ use std::{
|
||||
};
|
||||
|
||||
use ::fs::{copy_recursive, CopyOptions, Fs, RealFs};
|
||||
use ::http_client::HttpClientWithProxy;
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use clap::Parser;
|
||||
use extension::{
|
||||
extension_builder::{CompileExtensionOptions, ExtensionBuilder},
|
||||
ExtensionManifest,
|
||||
};
|
||||
use isahc_http_client::IsahcHttpClient;
|
||||
use language::LanguageConfig;
|
||||
use theme::ThemeRegistry;
|
||||
use tree_sitter::{Language, Query, WasmStore};
|
||||
@@ -66,13 +66,7 @@ async fn main() -> Result<()> {
|
||||
std::env::consts::OS,
|
||||
std::env::consts::ARCH
|
||||
);
|
||||
let http_client = Arc::new(
|
||||
IsahcHttpClient::builder()
|
||||
.default_header("User-Agent", user_agent)
|
||||
.build()
|
||||
.map(IsahcHttpClient::from)?,
|
||||
);
|
||||
|
||||
let http_client = Arc::new(HttpClientWithProxy::new(Some(user_agent), None));
|
||||
let builder = ExtensionBuilder::new(http_client, scratch_dir);
|
||||
builder
|
||||
.compile_extension(
|
||||
|
||||
@@ -44,8 +44,8 @@ const FEEDBACK_SUBMISSION_ERROR_TEXT: &str =
|
||||
struct FeedbackRequestBody<'a> {
|
||||
feedback_text: &'a str,
|
||||
email: Option<String>,
|
||||
installation_id: Option<Arc<str>>,
|
||||
metrics_id: Option<Arc<str>>,
|
||||
installation_id: Option<Arc<str>>,
|
||||
system_specs: SystemSpecs,
|
||||
is_staff: bool,
|
||||
}
|
||||
@@ -296,16 +296,16 @@ impl FeedbackModal {
|
||||
}
|
||||
|
||||
let telemetry = zed_client.telemetry();
|
||||
let installation_id = telemetry.installation_id();
|
||||
let metrics_id = telemetry.metrics_id();
|
||||
let installation_id = telemetry.installation_id();
|
||||
let is_staff = telemetry.is_staff();
|
||||
let http_client = zed_client.http_client();
|
||||
let feedback_endpoint = http_client.build_url("/api/feedback");
|
||||
let request = FeedbackRequestBody {
|
||||
feedback_text,
|
||||
email,
|
||||
installation_id,
|
||||
metrics_id,
|
||||
installation_id,
|
||||
system_specs,
|
||||
is_staff: is_staff.unwrap_or(false),
|
||||
};
|
||||
|
||||
@@ -48,10 +48,6 @@ pub struct DiffHunkSummary {
|
||||
impl sum_tree::Summary for DiffHunkSummary {
|
||||
type Context = text::BufferSnapshot;
|
||||
|
||||
fn zero(_cx: &Self::Context) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, other: &Self, buffer: &Self::Context) {
|
||||
self.buffer_range.start = self
|
||||
.buffer_range
|
||||
@@ -67,11 +63,17 @@ pub struct BufferDiff {
|
||||
tree: SumTree<DiffHunk<Anchor>>,
|
||||
}
|
||||
|
||||
impl Default for BufferDiff {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl BufferDiff {
|
||||
pub fn new(buffer: &BufferSnapshot) -> BufferDiff {
|
||||
pub fn new() -> BufferDiff {
|
||||
BufferDiff {
|
||||
last_buffer_version: None,
|
||||
tree: SumTree::new(buffer),
|
||||
tree: SumTree::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,13 +97,11 @@ impl BufferDiff {
|
||||
range: Range<Anchor>,
|
||||
buffer: &'a BufferSnapshot,
|
||||
) -> impl 'a + Iterator<Item = DiffHunk<u32>> {
|
||||
let mut cursor = self
|
||||
.tree
|
||||
.filter::<_, DiffHunkSummary>(buffer, move |summary| {
|
||||
let before_start = summary.buffer_range.end.cmp(&range.start, buffer).is_lt();
|
||||
let after_end = summary.buffer_range.start.cmp(&range.end, buffer).is_gt();
|
||||
!before_start && !after_end
|
||||
});
|
||||
let mut cursor = self.tree.filter::<_, DiffHunkSummary>(move |summary| {
|
||||
let before_start = summary.buffer_range.end.cmp(&range.start, buffer).is_lt();
|
||||
let after_end = summary.buffer_range.start.cmp(&range.end, buffer).is_gt();
|
||||
!before_start && !after_end
|
||||
});
|
||||
|
||||
let anchor_iter = std::iter::from_fn(move || {
|
||||
cursor.next(buffer);
|
||||
@@ -142,13 +142,11 @@ impl BufferDiff {
|
||||
range: Range<Anchor>,
|
||||
buffer: &'a BufferSnapshot,
|
||||
) -> impl 'a + Iterator<Item = DiffHunk<u32>> {
|
||||
let mut cursor = self
|
||||
.tree
|
||||
.filter::<_, DiffHunkSummary>(buffer, move |summary| {
|
||||
let before_start = summary.buffer_range.end.cmp(&range.start, buffer).is_lt();
|
||||
let after_end = summary.buffer_range.start.cmp(&range.end, buffer).is_gt();
|
||||
!before_start && !after_end
|
||||
});
|
||||
let mut cursor = self.tree.filter::<_, DiffHunkSummary>(move |summary| {
|
||||
let before_start = summary.buffer_range.end.cmp(&range.start, buffer).is_lt();
|
||||
let after_end = summary.buffer_range.start.cmp(&range.end, buffer).is_gt();
|
||||
!before_start && !after_end
|
||||
});
|
||||
|
||||
std::iter::from_fn(move || {
|
||||
cursor.prev(buffer);
|
||||
@@ -173,11 +171,11 @@ impl BufferDiff {
|
||||
#[cfg(test)]
|
||||
fn clear(&mut self, buffer: &text::BufferSnapshot) {
|
||||
self.last_buffer_version = Some(buffer.version().clone());
|
||||
self.tree = SumTree::new(buffer);
|
||||
self.tree = SumTree::new();
|
||||
}
|
||||
|
||||
pub async fn update(&mut self, diff_base: &Rope, buffer: &text::BufferSnapshot) {
|
||||
let mut tree = SumTree::new(buffer);
|
||||
let mut tree = SumTree::new();
|
||||
|
||||
let diff_base_text = diff_base.to_string();
|
||||
let buffer_text = buffer.as_rope().to_string();
|
||||
@@ -353,7 +351,7 @@ mod tests {
|
||||
.unindent();
|
||||
|
||||
let mut buffer = Buffer::new(0, BufferId::new(1).unwrap(), buffer_text);
|
||||
let mut diff = BufferDiff::new(&buffer);
|
||||
let mut diff = BufferDiff::new();
|
||||
smol::block_on(diff.update(&diff_base_rope, &buffer));
|
||||
assert_hunks(
|
||||
diff.hunks(&buffer),
|
||||
@@ -414,7 +412,7 @@ mod tests {
|
||||
.unindent();
|
||||
|
||||
let buffer = Buffer::new(0, BufferId::new(1).unwrap(), buffer_text);
|
||||
let mut diff = BufferDiff::new(&buffer);
|
||||
let mut diff = BufferDiff::new();
|
||||
smol::block_on(diff.update(&diff_base_rope, &buffer));
|
||||
assert_eq!(diff.hunks(&buffer).count(), 8);
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ futures.workspace = true
|
||||
git.workspace = true
|
||||
gpui.workspace = true
|
||||
http_client.workspace = true
|
||||
isahc.workspace = true
|
||||
regex.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
@@ -3,7 +3,9 @@ use std::sync::Arc;
|
||||
use anyhow::{bail, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use futures::AsyncReadExt;
|
||||
use http_client::{AsyncBody, HttpClient, Request};
|
||||
use http_client::HttpClient;
|
||||
use isahc::config::Configurable;
|
||||
use isahc::{AsyncBody, Request};
|
||||
use serde::Deserialize;
|
||||
use url::Url;
|
||||
|
||||
@@ -49,14 +51,16 @@ impl Codeberg {
|
||||
let url =
|
||||
format!("https://codeberg.org/api/v1/repos/{repo_owner}/{repo}/git/commits/{commit}");
|
||||
|
||||
let mut request = Request::get(&url).header("Content-Type", "application/json");
|
||||
let mut request = Request::get(&url)
|
||||
.redirect_policy(isahc::config::RedirectPolicy::Follow)
|
||||
.header("Content-Type", "application/json");
|
||||
|
||||
if let Ok(codeberg_token) = std::env::var("CODEBERG_TOKEN") {
|
||||
request = request.header("Authorization", format!("Bearer {}", codeberg_token));
|
||||
}
|
||||
|
||||
let mut response = client
|
||||
.send_with_redirect_policy(request.body(AsyncBody::default())?, true)
|
||||
.send(request.body(AsyncBody::default())?)
|
||||
.await
|
||||
.with_context(|| format!("error fetching Codeberg commit details at {:?}", url))?;
|
||||
|
||||
|
||||
@@ -3,7 +3,9 @@ use std::sync::{Arc, OnceLock};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use futures::AsyncReadExt;
|
||||
use http_client::{AsyncBody, HttpClient, Request};
|
||||
use http_client::HttpClient;
|
||||
use isahc::config::Configurable;
|
||||
use isahc::{AsyncBody, Request};
|
||||
use regex::Regex;
|
||||
use serde::Deserialize;
|
||||
use url::Url;
|
||||
@@ -53,14 +55,16 @@ impl Github {
|
||||
) -> Result<Option<User>> {
|
||||
let url = format!("https://api.github.com/repos/{repo_owner}/{repo}/commits/{commit}");
|
||||
|
||||
let mut request = Request::get(&url).header("Content-Type", "application/json");
|
||||
let mut request = Request::get(&url)
|
||||
.redirect_policy(isahc::config::RedirectPolicy::Follow)
|
||||
.header("Content-Type", "application/json");
|
||||
|
||||
if let Ok(github_token) = std::env::var("GITHUB_TOKEN") {
|
||||
request = request.header("Authorization", format!("Bearer {}", github_token));
|
||||
}
|
||||
|
||||
let mut response = client
|
||||
.send_with_redirect_policy(request.body(AsyncBody::default())?, true)
|
||||
.send(request.body(AsyncBody::default())?)
|
||||
.await
|
||||
.with_context(|| format!("error fetching GitHub commit details at {:?}", url))?;
|
||||
|
||||
|
||||
@@ -11,13 +11,13 @@ license = "Apache-2.0"
|
||||
workspace = true
|
||||
|
||||
[features]
|
||||
default = ["http_client"]
|
||||
default = []
|
||||
test-support = [
|
||||
"backtrace",
|
||||
"collections/test-support",
|
||||
"rand",
|
||||
"util/test-support",
|
||||
"http_client?/test-support",
|
||||
"http_client/test-support",
|
||||
]
|
||||
runtime_shaders = []
|
||||
macos-blade = ["blade-graphics", "blade-macros", "blade-util", "bytemuck"]
|
||||
@@ -40,7 +40,7 @@ derive_more.workspace = true
|
||||
etagere = "0.2"
|
||||
futures.workspace = true
|
||||
gpui_macros.workspace = true
|
||||
http_client = { optional = true, workspace = true }
|
||||
http_client.workspace = true
|
||||
image = "0.25.1"
|
||||
itertools.workspace = true
|
||||
linkme = "0.3"
|
||||
@@ -50,7 +50,7 @@ parking = "2.0.0"
|
||||
parking_lot.workspace = true
|
||||
postage.workspace = true
|
||||
profiling.workspace = true
|
||||
rand = { optional = true, workspace = true }
|
||||
rand = { optional = true, workspace = true}
|
||||
raw-window-handle = "0.6"
|
||||
refineable.workspace = true
|
||||
resvg = { version = "0.41.0", default-features = false }
|
||||
@@ -110,7 +110,6 @@ blade-graphics.workspace = true
|
||||
blade-macros.workspace = true
|
||||
blade-util.workspace = true
|
||||
bytemuck = "1"
|
||||
flume = "0.11"
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
as-raw-xcb-connection = "1"
|
||||
@@ -118,6 +117,7 @@ ashpd.workspace = true
|
||||
calloop = "0.13.0"
|
||||
calloop-wayland-source = "0.3.0"
|
||||
cosmic-text = { git = "https://github.com/pop-os/cosmic-text", rev = "542b20c" }
|
||||
flume = "0.11"
|
||||
wayland-backend = { version = "0.3.3", features = ["client_system", "dlopen"] }
|
||||
wayland-client = { version = "0.31.2" }
|
||||
wayland-cursor = "0.31.1"
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="480" height="480" viewBox="0 0 480 480" fill="none"
|
||||
stroke="currentColor" stroke-width="40" stroke-linecap="round" stroke-linejoin="round"
|
||||
class="lucide lucide-grip">
|
||||
<circle cx="240" cy="240" r="50" stroke="#44403c" />
|
||||
<circle cx="100" cy="100" r="20" stroke="#65a30d" />
|
||||
<circle cx="240" cy="100" r="30" stroke="#dc2626" />
|
||||
<circle cx="380" cy="100" r="20" stroke="#d97706" />
|
||||
<circle cx="380" cy="240" r="30" stroke="#06b6d4" />
|
||||
<circle cx="100" cy="240" r="30" stroke="#3b82f6" />
|
||||
<circle cx="240" cy="380" r="30" stroke="#7c3aed" />
|
||||
<circle cx="380" cy="380" r="20" stroke="#c026d3" />
|
||||
<circle cx="100" cy="380" r="20" stroke="#e11d48" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 741 B |
@@ -131,8 +131,7 @@ fn main() {
|
||||
PathBuf::from_str("crates/gpui/examples/image/app-icon.png").unwrap(),
|
||||
),
|
||||
remote_resource: "https://picsum.photos/512/512".into(),
|
||||
|
||||
asset_resource: "image/color.svg".into(),
|
||||
asset_resource: "image/app-icon.png".into(),
|
||||
})
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
@@ -117,7 +117,7 @@ impl App {
|
||||
Self(AppContext::new(
|
||||
current_platform(false),
|
||||
Arc::new(()),
|
||||
Arc::new(NullHttpClient),
|
||||
http_client::client(None, None),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ impl App {
|
||||
Self(AppContext::new(
|
||||
current_platform(true),
|
||||
Arc::new(()),
|
||||
Arc::new(NullHttpClient),
|
||||
http_client::client(None, None),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -142,14 +142,6 @@ impl App {
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the http client for the application
|
||||
pub fn with_http_client(self, http_client: Arc<dyn HttpClient>) -> Self {
|
||||
let mut context_lock = self.0.borrow_mut();
|
||||
context_lock.http_client = http_client;
|
||||
drop(context_lock);
|
||||
self
|
||||
}
|
||||
|
||||
/// Start the application. The provided callback will be called once the
|
||||
/// app is fully launched.
|
||||
pub fn run<F>(self, on_finish_launching: F)
|
||||
@@ -1520,22 +1512,3 @@ pub struct KeystrokeEvent {
|
||||
/// The action that was resolved for the keystroke, if any
|
||||
pub action: Option<Box<dyn Action>>,
|
||||
}
|
||||
|
||||
struct NullHttpClient;
|
||||
|
||||
impl HttpClient for NullHttpClient {
|
||||
fn send_with_redirect_policy(
|
||||
&self,
|
||||
_req: http_client::Request<http_client::AsyncBody>,
|
||||
_follow_redirects: bool,
|
||||
) -> futures::future::BoxFuture<
|
||||
'static,
|
||||
Result<http_client::Response<http_client::AsyncBody>, anyhow::Error>,
|
||||
> {
|
||||
async move { Err(anyhow!("No HttpClient available")) }.boxed()
|
||||
}
|
||||
|
||||
fn proxy(&self) -> Option<&http_client::Uri> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,10 +345,7 @@ impl Asset for ImageAsset {
|
||||
let bytes = match source.clone() {
|
||||
UriOrPath::Path(uri) => fs::read(uri.as_ref())?,
|
||||
UriOrPath::Uri(uri) => {
|
||||
let mut response = client
|
||||
.get(uri.as_ref(), ().into(), true)
|
||||
.await
|
||||
.map_err(|e| ImageCacheError::Client(Arc::new(e)))?;
|
||||
let mut response = client.get(uri.as_ref(), ().into(), true).await?;
|
||||
let mut body = Vec::new();
|
||||
response.body_mut().read_to_end(&mut body).await?;
|
||||
if !response.status().is_success() {
|
||||
@@ -411,14 +408,9 @@ impl Asset for ImageAsset {
|
||||
// TODO: Can we make svgs always rescale?
|
||||
svg_renderer.render_pixmap(&bytes, SvgSize::ScaleFactor(1.0))?;
|
||||
|
||||
let mut buffer =
|
||||
let buffer =
|
||||
ImageBuffer::from_raw(pixmap.width(), pixmap.height(), pixmap.take()).unwrap();
|
||||
|
||||
// Convert from RGBA to BGRA.
|
||||
for pixel in buffer.chunks_exact_mut(4) {
|
||||
pixel.swap(0, 2);
|
||||
}
|
||||
|
||||
RenderImage::new(SmallVec::from_elem(Frame::new(buffer), 1))
|
||||
};
|
||||
|
||||
@@ -432,7 +424,7 @@ impl Asset for ImageAsset {
|
||||
pub enum ImageCacheError {
|
||||
/// An error that occurred while fetching an image from a remote source.
|
||||
#[error("http error: {0}")]
|
||||
Client(#[from] Arc<anyhow::Error>),
|
||||
Client(#[from] http_client::Error),
|
||||
/// An error that occurred while reading the image from disk.
|
||||
#[error("IO error: {0}")]
|
||||
Io(Arc<std::io::Error>),
|
||||
|
||||
@@ -181,7 +181,7 @@ impl ListState {
|
||||
last_layout_bounds: None,
|
||||
last_padding: None,
|
||||
render_item: Box::new(render_item),
|
||||
items: SumTree::default(),
|
||||
items: SumTree::new(),
|
||||
logical_scroll_top: None,
|
||||
alignment,
|
||||
overdraw,
|
||||
@@ -228,7 +228,7 @@ impl ListState {
|
||||
) {
|
||||
let state = &mut *self.0.borrow_mut();
|
||||
|
||||
let mut old_items = state.items.cursor::<Count>(&());
|
||||
let mut old_items = state.items.cursor::<Count>();
|
||||
let mut new_items = old_items.slice(&Count(old_range.start), Bias::Right, &());
|
||||
old_items.seek_forward(&Count(old_range.end), Bias::Right, &());
|
||||
|
||||
@@ -297,7 +297,7 @@ impl ListState {
|
||||
scroll_top.item_ix = ix;
|
||||
scroll_top.offset_in_item = px(0.);
|
||||
} else {
|
||||
let mut cursor = state.items.cursor::<ListItemSummary>(&());
|
||||
let mut cursor = state.items.cursor::<ListItemSummary>();
|
||||
cursor.seek(&Count(ix + 1), Bias::Right, &());
|
||||
let bottom = cursor.start().height + padding.top;
|
||||
let goal_top = px(0.).max(bottom - height + padding.bottom);
|
||||
@@ -326,7 +326,7 @@ impl ListState {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut cursor = state.items.cursor::<(Count, Height)>(&());
|
||||
let mut cursor = state.items.cursor::<(Count, Height)>();
|
||||
cursor.seek(&Count(scroll_top.item_ix), Bias::Right, &());
|
||||
|
||||
let scroll_top = cursor.start().1 .0 + scroll_top.offset_in_item;
|
||||
@@ -348,7 +348,7 @@ impl ListState {
|
||||
|
||||
impl StateInner {
|
||||
fn visible_range(&self, height: Pixels, scroll_top: &ListOffset) -> Range<usize> {
|
||||
let mut cursor = self.items.cursor::<ListItemSummary>(&());
|
||||
let mut cursor = self.items.cursor::<ListItemSummary>();
|
||||
cursor.seek(&Count(scroll_top.item_ix), Bias::Right, &());
|
||||
let start_y = cursor.start().height + scroll_top.offset_in_item;
|
||||
cursor.seek_forward(&Height(start_y + height), Bias::Left, &());
|
||||
@@ -378,7 +378,7 @@ impl StateInner {
|
||||
if self.alignment == ListAlignment::Bottom && new_scroll_top == scroll_max {
|
||||
self.logical_scroll_top = None;
|
||||
} else {
|
||||
let mut cursor = self.items.cursor::<ListItemSummary>(&());
|
||||
let mut cursor = self.items.cursor::<ListItemSummary>();
|
||||
cursor.seek(&Height(new_scroll_top), Bias::Right, &());
|
||||
let item_ix = cursor.start().count;
|
||||
let offset_in_item = new_scroll_top - cursor.start().height;
|
||||
@@ -418,7 +418,7 @@ impl StateInner {
|
||||
}
|
||||
|
||||
fn scroll_top(&self, logical_scroll_top: &ListOffset) -> Pixels {
|
||||
let mut cursor = self.items.cursor::<ListItemSummary>(&());
|
||||
let mut cursor = self.items.cursor::<ListItemSummary>();
|
||||
cursor.seek(&Count(logical_scroll_top.item_ix), Bias::Right, &());
|
||||
cursor.start().height + logical_scroll_top.offset_in_item
|
||||
}
|
||||
@@ -445,7 +445,7 @@ impl StateInner {
|
||||
AvailableSpace::MinContent,
|
||||
);
|
||||
|
||||
let mut cursor = old_items.cursor::<Count>(&());
|
||||
let mut cursor = old_items.cursor::<Count>();
|
||||
|
||||
// Render items after the scroll top, including those in the trailing overdraw
|
||||
cursor.seek(&Count(scroll_top.item_ix), Bias::Right, &());
|
||||
@@ -560,7 +560,7 @@ impl StateInner {
|
||||
}
|
||||
|
||||
let measured_range = cursor.start().0..(cursor.start().0 + measured_items.len());
|
||||
let mut cursor = old_items.cursor::<Count>(&());
|
||||
let mut cursor = old_items.cursor::<Count>();
|
||||
let mut new_items = cursor.slice(&Count(measured_range.start), Bias::Right, &());
|
||||
new_items.extend(measured_items, &());
|
||||
cursor.seek(&Count(measured_range.end), Bias::Right, &());
|
||||
@@ -573,7 +573,7 @@ impl StateInner {
|
||||
if !rendered_focused_item {
|
||||
let mut cursor = self
|
||||
.items
|
||||
.filter::<_, Count>(&(), |summary| summary.has_focus_handles);
|
||||
.filter::<_, Count>(|summary| summary.has_focus_handles);
|
||||
cursor.next(&());
|
||||
while let Some(item) = cursor.item() {
|
||||
if item.contains_focused(cx) {
|
||||
@@ -629,7 +629,7 @@ impl StateInner {
|
||||
offset_in_item: autoscroll_bounds.top() - item_origin.y,
|
||||
});
|
||||
} else if autoscroll_bounds.bottom() > bounds.bottom() {
|
||||
let mut cursor = self.items.cursor::<Count>(&());
|
||||
let mut cursor = self.items.cursor::<Count>();
|
||||
cursor.seek(&Count(item.index), Bias::Right, &());
|
||||
let mut height = bounds.size.height - padding.top - padding.bottom;
|
||||
|
||||
@@ -883,10 +883,6 @@ impl sum_tree::Item for ListItem {
|
||||
impl sum_tree::Summary for ListItemSummary {
|
||||
type Context = ();
|
||||
|
||||
fn zero(_cx: &()) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, summary: &Self, _: &()) {
|
||||
self.count += summary.count;
|
||||
self.rendered_count += summary.rendered_count;
|
||||
@@ -897,20 +893,12 @@ impl sum_tree::Summary for ListItemSummary {
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, ListItemSummary> for Count {
|
||||
fn zero(_cx: &()) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, summary: &'a ListItemSummary, _: &()) {
|
||||
self.0 += summary.count;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, ListItemSummary> for Height {
|
||||
fn zero(_cx: &()) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, summary: &'a ListItemSummary, _: &()) {
|
||||
self.0 += summary.height;
|
||||
}
|
||||
|
||||
@@ -128,7 +128,6 @@ pub use executor::*;
|
||||
pub use geometry::*;
|
||||
pub use global::*;
|
||||
pub use gpui_macros::{register_action, test, IntoElement, Render};
|
||||
pub use http_client;
|
||||
pub use input::*;
|
||||
pub use interactive::*;
|
||||
use key_dispatch::*;
|
||||
|
||||
@@ -488,8 +488,8 @@ fn fs_underline(input: UnderlineVarying) -> @location(0) vec4<f32> {
|
||||
|
||||
let half_thickness = underline.thickness * 0.5;
|
||||
let st = (input.position.xy - underline.bounds.origin) / underline.bounds.size.y - vec2<f32>(0.0, 0.5);
|
||||
let frequency = M_PI_F * 3.0 * underline.thickness / 3.0;
|
||||
let amplitude = 1.0 / (4.0 * underline.thickness);
|
||||
let frequency = M_PI_F * 3.0 * underline.thickness / 8.0;
|
||||
let amplitude = 1.0 / (2.0 * underline.thickness);
|
||||
let sine = sin(st.x * frequency) * amplitude;
|
||||
let dSine = cos(st.x * frequency) * amplitude * frequency;
|
||||
let distance = (st.y - sine) / sqrt(1.0 + dSine * dSine);
|
||||
|
||||
@@ -3,39 +3,51 @@ use std::{
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use anyhow::Context;
|
||||
use async_task::Runnable;
|
||||
use flume::Sender;
|
||||
use parking::Parker;
|
||||
use parking_lot::Mutex;
|
||||
use util::ResultExt;
|
||||
use windows::{
|
||||
Foundation::TimeSpan,
|
||||
System::Threading::{
|
||||
ThreadPool, ThreadPoolTimer, TimerElapsedHandler, WorkItemHandler, WorkItemOptions,
|
||||
WorkItemPriority,
|
||||
System::{
|
||||
DispatcherQueue, DispatcherQueueController, DispatcherQueueHandler,
|
||||
Threading::{
|
||||
ThreadPool, ThreadPoolTimer, TimerElapsedHandler, WorkItemHandler, WorkItemOptions,
|
||||
WorkItemPriority,
|
||||
},
|
||||
},
|
||||
Win32::System::WinRT::{
|
||||
CreateDispatcherQueueController, DispatcherQueueOptions, DQTAT_COM_NONE,
|
||||
DQTYPE_THREAD_CURRENT,
|
||||
},
|
||||
Win32::{Foundation::HANDLE, System::Threading::SetEvent},
|
||||
};
|
||||
|
||||
use crate::{PlatformDispatcher, SafeHandle, TaskLabel};
|
||||
use crate::{PlatformDispatcher, TaskLabel};
|
||||
|
||||
pub(crate) struct WindowsDispatcher {
|
||||
main_sender: Sender<Runnable>,
|
||||
dispatch_event: SafeHandle,
|
||||
controller: DispatcherQueueController,
|
||||
main_queue: DispatcherQueue,
|
||||
parker: Mutex<Parker>,
|
||||
main_thread_id: ThreadId,
|
||||
}
|
||||
|
||||
impl WindowsDispatcher {
|
||||
pub(crate) fn new(main_sender: Sender<Runnable>, dispatch_event: HANDLE) -> Self {
|
||||
let dispatch_event = dispatch_event.into();
|
||||
pub(crate) fn new() -> Self {
|
||||
let controller = unsafe {
|
||||
let options = DispatcherQueueOptions {
|
||||
dwSize: std::mem::size_of::<DispatcherQueueOptions>() as u32,
|
||||
threadType: DQTYPE_THREAD_CURRENT,
|
||||
apartmentType: DQTAT_COM_NONE,
|
||||
};
|
||||
CreateDispatcherQueueController(options).unwrap()
|
||||
};
|
||||
let main_queue = controller.DispatcherQueue().unwrap();
|
||||
let parker = Mutex::new(Parker::new());
|
||||
let main_thread_id = current().id();
|
||||
|
||||
WindowsDispatcher {
|
||||
main_sender,
|
||||
dispatch_event,
|
||||
controller,
|
||||
main_queue,
|
||||
parker,
|
||||
main_thread_id,
|
||||
}
|
||||
@@ -74,6 +86,12 @@ impl WindowsDispatcher {
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for WindowsDispatcher {
|
||||
fn drop(&mut self) {
|
||||
self.controller.ShutdownQueueAsync().log_err();
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformDispatcher for WindowsDispatcher {
|
||||
fn is_main_thread(&self) -> bool {
|
||||
current().id() == self.main_thread_id
|
||||
@@ -87,11 +105,14 @@ impl PlatformDispatcher for WindowsDispatcher {
|
||||
}
|
||||
|
||||
fn dispatch_on_main_thread(&self, runnable: Runnable) {
|
||||
self.main_sender
|
||||
.send(runnable)
|
||||
.context("Dispatch on main thread failed")
|
||||
.log_err();
|
||||
unsafe { SetEvent(*self.dispatch_event).log_err() };
|
||||
let handler = {
|
||||
let mut task_wrapper = Some(runnable);
|
||||
DispatcherQueueHandler::new(move || {
|
||||
task_wrapper.take().unwrap().run();
|
||||
Ok(())
|
||||
})
|
||||
};
|
||||
self.main_queue.TryEnqueue(&handler).log_err();
|
||||
}
|
||||
|
||||
fn dispatch_after(&self, duration: Duration, runnable: Runnable) {
|
||||
|
||||
@@ -177,9 +177,6 @@ fn handle_timer_msg(
|
||||
state_ptr: Rc<WindowsWindowStatePtr>,
|
||||
) -> Option<isize> {
|
||||
if wparam.0 == SIZE_MOVE_LOOP_TIMER_ID {
|
||||
for runnable in state_ptr.main_receiver.drain() {
|
||||
runnable.run();
|
||||
}
|
||||
handle_paint_msg(handle, state_ptr)
|
||||
} else {
|
||||
None
|
||||
|
||||
@@ -8,7 +8,6 @@ use std::{
|
||||
|
||||
use ::util::ResultExt;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use async_task::Runnable;
|
||||
use futures::channel::oneshot::{self, Receiver};
|
||||
use itertools::Itertools;
|
||||
use parking_lot::RwLock;
|
||||
@@ -47,8 +46,6 @@ pub(crate) struct WindowsPlatform {
|
||||
raw_window_handles: RwLock<SmallVec<[HWND; 4]>>,
|
||||
// The below members will never change throughout the entire lifecycle of the app.
|
||||
icon: HICON,
|
||||
main_receiver: flume::Receiver<Runnable>,
|
||||
dispatch_event: HANDLE,
|
||||
background_executor: BackgroundExecutor,
|
||||
foreground_executor: ForegroundExecutor,
|
||||
text_system: Arc<DirectWriteTextSystem>,
|
||||
@@ -92,9 +89,7 @@ impl WindowsPlatform {
|
||||
unsafe {
|
||||
OleInitialize(None).expect("unable to initialize Windows OLE");
|
||||
}
|
||||
let (main_sender, main_receiver) = flume::unbounded::<Runnable>();
|
||||
let dispatch_event = unsafe { CreateEventW(None, false, false, None) }.unwrap();
|
||||
let dispatcher = Arc::new(WindowsDispatcher::new(main_sender, dispatch_event));
|
||||
let dispatcher = Arc::new(WindowsDispatcher::new());
|
||||
let background_executor = BackgroundExecutor::new(dispatcher.clone());
|
||||
let foreground_executor = ForegroundExecutor::new(dispatcher);
|
||||
let bitmap_factory = ManuallyDrop::new(unsafe {
|
||||
@@ -118,8 +113,6 @@ impl WindowsPlatform {
|
||||
state,
|
||||
raw_window_handles,
|
||||
icon,
|
||||
main_receiver,
|
||||
dispatch_event,
|
||||
background_executor,
|
||||
foreground_executor,
|
||||
text_system,
|
||||
@@ -183,24 +176,6 @@ impl WindowsPlatform {
|
||||
|
||||
lock.is_empty()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn run_foreground_tasks(&self) {
|
||||
for runnable in self.main_receiver.drain() {
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_creation_info(&self) -> WindowCreationInfo {
|
||||
WindowCreationInfo {
|
||||
icon: self.icon,
|
||||
executor: self.foreground_executor.clone(),
|
||||
current_cursor: self.state.borrow().current_cursor,
|
||||
windows_version: self.windows_version,
|
||||
validation_number: self.validation_number,
|
||||
main_receiver: self.main_receiver.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Platform for WindowsPlatform {
|
||||
@@ -222,21 +197,16 @@ impl Platform for WindowsPlatform {
|
||||
begin_vsync(*vsync_event);
|
||||
'a: loop {
|
||||
let wait_result = unsafe {
|
||||
MsgWaitForMultipleObjects(
|
||||
Some(&[*vsync_event, self.dispatch_event]),
|
||||
false,
|
||||
INFINITE,
|
||||
QS_ALLINPUT,
|
||||
)
|
||||
MsgWaitForMultipleObjects(Some(&[*vsync_event]), false, INFINITE, QS_ALLINPUT)
|
||||
};
|
||||
|
||||
match wait_result {
|
||||
// compositor clock ticked so we should draw a frame
|
||||
WAIT_EVENT(0) => self.redraw_all(),
|
||||
// foreground tasks are dispatched
|
||||
WAIT_EVENT(1) => self.run_foreground_tasks(),
|
||||
WAIT_EVENT(0) => {
|
||||
self.redraw_all();
|
||||
}
|
||||
// Windows thread messages are posted
|
||||
WAIT_EVENT(2) => {
|
||||
WAIT_EVENT(1) => {
|
||||
let mut msg = MSG::default();
|
||||
unsafe {
|
||||
while PeekMessageW(&mut msg, None, 0, 0, PM_REMOVE).as_bool() {
|
||||
@@ -260,8 +230,6 @@ impl Platform for WindowsPlatform {
|
||||
}
|
||||
}
|
||||
}
|
||||
// foreground tasks may have been queued in the message handlers
|
||||
self.run_foreground_tasks();
|
||||
}
|
||||
_ => {
|
||||
log::error!("Something went wrong while waiting {:?}", wait_result);
|
||||
@@ -351,7 +319,17 @@ impl Platform for WindowsPlatform {
|
||||
handle: AnyWindowHandle,
|
||||
options: WindowParams,
|
||||
) -> Result<Box<dyn PlatformWindow>> {
|
||||
let window = WindowsWindow::new(handle, options, self.generate_creation_info())?;
|
||||
let lock = self.state.borrow();
|
||||
let window = WindowsWindow::new(
|
||||
handle,
|
||||
options,
|
||||
self.icon,
|
||||
self.foreground_executor.clone(),
|
||||
lock.current_cursor,
|
||||
self.windows_version,
|
||||
self.validation_number,
|
||||
)?;
|
||||
drop(lock);
|
||||
let handle = window.get_raw_handle();
|
||||
self.raw_window_handles.write().push(handle);
|
||||
|
||||
@@ -580,15 +558,6 @@ impl Drop for WindowsPlatform {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct WindowCreationInfo {
|
||||
pub(crate) icon: HICON,
|
||||
pub(crate) executor: ForegroundExecutor,
|
||||
pub(crate) current_cursor: HCURSOR,
|
||||
pub(crate) windows_version: WindowsVersion,
|
||||
pub(crate) validation_number: usize,
|
||||
pub(crate) main_receiver: flume::Receiver<Runnable>,
|
||||
}
|
||||
|
||||
fn open_target(target: &str) {
|
||||
unsafe {
|
||||
let ret = ShellExecuteW(
|
||||
@@ -662,33 +631,22 @@ fn file_open_dialog(options: PathPromptOptions) -> Result<Option<Vec<PathBuf>>>
|
||||
|
||||
fn file_save_dialog(directory: PathBuf) -> Result<Option<PathBuf>> {
|
||||
let dialog: IFileSaveDialog = unsafe { CoCreateInstance(&FileSaveDialog, None, CLSCTX_ALL)? };
|
||||
if !directory.to_string_lossy().is_empty() {
|
||||
if let Some(full_path) = directory.canonicalize().log_err() {
|
||||
let full_path = full_path.to_string_lossy().to_string();
|
||||
if !full_path.is_empty() {
|
||||
let path_item: IShellItem =
|
||||
unsafe { SHCreateItemFromParsingName(&HSTRING::from(&full_path), None)? };
|
||||
unsafe { dialog.SetFolder(&path_item).log_err() };
|
||||
}
|
||||
if let Some(full_path) = directory.canonicalize().log_err() {
|
||||
let full_path = full_path.to_string_lossy().to_string();
|
||||
if !full_path.is_empty() {
|
||||
let path_item: IShellItem =
|
||||
unsafe { SHCreateItemFromParsingName(&HSTRING::from(&full_path), None)? };
|
||||
unsafe { dialog.SetFolder(&path_item).log_err() };
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
dialog.SetFileTypes(&[Common::COMDLG_FILTERSPEC {
|
||||
pszName: windows::core::w!("All files"),
|
||||
pszSpec: windows::core::w!("*.*"),
|
||||
}])?;
|
||||
if dialog.Show(None).is_err() {
|
||||
// User cancelled
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
let shell_item = unsafe { dialog.GetResult()? };
|
||||
let file_path_string = unsafe {
|
||||
let pwstr = shell_item.GetDisplayName(SIGDN_FILESYSPATH)?;
|
||||
let string = pwstr.to_string()?;
|
||||
CoTaskMemFree(Some(pwstr.0 as _));
|
||||
string
|
||||
};
|
||||
let file_path_string = unsafe { shell_item.GetDisplayName(SIGDN_FILESYSPATH)?.to_string()? };
|
||||
Ok(Some(PathBuf::from(file_path_string)))
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ use std::{
|
||||
|
||||
use ::util::ResultExt;
|
||||
use anyhow::{Context, Result};
|
||||
use async_task::Runnable;
|
||||
use futures::channel::oneshot::{self, Receiver};
|
||||
use itertools::Itertools;
|
||||
use raw_window_handle as rwh;
|
||||
@@ -64,7 +63,6 @@ pub(crate) struct WindowsWindowStatePtr {
|
||||
pub(crate) executor: ForegroundExecutor,
|
||||
pub(crate) windows_version: WindowsVersion,
|
||||
pub(crate) validation_number: usize,
|
||||
pub(crate) main_receiver: flume::Receiver<Runnable>,
|
||||
}
|
||||
|
||||
impl WindowsWindowState {
|
||||
@@ -228,7 +226,6 @@ impl WindowsWindowStatePtr {
|
||||
executor: context.executor.clone(),
|
||||
windows_version: context.windows_version,
|
||||
validation_number: context.validation_number,
|
||||
main_receiver: context.main_receiver.clone(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -256,23 +253,18 @@ struct WindowCreateContext {
|
||||
current_cursor: HCURSOR,
|
||||
windows_version: WindowsVersion,
|
||||
validation_number: usize,
|
||||
main_receiver: flume::Receiver<Runnable>,
|
||||
}
|
||||
|
||||
impl WindowsWindow {
|
||||
pub(crate) fn new(
|
||||
handle: AnyWindowHandle,
|
||||
params: WindowParams,
|
||||
creation_info: WindowCreationInfo,
|
||||
icon: HICON,
|
||||
executor: ForegroundExecutor,
|
||||
current_cursor: HCURSOR,
|
||||
windows_version: WindowsVersion,
|
||||
validation_number: usize,
|
||||
) -> Result<Self> {
|
||||
let WindowCreationInfo {
|
||||
icon,
|
||||
executor,
|
||||
current_cursor,
|
||||
windows_version,
|
||||
validation_number,
|
||||
main_receiver,
|
||||
} = creation_info;
|
||||
let classname = register_wnd_class(icon);
|
||||
let hide_title_bar = params
|
||||
.titlebar
|
||||
@@ -313,7 +305,6 @@ impl WindowsWindow {
|
||||
current_cursor,
|
||||
windows_version,
|
||||
validation_number,
|
||||
main_receiver,
|
||||
};
|
||||
let lpparam = Some(&context as *const _ as *const _);
|
||||
let creation_result = unsafe {
|
||||
|
||||
@@ -16,12 +16,13 @@ path = "src/http_client.rs"
|
||||
doctest = true
|
||||
|
||||
[dependencies]
|
||||
http = "0.2"
|
||||
http = "1.0.0"
|
||||
anyhow.workspace = true
|
||||
derive_more.workspace = true
|
||||
futures.workspace = true
|
||||
isahc.workspace = true
|
||||
log.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
smol.workspace = true
|
||||
futures-lite.workspace = true
|
||||
url.workspace = true
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
use std::{borrow::Cow, io::Read, pin::Pin, task::Poll};
|
||||
|
||||
use futures::{AsyncRead, AsyncReadExt};
|
||||
|
||||
/// Based on the implementation of AsyncBody in
|
||||
/// https://github.com/sagebind/isahc/blob/5c533f1ef4d6bdf1fd291b5103c22110f41d0bf0/src/body/mod.rs
|
||||
pub struct AsyncBody(pub Inner);
|
||||
|
||||
pub enum Inner {
|
||||
/// An empty body.
|
||||
Empty,
|
||||
|
||||
/// A body stored in memory.
|
||||
SyncReader(std::io::Cursor<Cow<'static, [u8]>>),
|
||||
|
||||
/// An asynchronous reader.
|
||||
AsyncReader(Pin<Box<dyn futures::AsyncRead + Send + Sync>>),
|
||||
}
|
||||
|
||||
impl AsyncBody {
|
||||
/// Create a new empty body.
|
||||
///
|
||||
/// An empty body represents the *absence* of a body, which is semantically
|
||||
/// different than the presence of a body of zero length.
|
||||
pub fn empty() -> Self {
|
||||
Self(Inner::Empty)
|
||||
}
|
||||
/// Create a streaming body that reads from the given reader.
|
||||
pub fn from_reader<R>(read: R) -> Self
|
||||
where
|
||||
R: AsyncRead + Send + Sync + 'static,
|
||||
{
|
||||
Self(Inner::AsyncReader(Box::pin(read)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AsyncBody {
|
||||
fn default() -> Self {
|
||||
Self(Inner::Empty)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<()> for AsyncBody {
|
||||
fn from(_: ()) -> Self {
|
||||
Self(Inner::Empty)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for AsyncBody {
|
||||
fn from(body: Vec<u8>) -> Self {
|
||||
Self(Inner::SyncReader(std::io::Cursor::new(Cow::Owned(body))))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'_ [u8]> for AsyncBody {
|
||||
fn from(body: &[u8]) -> Self {
|
||||
body.to_vec().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for AsyncBody {
|
||||
fn from(body: String) -> Self {
|
||||
body.into_bytes().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'_ str> for AsyncBody {
|
||||
fn from(body: &str) -> Self {
|
||||
body.as_bytes().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<Self>> From<Option<T>> for AsyncBody {
|
||||
fn from(body: Option<T>) -> Self {
|
||||
match body {
|
||||
Some(body) => body.into(),
|
||||
None => Self(Inner::Empty),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::io::Read for AsyncBody {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
match &mut self.0 {
|
||||
Inner::Empty => Ok(0),
|
||||
Inner::SyncReader(cursor) => cursor.read(buf),
|
||||
Inner::AsyncReader(async_reader) => smol::block_on(async_reader.read(buf)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl futures::AsyncRead for AsyncBody {
|
||||
fn poll_read(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
buf: &mut [u8],
|
||||
) -> std::task::Poll<std::io::Result<usize>> {
|
||||
// SAFETY: Standard Enum pin projection
|
||||
let inner = unsafe { &mut self.get_unchecked_mut().0 };
|
||||
match inner {
|
||||
Inner::Empty => Poll::Ready(Ok(0)),
|
||||
// Blocking call is over an in-memory buffer
|
||||
Inner::SyncReader(cursor) => Poll::Ready(cursor.read(buf)),
|
||||
Inner::AsyncReader(async_reader) => {
|
||||
AsyncRead::poll_read(async_reader.as_mut(), cx, buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ pub async fn latest_github_release(
|
||||
) -> Result<GithubRelease, anyhow::Error> {
|
||||
let mut response = http
|
||||
.get(
|
||||
format!("https://api.github.com/repos/{repo_name_with_owner}/releases").as_str(),
|
||||
&format!("https://api.github.com/repos/{repo_name_with_owner}/releases"),
|
||||
Default::default(),
|
||||
true,
|
||||
)
|
||||
@@ -91,14 +91,13 @@ pub async fn get_release_by_tag_name(
|
||||
.context("error fetching latest release")?;
|
||||
|
||||
let mut body = Vec::new();
|
||||
let status = response.status();
|
||||
response
|
||||
.body_mut()
|
||||
.read_to_end(&mut body)
|
||||
.await
|
||||
.context("error reading latest release")?;
|
||||
|
||||
if status.is_client_error() {
|
||||
if response.status().is_client_error() {
|
||||
let text = String::from_utf8_lossy(body.as_slice());
|
||||
bail!(
|
||||
"status error {}, response: {text:?}",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user