Compare commits
26 Commits
duckdb
...
assistant-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
967dd02414 | ||
|
|
a32dd07e27 | ||
|
|
57333fdcf0 | ||
|
|
dd5b4f23e6 | ||
|
|
397c19c103 | ||
|
|
5f515089ad | ||
|
|
f8fe881631 | ||
|
|
10eed50765 | ||
|
|
6b44b11fc4 | ||
|
|
2bb7567cbb | ||
|
|
0d8b8492f0 | ||
|
|
4bf61f892f | ||
|
|
fc524ad02a | ||
|
|
607fb34124 | ||
|
|
a931c7ac06 | ||
|
|
9322069dce | ||
|
|
52732e75a0 | ||
|
|
192aa78f94 | ||
|
|
c50b572faf | ||
|
|
2e54737a23 | ||
|
|
2e0ae8f1e1 | ||
|
|
f92c892b49 | ||
|
|
1504f9d661 | ||
|
|
a5e6b222dd | ||
|
|
23e3539f54 | ||
|
|
3303be678e |
2
.github/workflows/bump_collab_staging.yml
vendored
2
.github/workflows/bump_collab_staging.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
2
.github/workflows/bump_patch_version.yml
vendored
2
.github/workflows/bump_patch_version.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
- buildjet-16vcpu-ubuntu-2204
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
ref: ${{ github.event.inputs.branch }}
|
||||
ssh-key: ${{ secrets.ZED_BOT_DEPLOY_KEY }}
|
||||
|
||||
20
.github/workflows/ci.yml
vendored
20
.github/workflows/ci.yml
vendored
@@ -36,7 +36,7 @@ jobs:
|
||||
- test
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
clean: false
|
||||
fetch-depth: 0 # fetch full history
|
||||
@@ -78,13 +78,13 @@ jobs:
|
||||
- buildjet-8vcpu-ubuntu-2204
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
||||
|
||||
- name: Run style checks
|
||||
uses: ./.github/actions/check_style
|
||||
|
||||
- name: Check for typos
|
||||
uses: crate-ci/typos@8e6a4285bcbde632c5d79900a7779746e8b7ea3f # v1.24.6
|
||||
uses: crate-ci/typos@v1.24.6
|
||||
with:
|
||||
config: ./typos.toml
|
||||
|
||||
@@ -96,7 +96,7 @@ jobs:
|
||||
- test
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
clean: false
|
||||
|
||||
@@ -133,7 +133,7 @@ jobs:
|
||||
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
clean: false
|
||||
|
||||
@@ -165,7 +165,7 @@ jobs:
|
||||
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
clean: false
|
||||
|
||||
@@ -188,7 +188,7 @@ jobs:
|
||||
runs-on: hosted-windows-1
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
clean: false
|
||||
|
||||
@@ -229,7 +229,7 @@ jobs:
|
||||
node-version: "18"
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
# We need to fetch more than one commit so that `script/draft-release-notes`
|
||||
# is able to diff between the current and previous tag.
|
||||
@@ -314,7 +314,7 @@ jobs:
|
||||
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
clean: false
|
||||
|
||||
@@ -361,7 +361,7 @@ jobs:
|
||||
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
clean: false
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
- name: Set up uv
|
||||
uses: astral-sh/setup-uv@f3bcaebff5eace81a1c062af9f9011aae482ca9d # v3
|
||||
with:
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
- name: Set up uv
|
||||
uses: astral-sh/setup-uv@f3bcaebff5eace81a1c062af9f9011aae482ca9d # v3
|
||||
with:
|
||||
|
||||
2
.github/workflows/danger.yml
vendored
2
.github/workflows/danger.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
|
||||
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
|
||||
with:
|
||||
|
||||
2
.github/workflows/deploy_cloudflare.yml
vendored
2
.github/workflows/deploy_cloudflare.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
clean: false
|
||||
|
||||
|
||||
8
.github/workflows/deploy_collab.yml
vendored
8
.github/workflows/deploy_collab.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
- test
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
clean: false
|
||||
fetch-depth: 0
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
needs: style
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
clean: false
|
||||
fetch-depth: 0
|
||||
@@ -71,7 +71,7 @@ jobs:
|
||||
run: doctl registry login
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
clean: false
|
||||
|
||||
@@ -97,7 +97,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
clean: false
|
||||
|
||||
|
||||
4
.github/workflows/docs.yml
vendored
4
.github/workflows/docs.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
|
||||
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
|
||||
with:
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
}
|
||||
|
||||
- name: Check for Typos with Typos-CLI
|
||||
uses: crate-ci/typos@8e6a4285bcbde632c5d79900a7779746e8b7ea3f # v1.24.6
|
||||
uses: crate-ci/typos@v1.24.6
|
||||
with:
|
||||
config: ./typos.toml
|
||||
files: ./docs/
|
||||
|
||||
2
.github/workflows/publish_extension_cli.yml
vendored
2
.github/workflows/publish_extension_cli.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
- ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
clean: false
|
||||
|
||||
|
||||
2
.github/workflows/randomized_tests.yml
vendored
2
.github/workflows/randomized_tests.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
node-version: "18"
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
clean: false
|
||||
|
||||
|
||||
12
.github/workflows/release_nightly.yml
vendored
12
.github/workflows/release_nightly.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
- test
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
clean: false
|
||||
fetch-depth: 0
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
needs: style
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
clean: false
|
||||
|
||||
@@ -75,7 +75,7 @@ jobs:
|
||||
node-version: "18"
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
clean: false
|
||||
|
||||
@@ -109,7 +109,7 @@ jobs:
|
||||
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
clean: false
|
||||
|
||||
@@ -149,7 +149,7 @@ jobs:
|
||||
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
clean: false
|
||||
|
||||
@@ -182,7 +182,7 @@ jobs:
|
||||
- bundle-linux-arm
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
361
Cargo.lock
generated
361
Cargo.lock
generated
@@ -71,7 +71,6 @@ checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"const-random",
|
||||
"getrandom 0.2.15",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
@@ -262,9 +261,9 @@ checksum = "34cd60c5e3152cef0a592f1b296f1cc93715d89d2551d85315828c3a09575ff4"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.91"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8"
|
||||
checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6"
|
||||
|
||||
[[package]]
|
||||
name = "approx"
|
||||
@@ -307,168 +306,6 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrow"
|
||||
version = "53.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9ba0d7248932f4e2a12fb37f0a2e3ec82b3bdedbac2a1dce186e036843b8f8c"
|
||||
dependencies = [
|
||||
"arrow-arith",
|
||||
"arrow-array",
|
||||
"arrow-buffer",
|
||||
"arrow-cast",
|
||||
"arrow-data",
|
||||
"arrow-ord",
|
||||
"arrow-row",
|
||||
"arrow-schema",
|
||||
"arrow-select",
|
||||
"arrow-string",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrow-arith"
|
||||
version = "53.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d60afcdc004841a5c8d8da4f4fa22d64eb19c0c01ef4bcedd77f175a7cf6e38f"
|
||||
dependencies = [
|
||||
"arrow-array",
|
||||
"arrow-buffer",
|
||||
"arrow-data",
|
||||
"arrow-schema",
|
||||
"chrono",
|
||||
"half",
|
||||
"num",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrow-array"
|
||||
version = "53.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f16835e8599dbbb1659fd869d865254c4cf32c6c2bb60b6942ac9fc36bfa5da"
|
||||
dependencies = [
|
||||
"ahash 0.8.11",
|
||||
"arrow-buffer",
|
||||
"arrow-data",
|
||||
"arrow-schema",
|
||||
"chrono",
|
||||
"half",
|
||||
"hashbrown 0.14.5",
|
||||
"num",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrow-buffer"
|
||||
version = "53.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a1f34f0faae77da6b142db61deba2cb6d60167592b178be317b341440acba80"
|
||||
dependencies = [
|
||||
"bytes 1.7.2",
|
||||
"half",
|
||||
"num",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrow-cast"
|
||||
version = "53.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "450e4abb5775bca0740bec0bcf1b1a5ae07eff43bd625661c4436d8e8e4540c4"
|
||||
dependencies = [
|
||||
"arrow-array",
|
||||
"arrow-buffer",
|
||||
"arrow-data",
|
||||
"arrow-schema",
|
||||
"arrow-select",
|
||||
"atoi",
|
||||
"base64 0.22.1",
|
||||
"chrono",
|
||||
"comfy-table",
|
||||
"half",
|
||||
"lexical-core",
|
||||
"num",
|
||||
"ryu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrow-data"
|
||||
version = "53.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b1e618bbf714c7a9e8d97203c806734f012ff71ae3adc8ad1b075689f540634"
|
||||
dependencies = [
|
||||
"arrow-buffer",
|
||||
"arrow-schema",
|
||||
"half",
|
||||
"num",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrow-ord"
|
||||
version = "53.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2427f37b4459a4b9e533045abe87a5183a5e0995a3fc2c2fd45027ae2cc4ef3f"
|
||||
dependencies = [
|
||||
"arrow-array",
|
||||
"arrow-buffer",
|
||||
"arrow-data",
|
||||
"arrow-schema",
|
||||
"arrow-select",
|
||||
"half",
|
||||
"num",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrow-row"
|
||||
version = "53.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15959657d92e2261a7a323517640af87f5afd9fd8a6492e424ebee2203c567f6"
|
||||
dependencies = [
|
||||
"ahash 0.8.11",
|
||||
"arrow-array",
|
||||
"arrow-buffer",
|
||||
"arrow-data",
|
||||
"arrow-schema",
|
||||
"half",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrow-schema"
|
||||
version = "53.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbf0388a18fd7f7f3fe3de01852d30f54ed5182f9004db700fbe3ba843ed2794"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrow-select"
|
||||
version = "53.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b83e5723d307a38bf00ecd2972cd078d1339c7fd3eb044f609958a9a24463f3a"
|
||||
dependencies = [
|
||||
"ahash 0.8.11",
|
||||
"arrow-array",
|
||||
"arrow-buffer",
|
||||
"arrow-data",
|
||||
"arrow-schema",
|
||||
"num",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrow-string"
|
||||
version = "53.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ab3db7c09dd826e74079661d84ed01ed06547cf75d52c2818ef776d0d852305"
|
||||
dependencies = [
|
||||
"arrow-array",
|
||||
"arrow-buffer",
|
||||
"arrow-data",
|
||||
"arrow-schema",
|
||||
"arrow-select",
|
||||
"memchr",
|
||||
"num",
|
||||
"regex",
|
||||
"regex-syntax 0.8.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "as-raw-xcb-connection"
|
||||
version = "1.0.1"
|
||||
@@ -616,11 +453,9 @@ dependencies = [
|
||||
"anyhow",
|
||||
"collections",
|
||||
"derive_more",
|
||||
"futures 0.3.30",
|
||||
"gpui",
|
||||
"language",
|
||||
"parking_lot",
|
||||
"pretty_assertions",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"workspace",
|
||||
@@ -1033,7 +868,7 @@ dependencies = [
|
||||
"libc",
|
||||
"pin-project",
|
||||
"redox_syscall 0.2.16",
|
||||
"xattr 0.2.3",
|
||||
"xattr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1743,7 +1578,7 @@ dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"itertools 0.12.1",
|
||||
"itertools 0.10.5",
|
||||
"lazy_static",
|
||||
"lazycell",
|
||||
"proc-macro2",
|
||||
@@ -2714,7 +2549,6 @@ dependencies = [
|
||||
"dashmap 6.0.1",
|
||||
"derive_more",
|
||||
"dev_server_projects",
|
||||
"duckdb",
|
||||
"editor",
|
||||
"env_logger",
|
||||
"envy",
|
||||
@@ -2748,7 +2582,6 @@ dependencies = [
|
||||
"project",
|
||||
"prometheus",
|
||||
"prost",
|
||||
"r2d2",
|
||||
"rand 0.8.5",
|
||||
"recent_projects",
|
||||
"release_channel",
|
||||
@@ -2864,17 +2697,6 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "comfy-table"
|
||||
version = "7.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b34115915337defe99b2aff5c2ce6771e5fbc4079f4b506301f5cf394c8452f7"
|
||||
dependencies = [
|
||||
"strum 0.26.3",
|
||||
"strum_macros 0.26.4",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "command_palette"
|
||||
version = "0.1.0"
|
||||
@@ -3810,26 +3632,6 @@ dependencies = [
|
||||
"phf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "duckdb"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86844939330ba6ce345c4b5333d3be45c4f0c092779bf9617bba92efb8b841f5"
|
||||
dependencies = [
|
||||
"arrow",
|
||||
"cast",
|
||||
"fallible-iterator",
|
||||
"fallible-streaming-iterator",
|
||||
"hashlink",
|
||||
"libduckdb-sys",
|
||||
"memchr",
|
||||
"num-integer",
|
||||
"r2d2",
|
||||
"rust_decimal",
|
||||
"smallvec",
|
||||
"strum 0.25.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dwrote"
|
||||
version = "0.11.1"
|
||||
@@ -4385,12 +4187,6 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
|
||||
|
||||
[[package]]
|
||||
name = "fallible-streaming-iterator"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||
|
||||
[[package]]
|
||||
name = "fancy-regex"
|
||||
version = "0.12.0"
|
||||
@@ -5384,7 +5180,6 @@ checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crunchy",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5794,7 +5589,7 @@ dependencies = [
|
||||
"httpdate",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"socket2 0.5.7",
|
||||
"socket2 0.4.10",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
@@ -6633,70 +6428,6 @@ version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
||||
|
||||
[[package]]
|
||||
name = "lexical-core"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0431c65b318a590c1de6b8fd6e72798c92291d27762d94c9e6c37ed7a73d8458"
|
||||
dependencies = [
|
||||
"lexical-parse-float",
|
||||
"lexical-parse-integer",
|
||||
"lexical-util",
|
||||
"lexical-write-float",
|
||||
"lexical-write-integer",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lexical-parse-float"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb17a4bdb9b418051aa59d41d65b1c9be5affab314a872e5ad7f06231fb3b4e0"
|
||||
dependencies = [
|
||||
"lexical-parse-integer",
|
||||
"lexical-util",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lexical-parse-integer"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5df98f4a4ab53bf8b175b363a34c7af608fe31f93cc1fb1bf07130622ca4ef61"
|
||||
dependencies = [
|
||||
"lexical-util",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lexical-util"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85314db53332e5c192b6bca611fb10c114a80d1b831ddac0af1e9be1b9232ca0"
|
||||
dependencies = [
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lexical-write-float"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e7c3ad4e37db81c1cbe7cf34610340adc09c322871972f74877a712abc6c809"
|
||||
dependencies = [
|
||||
"lexical-util",
|
||||
"lexical-write-integer",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lexical-write-integer"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb89e9f6958b83258afa3deed90b5de9ef68eef090ad5086c791cd2345610162"
|
||||
dependencies = [
|
||||
"lexical-util",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.159"
|
||||
@@ -6713,21 +6444,6 @@ dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libduckdb-sys"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eac2de5219db852597558df5dcd617ffccd5cbd7b9f5402ccbf899aca6cb6047"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"flate2",
|
||||
"pkg-config",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tar",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
version = "0.4.7"
|
||||
@@ -6758,7 +6474,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.52.6",
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8797,7 +8513,6 @@ dependencies = [
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"settings",
|
||||
"smallvec",
|
||||
"theme",
|
||||
"ui",
|
||||
"util",
|
||||
@@ -9077,17 +8792,6 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r2d2"
|
||||
version = "0.8.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93"
|
||||
dependencies = [
|
||||
"log",
|
||||
"parking_lot",
|
||||
"scheduled-thread-pool",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "radium"
|
||||
version = "0.7.0"
|
||||
@@ -10163,15 +9867,6 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scheduled-thread-pool"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19"
|
||||
dependencies = [
|
||||
"parking_lot",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schemars"
|
||||
version = "0.8.21"
|
||||
@@ -11399,7 +11094,7 @@ version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
|
||||
dependencies = [
|
||||
"strum_macros 0.25.3",
|
||||
"strum_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11421,19 +11116,6 @@ dependencies = [
|
||||
"syn 2.0.76",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.26.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.76",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
@@ -11799,17 +11481,6 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||
|
||||
[[package]]
|
||||
name = "tar"
|
||||
version = "0.4.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ff6c40d3aedb5e06b57c6f669ad17ab063dd1e63d977c6a88e7f4dfa4f04020"
|
||||
dependencies = [
|
||||
"filetime",
|
||||
"libc",
|
||||
"xattr 1.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "target-lexicon"
|
||||
version = "0.12.16"
|
||||
@@ -14073,7 +13744,7 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -14748,17 +14419,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xattr"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"linux-raw-sys 0.4.14",
|
||||
"rustix 0.38.35",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xcursor"
|
||||
version = "0.3.8"
|
||||
@@ -14941,7 +14601,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed"
|
||||
version = "0.160.0"
|
||||
version = "0.159.0"
|
||||
dependencies = [
|
||||
"activity_indicator",
|
||||
"anyhow",
|
||||
@@ -15049,6 +14709,7 @@ dependencies = [
|
||||
"winresource",
|
||||
"workspace",
|
||||
"zed_actions",
|
||||
"zstd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -15180,7 +14841,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed_php"
|
||||
version = "0.2.2"
|
||||
version = "0.2.1"
|
||||
dependencies = [
|
||||
"zed_extension_api 0.1.0",
|
||||
]
|
||||
|
||||
@@ -346,8 +346,6 @@
|
||||
"git_status": true,
|
||||
// Amount of indentation for nested items.
|
||||
"indent_size": 20,
|
||||
// Whether to show indent guides in the project panel.
|
||||
"indent_guides": true,
|
||||
// Whether to reveal it in the project panel automatically,
|
||||
// when a corresponding project entry becomes active.
|
||||
// Gitignored entries are never auto revealed.
|
||||
@@ -805,7 +803,7 @@
|
||||
/// You can override this to use a version of node that is not in $PATH with:
|
||||
/// {
|
||||
/// "node": {
|
||||
/// "path": "/path/to/node"
|
||||
/// "node_path": "/path/to/node"
|
||||
/// "npm_path": "/path/to/npm" (defaults to node_path/../npm)
|
||||
/// }
|
||||
/// }
|
||||
@@ -1101,13 +1099,13 @@
|
||||
// }
|
||||
"command_aliases": {},
|
||||
// ssh_connections is an array of ssh connections.
|
||||
// By default this setting is null, which disables the direct ssh connection support.
|
||||
// You can configure these from `project: Open Remote` in the command palette.
|
||||
// Zed's ssh support will pull configuration from your ~/.ssh too.
|
||||
// Examples:
|
||||
// [
|
||||
// {
|
||||
// "host": "example-box",
|
||||
// // "port": 22, "username": "test", "args": ["-i", "/home/user/.ssh/id_rsa"]
|
||||
// "projects": [
|
||||
// {
|
||||
// "paths": ["/home/user/code/zed"]
|
||||
@@ -1115,7 +1113,7 @@
|
||||
// ]
|
||||
// }
|
||||
// ]
|
||||
"ssh_connections": [],
|
||||
"ssh_connections": null,
|
||||
// Configures the Context Server Protocol binaries
|
||||
//
|
||||
// Examples:
|
||||
|
||||
@@ -29,13 +29,13 @@ pub struct AnthropicModelCacheConfiguration {
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, EnumIter)]
|
||||
pub enum Model {
|
||||
#[default]
|
||||
#[serde(rename = "claude-3-5-sonnet", alias = "claude-3-5-sonnet-latest")]
|
||||
#[serde(rename = "claude-3-5-sonnet", alias = "claude-3-5-sonnet-20240620")]
|
||||
Claude3_5Sonnet,
|
||||
#[serde(rename = "claude-3-opus", alias = "claude-3-opus-latest")]
|
||||
#[serde(rename = "claude-3-opus", alias = "claude-3-opus-20240229")]
|
||||
Claude3Opus,
|
||||
#[serde(rename = "claude-3-sonnet", alias = "claude-3-sonnet-latest")]
|
||||
#[serde(rename = "claude-3-sonnet", alias = "claude-3-sonnet-20240229")]
|
||||
Claude3Sonnet,
|
||||
#[serde(rename = "claude-3-haiku", alias = "claude-3-haiku-latest")]
|
||||
#[serde(rename = "claude-3-haiku", alias = "claude-3-haiku-20240307")]
|
||||
Claude3Haiku,
|
||||
#[serde(rename = "custom")]
|
||||
Custom {
|
||||
@@ -69,10 +69,10 @@ impl Model {
|
||||
|
||||
pub fn id(&self) -> &str {
|
||||
match self {
|
||||
Model::Claude3_5Sonnet => "claude-3-5-sonnet-latest",
|
||||
Model::Claude3Opus => "claude-3-opus-latest",
|
||||
Model::Claude3Sonnet => "claude-3-sonnet-latest",
|
||||
Model::Claude3Haiku => "claude-3-haiku-latest",
|
||||
Model::Claude3_5Sonnet => "claude-3-5-sonnet-20240620",
|
||||
Model::Claude3Opus => "claude-3-opus-20240229",
|
||||
Model::Claude3Sonnet => "claude-3-sonnet-20240229",
|
||||
Model::Claude3Haiku => "claude-3-haiku-20240307",
|
||||
Self::Custom { name, .. } => name,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,8 +26,8 @@ use collections::{BTreeSet, HashMap, HashSet};
|
||||
use editor::{
|
||||
actions::{FoldAt, MoveToEndOfLine, Newline, ShowCompletions, UnfoldAt},
|
||||
display_map::{
|
||||
BlockContext, BlockDisposition, BlockId, BlockProperties, BlockStyle, Crease,
|
||||
CreaseMetadata, CustomBlockId, FoldId, RenderBlock, ToDisplayPoint,
|
||||
BlockContext, BlockId, BlockPlacement, BlockProperties, BlockStyle, Crease, CreaseMetadata,
|
||||
CustomBlockId, FoldId, RenderBlock, ToDisplayPoint,
|
||||
},
|
||||
scroll::{Autoscroll, AutoscrollStrategy},
|
||||
Anchor, Editor, EditorEvent, ProposedChangeLocation, ProposedChangesEditor, RowExt,
|
||||
@@ -1446,8 +1446,8 @@ struct ScrollPosition {
|
||||
}
|
||||
|
||||
struct PatchViewState {
|
||||
footer_block_id: CustomBlockId,
|
||||
crease_id: CreaseId,
|
||||
block_id: CustomBlockId,
|
||||
// crease_id: CreaseId,
|
||||
editor: Option<PatchEditorState>,
|
||||
update_task: Option<Task<()>>,
|
||||
}
|
||||
@@ -2009,13 +2009,12 @@ impl ContextEditor {
|
||||
})
|
||||
.map(|(command, error_message)| BlockProperties {
|
||||
style: BlockStyle::Fixed,
|
||||
position: Anchor {
|
||||
height: 1,
|
||||
placement: BlockPlacement::Below(Anchor {
|
||||
buffer_id: Some(buffer_id),
|
||||
excerpt_id,
|
||||
text_anchor: command.source_range.start,
|
||||
},
|
||||
height: 1,
|
||||
disposition: BlockDisposition::Below,
|
||||
}),
|
||||
render: slash_command_error_block_renderer(error_message),
|
||||
priority: 0,
|
||||
}),
|
||||
@@ -2159,8 +2158,8 @@ impl ContextEditor {
|
||||
for range in removed {
|
||||
if let Some(state) = self.patches.remove(range) {
|
||||
editors_to_close.extend(state.editor.and_then(|state| state.editor.upgrade()));
|
||||
removed_block_ids.insert(state.footer_block_id);
|
||||
removed_crease_ids.push(state.crease_id);
|
||||
removed_block_ids.insert(state.block_id);
|
||||
// removed_crease_ids.push(state.crease_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2190,7 +2189,7 @@ impl ContextEditor {
|
||||
let gutter_width = cx.gutter_dimensions.full_width();
|
||||
let block_id = cx.block_id;
|
||||
this.update(&mut **cx, |this, cx| {
|
||||
this.render_patch_footer(
|
||||
this.render_patch(
|
||||
patch_range.clone(),
|
||||
max_width,
|
||||
gutter_width,
|
||||
@@ -2204,26 +2203,9 @@ impl ContextEditor {
|
||||
}
|
||||
});
|
||||
|
||||
let header_placeholder = FoldPlaceholder {
|
||||
render: {
|
||||
let this = this.clone();
|
||||
let patch_range = range.clone();
|
||||
Arc::new(move |fold_id, _range, cx| {
|
||||
this.update(cx, |this, cx| {
|
||||
this.render_patch_header(patch_range.clone(), fold_id, cx)
|
||||
})
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap_or_else(|| Empty.into_any())
|
||||
})
|
||||
},
|
||||
constrain_width: false,
|
||||
merge_adjacent: false,
|
||||
};
|
||||
|
||||
let should_refold;
|
||||
if let Some(state) = self.patches.get_mut(&range) {
|
||||
replaced_blocks.insert(state.footer_block_id, render_block);
|
||||
replaced_blocks.insert(state.block_id, render_block);
|
||||
if let Some(editor_state) = &state.editor {
|
||||
if editor_state.opened_patch != patch {
|
||||
state.update_task = Some({
|
||||
@@ -2242,32 +2224,31 @@ impl ContextEditor {
|
||||
} else {
|
||||
let block_ids = editor.insert_blocks(
|
||||
[BlockProperties {
|
||||
position: patch_start,
|
||||
height: path_count as u32 + 1,
|
||||
style: BlockStyle::Flex,
|
||||
style: BlockStyle::Fixed,
|
||||
render: render_block,
|
||||
disposition: BlockDisposition::Below,
|
||||
placement: BlockPlacement::Replace(patch_start..patch_end),
|
||||
priority: 0,
|
||||
}],
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
|
||||
let new_crease_ids = editor.insert_creases(
|
||||
[Crease::new(
|
||||
patch_start..patch_end,
|
||||
header_placeholder.clone(),
|
||||
fold_toggle("patch-header"),
|
||||
|_, _, _| Empty.into_any_element(),
|
||||
)],
|
||||
cx,
|
||||
);
|
||||
// let new_crease_ids = editor.insert_creases(
|
||||
// [Crease::new(
|
||||
// patch_start..patch_end,
|
||||
// header_placeholder.clone(),
|
||||
// fold_toggle("patch-header"),
|
||||
// |_, _, _| Empty.into_any_element(),
|
||||
// )],
|
||||
// cx,
|
||||
// );
|
||||
|
||||
self.patches.insert(
|
||||
range.clone(),
|
||||
PatchViewState {
|
||||
footer_block_id: block_ids[0],
|
||||
crease_id: new_crease_ids[0],
|
||||
block_id: block_ids[0],
|
||||
// crease_id: new_crease_ids[0],
|
||||
editor: None,
|
||||
update_task: None,
|
||||
},
|
||||
@@ -2277,8 +2258,8 @@ impl ContextEditor {
|
||||
}
|
||||
|
||||
if should_refold {
|
||||
editor.unfold_ranges([patch_start..patch_end], true, false, cx);
|
||||
editor.fold_ranges([(patch_start..patch_end, header_placeholder)], false, cx);
|
||||
// editor.unfold_ranges([patch_start..patch_end], true, false, cx);
|
||||
// editor.fold_ranges([(patch_start..patch_end, header_placeholder)], false, cx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2731,12 +2712,13 @@ impl ContextEditor {
|
||||
})
|
||||
};
|
||||
let create_block_properties = |message: &Message| BlockProperties {
|
||||
position: buffer
|
||||
.anchor_in_excerpt(excerpt_id, message.anchor_range.start)
|
||||
.unwrap(),
|
||||
height: 2,
|
||||
style: BlockStyle::Sticky,
|
||||
disposition: BlockDisposition::Above,
|
||||
placement: BlockPlacement::Above(
|
||||
buffer
|
||||
.anchor_in_excerpt(excerpt_id, message.anchor_range.start)
|
||||
.unwrap(),
|
||||
),
|
||||
priority: usize::MAX,
|
||||
render: render_block(MessageMetadata::from(message)),
|
||||
};
|
||||
@@ -3372,7 +3354,7 @@ impl ContextEditor {
|
||||
let anchor = buffer.anchor_in_excerpt(excerpt_id, anchor).unwrap();
|
||||
let image = render_image.clone();
|
||||
anchor.is_valid(&buffer).then(|| BlockProperties {
|
||||
position: anchor,
|
||||
placement: BlockPlacement::Above(anchor),
|
||||
height: MAX_HEIGHT_IN_LINES,
|
||||
style: BlockStyle::Sticky,
|
||||
render: Box::new(move |cx| {
|
||||
@@ -3393,8 +3375,6 @@ impl ContextEditor {
|
||||
)
|
||||
.into_any_element()
|
||||
}),
|
||||
|
||||
disposition: BlockDisposition::Above,
|
||||
priority: 0,
|
||||
})
|
||||
})
|
||||
@@ -3434,28 +3414,7 @@ impl ContextEditor {
|
||||
.unwrap_or_else(|| Cow::Borrowed(DEFAULT_TAB_TITLE))
|
||||
}
|
||||
|
||||
fn render_patch_header(
|
||||
&self,
|
||||
range: Range<text::Anchor>,
|
||||
_id: FoldId,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<AnyElement> {
|
||||
let patch = self.context.read(cx).patch_for_range(&range, cx)?;
|
||||
let theme = cx.theme().clone();
|
||||
Some(
|
||||
h_flex()
|
||||
.px_1()
|
||||
.py_0p5()
|
||||
.border_b_1()
|
||||
.border_color(theme.status().info_border)
|
||||
.gap_1()
|
||||
.child(Icon::new(IconName::Diff).size(IconSize::Small))
|
||||
.child(Label::new(patch.title.clone()).size(LabelSize::Small))
|
||||
.into_any(),
|
||||
)
|
||||
}
|
||||
|
||||
fn render_patch_footer(
|
||||
fn render_patch(
|
||||
&mut self,
|
||||
range: Range<text::Anchor>,
|
||||
max_width: Pixels,
|
||||
@@ -3471,10 +3430,6 @@ impl ContextEditor {
|
||||
.anchor_in_excerpt(excerpt_id, range.start)
|
||||
.unwrap();
|
||||
|
||||
if !snapshot.intersects_fold(anchor) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let patch = self.context.read(cx).patch_for_range(&range, cx)?;
|
||||
let paths = patch
|
||||
.paths()
|
||||
@@ -3483,10 +3438,13 @@ impl ContextEditor {
|
||||
|
||||
Some(
|
||||
v_flex()
|
||||
.border_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.id(id)
|
||||
.pl(gutter_width)
|
||||
.w(max_width)
|
||||
.py_2()
|
||||
.ml(gutter_width)
|
||||
.p_2()
|
||||
.rounded_md()
|
||||
.min_h(cx.line_height() * 3.)
|
||||
.cursor(CursorStyle::PointingHand)
|
||||
.on_click(cx.listener(move |this, _, cx| {
|
||||
this.editor.update(cx, |editor, cx| {
|
||||
@@ -3496,6 +3454,7 @@ impl ContextEditor {
|
||||
});
|
||||
this.focus_active_patch(cx);
|
||||
}))
|
||||
.child(Label::new(patch.title.clone()))
|
||||
.children(paths.into_iter().map(|path| {
|
||||
h_flex()
|
||||
.pl_1()
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::{
|
||||
};
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use assistant_slash_command::{
|
||||
SlashCommandOutput, SlashCommandOutputSection, SlashCommandRegistry, SlashCommandResult,
|
||||
SlashCommandOutput, SlashCommandOutputSection, SlashCommandRegistry,
|
||||
};
|
||||
use assistant_tool::ToolRegistry;
|
||||
use client::{self, proto, telemetry::Telemetry};
|
||||
@@ -1677,7 +1677,7 @@ impl Context {
|
||||
pub fn insert_command_output(
|
||||
&mut self,
|
||||
command_range: Range<language::Anchor>,
|
||||
output: Task<SlashCommandResult>,
|
||||
output: Task<Result<SlashCommandOutput>>,
|
||||
ensure_trailing_newline: bool,
|
||||
expand_result: bool,
|
||||
cx: &mut ModelContext<Self>,
|
||||
@@ -1688,13 +1688,19 @@ impl Context {
|
||||
let command_range = command_range.clone();
|
||||
async move {
|
||||
let output = output.await;
|
||||
let output = match output {
|
||||
Ok(output) => SlashCommandOutput::from_event_stream(output).await,
|
||||
Err(err) => Err(err),
|
||||
};
|
||||
this.update(&mut cx, |this, cx| match output {
|
||||
Ok(mut output) => {
|
||||
output.ensure_valid_section_ranges();
|
||||
// Ensure section ranges are valid.
|
||||
for section in &mut output.sections {
|
||||
section.range.start = section.range.start.min(output.text.len());
|
||||
section.range.end = section.range.end.min(output.text.len());
|
||||
while !output.text.is_char_boundary(section.range.start) {
|
||||
section.range.start -= 1;
|
||||
}
|
||||
while !output.text.is_char_boundary(section.range.end) {
|
||||
section.range.end += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure there is a newline after the last section.
|
||||
if ensure_trailing_newline {
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::{
|
||||
use anyhow::Result;
|
||||
use assistant_slash_command::{
|
||||
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
|
||||
SlashCommandRegistry, SlashCommandResult,
|
||||
SlashCommandRegistry,
|
||||
};
|
||||
use collections::HashSet;
|
||||
use fs::FakeFs;
|
||||
@@ -1097,8 +1097,7 @@ async fn test_random_context_collaboration(cx: &mut TestAppContext, mut rng: Std
|
||||
text: output_text,
|
||||
sections,
|
||||
run_commands_in_text: false,
|
||||
}
|
||||
.to_event_stream())),
|
||||
})),
|
||||
true,
|
||||
false,
|
||||
cx,
|
||||
@@ -1417,12 +1416,11 @@ impl SlashCommand for FakeSlashCommand {
|
||||
_workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
_cx: &mut WindowContext,
|
||||
) -> Task<SlashCommandResult> {
|
||||
) -> Task<Result<SlashCommandOutput>> {
|
||||
Task::ready(Ok(SlashCommandOutput {
|
||||
text: format!("Executed fake command: {}", self.0),
|
||||
sections: vec![],
|
||||
run_commands_in_text: false,
|
||||
}
|
||||
.to_event_stream()))
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use collections::{hash_map, HashMap, HashSet, VecDeque};
|
||||
use editor::{
|
||||
actions::{MoveDown, MoveUp, SelectAll},
|
||||
display_map::{
|
||||
BlockContext, BlockDisposition, BlockProperties, BlockStyle, CustomBlockId, RenderBlock,
|
||||
BlockContext, BlockPlacement, BlockProperties, BlockStyle, CustomBlockId, RenderBlock,
|
||||
ToDisplayPoint,
|
||||
},
|
||||
Anchor, AnchorRangeExt, CodeActionProvider, Editor, EditorElement, EditorEvent, EditorMode,
|
||||
@@ -446,15 +446,14 @@ impl InlineAssistant {
|
||||
let assist_blocks = vec![
|
||||
BlockProperties {
|
||||
style: BlockStyle::Sticky,
|
||||
position: range.start,
|
||||
placement: BlockPlacement::Above(range.start),
|
||||
height: prompt_editor_height,
|
||||
render: build_assist_editor_renderer(prompt_editor),
|
||||
disposition: BlockDisposition::Above,
|
||||
priority: 0,
|
||||
},
|
||||
BlockProperties {
|
||||
style: BlockStyle::Sticky,
|
||||
position: range.end,
|
||||
placement: BlockPlacement::Below(range.end),
|
||||
height: 0,
|
||||
render: Box::new(|cx| {
|
||||
v_flex()
|
||||
@@ -464,7 +463,6 @@ impl InlineAssistant {
|
||||
.border_color(cx.theme().status().info_border)
|
||||
.into_any_element()
|
||||
}),
|
||||
disposition: BlockDisposition::Below,
|
||||
priority: 0,
|
||||
},
|
||||
];
|
||||
@@ -1179,7 +1177,7 @@ impl InlineAssistant {
|
||||
let height =
|
||||
deleted_lines_editor.update(cx, |editor, cx| editor.max_point(cx).row().0 + 1);
|
||||
new_blocks.push(BlockProperties {
|
||||
position: new_row,
|
||||
placement: BlockPlacement::Above(new_row),
|
||||
height,
|
||||
style: BlockStyle::Flex,
|
||||
render: Box::new(move |cx| {
|
||||
@@ -1191,7 +1189,6 @@ impl InlineAssistant {
|
||||
.child(deleted_lines_editor.clone())
|
||||
.into_any_element()
|
||||
}),
|
||||
disposition: BlockDisposition::Above,
|
||||
priority: 0,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use super::create_label_for_command;
|
||||
use super::{SlashCommand, SlashCommandOutput};
|
||||
use anyhow::{anyhow, Result};
|
||||
use assistant_slash_command::{
|
||||
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
|
||||
SlashCommandResult,
|
||||
};
|
||||
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
|
||||
use feature_flags::FeatureFlag;
|
||||
use futures::StreamExt;
|
||||
use gpui::{AppContext, AsyncAppContext, Task, WeakView};
|
||||
@@ -18,8 +17,6 @@ use ui::{BorrowAppContext, WindowContext};
|
||||
use util::ResultExt;
|
||||
use workspace::Workspace;
|
||||
|
||||
use crate::slash_command::create_label_for_command;
|
||||
|
||||
pub struct AutoSlashCommandFeatureFlag;
|
||||
|
||||
impl FeatureFlag for AutoSlashCommandFeatureFlag {
|
||||
@@ -95,7 +92,7 @@ impl SlashCommand for AutoCommand {
|
||||
workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Task<SlashCommandResult> {
|
||||
) -> Task<Result<SlashCommandOutput>> {
|
||||
let Some(workspace) = workspace.upgrade() else {
|
||||
return Task::ready(Err(anyhow::anyhow!("workspace was dropped")));
|
||||
};
|
||||
@@ -147,8 +144,7 @@ impl SlashCommand for AutoCommand {
|
||||
text: prompt,
|
||||
sections: Vec::new(),
|
||||
run_commands_in_text: true,
|
||||
}
|
||||
.to_event_stream())
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use super::{SlashCommand, SlashCommandOutput};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use assistant_slash_command::{
|
||||
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
|
||||
SlashCommandResult,
|
||||
};
|
||||
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
|
||||
use fs::Fs;
|
||||
use gpui::{AppContext, Model, Task, WeakView};
|
||||
use language::{BufferSnapshot, LspAdapterDelegate};
|
||||
@@ -125,7 +123,7 @@ impl SlashCommand for CargoWorkspaceSlashCommand {
|
||||
workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Task<SlashCommandResult> {
|
||||
) -> Task<Result<SlashCommandOutput>> {
|
||||
let output = workspace.update(cx, |workspace, cx| {
|
||||
let project = workspace.project().clone();
|
||||
let fs = workspace.project().read(cx).fs().clone();
|
||||
@@ -147,8 +145,7 @@ impl SlashCommand for CargoWorkspaceSlashCommand {
|
||||
metadata: None,
|
||||
}],
|
||||
run_commands_in_text: false,
|
||||
}
|
||||
.to_event_stream())
|
||||
})
|
||||
})
|
||||
});
|
||||
output.unwrap_or_else(|error| Task::ready(Err(error)))
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use super::create_label_for_command;
|
||||
use anyhow::{anyhow, Result};
|
||||
use assistant_slash_command::{
|
||||
AfterCompletion, ArgumentCompletion, SlashCommand, SlashCommandOutput,
|
||||
SlashCommandOutputSection, SlashCommandResult,
|
||||
SlashCommandOutputSection,
|
||||
};
|
||||
use collections::HashMap;
|
||||
use context_servers::{
|
||||
@@ -16,8 +17,6 @@ use text::LineEnding;
|
||||
use ui::{IconName, SharedString};
|
||||
use workspace::Workspace;
|
||||
|
||||
use crate::slash_command::create_label_for_command;
|
||||
|
||||
pub struct ContextServerSlashCommand {
|
||||
server_id: String,
|
||||
prompt: Prompt,
|
||||
@@ -129,7 +128,7 @@ impl SlashCommand for ContextServerSlashCommand {
|
||||
_workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Task<SlashCommandResult> {
|
||||
) -> Task<Result<SlashCommandOutput>> {
|
||||
let server_id = self.server_id.clone();
|
||||
let prompt_name = self.prompt.name.clone();
|
||||
|
||||
@@ -185,8 +184,7 @@ impl SlashCommand for ContextServerSlashCommand {
|
||||
}],
|
||||
text: prompt,
|
||||
run_commands_in_text: false,
|
||||
}
|
||||
.to_event_stream())
|
||||
})
|
||||
})
|
||||
} else {
|
||||
Task::ready(Err(anyhow!("Context server not found")))
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
use super::{SlashCommand, SlashCommandOutput};
|
||||
use crate::prompt_library::PromptStore;
|
||||
use anyhow::{anyhow, Result};
|
||||
use assistant_slash_command::{
|
||||
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
|
||||
SlashCommandResult,
|
||||
};
|
||||
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
|
||||
use gpui::{Task, WeakView};
|
||||
use language::{BufferSnapshot, LspAdapterDelegate};
|
||||
use std::{
|
||||
@@ -50,7 +48,7 @@ impl SlashCommand for DefaultSlashCommand {
|
||||
_workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Task<SlashCommandResult> {
|
||||
) -> Task<Result<SlashCommandOutput>> {
|
||||
let store = PromptStore::global(cx);
|
||||
cx.background_executor().spawn(async move {
|
||||
let store = store.await?;
|
||||
@@ -78,8 +76,7 @@ impl SlashCommand for DefaultSlashCommand {
|
||||
}],
|
||||
text,
|
||||
run_commands_in_text: true,
|
||||
}
|
||||
.to_event_stream())
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ use crate::slash_command::file_command::{FileCommandMetadata, FileSlashCommand};
|
||||
use anyhow::Result;
|
||||
use assistant_slash_command::{
|
||||
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
|
||||
SlashCommandResult,
|
||||
};
|
||||
use collections::HashSet;
|
||||
use futures::future;
|
||||
@@ -49,7 +48,7 @@ impl SlashCommand for DeltaSlashCommand {
|
||||
workspace: WeakView<Workspace>,
|
||||
delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Task<SlashCommandResult> {
|
||||
) -> Task<Result<SlashCommandOutput>> {
|
||||
let mut paths = HashSet::default();
|
||||
let mut file_command_old_outputs = Vec::new();
|
||||
let mut file_command_new_outputs = Vec::new();
|
||||
@@ -86,28 +85,25 @@ impl SlashCommand for DeltaSlashCommand {
|
||||
.zip(file_command_new_outputs)
|
||||
{
|
||||
if let Ok(new_output) = new_output {
|
||||
if let Ok(new_output) = SlashCommandOutput::from_event_stream(new_output).await
|
||||
{
|
||||
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);
|
||||
}
|
||||
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.to_event_stream())
|
||||
Ok(output)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use super::{create_label_for_command, SlashCommand, SlashCommandOutput};
|
||||
use anyhow::{anyhow, Result};
|
||||
use assistant_slash_command::{
|
||||
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
|
||||
SlashCommandResult,
|
||||
};
|
||||
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
|
||||
use fuzzy::{PathMatch, StringMatchCandidate};
|
||||
use gpui::{AppContext, Model, Task, View, WeakView};
|
||||
use language::{
|
||||
@@ -21,8 +19,6 @@ use util::paths::PathMatcher;
|
||||
use util::ResultExt;
|
||||
use workspace::Workspace;
|
||||
|
||||
use crate::slash_command::create_label_for_command;
|
||||
|
||||
pub(crate) struct DiagnosticsSlashCommand;
|
||||
|
||||
impl DiagnosticsSlashCommand {
|
||||
@@ -171,7 +167,7 @@ impl SlashCommand for DiagnosticsSlashCommand {
|
||||
workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Task<SlashCommandResult> {
|
||||
) -> Task<Result<SlashCommandOutput>> {
|
||||
let Some(workspace) = workspace.upgrade() else {
|
||||
return Task::ready(Err(anyhow!("workspace was dropped")));
|
||||
};
|
||||
@@ -180,11 +176,7 @@ impl SlashCommand for DiagnosticsSlashCommand {
|
||||
|
||||
let task = collect_diagnostics(workspace.read(cx).project().clone(), options, cx);
|
||||
|
||||
cx.spawn(move |_| async move {
|
||||
task.await?
|
||||
.map(|output| output.to_event_stream())
|
||||
.ok_or_else(|| anyhow!("No diagnostics found"))
|
||||
})
|
||||
cx.spawn(move |_| async move { task.await?.ok_or_else(|| anyhow!("No diagnostics found")) })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ use std::time::Duration;
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use assistant_slash_command::{
|
||||
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
|
||||
SlashCommandResult,
|
||||
};
|
||||
use gpui::{AppContext, BackgroundExecutor, Model, Task, WeakView};
|
||||
use indexed_docs::{
|
||||
@@ -275,7 +274,7 @@ impl SlashCommand for DocsSlashCommand {
|
||||
_workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Task<SlashCommandResult> {
|
||||
) -> Task<Result<SlashCommandOutput>> {
|
||||
if arguments.is_empty() {
|
||||
return Task::ready(Err(anyhow!("missing an argument")));
|
||||
};
|
||||
@@ -356,8 +355,7 @@ impl SlashCommand for DocsSlashCommand {
|
||||
})
|
||||
.collect(),
|
||||
run_commands_in_text: false,
|
||||
}
|
||||
.to_event_stream())
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ use std::sync::Arc;
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use assistant_slash_command::{
|
||||
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
|
||||
SlashCommandResult,
|
||||
};
|
||||
use futures::AsyncReadExt;
|
||||
use gpui::{Task, WeakView};
|
||||
@@ -134,7 +133,7 @@ impl SlashCommand for FetchSlashCommand {
|
||||
workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Task<SlashCommandResult> {
|
||||
) -> Task<Result<SlashCommandOutput>> {
|
||||
let Some(argument) = arguments.first() else {
|
||||
return Task::ready(Err(anyhow!("missing URL")));
|
||||
};
|
||||
@@ -167,8 +166,7 @@ impl SlashCommand for FetchSlashCommand {
|
||||
metadata: None,
|
||||
}],
|
||||
run_commands_in_text: false,
|
||||
}
|
||||
.to_event_stream())
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
use super::{diagnostics_command::collect_buffer_diagnostics, SlashCommand, SlashCommandOutput};
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use assistant_slash_command::{
|
||||
AfterCompletion, ArgumentCompletion, SlashCommand, SlashCommandContent, SlashCommandEvent,
|
||||
SlashCommandOutput, SlashCommandOutputSection, SlashCommandResult,
|
||||
};
|
||||
use futures::channel::mpsc;
|
||||
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 smol::stream::StreamExt;
|
||||
use std::{
|
||||
fmt::Write,
|
||||
ops::{Range, RangeInclusive},
|
||||
@@ -20,8 +16,6 @@ use ui::prelude::*;
|
||||
use util::ResultExt;
|
||||
use workspace::Workspace;
|
||||
|
||||
use crate::slash_command::diagnostics_command::collect_buffer_diagnostics;
|
||||
|
||||
pub(crate) struct FileSlashCommand;
|
||||
|
||||
impl FileSlashCommand {
|
||||
@@ -187,7 +181,7 @@ impl SlashCommand for FileSlashCommand {
|
||||
workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Task<SlashCommandResult> {
|
||||
) -> Task<Result<SlashCommandOutput>> {
|
||||
let Some(workspace) = workspace.upgrade() else {
|
||||
return Task::ready(Err(anyhow!("workspace was dropped")));
|
||||
};
|
||||
@@ -204,7 +198,7 @@ fn collect_files(
|
||||
project: Model<Project>,
|
||||
glob_inputs: &[String],
|
||||
cx: &mut AppContext,
|
||||
) -> Task<SlashCommandResult> {
|
||||
) -> Task<Result<SlashCommandOutput>> {
|
||||
let Ok(matchers) = glob_inputs
|
||||
.into_iter()
|
||||
.map(|glob_input| {
|
||||
@@ -223,11 +217,11 @@ fn collect_files(
|
||||
.map(|worktree| worktree.read(cx).snapshot())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let (events_tx, events_rx) = mpsc::unbounded();
|
||||
cx.spawn(|mut cx| async move {
|
||||
let mut output = SlashCommandOutput::default();
|
||||
for snapshot in snapshots {
|
||||
let worktree_id = snapshot.id();
|
||||
let mut directory_stack: Vec<Arc<Path>> = Vec::new();
|
||||
let mut directory_stack: Vec<(Arc<Path>, String, usize)> = Vec::new();
|
||||
let mut folded_directory_names_stack = Vec::new();
|
||||
let mut is_top_level_directory = true;
|
||||
|
||||
@@ -243,19 +237,17 @@ fn collect_files(
|
||||
continue;
|
||||
}
|
||||
|
||||
while let Some(dir) = directory_stack.last() {
|
||||
while let Some((dir, _, _)) = directory_stack.last() {
|
||||
if entry.path.starts_with(dir) {
|
||||
break;
|
||||
}
|
||||
directory_stack.pop().unwrap();
|
||||
events_tx
|
||||
.unbounded_send(Ok(SlashCommandEvent::EndSection { metadata: None }))?;
|
||||
events_tx.unbounded_send(Ok(SlashCommandEvent::Content(
|
||||
SlashCommandContent::Text {
|
||||
text: "\n".into(),
|
||||
run_commands_in_text: false,
|
||||
},
|
||||
)))?;
|
||||
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,
|
||||
));
|
||||
}
|
||||
|
||||
let filename = entry
|
||||
@@ -287,46 +279,23 @@ fn collect_files(
|
||||
continue;
|
||||
}
|
||||
let prefix_paths = folded_directory_names_stack.drain(..).as_slice().join("/");
|
||||
let entry_start = output.text.len();
|
||||
if prefix_paths.is_empty() {
|
||||
let label = if is_top_level_directory {
|
||||
if is_top_level_directory {
|
||||
output
|
||||
.text
|
||||
.push_str(&path_including_worktree_name.to_string_lossy());
|
||||
is_top_level_directory = false;
|
||||
path_including_worktree_name.to_string_lossy().to_string()
|
||||
} else {
|
||||
filename
|
||||
};
|
||||
events_tx.unbounded_send(Ok(SlashCommandEvent::StartSection {
|
||||
icon: IconName::Folder,
|
||||
label: label.clone().into(),
|
||||
metadata: None,
|
||||
}))?;
|
||||
events_tx.unbounded_send(Ok(SlashCommandEvent::Content(
|
||||
SlashCommandContent::Text {
|
||||
text: label,
|
||||
run_commands_in_text: false,
|
||||
},
|
||||
)))?;
|
||||
directory_stack.push(entry.path.clone());
|
||||
output.text.push_str(&filename);
|
||||
}
|
||||
directory_stack.push((entry.path.clone(), filename, entry_start));
|
||||
} else {
|
||||
let entry_name = format!("{}/{}", prefix_paths, &filename);
|
||||
events_tx.unbounded_send(Ok(SlashCommandEvent::StartSection {
|
||||
icon: IconName::Folder,
|
||||
label: entry_name.clone().into(),
|
||||
metadata: None,
|
||||
}))?;
|
||||
events_tx.unbounded_send(Ok(SlashCommandEvent::Content(
|
||||
SlashCommandContent::Text {
|
||||
text: entry_name,
|
||||
run_commands_in_text: false,
|
||||
},
|
||||
)))?;
|
||||
directory_stack.push(entry.path.clone());
|
||||
output.text.push_str(&entry_name);
|
||||
directory_stack.push((entry.path.clone(), entry_name, entry_start));
|
||||
}
|
||||
events_tx.unbounded_send(Ok(SlashCommandEvent::Content(
|
||||
SlashCommandContent::Text {
|
||||
text: "\n".into(),
|
||||
run_commands_in_text: false,
|
||||
},
|
||||
)))?;
|
||||
output.text.push('\n');
|
||||
} else if entry.is_file() {
|
||||
let Some(open_buffer_task) = project_handle
|
||||
.update(&mut cx, |project, cx| {
|
||||
@@ -337,7 +306,6 @@ fn collect_files(
|
||||
continue;
|
||||
};
|
||||
if let Some(buffer) = open_buffer_task.await.log_err() {
|
||||
let mut output = SlashCommandOutput::default();
|
||||
let snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot())?;
|
||||
append_buffer_to_output(
|
||||
&snapshot,
|
||||
@@ -345,19 +313,32 @@ fn collect_files(
|
||||
&mut output,
|
||||
)
|
||||
.log_err();
|
||||
let mut buffer_events = output.to_event_stream();
|
||||
while let Some(event) = buffer_events.next().await {
|
||||
events_tx.unbounded_send(event)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(_) = directory_stack.pop() {
|
||||
events_tx.unbounded_send(Ok(SlashCommandEvent::EndSection { metadata: None }))?;
|
||||
while let Some((dir, entry, start)) = directory_stack.pop() {
|
||||
if directory_stack.is_empty() {
|
||||
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,
|
||||
));
|
||||
} else {
|
||||
output.sections.push(build_entry_output_section(
|
||||
start..output.text.len(),
|
||||
Some(&PathBuf::from(entry.as_str())),
|
||||
true,
|
||||
None,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(events_rx.boxed())
|
||||
Ok(output)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -543,10 +524,8 @@ pub fn append_buffer_to_output(
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use assistant_slash_command::SlashCommandOutput;
|
||||
use fs::FakeFs;
|
||||
use gpui::TestAppContext;
|
||||
use pretty_assertions::assert_eq;
|
||||
use project::Project;
|
||||
use serde_json::json;
|
||||
use settings::SettingsStore;
|
||||
@@ -594,9 +573,6 @@ mod test {
|
||||
.update(|cx| collect_files(project.clone(), &["root/dir".to_string()], cx))
|
||||
.await
|
||||
.unwrap();
|
||||
let result_1 = SlashCommandOutput::from_event_stream(result_1)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(result_1.text.starts_with("root/dir"));
|
||||
// 4 files + 2 directories
|
||||
@@ -606,9 +582,6 @@ mod test {
|
||||
.update(|cx| collect_files(project.clone(), &["root/dir/".to_string()], cx))
|
||||
.await
|
||||
.unwrap();
|
||||
let result_2 = SlashCommandOutput::from_event_stream(result_2)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(result_1, result_2);
|
||||
|
||||
@@ -616,7 +589,6 @@ mod test {
|
||||
.update(|cx| collect_files(project.clone(), &["root/dir*".to_string()], cx))
|
||||
.await
|
||||
.unwrap();
|
||||
let result = SlashCommandOutput::from_event_stream(result).await.unwrap();
|
||||
|
||||
assert!(result.text.starts_with("root/dir"));
|
||||
// 5 files + 2 directories
|
||||
@@ -663,7 +635,6 @@ mod test {
|
||||
.update(|cx| collect_files(project.clone(), &["zed/assets/themes".to_string()], cx))
|
||||
.await
|
||||
.unwrap();
|
||||
let result = SlashCommandOutput::from_event_stream(result).await.unwrap();
|
||||
|
||||
// Sanity check
|
||||
assert!(result.text.starts_with("zed/assets/themes\n"));
|
||||
@@ -725,7 +696,6 @@ mod test {
|
||||
.update(|cx| collect_files(project.clone(), &["zed/assets/themes".to_string()], cx))
|
||||
.await
|
||||
.unwrap();
|
||||
let result = SlashCommandOutput::from_event_stream(result).await.unwrap();
|
||||
|
||||
assert!(result.text.starts_with("zed/assets/themes\n"));
|
||||
assert_eq!(result.sections[0].label, "zed/assets/themes/LICENSE");
|
||||
@@ -746,8 +716,6 @@ mod test {
|
||||
assert_eq!(result.sections[6].label, "summercamp");
|
||||
assert_eq!(result.sections[7].label, "zed/assets/themes");
|
||||
|
||||
assert_eq!(result.text, "zed/assets/themes\n```zed/assets/themes/LICENSE\n1\n```\n\nsummercamp\n```zed/assets/themes/summercamp/LICENSE\n1\n```\n\nsubdir\n```zed/assets/themes/summercamp/subdir/LICENSE\n1\n```\n\nsubsubdir\n```zed/assets/themes/summercamp/subdir/subsubdir/LICENSE\n3\n```\n\n");
|
||||
|
||||
// Ensure that the project lasts until after the last await
|
||||
drop(project);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ use std::sync::Arc;
|
||||
use anyhow::Result;
|
||||
use assistant_slash_command::{
|
||||
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
|
||||
SlashCommandResult,
|
||||
};
|
||||
use chrono::Local;
|
||||
use gpui::{Task, WeakView};
|
||||
@@ -49,7 +48,7 @@ impl SlashCommand for NowSlashCommand {
|
||||
_workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
_cx: &mut WindowContext,
|
||||
) -> Task<SlashCommandResult> {
|
||||
) -> Task<Result<SlashCommandOutput>> {
|
||||
let now = Local::now();
|
||||
let text = format!("Today is {now}.", now = now.to_rfc2822());
|
||||
let range = 0..text.len();
|
||||
@@ -63,7 +62,6 @@ impl SlashCommand for NowSlashCommand {
|
||||
metadata: None,
|
||||
}],
|
||||
run_commands_in_text: false,
|
||||
}
|
||||
.to_event_stream()))
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use super::{
|
||||
};
|
||||
use crate::PromptBuilder;
|
||||
use anyhow::{anyhow, Result};
|
||||
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection, SlashCommandResult};
|
||||
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
|
||||
use feature_flags::FeatureFlag;
|
||||
use gpui::{AppContext, Task, WeakView, WindowContext};
|
||||
use language::{Anchor, CodeLabel, LspAdapterDelegate};
|
||||
@@ -76,7 +76,7 @@ impl SlashCommand for ProjectSlashCommand {
|
||||
workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Task<SlashCommandResult> {
|
||||
) -> Task<Result<SlashCommandOutput>> {
|
||||
let model_registry = LanguageModelRegistry::read_global(cx);
|
||||
let current_model = model_registry.active_model();
|
||||
let prompt_builder = self.prompt_builder.clone();
|
||||
@@ -162,8 +162,7 @@ impl SlashCommand for ProjectSlashCommand {
|
||||
text: output,
|
||||
sections,
|
||||
run_commands_in_text: true,
|
||||
}
|
||||
.to_event_stream())
|
||||
})
|
||||
})
|
||||
.await
|
||||
})
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
use super::{SlashCommand, SlashCommandOutput};
|
||||
use crate::prompt_library::PromptStore;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use assistant_slash_command::{
|
||||
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
|
||||
SlashCommandResult,
|
||||
};
|
||||
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
|
||||
use gpui::{Task, WeakView};
|
||||
use language::{BufferSnapshot, LspAdapterDelegate};
|
||||
use std::sync::{atomic::AtomicBool, Arc};
|
||||
@@ -63,7 +61,7 @@ impl SlashCommand for PromptSlashCommand {
|
||||
_workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Task<SlashCommandResult> {
|
||||
) -> Task<Result<SlashCommandOutput>> {
|
||||
let title = arguments.to_owned().join(" ");
|
||||
if title.trim().is_empty() {
|
||||
return Task::ready(Err(anyhow!("missing prompt name")));
|
||||
@@ -102,8 +100,7 @@ impl SlashCommand for PromptSlashCommand {
|
||||
metadata: None,
|
||||
}],
|
||||
run_commands_in_text: true,
|
||||
}
|
||||
.to_event_stream())
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
use anyhow::Result;
|
||||
use assistant_slash_command::{
|
||||
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
|
||||
SlashCommandResult,
|
||||
use super::{
|
||||
create_label_for_command,
|
||||
file_command::{build_entry_output_section, codeblock_fence_for_path},
|
||||
SlashCommand, SlashCommandOutput,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
|
||||
use feature_flags::FeatureFlag;
|
||||
use gpui::{AppContext, Task, WeakView};
|
||||
use language::{CodeLabel, LspAdapterDelegate};
|
||||
@@ -14,9 +16,6 @@ use std::{
|
||||
use ui::{prelude::*, IconName};
|
||||
use workspace::Workspace;
|
||||
|
||||
use crate::slash_command::create_label_for_command;
|
||||
use crate::slash_command::file_command::{build_entry_output_section, codeblock_fence_for_path};
|
||||
|
||||
pub(crate) struct SearchSlashCommandFeatureFlag;
|
||||
|
||||
impl FeatureFlag for SearchSlashCommandFeatureFlag {
|
||||
@@ -64,7 +63,7 @@ impl SlashCommand for SearchSlashCommand {
|
||||
workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Task<SlashCommandResult> {
|
||||
) -> Task<Result<SlashCommandOutput>> {
|
||||
let Some(workspace) = workspace.upgrade() else {
|
||||
return Task::ready(Err(anyhow::anyhow!("workspace was dropped")));
|
||||
};
|
||||
@@ -130,7 +129,6 @@ impl SlashCommand for SearchSlashCommand {
|
||||
sections,
|
||||
run_commands_in_text: false,
|
||||
}
|
||||
.to_event_stream()
|
||||
})
|
||||
.await;
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use super::{SlashCommand, SlashCommandOutput};
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use assistant_slash_command::{
|
||||
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
|
||||
SlashCommandResult,
|
||||
};
|
||||
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
|
||||
use editor::Editor;
|
||||
use gpui::{Task, WeakView};
|
||||
use language::{BufferSnapshot, LspAdapterDelegate};
|
||||
@@ -48,7 +46,7 @@ impl SlashCommand for OutlineSlashCommand {
|
||||
workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Task<SlashCommandResult> {
|
||||
) -> Task<Result<SlashCommandOutput>> {
|
||||
let output = workspace.update(cx, |workspace, cx| {
|
||||
let Some(active_item) = workspace.active_item(cx) else {
|
||||
return Task::ready(Err(anyhow!("no active tab")));
|
||||
@@ -85,8 +83,7 @@ impl SlashCommand for OutlineSlashCommand {
|
||||
}],
|
||||
text: outline_text,
|
||||
run_commands_in_text: false,
|
||||
}
|
||||
.to_event_stream())
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use super::{file_command::append_buffer_to_output, SlashCommand, SlashCommandOutput};
|
||||
use anyhow::{Context, Result};
|
||||
use assistant_slash_command::{
|
||||
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
|
||||
SlashCommandResult,
|
||||
};
|
||||
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
|
||||
use collections::{HashMap, HashSet};
|
||||
use editor::Editor;
|
||||
use futures::future::join_all;
|
||||
@@ -16,8 +14,6 @@ use ui::{ActiveTheme, WindowContext};
|
||||
use util::ResultExt;
|
||||
use workspace::Workspace;
|
||||
|
||||
use crate::slash_command::file_command::append_buffer_to_output;
|
||||
|
||||
pub(crate) struct TabSlashCommand;
|
||||
|
||||
const ALL_TABS_COMPLETION_ITEM: &str = "all";
|
||||
@@ -136,7 +132,7 @@ impl SlashCommand for TabSlashCommand {
|
||||
workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Task<SlashCommandResult> {
|
||||
) -> Task<Result<SlashCommandOutput>> {
|
||||
let tab_items_search = tab_items_for_queries(
|
||||
Some(workspace),
|
||||
arguments,
|
||||
@@ -150,7 +146,7 @@ impl SlashCommand for TabSlashCommand {
|
||||
for (full_path, buffer, _) in tab_items_search.await? {
|
||||
append_buffer_to_output(&buffer, full_path.as_deref(), &mut output).log_err();
|
||||
}
|
||||
Ok(output.to_event_stream())
|
||||
Ok(output)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ use std::sync::Arc;
|
||||
use anyhow::Result;
|
||||
use assistant_slash_command::{
|
||||
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
|
||||
SlashCommandResult,
|
||||
};
|
||||
use gpui::{AppContext, Task, View, WeakView};
|
||||
use language::{BufferSnapshot, CodeLabel, LspAdapterDelegate};
|
||||
@@ -63,7 +62,7 @@ impl SlashCommand for TerminalSlashCommand {
|
||||
workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Task<SlashCommandResult> {
|
||||
) -> Task<Result<SlashCommandOutput>> {
|
||||
let Some(workspace) = workspace.upgrade() else {
|
||||
return Task::ready(Err(anyhow::anyhow!("workspace was dropped")));
|
||||
};
|
||||
@@ -97,8 +96,7 @@ impl SlashCommand for TerminalSlashCommand {
|
||||
metadata: None,
|
||||
}],
|
||||
run_commands_in_text: false,
|
||||
}
|
||||
.to_event_stream()))
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use crate::prompts::PromptBuilder;
|
||||
use std::sync::Arc;
|
||||
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
||||
use anyhow::Result;
|
||||
use assistant_slash_command::{
|
||||
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
|
||||
SlashCommandResult,
|
||||
};
|
||||
use gpui::{Task, WeakView};
|
||||
use language::{BufferSnapshot, LspAdapterDelegate};
|
||||
use ui::prelude::*;
|
||||
use workspace::Workspace;
|
||||
|
||||
use crate::prompts::PromptBuilder;
|
||||
use workspace::Workspace;
|
||||
|
||||
pub(crate) struct WorkflowSlashCommand {
|
||||
prompt_builder: Arc<PromptBuilder>,
|
||||
@@ -60,7 +60,7 @@ impl SlashCommand for WorkflowSlashCommand {
|
||||
_workspace: WeakView<Workspace>,
|
||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Task<SlashCommandResult> {
|
||||
) -> Task<Result<SlashCommandOutput>> {
|
||||
let prompt_builder = self.prompt_builder.clone();
|
||||
cx.spawn(|_cx| async move {
|
||||
let text = prompt_builder.generate_workflow_prompt()?;
|
||||
@@ -75,8 +75,7 @@ impl SlashCommand for WorkflowSlashCommand {
|
||||
metadata: None,
|
||||
}],
|
||||
run_commands_in_text: false,
|
||||
}
|
||||
.to_event_stream())
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,15 +15,9 @@ path = "src/assistant_slash_command.rs"
|
||||
anyhow.workspace = true
|
||||
collections.workspace = true
|
||||
derive_more.workspace = true
|
||||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
language.workspace = true
|
||||
parking_lot.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
workspace.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
gpui = { workspace = true, features = ["test-support"] }
|
||||
pretty_assertions.workspace = true
|
||||
workspace = { workspace = true, features = ["test-support"] }
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
mod slash_command_registry;
|
||||
|
||||
use anyhow::Result;
|
||||
use futures::stream::{self, BoxStream};
|
||||
use futures::StreamExt;
|
||||
use gpui::{AnyElement, AppContext, ElementId, SharedString, Task, WeakView, WindowContext};
|
||||
use language::{BufferSnapshot, CodeLabel, LspAdapterDelegate, OffsetRangeExt};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -58,8 +56,6 @@ pub struct ArgumentCompletion {
|
||||
pub replace_previous_arguments: bool,
|
||||
}
|
||||
|
||||
pub type SlashCommandResult = Result<BoxStream<'static, Result<SlashCommandEvent>>>;
|
||||
|
||||
pub trait SlashCommand: 'static + Send + Sync {
|
||||
fn name(&self) -> String;
|
||||
fn label(&self, _cx: &AppContext) -> CodeLabel {
|
||||
@@ -91,7 +87,7 @@ pub trait SlashCommand: 'static + Send + Sync {
|
||||
// perhaps another kind of delegate is needed here.
|
||||
delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Task<SlashCommandResult>;
|
||||
) -> Task<Result<SlashCommandOutput>>;
|
||||
}
|
||||
|
||||
pub type RenderFoldPlaceholder = Arc<
|
||||
@@ -100,146 +96,13 @@ pub type RenderFoldPlaceholder = Arc<
|
||||
+ Fn(ElementId, Arc<dyn Fn(&mut WindowContext)>, &mut WindowContext) -> AnyElement,
|
||||
>;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum SlashCommandContent {
|
||||
Text {
|
||||
text: String,
|
||||
run_commands_in_text: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum SlashCommandEvent {
|
||||
StartSection {
|
||||
icon: IconName,
|
||||
label: SharedString,
|
||||
metadata: Option<serde_json::Value>,
|
||||
},
|
||||
Content(SlashCommandContent),
|
||||
EndSection {
|
||||
metadata: Option<serde_json::Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Clone)]
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub struct SlashCommandOutput {
|
||||
pub text: String,
|
||||
pub sections: Vec<SlashCommandOutputSection<usize>>,
|
||||
pub run_commands_in_text: bool,
|
||||
}
|
||||
|
||||
impl SlashCommandOutput {
|
||||
pub fn ensure_valid_section_ranges(&mut self) {
|
||||
for section in &mut self.sections {
|
||||
section.range.start = section.range.start.min(self.text.len());
|
||||
section.range.end = section.range.end.min(self.text.len());
|
||||
while !self.text.is_char_boundary(section.range.start) {
|
||||
section.range.start -= 1;
|
||||
}
|
||||
while !self.text.is_char_boundary(section.range.end) {
|
||||
section.range.end += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns this [`SlashCommandOutput`] as a stream of [`SlashCommandEvent`]s.
|
||||
pub fn to_event_stream(mut self) -> BoxStream<'static, Result<SlashCommandEvent>> {
|
||||
self.ensure_valid_section_ranges();
|
||||
|
||||
let mut events = Vec::new();
|
||||
let mut last_section_end = 0;
|
||||
|
||||
for section in self.sections {
|
||||
if last_section_end < section.range.start {
|
||||
events.push(Ok(SlashCommandEvent::Content(SlashCommandContent::Text {
|
||||
text: self
|
||||
.text
|
||||
.get(last_section_end..section.range.start)
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
run_commands_in_text: self.run_commands_in_text,
|
||||
})));
|
||||
}
|
||||
|
||||
events.push(Ok(SlashCommandEvent::StartSection {
|
||||
icon: section.icon,
|
||||
label: section.label,
|
||||
metadata: section.metadata.clone(),
|
||||
}));
|
||||
events.push(Ok(SlashCommandEvent::Content(SlashCommandContent::Text {
|
||||
text: self
|
||||
.text
|
||||
.get(section.range.start..section.range.end)
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
run_commands_in_text: self.run_commands_in_text,
|
||||
})));
|
||||
events.push(Ok(SlashCommandEvent::EndSection {
|
||||
metadata: section.metadata,
|
||||
}));
|
||||
|
||||
last_section_end = section.range.end;
|
||||
}
|
||||
|
||||
if last_section_end < self.text.len() {
|
||||
events.push(Ok(SlashCommandEvent::Content(SlashCommandContent::Text {
|
||||
text: self.text[last_section_end..].to_string(),
|
||||
run_commands_in_text: self.run_commands_in_text,
|
||||
})));
|
||||
}
|
||||
|
||||
stream::iter(events).boxed()
|
||||
}
|
||||
|
||||
pub async fn from_event_stream(
|
||||
mut events: BoxStream<'static, Result<SlashCommandEvent>>,
|
||||
) -> Result<SlashCommandOutput> {
|
||||
let mut output = SlashCommandOutput::default();
|
||||
let mut section_stack = Vec::new();
|
||||
|
||||
while let Some(event) = events.next().await {
|
||||
match event? {
|
||||
SlashCommandEvent::StartSection {
|
||||
icon,
|
||||
label,
|
||||
metadata,
|
||||
} => {
|
||||
let start = output.text.len();
|
||||
section_stack.push(SlashCommandOutputSection {
|
||||
range: start..start,
|
||||
icon,
|
||||
label,
|
||||
metadata,
|
||||
});
|
||||
}
|
||||
SlashCommandEvent::Content(SlashCommandContent::Text {
|
||||
text,
|
||||
run_commands_in_text,
|
||||
}) => {
|
||||
output.text.push_str(&text);
|
||||
output.run_commands_in_text = run_commands_in_text;
|
||||
|
||||
if let Some(section) = section_stack.last_mut() {
|
||||
section.range.end = output.text.len();
|
||||
}
|
||||
}
|
||||
SlashCommandEvent::EndSection { metadata } => {
|
||||
if let Some(mut section) = section_stack.pop() {
|
||||
section.metadata = metadata;
|
||||
output.sections.push(section);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(section) = section_stack.pop() {
|
||||
output.sections.push(section);
|
||||
}
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct SlashCommandOutputSection<T> {
|
||||
pub range: Range<T>,
|
||||
@@ -253,243 +116,3 @@ impl SlashCommandOutputSection<language::Anchor> {
|
||||
self.range.start.is_valid(buffer) && !self.range.to_offset(buffer).is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::json;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_slash_command_output_to_events_round_trip() {
|
||||
// Test basic output consisting of a single section.
|
||||
{
|
||||
let text = "Hello, world!".to_string();
|
||||
let range = 0..text.len();
|
||||
let output = SlashCommandOutput {
|
||||
text,
|
||||
sections: vec![SlashCommandOutputSection {
|
||||
range,
|
||||
icon: IconName::Code,
|
||||
label: "Section 1".into(),
|
||||
metadata: None,
|
||||
}],
|
||||
run_commands_in_text: false,
|
||||
};
|
||||
|
||||
let events = output.clone().to_event_stream().collect::<Vec<_>>().await;
|
||||
let events = events
|
||||
.into_iter()
|
||||
.filter_map(|event| event.ok())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(
|
||||
events,
|
||||
vec![
|
||||
SlashCommandEvent::StartSection {
|
||||
icon: IconName::Code,
|
||||
label: "Section 1".into(),
|
||||
metadata: None
|
||||
},
|
||||
SlashCommandEvent::Content(SlashCommandContent::Text {
|
||||
text: "Hello, world!".into(),
|
||||
run_commands_in_text: false
|
||||
}),
|
||||
SlashCommandEvent::EndSection { metadata: None }
|
||||
]
|
||||
);
|
||||
|
||||
let new_output =
|
||||
SlashCommandOutput::from_event_stream(output.clone().to_event_stream())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(new_output, output);
|
||||
}
|
||||
|
||||
// Test output where the sections do not comprise all of the text.
|
||||
{
|
||||
let text = "Apple\nCucumber\nBanana\n".to_string();
|
||||
let output = SlashCommandOutput {
|
||||
text,
|
||||
sections: vec![
|
||||
SlashCommandOutputSection {
|
||||
range: 0..6,
|
||||
icon: IconName::Check,
|
||||
label: "Fruit".into(),
|
||||
metadata: None,
|
||||
},
|
||||
SlashCommandOutputSection {
|
||||
range: 15..22,
|
||||
icon: IconName::Check,
|
||||
label: "Fruit".into(),
|
||||
metadata: None,
|
||||
},
|
||||
],
|
||||
run_commands_in_text: false,
|
||||
};
|
||||
|
||||
let events = output.clone().to_event_stream().collect::<Vec<_>>().await;
|
||||
let events = events
|
||||
.into_iter()
|
||||
.filter_map(|event| event.ok())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(
|
||||
events,
|
||||
vec![
|
||||
SlashCommandEvent::StartSection {
|
||||
icon: IconName::Check,
|
||||
label: "Fruit".into(),
|
||||
metadata: None
|
||||
},
|
||||
SlashCommandEvent::Content(SlashCommandContent::Text {
|
||||
text: "Apple\n".into(),
|
||||
run_commands_in_text: false
|
||||
}),
|
||||
SlashCommandEvent::EndSection { metadata: None },
|
||||
SlashCommandEvent::Content(SlashCommandContent::Text {
|
||||
text: "Cucumber\n".into(),
|
||||
run_commands_in_text: false
|
||||
}),
|
||||
SlashCommandEvent::StartSection {
|
||||
icon: IconName::Check,
|
||||
label: "Fruit".into(),
|
||||
metadata: None
|
||||
},
|
||||
SlashCommandEvent::Content(SlashCommandContent::Text {
|
||||
text: "Banana\n".into(),
|
||||
run_commands_in_text: false
|
||||
}),
|
||||
SlashCommandEvent::EndSection { metadata: None }
|
||||
]
|
||||
);
|
||||
|
||||
let new_output =
|
||||
SlashCommandOutput::from_event_stream(output.clone().to_event_stream())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(new_output, output);
|
||||
}
|
||||
|
||||
// Test output consisting of multiple sections.
|
||||
{
|
||||
let text = "Line 1\nLine 2\nLine 3\nLine 4\n".to_string();
|
||||
let output = SlashCommandOutput {
|
||||
text,
|
||||
sections: vec![
|
||||
SlashCommandOutputSection {
|
||||
range: 0..6,
|
||||
icon: IconName::FileCode,
|
||||
label: "Section 1".into(),
|
||||
metadata: Some(json!({ "a": true })),
|
||||
},
|
||||
SlashCommandOutputSection {
|
||||
range: 7..13,
|
||||
icon: IconName::FileDoc,
|
||||
label: "Section 2".into(),
|
||||
metadata: Some(json!({ "b": true })),
|
||||
},
|
||||
SlashCommandOutputSection {
|
||||
range: 14..20,
|
||||
icon: IconName::FileGit,
|
||||
label: "Section 3".into(),
|
||||
metadata: Some(json!({ "c": true })),
|
||||
},
|
||||
SlashCommandOutputSection {
|
||||
range: 21..27,
|
||||
icon: IconName::FileToml,
|
||||
label: "Section 4".into(),
|
||||
metadata: Some(json!({ "d": true })),
|
||||
},
|
||||
],
|
||||
run_commands_in_text: false,
|
||||
};
|
||||
|
||||
let events = output.clone().to_event_stream().collect::<Vec<_>>().await;
|
||||
let events = events
|
||||
.into_iter()
|
||||
.filter_map(|event| event.ok())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(
|
||||
events,
|
||||
vec![
|
||||
SlashCommandEvent::StartSection {
|
||||
icon: IconName::FileCode,
|
||||
label: "Section 1".into(),
|
||||
metadata: Some(json!({ "a": true }))
|
||||
},
|
||||
SlashCommandEvent::Content(SlashCommandContent::Text {
|
||||
text: "Line 1".into(),
|
||||
run_commands_in_text: false
|
||||
}),
|
||||
SlashCommandEvent::EndSection {
|
||||
metadata: Some(json!({ "a": true }))
|
||||
},
|
||||
SlashCommandEvent::Content(SlashCommandContent::Text {
|
||||
text: "\n".into(),
|
||||
run_commands_in_text: false
|
||||
}),
|
||||
SlashCommandEvent::StartSection {
|
||||
icon: IconName::FileDoc,
|
||||
label: "Section 2".into(),
|
||||
metadata: Some(json!({ "b": true }))
|
||||
},
|
||||
SlashCommandEvent::Content(SlashCommandContent::Text {
|
||||
text: "Line 2".into(),
|
||||
run_commands_in_text: false
|
||||
}),
|
||||
SlashCommandEvent::EndSection {
|
||||
metadata: Some(json!({ "b": true }))
|
||||
},
|
||||
SlashCommandEvent::Content(SlashCommandContent::Text {
|
||||
text: "\n".into(),
|
||||
run_commands_in_text: false
|
||||
}),
|
||||
SlashCommandEvent::StartSection {
|
||||
icon: IconName::FileGit,
|
||||
label: "Section 3".into(),
|
||||
metadata: Some(json!({ "c": true }))
|
||||
},
|
||||
SlashCommandEvent::Content(SlashCommandContent::Text {
|
||||
text: "Line 3".into(),
|
||||
run_commands_in_text: false
|
||||
}),
|
||||
SlashCommandEvent::EndSection {
|
||||
metadata: Some(json!({ "c": true }))
|
||||
},
|
||||
SlashCommandEvent::Content(SlashCommandContent::Text {
|
||||
text: "\n".into(),
|
||||
run_commands_in_text: false
|
||||
}),
|
||||
SlashCommandEvent::StartSection {
|
||||
icon: IconName::FileToml,
|
||||
label: "Section 4".into(),
|
||||
metadata: Some(json!({ "d": true }))
|
||||
},
|
||||
SlashCommandEvent::Content(SlashCommandContent::Text {
|
||||
text: "Line 4".into(),
|
||||
run_commands_in_text: false
|
||||
}),
|
||||
SlashCommandEvent::EndSection {
|
||||
metadata: Some(json!({ "d": true }))
|
||||
},
|
||||
SlashCommandEvent::Content(SlashCommandContent::Text {
|
||||
text: "\n".into(),
|
||||
run_commands_in_text: false
|
||||
}),
|
||||
]
|
||||
);
|
||||
|
||||
let new_output =
|
||||
SlashCommandOutput::from_event_stream(output.clone().to_event_stream())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(new_output, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -474,39 +474,6 @@ impl AutoUpdater {
|
||||
Ok(version_path)
|
||||
}
|
||||
|
||||
pub async fn get_latest_remote_server_release_url(
|
||||
os: &str,
|
||||
arch: &str,
|
||||
mut release_channel: ReleaseChannel,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Result<(String, String)> {
|
||||
let this = cx.update(|cx| {
|
||||
cx.default_global::<GlobalAutoUpdate>()
|
||||
.0
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow!("auto-update not initialized"))
|
||||
})??;
|
||||
|
||||
if release_channel == ReleaseChannel::Dev {
|
||||
release_channel = ReleaseChannel::Nightly;
|
||||
}
|
||||
|
||||
let release = Self::get_latest_release(
|
||||
&this,
|
||||
"zed-remote-server",
|
||||
os,
|
||||
arch,
|
||||
Some(release_channel),
|
||||
cx,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let update_request_body = build_remote_server_update_request_body(cx)?;
|
||||
let body = serde_json::to_string(&update_request_body)?;
|
||||
|
||||
Ok((release.url, body))
|
||||
}
|
||||
|
||||
async fn get_latest_release(
|
||||
this: &Model<Self>,
|
||||
asset: &str,
|
||||
@@ -662,15 +629,6 @@ async fn download_remote_server_binary(
|
||||
cx: &AsyncAppContext,
|
||||
) -> Result<()> {
|
||||
let mut target_file = File::create(&target_path).await?;
|
||||
let update_request_body = build_remote_server_update_request_body(cx)?;
|
||||
let request_body = AsyncBody::from(serde_json::to_string(&update_request_body)?);
|
||||
|
||||
let mut response = client.get(&release.url, request_body, true).await?;
|
||||
smol::io::copy(response.body_mut(), &mut target_file).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn build_remote_server_update_request_body(cx: &AsyncAppContext) -> Result<UpdateRequestBody> {
|
||||
let (installation_id, release_channel, telemetry_enabled, is_staff) = cx.update(|cx| {
|
||||
let telemetry = Client::global(cx).telemetry().clone();
|
||||
let is_staff = telemetry.is_staff();
|
||||
@@ -686,14 +644,17 @@ fn build_remote_server_update_request_body(cx: &AsyncAppContext) -> Result<Updat
|
||||
is_staff,
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(UpdateRequestBody {
|
||||
let request_body = AsyncBody::from(serde_json::to_string(&UpdateRequestBody {
|
||||
installation_id,
|
||||
release_channel,
|
||||
telemetry: telemetry_enabled,
|
||||
is_staff,
|
||||
destination: "remote",
|
||||
})
|
||||
})?);
|
||||
|
||||
let mut response = client.get(&release.url, request_body, true).await?;
|
||||
smol::io::copy(response.body_mut(), &mut target_file).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn download_release(
|
||||
|
||||
@@ -33,7 +33,6 @@ clock.workspace = true
|
||||
collections.workspace = true
|
||||
dashmap.workspace = true
|
||||
derive_more.workspace = true
|
||||
duckdb = { version = "1.1.1", features = ["r2d2"] }
|
||||
envy = "0.4.2"
|
||||
futures.workspace = true
|
||||
google_ai.workspace = true
|
||||
@@ -52,7 +51,6 @@ reqwest = { version = "0.11", features = ["json"] }
|
||||
reqwest_client.workspace = true
|
||||
rpc.workspace = true
|
||||
rustc-demangle.workspace = true
|
||||
r2d2 = "0.8.9"
|
||||
scrypt = "0.11"
|
||||
sea-orm = { version = "1.1.0-rc.1", features = ["sqlx-postgres", "postgres-array", "runtime-tokio-rustls", "with-uuid"] }
|
||||
semantic_version.workspace = true
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use super::ips_file::IpsFile;
|
||||
use crate::api::CloudflareIpCountryHeader;
|
||||
use crate::clickhouse;
|
||||
use crate::duckdb;
|
||||
use crate::clickhouse::write_to_table;
|
||||
use crate::{api::slack, AppState, Error, Result};
|
||||
use anyhow::{anyhow, Context};
|
||||
use aws_sdk_s3::primitives::ByteStream;
|
||||
@@ -12,7 +11,6 @@ use axum::{
|
||||
routing::post,
|
||||
Extension, Router, TypedHeader,
|
||||
};
|
||||
use duckdb::Connection as DuckDbConnection;
|
||||
use rpc::ExtensionMetadata;
|
||||
use semantic_version::SemanticVersion;
|
||||
use serde::{Serialize, Serializer};
|
||||
@@ -390,6 +388,13 @@ pub async fn post_events(
|
||||
country_code_header: Option<TypedHeader<CloudflareIpCountryHeader>>,
|
||||
body: Bytes,
|
||||
) -> Result<()> {
|
||||
let Some(clickhouse_client) = app.clickhouse_client.clone() else {
|
||||
Err(Error::http(
|
||||
StatusCode::NOT_IMPLEMENTED,
|
||||
"not supported".into(),
|
||||
))?
|
||||
};
|
||||
|
||||
let Some(expected) = calculate_json_checksum(app.clone(), &body) else {
|
||||
return Err(Error::http(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
@@ -522,26 +527,10 @@ pub async fn post_events(
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(clickhouse_client) = app.clickhouse_client.clone() {
|
||||
to_upload
|
||||
.upload_to_clickhouse(&clickhouse_client)
|
||||
.await
|
||||
.map_err(|err| Error::Internal(anyhow!(err)))?;
|
||||
}
|
||||
|
||||
if let Some(pool) = app.duckdb_pool.clone() {
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let connection = pool
|
||||
.get()
|
||||
.context("can't get duckdb connection from pool")?;
|
||||
to_upload
|
||||
.upload_to_duckdb(&connection)
|
||||
.map_err(|err| Error::Internal(anyhow!(err)))?;
|
||||
anyhow::Ok(())
|
||||
})
|
||||
to_upload
|
||||
.upload(&clickhouse_client)
|
||||
.await
|
||||
.context("error spawning duckdb write")??;
|
||||
}
|
||||
.map_err(|err| Error::Internal(anyhow!(err)))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -563,17 +552,14 @@ struct ToUpload {
|
||||
}
|
||||
|
||||
impl ToUpload {
|
||||
pub async fn upload_to_clickhouse(
|
||||
&self,
|
||||
clickhouse_client: &clickhouse::Client,
|
||||
) -> anyhow::Result<()> {
|
||||
pub async fn upload(&self, clickhouse_client: &clickhouse::Client) -> anyhow::Result<()> {
|
||||
const EDITOR_EVENTS_TABLE: &str = "editor_events";
|
||||
clickhouse::write_to_table(EDITOR_EVENTS_TABLE, &self.editor_events, clickhouse_client)
|
||||
write_to_table(EDITOR_EVENTS_TABLE, &self.editor_events, clickhouse_client)
|
||||
.await
|
||||
.with_context(|| format!("failed to upload to table '{EDITOR_EVENTS_TABLE}'"))?;
|
||||
|
||||
const INLINE_COMPLETION_EVENTS_TABLE: &str = "inline_completion_events";
|
||||
clickhouse::write_to_table(
|
||||
write_to_table(
|
||||
INLINE_COMPLETION_EVENTS_TABLE,
|
||||
&self.inline_completion_events,
|
||||
clickhouse_client,
|
||||
@@ -582,7 +568,7 @@ impl ToUpload {
|
||||
.with_context(|| format!("failed to upload to table '{INLINE_COMPLETION_EVENTS_TABLE}'"))?;
|
||||
|
||||
const ASSISTANT_EVENTS_TABLE: &str = "assistant_events";
|
||||
clickhouse::write_to_table(
|
||||
write_to_table(
|
||||
ASSISTANT_EVENTS_TABLE,
|
||||
&self.assistant_events,
|
||||
clickhouse_client,
|
||||
@@ -591,27 +577,27 @@ impl ToUpload {
|
||||
.with_context(|| format!("failed to upload to table '{ASSISTANT_EVENTS_TABLE}'"))?;
|
||||
|
||||
const CALL_EVENTS_TABLE: &str = "call_events";
|
||||
clickhouse::write_to_table(CALL_EVENTS_TABLE, &self.call_events, clickhouse_client)
|
||||
write_to_table(CALL_EVENTS_TABLE, &self.call_events, clickhouse_client)
|
||||
.await
|
||||
.with_context(|| format!("failed to upload to table '{CALL_EVENTS_TABLE}'"))?;
|
||||
|
||||
const CPU_EVENTS_TABLE: &str = "cpu_events";
|
||||
clickhouse::write_to_table(CPU_EVENTS_TABLE, &self.cpu_events, clickhouse_client)
|
||||
write_to_table(CPU_EVENTS_TABLE, &self.cpu_events, clickhouse_client)
|
||||
.await
|
||||
.with_context(|| format!("failed to upload to table '{CPU_EVENTS_TABLE}'"))?;
|
||||
|
||||
const MEMORY_EVENTS_TABLE: &str = "memory_events";
|
||||
clickhouse::write_to_table(MEMORY_EVENTS_TABLE, &self.memory_events, clickhouse_client)
|
||||
write_to_table(MEMORY_EVENTS_TABLE, &self.memory_events, clickhouse_client)
|
||||
.await
|
||||
.with_context(|| format!("failed to upload to table '{MEMORY_EVENTS_TABLE}'"))?;
|
||||
|
||||
const APP_EVENTS_TABLE: &str = "app_events";
|
||||
clickhouse::write_to_table(APP_EVENTS_TABLE, &self.app_events, clickhouse_client)
|
||||
write_to_table(APP_EVENTS_TABLE, &self.app_events, clickhouse_client)
|
||||
.await
|
||||
.with_context(|| format!("failed to upload to table '{APP_EVENTS_TABLE}'"))?;
|
||||
|
||||
const SETTING_EVENTS_TABLE: &str = "setting_events";
|
||||
clickhouse::write_to_table(
|
||||
write_to_table(
|
||||
SETTING_EVENTS_TABLE,
|
||||
&self.setting_events,
|
||||
clickhouse_client,
|
||||
@@ -620,7 +606,7 @@ impl ToUpload {
|
||||
.with_context(|| format!("failed to upload to table '{SETTING_EVENTS_TABLE}'"))?;
|
||||
|
||||
const EXTENSION_EVENTS_TABLE: &str = "extension_events";
|
||||
clickhouse::write_to_table(
|
||||
write_to_table(
|
||||
EXTENSION_EVENTS_TABLE,
|
||||
&self.extension_events,
|
||||
clickhouse_client,
|
||||
@@ -629,29 +615,22 @@ impl ToUpload {
|
||||
.with_context(|| format!("failed to upload to table '{EXTENSION_EVENTS_TABLE}'"))?;
|
||||
|
||||
const EDIT_EVENTS_TABLE: &str = "edit_events";
|
||||
clickhouse::write_to_table(EDIT_EVENTS_TABLE, &self.edit_events, clickhouse_client)
|
||||
write_to_table(EDIT_EVENTS_TABLE, &self.edit_events, clickhouse_client)
|
||||
.await
|
||||
.with_context(|| format!("failed to upload to table '{EDIT_EVENTS_TABLE}'"))?;
|
||||
|
||||
const ACTION_EVENTS_TABLE: &str = "action_events";
|
||||
clickhouse::write_to_table(ACTION_EVENTS_TABLE, &self.action_events, clickhouse_client)
|
||||
write_to_table(ACTION_EVENTS_TABLE, &self.action_events, clickhouse_client)
|
||||
.await
|
||||
.with_context(|| format!("failed to upload to table '{ACTION_EVENTS_TABLE}'"))?;
|
||||
|
||||
const REPL_EVENTS_TABLE: &str = "repl_events";
|
||||
clickhouse::write_to_table(REPL_EVENTS_TABLE, &self.repl_events, clickhouse_client)
|
||||
write_to_table(REPL_EVENTS_TABLE, &self.repl_events, clickhouse_client)
|
||||
.await
|
||||
.with_context(|| format!("failed to upload to table '{REPL_EVENTS_TABLE}'"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn upload_to_duckdb(&self, connection: &DuckDbConnection) -> anyhow::Result<()> {
|
||||
duckdb::write_to_table("edit_events", &self.edit_events, &connection)
|
||||
.with_context(|| format!("failed to upload to table 'edit_events"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn serialize_country_code<S>(country_code: &str, serializer: S) -> Result<S::Ok, S::Error>
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
pub use clickhouse::*;
|
||||
|
||||
use ::serde::Serialize;
|
||||
use serde::Serialize;
|
||||
|
||||
/// Writes the given rows to the specified Clickhouse table.
|
||||
pub async fn write_to_table<T: clickhouse::Row + Serialize + std::fmt::Debug>(
|
||||
|
||||
@@ -272,16 +272,6 @@ impl Database {
|
||||
update: &proto::UpdateWorktree,
|
||||
connection: ConnectionId,
|
||||
) -> Result<TransactionGuard<Vec<ConnectionId>>> {
|
||||
if update.removed_entries.len() > proto::MAX_WORKTREE_UPDATE_MAX_CHUNK_SIZE
|
||||
|| update.updated_entries.len() > proto::MAX_WORKTREE_UPDATE_MAX_CHUNK_SIZE
|
||||
{
|
||||
return Err(anyhow!(
|
||||
"invalid worktree update. removed entries: {}, updated entries: {}",
|
||||
update.removed_entries.len(),
|
||||
update.updated_entries.len()
|
||||
))?;
|
||||
}
|
||||
|
||||
let project_id = ProjectId::from_proto(update.project_id);
|
||||
let worktree_id = update.worktree_id as i64;
|
||||
self.project_transaction(project_id, |tx| async move {
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
pub use duckdb::*;
|
||||
|
||||
pub fn write_to_table<T>(
|
||||
table_name: &str,
|
||||
rows: &[T],
|
||||
connection: &duckdb::Connection,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
T: serde::Serialize,
|
||||
{
|
||||
let mut stmt = connection.prepare(&format!(
|
||||
"INSERT INTO {} SELECT * FROM json_each(?)",
|
||||
table_name
|
||||
))?;
|
||||
let json = serde_json::to_string(rows)?;
|
||||
stmt.execute([json])?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -3,7 +3,6 @@ pub mod auth;
|
||||
mod cents;
|
||||
pub mod clickhouse;
|
||||
pub mod db;
|
||||
mod duckdb;
|
||||
pub mod env;
|
||||
pub mod executor;
|
||||
pub mod llm;
|
||||
@@ -25,7 +24,6 @@ use axum::{
|
||||
};
|
||||
pub use cents::*;
|
||||
use db::{ChannelId, Database};
|
||||
use duckdb::DuckdbConnectionManager;
|
||||
use executor::Executor;
|
||||
use llm::db::LlmDatabase;
|
||||
pub use rate_limiter::*;
|
||||
@@ -157,7 +155,6 @@ pub struct Config {
|
||||
pub clickhouse_user: Option<String>,
|
||||
pub clickhouse_password: Option<String>,
|
||||
pub clickhouse_database: Option<String>,
|
||||
pub duckdb_path: Option<String>,
|
||||
pub invite_link_prefix: String,
|
||||
pub live_kit_server: Option<String>,
|
||||
pub live_kit_key: Option<String>,
|
||||
@@ -233,7 +230,6 @@ impl Config {
|
||||
clickhouse_user: None,
|
||||
clickhouse_password: None,
|
||||
clickhouse_database: None,
|
||||
duckdb_path: None,
|
||||
zed_client_checksum_seed: None,
|
||||
slack_panics_webhook: None,
|
||||
auto_join_channel_id: None,
|
||||
@@ -280,16 +276,9 @@ pub struct AppState {
|
||||
pub rate_limiter: Arc<RateLimiter>,
|
||||
pub executor: Executor,
|
||||
pub clickhouse_client: Option<::clickhouse::Client>,
|
||||
pub duckdb_pool: Option<r2d2::Pool<DuckdbConnectionManager>>,
|
||||
pub config: Config,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_app_state_is_send_and_sync() {
|
||||
fn assert_send_sync<T: Send + Sync>() {}
|
||||
assert_send_sync::<AppState>();
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
pub async fn new(config: Config, executor: Executor) -> Result<Arc<Self>> {
|
||||
let mut db_options = db::ConnectOptions::new(config.database_url.clone());
|
||||
@@ -328,14 +317,6 @@ impl AppState {
|
||||
|
||||
let db = Arc::new(db);
|
||||
let stripe_client = build_stripe_client(&config).map(Arc::new).log_err();
|
||||
|
||||
let duckdb_pool = config.duckdb_path.as_ref().and_then(|path| {
|
||||
r2d2::Pool::builder()
|
||||
.max_size(15)
|
||||
.build(DuckdbConnectionManager::file(path).log_err()?)
|
||||
.log_err()
|
||||
});
|
||||
|
||||
let this = Self {
|
||||
db: db.clone(),
|
||||
llm_db,
|
||||
@@ -351,7 +332,6 @@ impl AppState {
|
||||
.clickhouse_url
|
||||
.as_ref()
|
||||
.and_then(|_| build_clickhouse_client(&config).log_err()),
|
||||
duckdb_pool,
|
||||
config,
|
||||
};
|
||||
Ok(Arc::new(this))
|
||||
|
||||
@@ -84,8 +84,6 @@ async fn main() -> Result<()> {
|
||||
|
||||
let config = envy::from_env::<Config>().expect("error loading config");
|
||||
init_tracing(&config);
|
||||
init_panic_hook();
|
||||
|
||||
let mut app = Router::new()
|
||||
.route("/", get(handle_root))
|
||||
.route("/healthz", get(handle_liveness_probe))
|
||||
@@ -380,20 +378,3 @@ pub fn init_tracing(config: &Config) -> Option<()> {
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn init_panic_hook() {
|
||||
std::panic::set_hook(Box::new(move |panic_info| {
|
||||
let panic_message = match panic_info.payload().downcast_ref::<&'static str>() {
|
||||
Some(message) => *message,
|
||||
None => match panic_info.payload().downcast_ref::<String>() {
|
||||
Some(message) => message.as_str(),
|
||||
None => "Box<Any>",
|
||||
},
|
||||
};
|
||||
let backtrace = std::backtrace::Backtrace::force_capture();
|
||||
let location = panic_info
|
||||
.location()
|
||||
.map(|loc| format!("{}:{}", loc.file(), loc.line()));
|
||||
tracing::error!(panic = true, ?location, %panic_message, %backtrace, "Server Panic");
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1713,6 +1713,11 @@ fn notify_rejoined_projects(
|
||||
|
||||
for project in rejoined_projects {
|
||||
for worktree in mem::take(&mut project.worktrees) {
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
const MAX_CHUNK_SIZE: usize = 2;
|
||||
#[cfg(not(any(test, feature = "test-support")))]
|
||||
const MAX_CHUNK_SIZE: usize = 256;
|
||||
|
||||
// Stream this worktree's entries.
|
||||
let message = proto::UpdateWorktree {
|
||||
project_id: project.id.to_proto(),
|
||||
@@ -1726,7 +1731,7 @@ fn notify_rejoined_projects(
|
||||
updated_repositories: worktree.updated_repositories,
|
||||
removed_repositories: worktree.removed_repositories,
|
||||
};
|
||||
for update in proto::split_worktree_update(message) {
|
||||
for update in proto::split_worktree_update(message, MAX_CHUNK_SIZE) {
|
||||
session.peer.send(session.connection_id, update.clone())?;
|
||||
}
|
||||
|
||||
@@ -2190,6 +2195,11 @@ fn join_project_internal(
|
||||
})?;
|
||||
|
||||
for (worktree_id, worktree) in mem::take(&mut project.worktrees) {
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
const MAX_CHUNK_SIZE: usize = 2;
|
||||
#[cfg(not(any(test, feature = "test-support")))]
|
||||
const MAX_CHUNK_SIZE: usize = 256;
|
||||
|
||||
// Stream this worktree's entries.
|
||||
let message = proto::UpdateWorktree {
|
||||
project_id: project_id.to_proto(),
|
||||
@@ -2203,7 +2213,7 @@ fn join_project_internal(
|
||||
updated_repositories: worktree.repository_entries.into_values().collect(),
|
||||
removed_repositories: Default::default(),
|
||||
};
|
||||
for update in proto::split_worktree_update(message) {
|
||||
for update in proto::split_worktree_update(message, MAX_CHUNK_SIZE) {
|
||||
session.peer.send(session.connection_id, update.clone())?;
|
||||
}
|
||||
|
||||
|
||||
@@ -643,7 +643,6 @@ impl TestServer {
|
||||
rate_limiter: Arc::new(RateLimiter::new(test_db.db().clone())),
|
||||
executor,
|
||||
clickhouse_client: None,
|
||||
duckdb_pool: None,
|
||||
config: Config {
|
||||
http_port: 0,
|
||||
database_url: "".into(),
|
||||
@@ -674,7 +673,6 @@ impl TestServer {
|
||||
clickhouse_user: None,
|
||||
clickhouse_password: None,
|
||||
clickhouse_database: None,
|
||||
duckdb_path: None,
|
||||
zed_client_checksum_seed: None,
|
||||
slack_panics_webhook: None,
|
||||
auto_join_channel_id: None,
|
||||
|
||||
@@ -9,7 +9,7 @@ use anyhow::Result;
|
||||
use collections::{BTreeSet, HashSet};
|
||||
use editor::{
|
||||
diagnostic_block_renderer,
|
||||
display_map::{BlockDisposition, BlockProperties, BlockStyle, CustomBlockId, RenderBlock},
|
||||
display_map::{BlockPlacement, BlockProperties, BlockStyle, CustomBlockId, RenderBlock},
|
||||
highlight_diagnostic_message,
|
||||
scroll::Autoscroll,
|
||||
Editor, EditorEvent, ExcerptId, ExcerptRange, MultiBuffer, ToOffset,
|
||||
@@ -439,11 +439,10 @@ impl ProjectDiagnosticsEditor {
|
||||
primary.message.split('\n').next().unwrap().to_string();
|
||||
group_state.block_count += 1;
|
||||
blocks_to_add.push(BlockProperties {
|
||||
position: header_position,
|
||||
placement: BlockPlacement::Above(header_position),
|
||||
height: 2,
|
||||
style: BlockStyle::Sticky,
|
||||
render: diagnostic_header_renderer(primary),
|
||||
disposition: BlockDisposition::Above,
|
||||
priority: 0,
|
||||
});
|
||||
}
|
||||
@@ -459,13 +458,15 @@ impl ProjectDiagnosticsEditor {
|
||||
if !diagnostic.message.is_empty() {
|
||||
group_state.block_count += 1;
|
||||
blocks_to_add.push(BlockProperties {
|
||||
position: (excerpt_id, entry.range.start),
|
||||
placement: BlockPlacement::Below((
|
||||
excerpt_id,
|
||||
entry.range.start,
|
||||
)),
|
||||
height: diagnostic.message.matches('\n').count() as u32 + 1,
|
||||
style: BlockStyle::Fixed,
|
||||
render: diagnostic_block_renderer(
|
||||
diagnostic, None, true, true,
|
||||
),
|
||||
disposition: BlockDisposition::Below,
|
||||
priority: 0,
|
||||
});
|
||||
}
|
||||
@@ -498,13 +499,24 @@ impl ProjectDiagnosticsEditor {
|
||||
editor.remove_blocks(blocks_to_remove, None, cx);
|
||||
let block_ids = editor.insert_blocks(
|
||||
blocks_to_add.into_iter().flat_map(|block| {
|
||||
let (excerpt_id, text_anchor) = block.position;
|
||||
let placement = match block.placement {
|
||||
BlockPlacement::Above((excerpt_id, text_anchor)) => BlockPlacement::Above(
|
||||
excerpts_snapshot.anchor_in_excerpt(excerpt_id, text_anchor)?,
|
||||
),
|
||||
BlockPlacement::Below((excerpt_id, text_anchor)) => BlockPlacement::Below(
|
||||
excerpts_snapshot.anchor_in_excerpt(excerpt_id, text_anchor)?,
|
||||
),
|
||||
BlockPlacement::Replace(_) => {
|
||||
unreachable!(
|
||||
"no Replace block should have been pushed to blocks_to_add"
|
||||
)
|
||||
}
|
||||
};
|
||||
Some(BlockProperties {
|
||||
position: excerpts_snapshot.anchor_in_excerpt(excerpt_id, text_anchor)?,
|
||||
placement,
|
||||
height: block.height,
|
||||
style: block.style,
|
||||
render: block.render,
|
||||
disposition: block.disposition,
|
||||
priority: 0,
|
||||
})
|
||||
}),
|
||||
|
||||
@@ -18,7 +18,7 @@ pub struct SelectPrevious {
|
||||
#[derive(PartialEq, Clone, Deserialize, Default)]
|
||||
pub struct MoveToBeginningOfLine {
|
||||
#[serde(default = "default_true")]
|
||||
pub stop_at_soft_wraps: bool,
|
||||
pub(super) stop_at_soft_wraps: bool,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default)]
|
||||
|
||||
@@ -29,8 +29,8 @@ use crate::{
|
||||
hover_links::InlayHighlight, movement::TextLayoutDetails, EditorStyle, InlayId, RowExt,
|
||||
};
|
||||
pub use block_map::{
|
||||
Block, BlockBufferRows, BlockChunks as DisplayChunks, BlockContext, BlockDisposition, BlockId,
|
||||
BlockMap, BlockPoint, BlockProperties, BlockStyle, CustomBlockId, RenderBlock,
|
||||
Block, BlockBufferRows, BlockChunks as DisplayChunks, BlockContext, BlockId, BlockMap,
|
||||
BlockPlacement, BlockPoint, BlockProperties, BlockStyle, CustomBlockId, RenderBlock,
|
||||
};
|
||||
use block_map::{BlockRow, BlockSnapshot};
|
||||
use char_map::{CharMap, CharSnapshot};
|
||||
@@ -1180,6 +1180,7 @@ impl ToDisplayPoint for Anchor {
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
use crate::{movement, test::marked_display_snapshot};
|
||||
use block_map::BlockPlacement;
|
||||
use gpui::{div, font, observe, px, AppContext, BorrowAppContext, Context, Element, Hsla};
|
||||
use language::{
|
||||
language_settings::{AllLanguageSettings, AllLanguageSettingsContent},
|
||||
@@ -1293,24 +1294,22 @@ pub mod tests {
|
||||
Bias::Left,
|
||||
));
|
||||
|
||||
let disposition = if rng.gen() {
|
||||
BlockDisposition::Above
|
||||
let placement = if rng.gen() {
|
||||
BlockPlacement::Above(position)
|
||||
} else {
|
||||
BlockDisposition::Below
|
||||
BlockPlacement::Below(position)
|
||||
};
|
||||
let height = rng.gen_range(1..5);
|
||||
log::info!(
|
||||
"inserting block {:?} {:?} with height {}",
|
||||
disposition,
|
||||
position.to_point(&buffer),
|
||||
"inserting block {:?} with height {}",
|
||||
placement.as_ref().map(|p| p.to_point(&buffer)),
|
||||
height
|
||||
);
|
||||
let priority = rng.gen_range(1..100);
|
||||
BlockProperties {
|
||||
placement,
|
||||
style: BlockStyle::Fixed,
|
||||
position,
|
||||
height,
|
||||
disposition,
|
||||
render: Box::new(|_| div().into_any()),
|
||||
priority,
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -252,6 +252,7 @@ impl CharSnapshot {
|
||||
};
|
||||
|
||||
TabChunks {
|
||||
snapshot: self,
|
||||
fold_chunks: self.fold_snapshot.chunks(
|
||||
input_start..input_end,
|
||||
language_aware,
|
||||
@@ -492,6 +493,7 @@ impl<'a> std::ops::AddAssign<&'a Self> for TextSummary {
|
||||
const SPACES: &str = " ";
|
||||
|
||||
pub struct TabChunks<'a> {
|
||||
snapshot: &'a CharSnapshot,
|
||||
fold_chunks: FoldChunks<'a>,
|
||||
chunk: Chunk<'a>,
|
||||
column: u32,
|
||||
@@ -503,6 +505,37 @@ pub struct TabChunks<'a> {
|
||||
inside_leading_tab: bool,
|
||||
}
|
||||
|
||||
impl<'a> TabChunks<'a> {
|
||||
pub(crate) fn seek(&mut self, range: Range<CharPoint>) {
|
||||
let (input_start, expanded_char_column, to_next_stop) =
|
||||
self.snapshot.to_fold_point(range.start, Bias::Left);
|
||||
let input_column = input_start.column();
|
||||
let input_start = input_start.to_offset(&self.snapshot.fold_snapshot);
|
||||
let input_end = self
|
||||
.snapshot
|
||||
.to_fold_point(range.end, Bias::Right)
|
||||
.0
|
||||
.to_offset(&self.snapshot.fold_snapshot);
|
||||
let to_next_stop = if range.start.0 + Point::new(0, to_next_stop) > range.end.0 {
|
||||
range.end.column() - range.start.column()
|
||||
} else {
|
||||
to_next_stop
|
||||
};
|
||||
|
||||
self.fold_chunks.seek(input_start..input_end);
|
||||
self.input_column = input_column;
|
||||
self.column = expanded_char_column;
|
||||
self.output_position = range.start.0;
|
||||
self.max_output_position = range.end.0;
|
||||
self.chunk = Chunk {
|
||||
text: &SPACES[0..(to_next_stop as usize)],
|
||||
is_tab: true,
|
||||
..Default::default()
|
||||
};
|
||||
self.inside_leading_tab = to_next_stop > 0;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for TabChunks<'a> {
|
||||
type Item = Chunk<'a>;
|
||||
|
||||
|
||||
@@ -1100,6 +1100,17 @@ pub struct FoldBufferRows<'a> {
|
||||
fold_point: FoldPoint,
|
||||
}
|
||||
|
||||
impl<'a> FoldBufferRows<'a> {
|
||||
pub(crate) fn seek(&mut self, row: u32) {
|
||||
let fold_point = FoldPoint::new(row, 0);
|
||||
self.cursor.seek(&fold_point, Bias::Left, &());
|
||||
let overshoot = fold_point.0 - self.cursor.start().0 .0;
|
||||
let inlay_point = InlayPoint(self.cursor.start().1 .0 + overshoot);
|
||||
self.input_buffer_rows.seek(inlay_point.row());
|
||||
self.fold_point = fold_point;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for FoldBufferRows<'a> {
|
||||
type Item = Option<u32>;
|
||||
|
||||
@@ -1135,6 +1146,38 @@ pub struct FoldChunks<'a> {
|
||||
max_output_offset: FoldOffset,
|
||||
}
|
||||
|
||||
impl<'a> FoldChunks<'a> {
|
||||
pub(crate) fn seek(&mut self, range: Range<FoldOffset>) {
|
||||
self.transform_cursor.seek(&range.start, Bias::Right, &());
|
||||
|
||||
let inlay_start = {
|
||||
let overshoot = range.start.0 - self.transform_cursor.start().0 .0;
|
||||
self.transform_cursor.start().1 + InlayOffset(overshoot)
|
||||
};
|
||||
|
||||
let transform_end = self.transform_cursor.end(&());
|
||||
|
||||
let inlay_end = if self
|
||||
.transform_cursor
|
||||
.item()
|
||||
.map_or(true, |transform| transform.is_fold())
|
||||
{
|
||||
inlay_start
|
||||
} else if range.end < transform_end.0 {
|
||||
let overshoot = range.end.0 - self.transform_cursor.start().0 .0;
|
||||
self.transform_cursor.start().1 + InlayOffset(overshoot)
|
||||
} else {
|
||||
transform_end.1
|
||||
};
|
||||
|
||||
self.inlay_chunks.seek(inlay_start..inlay_end);
|
||||
self.inlay_chunk = None;
|
||||
self.inlay_offset = inlay_start;
|
||||
self.output_offset = range.start;
|
||||
self.max_output_offset = range.end;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for FoldChunks<'a> {
|
||||
type Item = Chunk<'a>;
|
||||
|
||||
|
||||
@@ -56,6 +56,7 @@ pub struct WrapChunks<'a> {
|
||||
output_position: WrapPoint,
|
||||
max_output_row: u32,
|
||||
transforms: Cursor<'a, Transform, (WrapPoint, CharPoint)>,
|
||||
snapshot: &'a WrapSnapshot,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -68,6 +69,21 @@ pub struct WrapBufferRows<'a> {
|
||||
transforms: Cursor<'a, Transform, (WrapPoint, CharPoint)>,
|
||||
}
|
||||
|
||||
impl<'a> WrapBufferRows<'a> {
|
||||
pub(crate) fn seek(&mut self, start_row: u32) {
|
||||
self.transforms
|
||||
.seek(&WrapPoint::new(start_row, 0), Bias::Left, &());
|
||||
let mut input_row = self.transforms.start().1.row();
|
||||
if self.transforms.item().map_or(false, |t| t.is_isomorphic()) {
|
||||
input_row += start_row - self.transforms.start().0.row();
|
||||
}
|
||||
self.soft_wrapped = self.transforms.item().map_or(false, |t| !t.is_isomorphic());
|
||||
self.input_buffer_rows.seek(input_row);
|
||||
self.input_buffer_row = self.input_buffer_rows.next().unwrap();
|
||||
self.output_row = start_row;
|
||||
}
|
||||
}
|
||||
|
||||
impl WrapMap {
|
||||
pub fn new(
|
||||
char_snapshot: CharSnapshot,
|
||||
@@ -602,6 +618,7 @@ impl WrapSnapshot {
|
||||
output_position: output_start,
|
||||
max_output_row: rows.end,
|
||||
transforms,
|
||||
snapshot: self,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -629,6 +646,67 @@ impl WrapSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn text_summary_for_range(&self, rows: Range<u32>) -> TextSummary {
|
||||
let mut summary = TextSummary::default();
|
||||
|
||||
let start = WrapPoint::new(rows.start, 0);
|
||||
let end = WrapPoint::new(rows.end, 0);
|
||||
|
||||
let mut cursor = self.transforms.cursor::<(WrapPoint, CharPoint)>(&());
|
||||
cursor.seek(&start, Bias::Right, &());
|
||||
if let Some(transform) = cursor.item() {
|
||||
let start_in_transform = start.0 - cursor.start().0 .0;
|
||||
let end_in_transform = cmp::min(end, cursor.end(&()).0).0 - cursor.start().0 .0;
|
||||
if transform.is_isomorphic() {
|
||||
let char_start = CharPoint(cursor.start().1 .0 + start_in_transform);
|
||||
let char_end = CharPoint(cursor.start().1 .0 + end_in_transform);
|
||||
summary += &self
|
||||
.char_snapshot
|
||||
.text_summary_for_range(char_start..char_end);
|
||||
} else {
|
||||
debug_assert_eq!(start_in_transform.row, end_in_transform.row);
|
||||
let indent_len = end_in_transform.column - start_in_transform.column;
|
||||
summary += &TextSummary {
|
||||
lines: Point::new(0, indent_len),
|
||||
first_line_chars: indent_len,
|
||||
last_line_chars: indent_len,
|
||||
longest_row: 0,
|
||||
longest_row_chars: indent_len,
|
||||
};
|
||||
}
|
||||
|
||||
cursor.next(&());
|
||||
}
|
||||
|
||||
if rows.end > cursor.start().0.row() {
|
||||
summary += &cursor
|
||||
.summary::<_, TransformSummary>(&WrapPoint::new(rows.end, 0), Bias::Right, &())
|
||||
.output;
|
||||
|
||||
if let Some(transform) = cursor.item() {
|
||||
let end_in_transform = end.0 - cursor.start().0 .0;
|
||||
if transform.is_isomorphic() {
|
||||
let char_start = cursor.start().1;
|
||||
let char_end = CharPoint(char_start.0 + end_in_transform);
|
||||
summary += &self
|
||||
.char_snapshot
|
||||
.text_summary_for_range(char_start..char_end);
|
||||
} else {
|
||||
debug_assert_eq!(end_in_transform, Point::new(1, 0));
|
||||
summary += &TextSummary {
|
||||
lines: Point::new(1, 0),
|
||||
first_line_chars: 0,
|
||||
last_line_chars: 0,
|
||||
longest_row: 0,
|
||||
longest_row_chars: 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
summary
|
||||
}
|
||||
|
||||
pub fn soft_wrap_indent(&self, row: u32) -> Option<u32> {
|
||||
let mut cursor = self.transforms.cursor::<WrapPoint>(&());
|
||||
cursor.seek(&WrapPoint::new(row + 1, 0), Bias::Right, &());
|
||||
@@ -745,6 +823,21 @@ impl WrapSnapshot {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn text(&self) -> String {
|
||||
self.text_chunks(0).collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn text_chunks(&self, wrap_row: u32) -> impl Iterator<Item = &str> {
|
||||
self.chunks(
|
||||
wrap_row..self.max_point().row() + 1,
|
||||
false,
|
||||
Highlights::default(),
|
||||
)
|
||||
.map(|h| h.text)
|
||||
}
|
||||
|
||||
fn check_invariants(&self) {
|
||||
#[cfg(test)]
|
||||
{
|
||||
@@ -791,6 +884,26 @@ impl WrapSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> WrapChunks<'a> {
|
||||
pub(crate) fn seek(&mut self, rows: Range<u32>) {
|
||||
let output_start = WrapPoint::new(rows.start, 0);
|
||||
let output_end = WrapPoint::new(rows.end, 0);
|
||||
self.transforms.seek(&output_start, Bias::Right, &());
|
||||
let mut input_start = CharPoint(self.transforms.start().1 .0);
|
||||
if self.transforms.item().map_or(false, |t| t.is_isomorphic()) {
|
||||
input_start.0 += output_start.0 - self.transforms.start().0 .0;
|
||||
}
|
||||
let input_end = self
|
||||
.snapshot
|
||||
.to_char_point(output_end)
|
||||
.min(self.snapshot.char_snapshot.max_point());
|
||||
self.input_chunks.seek(input_start..input_end);
|
||||
self.input_chunk = Chunk::default();
|
||||
self.output_position = output_start;
|
||||
self.max_output_row = rows.end;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for WrapChunks<'a> {
|
||||
type Item = Chunk<'a>;
|
||||
|
||||
@@ -1336,19 +1449,6 @@ mod tests {
|
||||
}
|
||||
|
||||
impl WrapSnapshot {
|
||||
pub fn text(&self) -> String {
|
||||
self.text_chunks(0).collect()
|
||||
}
|
||||
|
||||
pub fn text_chunks(&self, wrap_row: u32) -> impl Iterator<Item = &str> {
|
||||
self.chunks(
|
||||
wrap_row..self.max_point().row() + 1,
|
||||
false,
|
||||
Highlights::default(),
|
||||
)
|
||||
.map(|h| h.text)
|
||||
}
|
||||
|
||||
fn verify_chunks(&mut self, rng: &mut impl Rng) {
|
||||
for _ in 0..5 {
|
||||
let mut end_row = rng.gen_range(0..=self.max_point().row());
|
||||
|
||||
@@ -10210,7 +10210,7 @@ impl Editor {
|
||||
let block_id = this.insert_blocks(
|
||||
[BlockProperties {
|
||||
style: BlockStyle::Flex,
|
||||
position: range.start,
|
||||
placement: BlockPlacement::Below(range.start),
|
||||
height: 1,
|
||||
render: Box::new({
|
||||
let rename_editor = rename_editor.clone();
|
||||
@@ -10246,7 +10246,6 @@ impl Editor {
|
||||
.into_any_element()
|
||||
}
|
||||
}),
|
||||
disposition: BlockDisposition::Below,
|
||||
priority: 0,
|
||||
}],
|
||||
Some(Autoscroll::fit()),
|
||||
@@ -10531,10 +10530,11 @@ impl Editor {
|
||||
let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
|
||||
BlockProperties {
|
||||
style: BlockStyle::Fixed,
|
||||
position: buffer.anchor_after(entry.range.start),
|
||||
placement: BlockPlacement::Below(
|
||||
buffer.anchor_after(entry.range.start),
|
||||
),
|
||||
height: message_height,
|
||||
render: diagnostic_block_renderer(diagnostic, None, true, true),
|
||||
disposition: BlockDisposition::Below,
|
||||
priority: 0,
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -3868,8 +3868,7 @@ fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
|
||||
editor.insert_blocks(
|
||||
[BlockProperties {
|
||||
style: BlockStyle::Fixed,
|
||||
position: snapshot.anchor_after(Point::new(2, 0)),
|
||||
disposition: BlockDisposition::Below,
|
||||
placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
|
||||
height: 1,
|
||||
render: Box::new(|_| div().into_any()),
|
||||
priority: 0,
|
||||
|
||||
@@ -2071,7 +2071,7 @@ impl EditorElement {
|
||||
let mut element = match block {
|
||||
Block::Custom(block) => {
|
||||
let align_to = block
|
||||
.position()
|
||||
.start()
|
||||
.to_point(&snapshot.buffer_snapshot)
|
||||
.to_display_point(snapshot);
|
||||
let anchor_x = text_x
|
||||
@@ -6294,7 +6294,7 @@ fn compute_auto_height_layout(
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
display_map::{BlockDisposition, BlockProperties},
|
||||
display_map::{BlockPlacement, BlockProperties},
|
||||
editor_tests::{init_test, update_test_language_settings},
|
||||
Editor, MultiBuffer,
|
||||
};
|
||||
@@ -6550,9 +6550,8 @@ mod tests {
|
||||
editor.insert_blocks(
|
||||
[BlockProperties {
|
||||
style: BlockStyle::Fixed,
|
||||
disposition: BlockDisposition::Above,
|
||||
placement: BlockPlacement::Above(Anchor::min()),
|
||||
height: 3,
|
||||
position: Anchor::min(),
|
||||
render: Box::new(|cx| div().h(3. * cx.line_height()).into_any()),
|
||||
priority: 0,
|
||||
}],
|
||||
|
||||
@@ -17,7 +17,7 @@ use workspace::Item;
|
||||
|
||||
use crate::{
|
||||
editor_settings::CurrentLineHighlight, hunk_status, hunks_for_selections, ApplyDiffHunk,
|
||||
BlockDisposition, BlockProperties, BlockStyle, CustomBlockId, DiffRowHighlight, DisplayRow,
|
||||
BlockPlacement, BlockProperties, BlockStyle, CustomBlockId, DiffRowHighlight, DisplayRow,
|
||||
DisplaySnapshot, Editor, EditorElement, ExpandAllHunkDiffs, GoToHunk, GoToPrevHunk, RevertFile,
|
||||
RevertSelectedHunks, ToDisplayPoint, ToggleHunkDiff,
|
||||
};
|
||||
@@ -417,10 +417,9 @@ impl Editor {
|
||||
};
|
||||
|
||||
BlockProperties {
|
||||
position: hunk.multi_buffer_range.start,
|
||||
placement: BlockPlacement::Above(hunk.multi_buffer_range.start),
|
||||
height: 1,
|
||||
style: BlockStyle::Sticky,
|
||||
disposition: BlockDisposition::Above,
|
||||
priority: 0,
|
||||
render: Box::new({
|
||||
let editor = cx.view().clone();
|
||||
@@ -700,10 +699,9 @@ impl Editor {
|
||||
let hunk = hunk.clone();
|
||||
let height = editor_height.max(deleted_text_height);
|
||||
BlockProperties {
|
||||
position: hunk.multi_buffer_range.start,
|
||||
placement: BlockPlacement::Above(hunk.multi_buffer_range.start),
|
||||
height,
|
||||
style: BlockStyle::Flex,
|
||||
disposition: BlockDisposition::Above,
|
||||
priority: 0,
|
||||
render: Box::new(move |cx| {
|
||||
let width = EditorElement::diff_hunk_strip_width(cx.line_height());
|
||||
|
||||
@@ -936,7 +936,7 @@ impl SerializableItem for Editor {
|
||||
|
||||
fn deserialize(
|
||||
project: Model<Project>,
|
||||
workspace: WeakView<Workspace>,
|
||||
_workspace: WeakView<Workspace>,
|
||||
workspace_id: workspace::WorkspaceId,
|
||||
item_id: ItemId,
|
||||
cx: &mut ViewContext<Pane>,
|
||||
@@ -953,7 +953,7 @@ impl SerializableItem for Editor {
|
||||
serialized_editor
|
||||
} else {
|
||||
SerializedEditor {
|
||||
abs_path: serialized_editor.abs_path,
|
||||
path: serialized_editor.path,
|
||||
contents: None,
|
||||
language: None,
|
||||
mtime: None,
|
||||
@@ -968,13 +968,13 @@ impl SerializableItem for Editor {
|
||||
}
|
||||
};
|
||||
|
||||
match serialized_editor {
|
||||
let buffer_task = match serialized_editor {
|
||||
SerializedEditor {
|
||||
abs_path: None,
|
||||
path: None,
|
||||
contents: Some(contents),
|
||||
language,
|
||||
..
|
||||
} => cx.spawn(|pane, mut cx| {
|
||||
} => cx.spawn(|_, mut cx| {
|
||||
let project = project.clone();
|
||||
async move {
|
||||
let language = if let Some(language_name) = language {
|
||||
@@ -1001,34 +1001,30 @@ impl SerializableItem for Editor {
|
||||
buffer.set_text(contents, cx);
|
||||
})?;
|
||||
|
||||
pane.update(&mut cx, |_, cx| {
|
||||
cx.new_view(|cx| {
|
||||
let mut editor = Editor::for_buffer(buffer, Some(project), cx);
|
||||
|
||||
editor.read_scroll_position_from_db(item_id, workspace_id, cx);
|
||||
editor
|
||||
})
|
||||
})
|
||||
anyhow::Ok(buffer)
|
||||
}
|
||||
}),
|
||||
SerializedEditor {
|
||||
abs_path: Some(abs_path),
|
||||
path: Some(path),
|
||||
contents,
|
||||
mtime,
|
||||
..
|
||||
} => {
|
||||
let project_item = project.update(cx, |project, cx| {
|
||||
let (worktree, path) = project.find_worktree(&abs_path, cx)?;
|
||||
let (worktree, path) = project
|
||||
.find_worktree(&path, cx)
|
||||
.with_context(|| format!("No worktree for path: {path:?}"))?;
|
||||
let project_path = ProjectPath {
|
||||
worktree_id: worktree.read(cx).id(),
|
||||
path: path.into(),
|
||||
};
|
||||
Some(project.open_path(project_path, cx))
|
||||
|
||||
Ok(project.open_path(project_path, cx))
|
||||
});
|
||||
|
||||
match project_item {
|
||||
Some(project_item) => {
|
||||
cx.spawn(|pane, mut cx| async move {
|
||||
project_item
|
||||
.map(|project_item| {
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
let (_, project_item) = project_item.await?;
|
||||
let buffer = project_item.downcast::<Buffer>().map_err(|_| {
|
||||
anyhow!("Project item at stored path was not a buffer")
|
||||
@@ -1055,36 +1051,26 @@ impl SerializableItem for Editor {
|
||||
})?;
|
||||
}
|
||||
|
||||
pane.update(&mut cx, |_, cx| {
|
||||
cx.new_view(|cx| {
|
||||
let mut editor = Editor::for_buffer(buffer, Some(project), cx);
|
||||
|
||||
editor.read_scroll_position_from_db(item_id, workspace_id, cx);
|
||||
editor
|
||||
})
|
||||
})
|
||||
Ok(buffer)
|
||||
})
|
||||
}
|
||||
None => {
|
||||
let open_by_abs_path = workspace.update(cx, |workspace, cx| {
|
||||
workspace.open_abs_path(abs_path.clone(), false, cx)
|
||||
});
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
let editor = open_by_abs_path?.await?.downcast::<Editor>().with_context(|| format!("Failed to downcast to Editor after opening abs path {abs_path:?}"))?;
|
||||
editor.update(&mut cx, |editor, cx| {
|
||||
editor.read_scroll_position_from_db(item_id, workspace_id, cx);
|
||||
})?;
|
||||
Ok(editor)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|error| Task::ready(Err(error)))
|
||||
}
|
||||
SerializedEditor {
|
||||
abs_path: None,
|
||||
contents: None,
|
||||
..
|
||||
} => Task::ready(Err(anyhow!("No path or contents found for buffer"))),
|
||||
}
|
||||
_ => return Task::ready(Err(anyhow!("No path or contents found for buffer"))),
|
||||
};
|
||||
|
||||
cx.spawn(|pane, mut cx| async move {
|
||||
let buffer = buffer_task.await?;
|
||||
|
||||
pane.update(&mut cx, |_, cx| {
|
||||
cx.new_view(|cx| {
|
||||
let mut editor = Editor::for_buffer(buffer, Some(project), cx);
|
||||
|
||||
editor.read_scroll_position_from_db(item_id, workspace_id, cx);
|
||||
editor
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn serialize(
|
||||
@@ -1110,19 +1096,12 @@ impl SerializableItem for Editor {
|
||||
let workspace_id = workspace.database_id()?;
|
||||
|
||||
let buffer = self.buffer().read(cx).as_singleton()?;
|
||||
|
||||
let abs_path = buffer.read(cx).file().and_then(|file| {
|
||||
let worktree_id = file.worktree_id(cx);
|
||||
project
|
||||
.read(cx)
|
||||
.worktree_for_id(worktree_id, cx)
|
||||
.and_then(|worktree| worktree.read(cx).absolutize(&file.path()).ok())
|
||||
.or_else(|| {
|
||||
let full_path = file.full_path(cx);
|
||||
let project_path = project.read(cx).find_project_path(&full_path, cx)?;
|
||||
project.read(cx).absolute_path(&project_path, cx)
|
||||
})
|
||||
});
|
||||
let path = buffer
|
||||
.read(cx)
|
||||
.file()
|
||||
.map(|file| file.full_path(cx))
|
||||
.and_then(|full_path| project.read(cx).find_project_path(&full_path, cx))
|
||||
.and_then(|project_path| project.read(cx).absolute_path(&project_path, cx));
|
||||
|
||||
let is_dirty = buffer.read(cx).is_dirty();
|
||||
let mtime = buffer.read(cx).saved_mtime();
|
||||
@@ -1141,7 +1120,7 @@ impl SerializableItem for Editor {
|
||||
};
|
||||
|
||||
let editor = SerializedEditor {
|
||||
abs_path,
|
||||
path,
|
||||
contents,
|
||||
language,
|
||||
mtime,
|
||||
@@ -1654,7 +1633,7 @@ mod tests {
|
||||
let item_id = 1234 as ItemId;
|
||||
|
||||
let serialized_editor = SerializedEditor {
|
||||
abs_path: Some(PathBuf::from("/file.rs")),
|
||||
path: Some(PathBuf::from("/file.rs")),
|
||||
contents: Some("fn main() {}".to_string()),
|
||||
language: Some("Rust".to_string()),
|
||||
mtime: Some(now),
|
||||
@@ -1685,7 +1664,7 @@ mod tests {
|
||||
|
||||
let item_id = 5678 as ItemId;
|
||||
let serialized_editor = SerializedEditor {
|
||||
abs_path: Some(PathBuf::from("/file.rs")),
|
||||
path: Some(PathBuf::from("/file.rs")),
|
||||
contents: None,
|
||||
language: None,
|
||||
mtime: None,
|
||||
@@ -1720,7 +1699,7 @@ mod tests {
|
||||
|
||||
let item_id = 9012 as ItemId;
|
||||
let serialized_editor = SerializedEditor {
|
||||
abs_path: None,
|
||||
path: None,
|
||||
contents: Some("hello".to_string()),
|
||||
language: Some("Rust".to_string()),
|
||||
mtime: None,
|
||||
@@ -1758,7 +1737,7 @@ mod tests {
|
||||
.checked_sub(std::time::Duration::from_secs(60 * 60 * 24))
|
||||
.unwrap();
|
||||
let serialized_editor = SerializedEditor {
|
||||
abs_path: Some(PathBuf::from("/file.rs")),
|
||||
path: Some(PathBuf::from("/file.rs")),
|
||||
contents: Some("fn main() {}".to_string()),
|
||||
language: Some("Rust".to_string()),
|
||||
mtime: Some(old_mtime),
|
||||
|
||||
@@ -11,7 +11,7 @@ use workspace::{ItemId, WorkspaceDb, WorkspaceId};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Default)]
|
||||
pub(crate) struct SerializedEditor {
|
||||
pub(crate) abs_path: Option<PathBuf>,
|
||||
pub(crate) path: Option<PathBuf>,
|
||||
pub(crate) contents: Option<String>,
|
||||
pub(crate) language: Option<String>,
|
||||
pub(crate) mtime: Option<SystemTime>,
|
||||
@@ -25,7 +25,7 @@ impl StaticColumnCount for SerializedEditor {
|
||||
|
||||
impl Bind for SerializedEditor {
|
||||
fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
|
||||
let start_index = statement.bind(&self.abs_path, start_index)?;
|
||||
let start_index = statement.bind(&self.path, start_index)?;
|
||||
let start_index = statement.bind(&self.contents, start_index)?;
|
||||
let start_index = statement.bind(&self.language, start_index)?;
|
||||
|
||||
@@ -51,8 +51,7 @@ impl Bind for SerializedEditor {
|
||||
|
||||
impl Column for SerializedEditor {
|
||||
fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
|
||||
let (abs_path, start_index): (Option<PathBuf>, i32) =
|
||||
Column::column(statement, start_index)?;
|
||||
let (path, start_index): (Option<PathBuf>, i32) = Column::column(statement, start_index)?;
|
||||
let (contents, start_index): (Option<String>, i32) =
|
||||
Column::column(statement, start_index)?;
|
||||
let (language, start_index): (Option<String>, i32) =
|
||||
@@ -67,7 +66,7 @@ impl Column for SerializedEditor {
|
||||
.map(|(seconds, nanos)| UNIX_EPOCH + Duration::new(seconds as u64, nanos as u32));
|
||||
|
||||
let editor = Self {
|
||||
abs_path,
|
||||
path,
|
||||
contents,
|
||||
language,
|
||||
mtime,
|
||||
@@ -227,7 +226,7 @@ mod tests {
|
||||
let workspace_id = workspace::WORKSPACE_DB.next_id().await.unwrap();
|
||||
|
||||
let serialized_editor = SerializedEditor {
|
||||
abs_path: Some(PathBuf::from("testing.txt")),
|
||||
path: Some(PathBuf::from("testing.txt")),
|
||||
contents: None,
|
||||
language: None,
|
||||
mtime: None,
|
||||
@@ -245,7 +244,7 @@ mod tests {
|
||||
|
||||
// Now update contents and language
|
||||
let serialized_editor = SerializedEditor {
|
||||
abs_path: Some(PathBuf::from("testing.txt")),
|
||||
path: Some(PathBuf::from("testing.txt")),
|
||||
contents: Some("Test".to_owned()),
|
||||
language: Some("Go".to_owned()),
|
||||
mtime: None,
|
||||
@@ -263,7 +262,7 @@ mod tests {
|
||||
|
||||
// Now set all the fields to NULL
|
||||
let serialized_editor = SerializedEditor {
|
||||
abs_path: None,
|
||||
path: None,
|
||||
contents: None,
|
||||
language: None,
|
||||
mtime: None,
|
||||
@@ -282,7 +281,7 @@ mod tests {
|
||||
// Storing and retrieving mtime
|
||||
let now = SystemTime::now();
|
||||
let serialized_editor = SerializedEditor {
|
||||
abs_path: None,
|
||||
path: None,
|
||||
contents: None,
|
||||
language: None,
|
||||
mtime: Some(now),
|
||||
|
||||
@@ -3,7 +3,6 @@ use std::sync::{atomic::AtomicBool, Arc};
|
||||
use anyhow::{anyhow, Result};
|
||||
use assistant_slash_command::{
|
||||
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
|
||||
SlashCommandResult,
|
||||
};
|
||||
use futures::FutureExt;
|
||||
use gpui::{Task, WeakView, WindowContext};
|
||||
@@ -88,7 +87,7 @@ impl SlashCommand for ExtensionSlashCommand {
|
||||
_workspace: WeakView<Workspace>,
|
||||
delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Task<SlashCommandResult> {
|
||||
) -> Task<Result<SlashCommandOutput>> {
|
||||
let arguments = arguments.to_owned();
|
||||
let output = cx.background_executor().spawn(async move {
|
||||
self.extension
|
||||
@@ -128,8 +127,7 @@ impl SlashCommand for ExtensionSlashCommand {
|
||||
})
|
||||
.collect(),
|
||||
run_commands_in_text: false,
|
||||
}
|
||||
.to_event_stream())
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,6 @@ where
|
||||
item_count,
|
||||
item_to_measure_index: 0,
|
||||
render_items: Box::new(render_range),
|
||||
decorations: Vec::new(),
|
||||
interactivity: Interactivity {
|
||||
element_id: Some(id),
|
||||
base_style: Box::new(base_style),
|
||||
@@ -70,7 +69,6 @@ pub struct UniformList {
|
||||
item_to_measure_index: usize,
|
||||
render_items:
|
||||
Box<dyn for<'a> Fn(Range<usize>, &'a mut WindowContext) -> SmallVec<[AnyElement; 64]>>,
|
||||
decorations: Vec<Box<dyn UniformListDecoration>>,
|
||||
interactivity: Interactivity,
|
||||
scroll_handle: Option<UniformListScrollHandle>,
|
||||
sizing_behavior: ListSizingBehavior,
|
||||
@@ -80,7 +78,6 @@ pub struct UniformList {
|
||||
/// Frame state used by the [UniformList].
|
||||
pub struct UniformListFrameState {
|
||||
items: SmallVec<[AnyElement; 32]>,
|
||||
decorations: SmallVec<[AnyElement; 1]>,
|
||||
}
|
||||
|
||||
/// A handle for controlling the scroll position of a uniform list.
|
||||
@@ -188,7 +185,6 @@ impl Element for UniformList {
|
||||
layout_id,
|
||||
UniformListFrameState {
|
||||
items: SmallVec::new(),
|
||||
decorations: SmallVec::new(),
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -296,10 +292,9 @@ impl Element for UniformList {
|
||||
..cmp::min(last_visible_element_ix, self.item_count);
|
||||
|
||||
let mut items = (self.render_items)(visible_range.clone(), cx);
|
||||
|
||||
let content_mask = ContentMask { bounds };
|
||||
cx.with_content_mask(Some(content_mask), |cx| {
|
||||
for (mut item, ix) in items.into_iter().zip(visible_range.clone()) {
|
||||
for (mut item, ix) in items.into_iter().zip(visible_range) {
|
||||
let item_origin = padded_bounds.origin
|
||||
+ point(
|
||||
if can_scroll_horizontally {
|
||||
@@ -322,34 +317,6 @@ impl Element for UniformList {
|
||||
item.prepaint_at(item_origin, cx);
|
||||
frame_state.items.push(item);
|
||||
}
|
||||
|
||||
let bounds = Bounds::new(
|
||||
padded_bounds.origin
|
||||
+ point(
|
||||
if can_scroll_horizontally {
|
||||
scroll_offset.x + padding.left
|
||||
} else {
|
||||
scroll_offset.x
|
||||
},
|
||||
scroll_offset.y + padding.top,
|
||||
),
|
||||
padded_bounds.size,
|
||||
);
|
||||
for decoration in &self.decorations {
|
||||
let mut decoration = decoration.as_ref().compute(
|
||||
visible_range.clone(),
|
||||
bounds,
|
||||
item_height,
|
||||
cx,
|
||||
);
|
||||
let available_space = size(
|
||||
AvailableSpace::Definite(bounds.size.width),
|
||||
AvailableSpace::Definite(bounds.size.height),
|
||||
);
|
||||
decoration.layout_as_root(available_space, cx);
|
||||
decoration.prepaint_at(bounds.origin, cx);
|
||||
frame_state.decorations.push(decoration);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -371,9 +338,6 @@ impl Element for UniformList {
|
||||
for item in &mut request_layout.items {
|
||||
item.paint(cx);
|
||||
}
|
||||
for decoration in &mut request_layout.decorations {
|
||||
decoration.paint(cx);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -386,20 +350,6 @@ impl IntoElement for UniformList {
|
||||
}
|
||||
}
|
||||
|
||||
/// A decoration for a [`UniformList`]. This can be used for various things,
|
||||
/// such as rendering indent guides, or other visual effects.
|
||||
pub trait UniformListDecoration {
|
||||
/// Compute the decoration element, given the visible range of list items,
|
||||
/// the bounds of the list, and the height of each item.
|
||||
fn compute(
|
||||
&self,
|
||||
visible_range: Range<usize>,
|
||||
bounds: Bounds<Pixels>,
|
||||
item_height: Pixels,
|
||||
cx: &mut WindowContext,
|
||||
) -> AnyElement;
|
||||
}
|
||||
|
||||
impl UniformList {
|
||||
/// Selects a specific list item for measurement.
|
||||
pub fn with_width_from_item(mut self, item_index: Option<usize>) -> Self {
|
||||
@@ -432,12 +382,6 @@ impl UniformList {
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a decoration element to the list.
|
||||
pub fn with_decoration(mut self, decoration: impl UniformListDecoration + 'static) -> Self {
|
||||
self.decorations.push(Box::new(decoration));
|
||||
self
|
||||
}
|
||||
|
||||
fn measure_item(&self, list_width: Option<Pixels>, cx: &mut WindowContext) -> Size<Pixels> {
|
||||
if self.item_count == 0 {
|
||||
return Size::default();
|
||||
|
||||
@@ -40,7 +40,7 @@ pub struct AnthropicSettings {
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct AvailableModel {
|
||||
/// The model's name in the Anthropic API. e.g. claude-3-5-sonnet-latest, claude-3-opus-20240229, etc
|
||||
/// The model's name in the Anthropic API. e.g. claude-3-5-sonnet-20240620
|
||||
pub name: String,
|
||||
/// The model's name in Zed's UI, such as in the model selector dropdown menu in the assistant panel.
|
||||
pub display_name: Option<String>,
|
||||
|
||||
@@ -9,8 +9,7 @@ use gpui::{
|
||||
};
|
||||
use language::{LanguageServerId, LanguageServerName};
|
||||
use lsp::{
|
||||
notification::SetTrace, IoKind, LanguageServer, MessageType, ServerCapabilities,
|
||||
SetTraceParams, TraceValue,
|
||||
notification::SetTrace, IoKind, LanguageServer, MessageType, SetTraceParams, TraceValue,
|
||||
};
|
||||
use project::{search::SearchQuery, Project, WorktreeId};
|
||||
use std::{borrow::Cow, sync::Arc};
|
||||
@@ -108,7 +107,6 @@ struct LanguageServerState {
|
||||
rpc_state: Option<LanguageServerRpcState>,
|
||||
trace_level: TraceValue,
|
||||
log_level: MessageType,
|
||||
capabilities: ServerCapabilities,
|
||||
io_logs_subscription: Option<lsp::Subscription>,
|
||||
}
|
||||
|
||||
@@ -178,7 +176,6 @@ pub enum LogKind {
|
||||
Trace,
|
||||
#[default]
|
||||
Logs,
|
||||
Capabilities,
|
||||
}
|
||||
|
||||
impl LogKind {
|
||||
@@ -187,7 +184,6 @@ impl LogKind {
|
||||
LogKind::Rpc => RPC_MESSAGES,
|
||||
LogKind::Trace => SERVER_TRACE,
|
||||
LogKind::Logs => SERVER_LOGS,
|
||||
LogKind::Capabilities => SERVER_CAPABILITIES,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -378,7 +374,6 @@ impl LogStore {
|
||||
trace_level: TraceValue::Off,
|
||||
log_level: MessageType::LOG,
|
||||
io_logs_subscription: None,
|
||||
capabilities: ServerCapabilities::default(),
|
||||
}
|
||||
});
|
||||
|
||||
@@ -389,10 +384,7 @@ impl LogStore {
|
||||
server_state.worktree_id = Some(worktree_id);
|
||||
}
|
||||
|
||||
if let Some(server) = server
|
||||
.clone()
|
||||
.filter(|_| server_state.io_logs_subscription.is_none())
|
||||
{
|
||||
if let Some(server) = server.filter(|_| server_state.io_logs_subscription.is_none()) {
|
||||
let io_tx = self.io_tx.clone();
|
||||
let server_id = server.server_id();
|
||||
server_state.io_logs_subscription = Some(server.on_io(move |io_kind, message| {
|
||||
@@ -401,11 +393,6 @@ impl LogStore {
|
||||
.ok();
|
||||
}));
|
||||
}
|
||||
|
||||
if let Some(server) = server {
|
||||
server_state.capabilities = server.capabilities();
|
||||
}
|
||||
|
||||
Some(server_state)
|
||||
}
|
||||
|
||||
@@ -490,10 +477,6 @@ impl LogStore {
|
||||
Some(&self.language_servers.get(&server_id)?.trace_messages)
|
||||
}
|
||||
|
||||
fn server_capabilities(&self, server_id: LanguageServerId) -> Option<&ServerCapabilities> {
|
||||
Some(&self.language_servers.get(&server_id)?.capabilities)
|
||||
}
|
||||
|
||||
fn server_ids_for_project<'a>(
|
||||
&'a self,
|
||||
lookup_project: &'a WeakModel<Project>,
|
||||
@@ -619,9 +602,6 @@ impl LspLogView {
|
||||
LogKind::Rpc => this.show_rpc_trace_for_server(server_id, cx),
|
||||
LogKind::Trace => this.show_trace_for_server(server_id, cx),
|
||||
LogKind::Logs => this.show_logs_for_server(server_id, cx),
|
||||
LogKind::Capabilities => {
|
||||
this.show_capabilities_for_server(server_id, cx)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.current_server_id = None;
|
||||
@@ -638,7 +618,6 @@ impl LspLogView {
|
||||
LogKind::Rpc => this.show_rpc_trace_for_server(server_id, cx),
|
||||
LogKind::Trace => this.show_trace_for_server(server_id, cx),
|
||||
LogKind::Logs => this.show_logs_for_server(server_id, cx),
|
||||
LogKind::Capabilities => this.show_capabilities_for_server(server_id, cx),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -716,33 +695,6 @@ impl LspLogView {
|
||||
(editor, vec![editor_subscription, search_subscription])
|
||||
}
|
||||
|
||||
fn editor_for_capabilities(
|
||||
capabilities: ServerCapabilities,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> (View<Editor>, Vec<Subscription>) {
|
||||
let editor = cx.new_view(|cx| {
|
||||
let mut editor = Editor::multi_line(cx);
|
||||
editor.set_text(serde_json::to_string_pretty(&capabilities).unwrap(), cx);
|
||||
editor.move_to_end(&MoveToEnd, cx);
|
||||
editor.set_read_only(true);
|
||||
editor.set_show_inline_completions(Some(false), cx);
|
||||
editor
|
||||
});
|
||||
let editor_subscription = cx.subscribe(
|
||||
&editor,
|
||||
|_, _, event: &EditorEvent, cx: &mut ViewContext<'_, LspLogView>| {
|
||||
cx.emit(event.clone())
|
||||
},
|
||||
);
|
||||
let search_subscription = cx.subscribe(
|
||||
&editor,
|
||||
|_, _, event: &SearchEvent, cx: &mut ViewContext<'_, LspLogView>| {
|
||||
cx.emit(event.clone())
|
||||
},
|
||||
);
|
||||
(editor, vec![editor_subscription, search_subscription])
|
||||
}
|
||||
|
||||
pub(crate) fn menu_items<'a>(&'a self, cx: &'a AppContext) -> Option<Vec<LogMenuItem>> {
|
||||
let log_store = self.log_store.read(cx);
|
||||
|
||||
@@ -929,7 +881,6 @@ impl LspLogView {
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
fn update_trace_level(
|
||||
&self,
|
||||
server_id: LanguageServerId,
|
||||
@@ -948,25 +899,6 @@ impl LspLogView {
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
fn show_capabilities_for_server(
|
||||
&mut self,
|
||||
server_id: LanguageServerId,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
let capabilities = self.log_store.read(cx).server_capabilities(server_id);
|
||||
|
||||
if let Some(capabilities) = capabilities {
|
||||
self.current_server_id = Some(server_id);
|
||||
self.active_entry_kind = LogKind::Capabilities;
|
||||
let (editor, editor_subscriptions) =
|
||||
Self::editor_for_capabilities(capabilities.clone(), cx);
|
||||
self.editor = editor;
|
||||
self.editor_subscriptions = editor_subscriptions;
|
||||
cx.notify();
|
||||
}
|
||||
cx.focus(&self.focus_handle);
|
||||
}
|
||||
}
|
||||
|
||||
fn log_filter<T: Message>(line: &T, cmp: <T as Message>::Level) -> Option<&str> {
|
||||
@@ -1035,7 +967,6 @@ impl Item for LspLogView {
|
||||
LogKind::Rpc => new_view.show_rpc_trace_for_server(server_id, cx),
|
||||
LogKind::Trace => new_view.show_trace_for_server(server_id, cx),
|
||||
LogKind::Logs => new_view.show_logs_for_server(server_id, cx),
|
||||
LogKind::Capabilities => new_view.show_capabilities_for_server(server_id, cx),
|
||||
}
|
||||
}
|
||||
new_view
|
||||
@@ -1237,13 +1168,6 @@ impl Render for LspLogToolbarItemView {
|
||||
view.show_rpc_trace_for_server(row.server_id, cx);
|
||||
}),
|
||||
);
|
||||
menu = menu.entry(
|
||||
SERVER_CAPABILITIES,
|
||||
None,
|
||||
cx.handler_for(&log_view, move |view, cx| {
|
||||
view.show_capabilities_for_server(row.server_id, cx);
|
||||
}),
|
||||
);
|
||||
if server_selected && row.selected_entry == LogKind::Rpc {
|
||||
let selected_ix = menu.select_last();
|
||||
debug_assert_eq!(
|
||||
@@ -1393,7 +1317,6 @@ impl Render for LspLogToolbarItemView {
|
||||
const RPC_MESSAGES: &str = "RPC Messages";
|
||||
const SERVER_LOGS: &str = "Server Logs";
|
||||
const SERVER_TRACE: &str = "Server Trace";
|
||||
const SERVER_CAPABILITIES: &str = "Server Capabilities";
|
||||
|
||||
impl Default for LspLogToolbarItemView {
|
||||
fn default() -> Self {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
declarator: (function_declarator
|
||||
declarator: (identifier) @run
|
||||
)
|
||||
) @_c-main
|
||||
) @c-main
|
||||
(#eq? @run "main")
|
||||
(#set! tag c-main)
|
||||
)
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
(string_fragment) @run
|
||||
)
|
||||
)
|
||||
) @_js-test
|
||||
) @js-test
|
||||
(#set! tag js-test)
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
(attribute (identifier) @_superclass)]
|
||||
)
|
||||
(#eq? @_superclass "TestCase")
|
||||
) @_python-unittest-class
|
||||
) @python-unittest-class
|
||||
(#set! tag python-unittest-class)
|
||||
)
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
(function_definition
|
||||
name: (identifier) @run @_unittest_method_name
|
||||
(#match? @_unittest_method_name "^test.*")
|
||||
) @_python-unittest-method
|
||||
) @python-unittest-method
|
||||
(#set! tag python-unittest-method)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
(mod_item
|
||||
name: (_) @run
|
||||
(#eq? @run "tests")
|
||||
)
|
||||
) @rust-mod-test
|
||||
(#set! tag rust-mod-test)
|
||||
)
|
||||
|
||||
@@ -14,14 +14,14 @@
|
||||
(scoped_identifier (identifier) @_attribute)
|
||||
])
|
||||
(#match? @_attribute "test")
|
||||
) @_start
|
||||
) @start
|
||||
.
|
||||
(attribute_item) *
|
||||
.
|
||||
(function_item
|
||||
name: (_) @run
|
||||
body: _
|
||||
) @_end
|
||||
) @end
|
||||
)
|
||||
(#set! tag rust-test)
|
||||
)
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
(string_fragment) @run
|
||||
)
|
||||
)
|
||||
) @_ts-test
|
||||
) @ts-test
|
||||
(#set! tag ts-test)
|
||||
)
|
||||
|
||||
@@ -3356,12 +3356,11 @@ impl LspStore {
|
||||
diagnostics: Vec<DiagnosticEntry<Unclipped<PointUtf16>>>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
let Some((worktree, relative_path)) =
|
||||
self.worktree_store.read(cx).find_worktree(&abs_path, cx)
|
||||
else {
|
||||
log::warn!("skipping diagnostics update, no worktree found for path {abs_path:?}");
|
||||
return Ok(());
|
||||
};
|
||||
let (worktree, relative_path) =
|
||||
self.worktree_store
|
||||
.read(cx)
|
||||
.find_worktree(&abs_path, cx)
|
||||
.ok_or_else(|| anyhow!("no worktree found for diagnostics path {abs_path:?}"))?;
|
||||
|
||||
let project_path = ProjectPath {
|
||||
worktree_id: worktree.read(cx).id(),
|
||||
|
||||
@@ -22,7 +22,7 @@ pub use environment::EnvironmentErrorMessage;
|
||||
pub mod search_history;
|
||||
mod yarn;
|
||||
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use anyhow::{anyhow, Result};
|
||||
use buffer_store::{BufferStore, BufferStoreEvent};
|
||||
use client::{
|
||||
proto, Client, Collaborator, DevServerProjectId, PendingEntitySubscription, ProjectId,
|
||||
@@ -40,8 +40,8 @@ use futures::{
|
||||
|
||||
use git::{blame::Blame, repository::GitRepository};
|
||||
use gpui::{
|
||||
AnyModel, AppContext, AsyncAppContext, BorrowAppContext, Context as _, EventEmitter, Hsla,
|
||||
Model, ModelContext, SharedString, Task, WeakModel, WindowContext,
|
||||
AnyModel, AppContext, AsyncAppContext, BorrowAppContext, Context, EventEmitter, Hsla, Model,
|
||||
ModelContext, SharedString, Task, WeakModel, WindowContext,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use language::{
|
||||
@@ -52,7 +52,6 @@ use language::{
|
||||
};
|
||||
use lsp::{
|
||||
CompletionContext, CompletionItemKind, DocumentHighlightKind, LanguageServer, LanguageServerId,
|
||||
MessageActionItem,
|
||||
};
|
||||
use lsp_command::*;
|
||||
use node_runtime::NodeRuntime;
|
||||
@@ -60,10 +59,7 @@ use parking_lot::{Mutex, RwLock};
|
||||
pub use prettier_store::PrettierStore;
|
||||
use project_settings::{ProjectSettings, SettingsObserver, SettingsObserverEvent};
|
||||
use remote::{SshConnectionOptions, SshRemoteClient};
|
||||
use rpc::{
|
||||
proto::{LanguageServerPromptResponse, SSH_PROJECT_ID},
|
||||
AnyProtoClient, ErrorCode,
|
||||
};
|
||||
use rpc::{proto::SSH_PROJECT_ID, AnyProtoClient, ErrorCode};
|
||||
use search::{SearchInputKind, SearchQuery, SearchResult};
|
||||
use search_history::SearchHistory;
|
||||
use settings::{InvalidSettingsError, Settings, SettingsLocation, SettingsStore};
|
||||
@@ -732,7 +728,12 @@ impl Project {
|
||||
});
|
||||
|
||||
let settings_observer = cx.new_model(|cx| {
|
||||
SettingsObserver::new_remote(worktree_store.clone(), task_store.clone(), cx)
|
||||
SettingsObserver::new_ssh(
|
||||
ssh_proto.clone(),
|
||||
worktree_store.clone(),
|
||||
task_store.clone(),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
cx.subscribe(&settings_observer, Self::on_settings_observer_event)
|
||||
.detach();
|
||||
@@ -814,7 +815,6 @@ impl Project {
|
||||
ssh_proto.add_model_message_handler(Self::handle_update_worktree);
|
||||
ssh_proto.add_model_message_handler(Self::handle_update_project);
|
||||
ssh_proto.add_model_message_handler(Self::handle_toast);
|
||||
ssh_proto.add_model_request_handler(Self::handle_language_server_prompt_request);
|
||||
ssh_proto.add_model_message_handler(Self::handle_hide_toast);
|
||||
ssh_proto.add_model_request_handler(BufferStore::handle_update_buffer);
|
||||
BufferStore::init(&ssh_proto);
|
||||
@@ -1185,7 +1185,6 @@ impl Project {
|
||||
cx: &mut gpui::TestAppContext,
|
||||
) -> Model<Project> {
|
||||
use clock::FakeSystemClock;
|
||||
use gpui::Context;
|
||||
|
||||
let languages = LanguageRegistry::test(cx.executor());
|
||||
let clock = Arc::new(FakeSystemClock::default());
|
||||
@@ -3628,45 +3627,6 @@ impl Project {
|
||||
})?
|
||||
}
|
||||
|
||||
async fn handle_language_server_prompt_request(
|
||||
this: Model<Self>,
|
||||
envelope: TypedEnvelope<proto::LanguageServerPromptRequest>,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> Result<proto::LanguageServerPromptResponse> {
|
||||
let (tx, mut rx) = smol::channel::bounded(1);
|
||||
let actions: Vec<_> = envelope
|
||||
.payload
|
||||
.actions
|
||||
.into_iter()
|
||||
.map(|action| MessageActionItem {
|
||||
title: action,
|
||||
properties: Default::default(),
|
||||
})
|
||||
.collect();
|
||||
this.update(&mut cx, |_, cx| {
|
||||
cx.emit(Event::LanguageServerPrompt(LanguageServerPromptRequest {
|
||||
level: proto_to_prompt(envelope.payload.level.context("Invalid prompt level")?),
|
||||
message: envelope.payload.message,
|
||||
actions: actions.clone(),
|
||||
lsp_name: envelope.payload.lsp_name,
|
||||
response_channel: tx,
|
||||
}));
|
||||
|
||||
anyhow::Ok(())
|
||||
})??;
|
||||
|
||||
let answer = rx.next().await;
|
||||
|
||||
Ok(LanguageServerPromptResponse {
|
||||
action_response: answer.and_then(|answer| {
|
||||
actions
|
||||
.iter()
|
||||
.position(|action| *action == answer)
|
||||
.map(|index| index as u64)
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
async fn handle_hide_toast(
|
||||
this: Model<Self>,
|
||||
envelope: TypedEnvelope<proto::HideToast>,
|
||||
@@ -4302,11 +4262,3 @@ pub fn sort_worktree_entries(entries: &mut [Entry]) {
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
fn proto_to_prompt(level: proto::language_server_prompt_request::Level) -> gpui::PromptLevel {
|
||||
match level {
|
||||
proto::language_server_prompt_request::Level::Info(_) => gpui::PromptLevel::Info,
|
||||
proto::language_server_prompt_request::Level::Warning(_) => gpui::PromptLevel::Warning,
|
||||
proto::language_server_prompt_request::Level::Critical(_) => gpui::PromptLevel::Critical,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,6 +196,7 @@ impl Settings for ProjectSettings {
|
||||
|
||||
pub enum SettingsObserverMode {
|
||||
Local(Arc<dyn Fs>),
|
||||
Ssh(AnyProtoClient),
|
||||
Remote,
|
||||
}
|
||||
|
||||
@@ -222,6 +223,7 @@ pub struct SettingsObserver {
|
||||
impl SettingsObserver {
|
||||
pub fn init(client: &AnyProtoClient) {
|
||||
client.add_model_message_handler(Self::handle_update_worktree_settings);
|
||||
client.add_model_message_handler(Self::handle_update_user_settings)
|
||||
}
|
||||
|
||||
pub fn new_local(
|
||||
@@ -242,6 +244,23 @@ impl SettingsObserver {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_ssh(
|
||||
client: AnyProtoClient,
|
||||
worktree_store: Model<WorktreeStore>,
|
||||
task_store: Model<TaskStore>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Self {
|
||||
let this = Self {
|
||||
worktree_store,
|
||||
task_store,
|
||||
mode: SettingsObserverMode::Ssh(client.clone()),
|
||||
downstream_client: None,
|
||||
project_id: 0,
|
||||
};
|
||||
this.maintain_ssh_settings(client, cx);
|
||||
this
|
||||
}
|
||||
|
||||
pub fn new_remote(
|
||||
worktree_store: Model<WorktreeStore>,
|
||||
task_store: Model<TaskStore>,
|
||||
@@ -334,6 +353,62 @@ impl SettingsObserver {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn handle_update_user_settings(
|
||||
settings_observer: Model<Self>,
|
||||
envelope: TypedEnvelope<proto::UpdateUserSettings>,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> anyhow::Result<()> {
|
||||
match envelope.payload.kind() {
|
||||
proto::update_user_settings::Kind::Settings => {
|
||||
cx.update_global(move |settings_store: &mut SettingsStore, cx| {
|
||||
settings_store.set_user_settings(&envelope.payload.content, cx)
|
||||
})
|
||||
}
|
||||
proto::update_user_settings::Kind::Tasks => {
|
||||
settings_observer.update(&mut cx, |settings_observer, cx| {
|
||||
settings_observer.task_store.update(cx, |task_store, cx| {
|
||||
task_store.update_user_tasks(None, Some(&envelope.payload.content), cx)
|
||||
})
|
||||
})
|
||||
}
|
||||
}??;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn maintain_ssh_settings(&self, ssh: AnyProtoClient, cx: &mut ModelContext<Self>) {
|
||||
let settings_store = cx.global::<SettingsStore>();
|
||||
|
||||
let mut settings = settings_store.raw_user_settings().clone();
|
||||
if let Some(content) = serde_json::to_string(&settings).log_err() {
|
||||
ssh.send(proto::UpdateUserSettings {
|
||||
project_id: 0,
|
||||
content,
|
||||
kind: Some(proto::LocalSettingsKind::Settings.into()),
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
|
||||
let weak_client = ssh.downgrade();
|
||||
cx.observe_global::<SettingsStore>(move |_, cx| {
|
||||
let new_settings = cx.global::<SettingsStore>().raw_user_settings();
|
||||
if &settings != new_settings {
|
||||
settings = new_settings.clone()
|
||||
}
|
||||
if let Some(content) = serde_json::to_string(&settings).log_err() {
|
||||
if let Some(ssh) = weak_client.upgrade() {
|
||||
ssh.send(proto::UpdateUserSettings {
|
||||
project_id: 0,
|
||||
content,
|
||||
kind: Some(proto::LocalSettingsKind::Settings.into()),
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
fn on_worktree_store_event(
|
||||
&mut self,
|
||||
_: Model<WorktreeStore>,
|
||||
|
||||
@@ -67,15 +67,13 @@ impl Project {
|
||||
}
|
||||
}
|
||||
|
||||
fn ssh_details(&self, cx: &AppContext) -> Option<(String, SshCommand)> {
|
||||
if let Some(ssh_client) = &self.ssh_client {
|
||||
let ssh_client = ssh_client.read(cx);
|
||||
if let Some(args) = ssh_client.ssh_args() {
|
||||
return Some((
|
||||
ssh_client.connection_options().host.clone(),
|
||||
SshCommand::Direct(args),
|
||||
));
|
||||
}
|
||||
fn ssh_command(&self, cx: &AppContext) -> Option<SshCommand> {
|
||||
if let Some(args) = self
|
||||
.ssh_client
|
||||
.as_ref()
|
||||
.and_then(|session| session.read(cx).ssh_args())
|
||||
{
|
||||
return Some(SshCommand::Direct(args));
|
||||
}
|
||||
|
||||
let dev_server_project_id = self.dev_server_project_id()?;
|
||||
@@ -85,7 +83,7 @@ impl Project {
|
||||
.ssh_connection_string
|
||||
.as_ref()?
|
||||
.to_string();
|
||||
Some(("".to_string(), SshCommand::DevServer(ssh_command)))
|
||||
Some(SshCommand::DevServer(ssh_command))
|
||||
}
|
||||
|
||||
pub fn create_terminal(
|
||||
@@ -104,7 +102,7 @@ impl Project {
|
||||
}
|
||||
}
|
||||
};
|
||||
let ssh_details = self.ssh_details(cx);
|
||||
let ssh_command = self.ssh_command(cx);
|
||||
|
||||
let mut settings_location = None;
|
||||
if let Some(path) = path.as_ref() {
|
||||
@@ -129,7 +127,7 @@ impl Project {
|
||||
// precedence.
|
||||
env.extend(settings.env.clone());
|
||||
|
||||
let local_path = if ssh_details.is_none() {
|
||||
let local_path = if ssh_command.is_none() {
|
||||
path.clone()
|
||||
} else {
|
||||
None
|
||||
@@ -146,8 +144,8 @@ impl Project {
|
||||
self.python_activate_command(&python_venv_directory, settings);
|
||||
}
|
||||
|
||||
match &ssh_details {
|
||||
Some((host, ssh_command)) => {
|
||||
match &ssh_command {
|
||||
Some(ssh_command) => {
|
||||
log::debug!("Connecting to a remote server: {ssh_command:?}");
|
||||
|
||||
// Alacritty sets its terminfo to `alacritty`, this requiring hosts to have it installed
|
||||
@@ -160,14 +158,7 @@ impl Project {
|
||||
let (program, args) =
|
||||
wrap_for_ssh(ssh_command, None, path.as_deref(), env, None);
|
||||
env = HashMap::default();
|
||||
(
|
||||
None,
|
||||
Shell::WithArguments {
|
||||
program,
|
||||
args,
|
||||
title_override: Some(format!("{} — Terminal", host).into()),
|
||||
},
|
||||
)
|
||||
(None, Shell::WithArguments { program, args })
|
||||
}
|
||||
None => (None, settings.shell.clone()),
|
||||
}
|
||||
@@ -192,8 +183,8 @@ impl Project {
|
||||
);
|
||||
}
|
||||
|
||||
match &ssh_details {
|
||||
Some((host, ssh_command)) => {
|
||||
match &ssh_command {
|
||||
Some(ssh_command) => {
|
||||
log::debug!("Connecting to a remote server: {ssh_command:?}");
|
||||
env.entry("TERM".to_string())
|
||||
.or_insert_with(|| "xterm-256color".to_string());
|
||||
@@ -205,14 +196,7 @@ impl Project {
|
||||
python_venv_directory,
|
||||
);
|
||||
env = HashMap::default();
|
||||
(
|
||||
task_state,
|
||||
Shell::WithArguments {
|
||||
program,
|
||||
args,
|
||||
title_override: Some(format!("{} — Terminal", host).into()),
|
||||
},
|
||||
)
|
||||
(task_state, Shell::WithArguments { program, args })
|
||||
}
|
||||
None => {
|
||||
if let Some(venv_path) = &python_venv_directory {
|
||||
@@ -224,7 +208,6 @@ impl Project {
|
||||
Shell::WithArguments {
|
||||
program: spawn_task.command,
|
||||
args: spawn_task.args,
|
||||
title_override: None,
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -240,7 +223,6 @@ impl Project {
|
||||
settings.cursor_shape.unwrap_or_default(),
|
||||
settings.alternate_scroll,
|
||||
settings.max_scroll_history_lines,
|
||||
ssh_details.is_some(),
|
||||
window,
|
||||
completion_tx,
|
||||
cx,
|
||||
|
||||
@@ -30,7 +30,6 @@ serde.workspace = true
|
||||
serde_derive.workspace = true
|
||||
serde_json.workspace = true
|
||||
settings.workspace = true
|
||||
smallvec.workspace = true
|
||||
theme.workspace = true
|
||||
ui.workspace = true
|
||||
util.workspace = true
|
||||
|
||||
@@ -16,13 +16,12 @@ use anyhow::{anyhow, Context as _, Result};
|
||||
use collections::{hash_map, BTreeSet, HashMap};
|
||||
use git::repository::GitFileStatus;
|
||||
use gpui::{
|
||||
actions, anchored, deferred, div, impl_actions, point, px, size, uniform_list, Action,
|
||||
AnyElement, AppContext, AssetSource, AsyncWindowContext, Bounds, ClipboardItem, DismissEvent,
|
||||
Div, DragMoveEvent, EventEmitter, ExternalPaths, FocusHandle, FocusableView,
|
||||
InteractiveElement, KeyContext, ListHorizontalSizingBehavior, ListSizingBehavior, Model,
|
||||
MouseButton, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Render, Stateful,
|
||||
Styled, Subscription, Task, UniformListScrollHandle, View, ViewContext, VisualContext as _,
|
||||
WeakView, WindowContext,
|
||||
actions, anchored, deferred, div, impl_actions, px, uniform_list, Action, AnyElement,
|
||||
AppContext, AssetSource, AsyncWindowContext, ClipboardItem, DismissEvent, Div, DragMoveEvent,
|
||||
EventEmitter, ExternalPaths, FocusHandle, FocusableView, InteractiveElement, KeyContext,
|
||||
ListHorizontalSizingBehavior, ListSizingBehavior, Model, MouseButton, MouseDownEvent,
|
||||
ParentElement, Pixels, Point, PromptLevel, Render, Stateful, Styled, Subscription, Task,
|
||||
UniformListScrollHandle, View, ViewContext, VisualContext as _, WeakView, WindowContext,
|
||||
};
|
||||
use indexmap::IndexMap;
|
||||
use menu::{Confirm, SelectFirst, SelectLast, SelectNext, SelectPrev};
|
||||
@@ -32,7 +31,6 @@ use project::{
|
||||
};
|
||||
use project_panel_settings::{ProjectPanelDockPosition, ProjectPanelSettings};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
cell::OnceCell,
|
||||
collections::HashSet,
|
||||
@@ -43,10 +41,7 @@ use std::{
|
||||
time::Duration,
|
||||
};
|
||||
use theme::ThemeSettings;
|
||||
use ui::{
|
||||
prelude::*, v_flex, ContextMenu, Icon, IndentGuideColors, IndentGuideLayout, KeyBinding, Label,
|
||||
ListItem, Tooltip,
|
||||
};
|
||||
use ui::{prelude::*, v_flex, ContextMenu, Icon, KeyBinding, Label, ListItem, Tooltip};
|
||||
use util::{maybe, ResultExt, TryFutureExt};
|
||||
use workspace::{
|
||||
dock::{DockPosition, Panel, PanelEvent},
|
||||
@@ -659,52 +654,42 @@ impl ProjectPanel {
|
||||
}
|
||||
|
||||
fn collapse_selected_entry(&mut self, _: &CollapseSelectedEntry, cx: &mut ViewContext<Self>) {
|
||||
let Some((worktree, entry)) = self.selected_entry_handle(cx) else {
|
||||
return;
|
||||
};
|
||||
self.collapse_entry(entry.clone(), worktree, cx)
|
||||
}
|
||||
|
||||
fn collapse_entry(
|
||||
&mut self,
|
||||
entry: Entry,
|
||||
worktree: Model<Worktree>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
let worktree = worktree.read(cx);
|
||||
if let Some(folded_ancestors) = self.ancestors.get_mut(&entry.id) {
|
||||
if folded_ancestors.current_ancestor_depth + 1 < folded_ancestors.max_ancestor_depth() {
|
||||
folded_ancestors.current_ancestor_depth += 1;
|
||||
cx.notify();
|
||||
return;
|
||||
}
|
||||
}
|
||||
let worktree_id = worktree.id();
|
||||
let expanded_dir_ids =
|
||||
if let Some(expanded_dir_ids) = self.expanded_dir_ids.get_mut(&worktree_id) {
|
||||
expanded_dir_ids
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut entry = &entry;
|
||||
loop {
|
||||
let entry_id = entry.id;
|
||||
match expanded_dir_ids.binary_search(&entry_id) {
|
||||
Ok(ix) => {
|
||||
expanded_dir_ids.remove(ix);
|
||||
self.update_visible_entries(Some((worktree_id, entry_id)), cx);
|
||||
if let Some((worktree, mut entry)) = self.selected_entry(cx) {
|
||||
if let Some(folded_ancestors) = self.ancestors.get_mut(&entry.id) {
|
||||
if folded_ancestors.current_ancestor_depth + 1
|
||||
< folded_ancestors.max_ancestor_depth()
|
||||
{
|
||||
folded_ancestors.current_ancestor_depth += 1;
|
||||
cx.notify();
|
||||
break;
|
||||
return;
|
||||
}
|
||||
Err(_) => {
|
||||
if let Some(parent_entry) =
|
||||
entry.path.parent().and_then(|p| worktree.entry_for_path(p))
|
||||
{
|
||||
entry = parent_entry;
|
||||
} else {
|
||||
}
|
||||
let worktree_id = worktree.id();
|
||||
let expanded_dir_ids =
|
||||
if let Some(expanded_dir_ids) = self.expanded_dir_ids.get_mut(&worktree_id) {
|
||||
expanded_dir_ids
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
loop {
|
||||
let entry_id = entry.id;
|
||||
match expanded_dir_ids.binary_search(&entry_id) {
|
||||
Ok(ix) => {
|
||||
expanded_dir_ids.remove(ix);
|
||||
self.update_visible_entries(Some((worktree_id, entry_id)), cx);
|
||||
cx.notify();
|
||||
break;
|
||||
}
|
||||
Err(_) => {
|
||||
if let Some(parent_entry) =
|
||||
entry.path.parent().and_then(|p| worktree.entry_for_path(p))
|
||||
{
|
||||
entry = parent_entry;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -741,19 +726,6 @@ impl ProjectPanel {
|
||||
}
|
||||
|
||||
fn select_prev(&mut self, _: &SelectPrev, cx: &mut ViewContext<Self>) {
|
||||
if let Some(edit_state) = &self.edit_state {
|
||||
if edit_state.processing_filename.is_none() {
|
||||
self.filename_editor.update(cx, |editor, cx| {
|
||||
editor.move_to_beginning_of_line(
|
||||
&editor::actions::MoveToBeginningOfLine {
|
||||
stop_at_soft_wraps: false,
|
||||
},
|
||||
cx,
|
||||
);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
if let Some(selection) = self.selection {
|
||||
let (mut worktree_ix, mut entry_ix, _) =
|
||||
self.index_for_selection(selection).unwrap_or_default();
|
||||
@@ -1224,19 +1196,6 @@ impl ProjectPanel {
|
||||
}
|
||||
|
||||
fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext<Self>) {
|
||||
if let Some(edit_state) = &self.edit_state {
|
||||
if edit_state.processing_filename.is_none() {
|
||||
self.filename_editor.update(cx, |editor, cx| {
|
||||
editor.move_to_end_of_line(
|
||||
&editor::actions::MoveToEndOfLine {
|
||||
stop_at_soft_wraps: false,
|
||||
},
|
||||
cx,
|
||||
);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
if let Some(selection) = self.selection {
|
||||
let (mut worktree_ix, mut entry_ix, _) =
|
||||
self.index_for_selection(selection).unwrap_or_default();
|
||||
@@ -1742,7 +1701,6 @@ impl ProjectPanel {
|
||||
.copied()
|
||||
.unwrap_or(id)
|
||||
}
|
||||
|
||||
pub fn selected_entry<'a>(
|
||||
&self,
|
||||
cx: &'a AppContext,
|
||||
@@ -2160,74 +2118,6 @@ impl ProjectPanel {
|
||||
}
|
||||
}
|
||||
|
||||
fn index_for_entry(
|
||||
&self,
|
||||
entry_id: ProjectEntryId,
|
||||
worktree_id: WorktreeId,
|
||||
) -> Option<(usize, usize, usize)> {
|
||||
let mut worktree_ix = 0;
|
||||
let mut total_ix = 0;
|
||||
for (current_worktree_id, visible_worktree_entries, _) in &self.visible_entries {
|
||||
if worktree_id != *current_worktree_id {
|
||||
total_ix += visible_worktree_entries.len();
|
||||
worktree_ix += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
return visible_worktree_entries
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, entry)| entry.id == entry_id)
|
||||
.map(|(ix, _)| (worktree_ix, ix, total_ix + ix));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn entry_at_index(&self, index: usize) -> Option<(WorktreeId, &Entry)> {
|
||||
let mut offset = 0;
|
||||
for (worktree_id, visible_worktree_entries, _) in &self.visible_entries {
|
||||
if visible_worktree_entries.len() > offset + index {
|
||||
return visible_worktree_entries
|
||||
.get(index)
|
||||
.map(|entry| (*worktree_id, entry));
|
||||
}
|
||||
offset += visible_worktree_entries.len();
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn iter_visible_entries(
|
||||
&self,
|
||||
range: Range<usize>,
|
||||
cx: &mut ViewContext<ProjectPanel>,
|
||||
mut callback: impl FnMut(&Entry, &HashSet<Arc<Path>>, &mut ViewContext<ProjectPanel>),
|
||||
) {
|
||||
let mut ix = 0;
|
||||
for (_, visible_worktree_entries, entries_paths) in &self.visible_entries {
|
||||
if ix >= range.end {
|
||||
return;
|
||||
}
|
||||
|
||||
if ix + visible_worktree_entries.len() <= range.start {
|
||||
ix += visible_worktree_entries.len();
|
||||
continue;
|
||||
}
|
||||
|
||||
let end_ix = range.end.min(ix + visible_worktree_entries.len());
|
||||
let entry_range = range.start.saturating_sub(ix)..end_ix - ix;
|
||||
let entries = entries_paths.get_or_init(|| {
|
||||
visible_worktree_entries
|
||||
.iter()
|
||||
.map(|e| (e.path.clone()))
|
||||
.collect()
|
||||
});
|
||||
for entry in visible_worktree_entries[entry_range].iter() {
|
||||
callback(entry, entries, cx);
|
||||
}
|
||||
ix = end_ix;
|
||||
}
|
||||
}
|
||||
|
||||
fn for_each_visible_entry(
|
||||
&self,
|
||||
range: Range<usize>,
|
||||
@@ -2900,70 +2790,6 @@ impl ProjectPanel {
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
fn find_active_indent_guide(
|
||||
&self,
|
||||
indent_guides: &[IndentGuideLayout],
|
||||
cx: &AppContext,
|
||||
) -> Option<usize> {
|
||||
let (worktree, entry) = self.selected_entry(cx)?;
|
||||
|
||||
// Find the parent entry of the indent guide, this will either be the
|
||||
// expanded folder we have selected, or the parent of the currently
|
||||
// selected file/collapsed directory
|
||||
let mut entry = entry;
|
||||
loop {
|
||||
let is_expanded_dir = entry.is_dir()
|
||||
&& self
|
||||
.expanded_dir_ids
|
||||
.get(&worktree.id())
|
||||
.map(|ids| ids.binary_search(&entry.id).is_ok())
|
||||
.unwrap_or(false);
|
||||
if is_expanded_dir {
|
||||
break;
|
||||
}
|
||||
entry = worktree.entry_for_path(&entry.path.parent()?)?;
|
||||
}
|
||||
|
||||
let (active_indent_range, depth) = {
|
||||
let (worktree_ix, child_offset, ix) = self.index_for_entry(entry.id, worktree.id())?;
|
||||
let child_paths = &self.visible_entries[worktree_ix].1;
|
||||
let mut child_count = 0;
|
||||
let depth = entry.path.ancestors().count();
|
||||
while let Some(entry) = child_paths.get(child_offset + child_count + 1) {
|
||||
if entry.path.ancestors().count() <= depth {
|
||||
break;
|
||||
}
|
||||
child_count += 1;
|
||||
}
|
||||
|
||||
let start = ix + 1;
|
||||
let end = start + child_count;
|
||||
|
||||
let (_, entries, paths) = &self.visible_entries[worktree_ix];
|
||||
let visible_worktree_entries =
|
||||
paths.get_or_init(|| entries.iter().map(|e| (e.path.clone())).collect());
|
||||
|
||||
// Calculate the actual depth of the entry, taking into account that directories can be auto-folded.
|
||||
let (depth, _) = Self::calculate_depth_and_difference(entry, visible_worktree_entries);
|
||||
(start..end, depth)
|
||||
};
|
||||
|
||||
let candidates = indent_guides
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, indent_guide)| indent_guide.offset.x == depth);
|
||||
|
||||
for (i, indent) in candidates {
|
||||
// Find matches that are either an exact match, partially on screen, or inside the enclosing indent
|
||||
if active_indent_range.start <= indent.offset.y + indent.length
|
||||
&& indent.offset.y <= active_indent_range.end
|
||||
{
|
||||
return Some(i);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn item_width_estimate(depth: usize, item_text_chars: usize, is_symlink: bool) -> usize {
|
||||
@@ -2979,8 +2805,6 @@ impl Render for ProjectPanel {
|
||||
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement {
|
||||
let has_worktree = !self.visible_entries.is_empty();
|
||||
let project = self.project.read(cx);
|
||||
let indent_size = ProjectPanelSettings::get_global(cx).indent_size;
|
||||
let indent_guides = ProjectPanelSettings::get_global(cx).indent_guides;
|
||||
let is_local = project.is_local();
|
||||
|
||||
if has_worktree {
|
||||
@@ -3084,103 +2908,6 @@ impl Render for ProjectPanel {
|
||||
items
|
||||
}
|
||||
})
|
||||
.when(indent_guides, |list| {
|
||||
list.with_decoration(
|
||||
ui::indent_guides(
|
||||
cx.view().clone(),
|
||||
px(indent_size),
|
||||
IndentGuideColors::panel(cx),
|
||||
|this, range, cx| {
|
||||
let mut items =
|
||||
SmallVec::with_capacity(range.end - range.start);
|
||||
this.iter_visible_entries(range, cx, |entry, entries, _| {
|
||||
let (depth, _) =
|
||||
Self::calculate_depth_and_difference(entry, entries);
|
||||
items.push(depth);
|
||||
});
|
||||
items
|
||||
},
|
||||
)
|
||||
.on_click(cx.listener(
|
||||
|this, active_indent_guide: &IndentGuideLayout, cx| {
|
||||
if cx.modifiers().secondary() {
|
||||
let ix = active_indent_guide.offset.y;
|
||||
let Some((target_entry, worktree)) = maybe!({
|
||||
let (worktree_id, entry) = this.entry_at_index(ix)?;
|
||||
let worktree = this
|
||||
.project
|
||||
.read(cx)
|
||||
.worktree_for_id(worktree_id, cx)?;
|
||||
let target_entry = worktree
|
||||
.read(cx)
|
||||
.entry_for_path(&entry.path.parent()?)?;
|
||||
Some((target_entry, worktree))
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
|
||||
this.collapse_entry(target_entry.clone(), worktree, cx);
|
||||
}
|
||||
},
|
||||
))
|
||||
.with_render_fn(
|
||||
cx.view().clone(),
|
||||
move |this, params, cx| {
|
||||
const LEFT_OFFSET: f32 = 14.;
|
||||
const PADDING_Y: f32 = 4.;
|
||||
const HITBOX_OVERDRAW: f32 = 3.;
|
||||
|
||||
let active_indent_guide_index =
|
||||
this.find_active_indent_guide(¶ms.indent_guides, cx);
|
||||
|
||||
let indent_size = params.indent_size;
|
||||
let item_height = params.item_height;
|
||||
|
||||
params
|
||||
.indent_guides
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(idx, layout)| {
|
||||
let offset = if layout.continues_offscreen {
|
||||
px(0.)
|
||||
} else {
|
||||
px(PADDING_Y)
|
||||
};
|
||||
let bounds = Bounds::new(
|
||||
point(
|
||||
px(layout.offset.x as f32) * indent_size
|
||||
+ px(LEFT_OFFSET),
|
||||
px(layout.offset.y as f32) * item_height
|
||||
+ offset,
|
||||
),
|
||||
size(
|
||||
px(1.),
|
||||
px(layout.length as f32) * item_height
|
||||
- px(offset.0 * 2.),
|
||||
),
|
||||
);
|
||||
ui::RenderedIndentGuide {
|
||||
bounds,
|
||||
layout,
|
||||
is_active: Some(idx) == active_indent_guide_index,
|
||||
hitbox: Some(Bounds::new(
|
||||
point(
|
||||
bounds.origin.x - px(HITBOX_OVERDRAW),
|
||||
bounds.origin.y,
|
||||
),
|
||||
size(
|
||||
bounds.size.width
|
||||
+ px(2. * HITBOX_OVERDRAW),
|
||||
bounds.size.height,
|
||||
),
|
||||
)),
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
),
|
||||
)
|
||||
})
|
||||
.size_full()
|
||||
.with_sizing_behavior(ListSizingBehavior::Infer)
|
||||
.with_horizontal_sizing_behavior(ListHorizontalSizingBehavior::Unconstrained)
|
||||
@@ -3212,7 +2939,7 @@ impl Render for ProjectPanel {
|
||||
.key_binding(KeyBinding::for_action(&workspace::Open, cx))
|
||||
.on_click(cx.listener(|this, _, cx| {
|
||||
this.workspace
|
||||
.update(cx, |_, cx| cx.dispatch_action(Box::new(workspace::Open)))
|
||||
.update(cx, |workspace, cx| workspace.open(&workspace::Open, cx))
|
||||
.log_err();
|
||||
})),
|
||||
)
|
||||
|
||||
@@ -20,7 +20,6 @@ pub struct ProjectPanelSettings {
|
||||
pub folder_icons: bool,
|
||||
pub git_status: bool,
|
||||
pub indent_size: f32,
|
||||
pub indent_guides: bool,
|
||||
pub auto_reveal_entries: bool,
|
||||
pub auto_fold_dirs: bool,
|
||||
pub scrollbar: ScrollbarSettings,
|
||||
@@ -72,10 +71,6 @@ pub struct ProjectPanelSettingsContent {
|
||||
///
|
||||
/// Default: 20
|
||||
pub indent_size: Option<f32>,
|
||||
/// Whether to show indent guides in the project panel.
|
||||
///
|
||||
/// Default: true
|
||||
pub indent_guides: Option<bool>,
|
||||
/// Whether to reveal it in the project panel automatically,
|
||||
/// when a corresponding project entry becomes active.
|
||||
/// Gitignored entries are never auto revealed.
|
||||
|
||||
@@ -299,9 +299,6 @@ message Envelope {
|
||||
GetPermalinkToLineResponse get_permalink_to_line_response = 265;
|
||||
|
||||
FlushBufferedMessages flush_buffered_messages = 267;
|
||||
|
||||
LanguageServerPromptRequest language_server_prompt_request = 268;
|
||||
LanguageServerPromptResponse language_server_prompt_response = 269; // current max
|
||||
}
|
||||
|
||||
reserved 87 to 88;
|
||||
@@ -2531,25 +2528,3 @@ message GetPermalinkToLineResponse {
|
||||
|
||||
message FlushBufferedMessages {}
|
||||
message FlushBufferedMessagesResponse {}
|
||||
|
||||
message LanguageServerPromptRequest {
|
||||
uint64 project_id = 1;
|
||||
|
||||
oneof level {
|
||||
Info info = 2;
|
||||
Warning warning = 3;
|
||||
Critical critical = 4;
|
||||
}
|
||||
|
||||
message Info {}
|
||||
message Warning {}
|
||||
message Critical {}
|
||||
|
||||
string message = 5;
|
||||
repeated string actions = 6;
|
||||
string lsp_name = 7;
|
||||
}
|
||||
|
||||
message LanguageServerPromptResponse {
|
||||
optional uint64 action_response = 1;
|
||||
}
|
||||
|
||||
@@ -373,8 +373,6 @@ messages!(
|
||||
(GetPermalinkToLine, Foreground),
|
||||
(GetPermalinkToLineResponse, Foreground),
|
||||
(FlushBufferedMessages, Foreground),
|
||||
(LanguageServerPromptRequest, Foreground),
|
||||
(LanguageServerPromptResponse, Foreground),
|
||||
);
|
||||
|
||||
request_messages!(
|
||||
@@ -502,7 +500,6 @@ request_messages!(
|
||||
(OpenServerSettings, OpenBufferResponse),
|
||||
(GetPermalinkToLine, GetPermalinkToLineResponse),
|
||||
(FlushBufferedMessages, Ack),
|
||||
(LanguageServerPromptRequest, LanguageServerPromptResponse),
|
||||
);
|
||||
|
||||
entity_messages!(
|
||||
@@ -580,7 +577,6 @@ entity_messages!(
|
||||
HideToast,
|
||||
OpenServerSettings,
|
||||
GetPermalinkToLine,
|
||||
LanguageServerPromptRequest
|
||||
);
|
||||
|
||||
entity_messages!(
|
||||
@@ -630,12 +626,10 @@ impl From<Nonce> for u128 {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub const MAX_WORKTREE_UPDATE_MAX_CHUNK_SIZE: usize = 2;
|
||||
#[cfg(not(any(test, feature = "test-support")))]
|
||||
pub const MAX_WORKTREE_UPDATE_MAX_CHUNK_SIZE: usize = 256;
|
||||
|
||||
pub fn split_worktree_update(mut message: UpdateWorktree) -> impl Iterator<Item = UpdateWorktree> {
|
||||
pub fn split_worktree_update(
|
||||
mut message: UpdateWorktree,
|
||||
max_chunk_size: usize,
|
||||
) -> impl Iterator<Item = UpdateWorktree> {
|
||||
let mut done_files = false;
|
||||
|
||||
let mut repository_map = message
|
||||
@@ -649,19 +643,13 @@ pub fn split_worktree_update(mut message: UpdateWorktree) -> impl Iterator<Item
|
||||
return None;
|
||||
}
|
||||
|
||||
let updated_entries_chunk_size = cmp::min(
|
||||
message.updated_entries.len(),
|
||||
MAX_WORKTREE_UPDATE_MAX_CHUNK_SIZE,
|
||||
);
|
||||
let updated_entries_chunk_size = cmp::min(message.updated_entries.len(), max_chunk_size);
|
||||
let updated_entries: Vec<_> = message
|
||||
.updated_entries
|
||||
.drain(..updated_entries_chunk_size)
|
||||
.collect();
|
||||
|
||||
let removed_entries_chunk_size = cmp::min(
|
||||
message.removed_entries.len(),
|
||||
MAX_WORKTREE_UPDATE_MAX_CHUNK_SIZE,
|
||||
);
|
||||
let removed_entries_chunk_size = cmp::min(message.removed_entries.len(), max_chunk_size);
|
||||
let removed_entries = message
|
||||
.removed_entries
|
||||
.drain(..removed_entries_chunk_size)
|
||||
|
||||
@@ -215,94 +215,93 @@ impl ProjectPicker {
|
||||
connection.port,
|
||||
&connection.username,
|
||||
);
|
||||
let _path_task = cx
|
||||
.spawn({
|
||||
let workspace = workspace.clone();
|
||||
move |this, mut cx| async move {
|
||||
let Ok(Some(paths)) = rx.await else {
|
||||
workspace
|
||||
.update(&mut cx, |workspace, cx| {
|
||||
let weak = cx.view().downgrade();
|
||||
workspace
|
||||
.toggle_modal(cx, |cx| RemoteServerProjects::new(cx, weak));
|
||||
})
|
||||
.log_err()?;
|
||||
return None;
|
||||
};
|
||||
|
||||
let app_state = workspace
|
||||
.update(&mut cx, |workspace, _| workspace.app_state().clone())
|
||||
.ok()?;
|
||||
let options = cx
|
||||
.update(|cx| (app_state.build_window_options)(None, cx))
|
||||
.log_err()?;
|
||||
|
||||
cx.open_window(options, |cx| {
|
||||
cx.activate_window();
|
||||
|
||||
let fs = app_state.fs.clone();
|
||||
update_settings_file::<SshSettings>(fs, cx, {
|
||||
let paths = paths
|
||||
.iter()
|
||||
.map(|path| path.to_string_lossy().to_string())
|
||||
.collect();
|
||||
move |setting, _| {
|
||||
if let Some(server) = setting
|
||||
.ssh_connections
|
||||
.as_mut()
|
||||
.and_then(|connections| connections.get_mut(ix))
|
||||
{
|
||||
server.projects.push(SshProject { paths })
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let tasks = paths
|
||||
.into_iter()
|
||||
.map(|path| {
|
||||
project.update(cx, |project, cx| {
|
||||
project.find_or_create_worktree(&path, true, cx)
|
||||
cx.new_view(|cx| {
|
||||
let _path_task = cx
|
||||
.spawn({
|
||||
let workspace = workspace.clone();
|
||||
move |_, mut cx| async move {
|
||||
let Ok(Some(paths)) = rx.await else {
|
||||
workspace
|
||||
.update(&mut cx, |workspace, cx| {
|
||||
let weak = cx.view().downgrade();
|
||||
workspace
|
||||
.toggle_modal(cx, |cx| RemoteServerProjects::new(cx, weak));
|
||||
})
|
||||
.log_err()?;
|
||||
return None;
|
||||
};
|
||||
|
||||
let app_state = workspace
|
||||
.update(&mut cx, |workspace, _| workspace.app_state().clone())
|
||||
.ok()?;
|
||||
let options = cx
|
||||
.update(|cx| (app_state.build_window_options)(None, cx))
|
||||
.log_err()?;
|
||||
|
||||
cx.open_window(options, |cx| {
|
||||
cx.activate_window();
|
||||
|
||||
let fs = app_state.fs.clone();
|
||||
update_settings_file::<SshSettings>(fs, cx, {
|
||||
let paths = paths
|
||||
.iter()
|
||||
.map(|path| path.to_string_lossy().to_string())
|
||||
.collect();
|
||||
move |setting, _| {
|
||||
if let Some(server) = setting
|
||||
.ssh_connections
|
||||
.as_mut()
|
||||
.and_then(|connections| connections.get_mut(ix))
|
||||
{
|
||||
server.projects.push(SshProject { paths })
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let tasks = paths
|
||||
.into_iter()
|
||||
.map(|path| {
|
||||
project.update(cx, |project, cx| {
|
||||
project.find_or_create_worktree(&path, true, cx)
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
cx.spawn(|_| async move {
|
||||
for task in tasks {
|
||||
task.await?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.detach_and_prompt_err(
|
||||
"Failed to open path",
|
||||
cx,
|
||||
|_, _| None,
|
||||
);
|
||||
|
||||
cx.new_view(|cx| {
|
||||
let workspace =
|
||||
Workspace::new(None, project.clone(), app_state.clone(), cx);
|
||||
|
||||
workspace
|
||||
.client()
|
||||
.telemetry()
|
||||
.report_app_event("create ssh project".to_string());
|
||||
|
||||
workspace
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
cx.spawn(|_| async move {
|
||||
for task in tasks {
|
||||
task.await?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.detach_and_prompt_err(
|
||||
"Failed to open path",
|
||||
cx,
|
||||
|_, _| None,
|
||||
);
|
||||
.log_err();
|
||||
Some(())
|
||||
}
|
||||
})
|
||||
.shared();
|
||||
|
||||
cx.new_view(|cx| {
|
||||
let workspace =
|
||||
Workspace::new(None, project.clone(), app_state.clone(), cx);
|
||||
|
||||
workspace
|
||||
.client()
|
||||
.telemetry()
|
||||
.report_app_event("create ssh project".to_string());
|
||||
|
||||
workspace
|
||||
})
|
||||
})
|
||||
.log_err();
|
||||
this.update(&mut cx, |_, cx| {
|
||||
cx.emit(DismissEvent);
|
||||
})
|
||||
.ok();
|
||||
Some(())
|
||||
}
|
||||
})
|
||||
.shared();
|
||||
cx.new_view(|_| Self {
|
||||
_path_task,
|
||||
picker,
|
||||
connection_string,
|
||||
nickname,
|
||||
Self {
|
||||
_path_task,
|
||||
picker,
|
||||
connection_string,
|
||||
nickname,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -313,7 +312,6 @@ impl gpui::Render for ProjectPicker {
|
||||
.child(
|
||||
SshConnectionHeader {
|
||||
connection_string: self.connection_string.clone(),
|
||||
paths: Default::default(),
|
||||
nickname: self.nickname.clone(),
|
||||
}
|
||||
.render(cx),
|
||||
@@ -373,12 +371,23 @@ impl RemoteServerProjects {
|
||||
}
|
||||
}
|
||||
|
||||
fn scroll_to_selected(&self, _: &mut ViewContext<Self>) {
|
||||
if let Mode::Default(scroll_state) = &self.mode {
|
||||
if let ui::ScrollableHandle::NonUniform(scroll_handle) = scroll_state.scroll_handle() {
|
||||
if let Some(active_item) = self.selectable_items.active_item {
|
||||
scroll_handle.scroll_to_item(active_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn next_item(&mut self, _: &menu::SelectNext, cx: &mut ViewContext<Self>) {
|
||||
if !matches!(self.mode, Mode::Default(_) | Mode::ViewServerOptions(_, _)) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.selectable_items.next(cx);
|
||||
cx.notify();
|
||||
self.scroll_to_selected(cx);
|
||||
}
|
||||
|
||||
fn prev_item(&mut self, _: &menu::SelectPrev, cx: &mut ViewContext<Self>) {
|
||||
@@ -386,6 +395,8 @@ impl RemoteServerProjects {
|
||||
return;
|
||||
}
|
||||
self.selectable_items.prev(cx);
|
||||
cx.notify();
|
||||
self.scroll_to_selected(cx);
|
||||
}
|
||||
|
||||
pub fn project_picker(
|
||||
@@ -508,7 +519,7 @@ impl RemoteServerProjects {
|
||||
workspace.update(cx, |_, cx| {
|
||||
cx.defer(move |workspace, cx| {
|
||||
workspace.toggle_modal(cx, |cx| {
|
||||
SshConnectionModal::new(&connection_options, Vec::new(), nickname, cx)
|
||||
SshConnectionModal::new(&connection_options, nickname, cx)
|
||||
});
|
||||
let prompt = workspace
|
||||
.active_modal::<SshConnectionModal>(cx)
|
||||
@@ -660,12 +671,11 @@ impl RemoteServerProjects {
|
||||
.px_3()
|
||||
.gap_1()
|
||||
.overflow_hidden()
|
||||
.whitespace_nowrap()
|
||||
.child(
|
||||
div().max_w_96().overflow_hidden().text_ellipsis().child(
|
||||
Label::new(main_label)
|
||||
.size(LabelSize::Small)
|
||||
.color(Color::Muted),
|
||||
),
|
||||
Label::new(main_label)
|
||||
.size(LabelSize::Small)
|
||||
.color(Color::Muted),
|
||||
)
|
||||
.children(
|
||||
aux_label.map(|label| {
|
||||
@@ -757,7 +767,7 @@ impl RemoteServerProjects {
|
||||
};
|
||||
let project = project.clone();
|
||||
let server = server.clone();
|
||||
cx.spawn(|remote_server_projects, mut cx| async move {
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
let nickname = server.nickname.clone();
|
||||
let result = open_ssh_project(
|
||||
server.into(),
|
||||
@@ -778,10 +788,6 @@ impl RemoteServerProjects {
|
||||
)
|
||||
.await
|
||||
.ok();
|
||||
} else {
|
||||
remote_server_projects
|
||||
.update(&mut cx, |_, cx| cx.emit(DismissEvent))
|
||||
.ok();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
@@ -805,19 +811,14 @@ impl RemoteServerProjects {
|
||||
.child(Label::new(project.paths.join(", ")))
|
||||
.on_click(cx.listener(move |this, _, cx| callback(this, cx)))
|
||||
.end_hover_slot::<AnyElement>(Some(
|
||||
div()
|
||||
.mr_2()
|
||||
.child(
|
||||
// Right-margin to offset it from the Scrollbar
|
||||
IconButton::new("remove-remote-project", IconName::TrashAlt)
|
||||
.icon_size(IconSize::Small)
|
||||
.shape(IconButtonShape::Square)
|
||||
.size(ButtonSize::Large)
|
||||
.tooltip(|cx| Tooltip::text("Delete Remote Project", cx))
|
||||
.on_click(cx.listener(move |this, _, cx| {
|
||||
this.delete_ssh_project(server_ix, ix, cx)
|
||||
})),
|
||||
IconButton::new("remove-remote-project", IconName::TrashAlt)
|
||||
.icon_size(IconSize::Small)
|
||||
.shape(IconButtonShape::Square)
|
||||
.on_click(
|
||||
cx.listener(move |this, _, cx| this.delete_ssh_project(server_ix, ix, cx)),
|
||||
)
|
||||
.size(ButtonSize::Large)
|
||||
.tooltip(|cx| Tooltip::text("Delete Remote Project", cx))
|
||||
.into_any_element(),
|
||||
))
|
||||
}
|
||||
@@ -964,7 +965,6 @@ impl RemoteServerProjects {
|
||||
.child(
|
||||
SshConnectionHeader {
|
||||
connection_string: connection_string.clone(),
|
||||
paths: Default::default(),
|
||||
nickname: connection.nickname.clone(),
|
||||
}
|
||||
.render(cx),
|
||||
@@ -1152,7 +1152,6 @@ impl RemoteServerProjects {
|
||||
.child(
|
||||
SshConnectionHeader {
|
||||
connection_string,
|
||||
paths: Default::default(),
|
||||
nickname: connection.nickname.clone(),
|
||||
}
|
||||
.render(cx),
|
||||
@@ -1209,6 +1208,7 @@ impl RemoteServerProjects {
|
||||
List::new()
|
||||
.empty_message(
|
||||
v_flex()
|
||||
// .child(ListSeparator)
|
||||
.child(div().px_3().child(
|
||||
Label::new("No remote servers registered yet.").color(Color::Muted),
|
||||
))
|
||||
@@ -1232,6 +1232,7 @@ impl RemoteServerProjects {
|
||||
Section::new().padded(false).child(
|
||||
v_flex()
|
||||
.min_h(rems(20.))
|
||||
.group("remote-projects-section")
|
||||
.size_full()
|
||||
.relative()
|
||||
.child(ListSeparator)
|
||||
@@ -1253,6 +1254,7 @@ impl RemoteServerProjects {
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.visible_on_hover("remote-projects-section")
|
||||
.occlude()
|
||||
.h_full()
|
||||
.absolute()
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
use std::{path::PathBuf, sync::Arc, time::Duration};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use anyhow::Result;
|
||||
use auto_update::AutoUpdater;
|
||||
use editor::Editor;
|
||||
use futures::channel::oneshot;
|
||||
use gpui::{
|
||||
percentage, Animation, AnimationExt, AnyWindowHandle, AsyncAppContext, DismissEvent,
|
||||
EventEmitter, FocusableView, ParentElement as _, PromptLevel, Render, SemanticVersion,
|
||||
SharedString, Task, TextStyleRefinement, Transformation, View, WeakView,
|
||||
SharedString, Task, TextStyleRefinement, Transformation, View,
|
||||
};
|
||||
use gpui::{AppContext, Model};
|
||||
|
||||
use language::CursorShape;
|
||||
use markdown::{Markdown, MarkdownStyle};
|
||||
use release_channel::{AppVersion, ReleaseChannel};
|
||||
use remote::ssh_session::ServerBinary;
|
||||
use remote::{SshConnectionOptions, SshPlatform, SshRemoteClient};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -26,15 +25,9 @@ use ui::{
|
||||
};
|
||||
use workspace::{AppState, ModalView, Workspace};
|
||||
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct RemoteServerSettings {
|
||||
pub download_on_host: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SshSettings {
|
||||
pub ssh_connections: Option<Vec<SshConnection>>,
|
||||
pub remote_server: Option<RemoteServerSettings>,
|
||||
}
|
||||
|
||||
impl SshSettings {
|
||||
@@ -114,7 +107,6 @@ pub struct SshProject {
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct RemoteSettingsContent {
|
||||
pub ssh_connections: Option<Vec<SshConnection>>,
|
||||
pub remote_server: Option<RemoteServerSettings>,
|
||||
}
|
||||
|
||||
impl Settings for SshSettings {
|
||||
@@ -136,17 +128,8 @@ pub struct SshPrompt {
|
||||
editor: View<Editor>,
|
||||
}
|
||||
|
||||
impl Drop for SshPrompt {
|
||||
fn drop(&mut self) {
|
||||
if let Some(cancel) = self.cancellation.take() {
|
||||
cancel.send(()).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SshConnectionModal {
|
||||
pub(crate) prompt: View<SshPrompt>,
|
||||
paths: Vec<PathBuf>,
|
||||
finished: bool,
|
||||
}
|
||||
|
||||
@@ -157,7 +140,6 @@ impl SshPrompt {
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
let connection_string = connection_options.connection_string().into();
|
||||
|
||||
Self {
|
||||
connection_string,
|
||||
nickname,
|
||||
@@ -275,14 +257,12 @@ impl Render for SshPrompt {
|
||||
impl SshConnectionModal {
|
||||
pub(crate) fn new(
|
||||
connection_options: &SshConnectionOptions,
|
||||
paths: Vec<PathBuf>,
|
||||
nickname: Option<SharedString>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
Self {
|
||||
prompt: cx.new_view(|cx| SshPrompt::new(connection_options, nickname, cx)),
|
||||
finished: false,
|
||||
paths,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,7 +288,6 @@ impl SshConnectionModal {
|
||||
|
||||
pub(crate) struct SshConnectionHeader {
|
||||
pub(crate) connection_string: SharedString,
|
||||
pub(crate) paths: Vec<PathBuf>,
|
||||
pub(crate) nickname: Option<SharedString>,
|
||||
}
|
||||
|
||||
@@ -336,26 +315,8 @@ impl RenderOnce for SshConnectionHeader {
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.overflow_x_hidden()
|
||||
.child(
|
||||
div()
|
||||
.max_w_96()
|
||||
.overflow_x_hidden()
|
||||
.text_ellipsis()
|
||||
.child(Headline::new(main_label).size(HeadlineSize::XSmall)),
|
||||
)
|
||||
.children(
|
||||
meta_label.map(|label| {
|
||||
Label::new(label).color(Color::Muted).size(LabelSize::Small)
|
||||
}),
|
||||
)
|
||||
.child(div().overflow_x_hidden().text_ellipsis().children(
|
||||
self.paths.into_iter().map(|path| {
|
||||
Label::new(path.to_string_lossy().to_string())
|
||||
.size(LabelSize::Small)
|
||||
.color(Color::Muted)
|
||||
}),
|
||||
)),
|
||||
.child(Headline::new(main_label).size(HeadlineSize::XSmall))
|
||||
.children(meta_label.map(|label| Label::new(label).color(Color::Muted))),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -379,7 +340,6 @@ impl Render for SshConnectionModal {
|
||||
.on_action(cx.listener(Self::confirm))
|
||||
.child(
|
||||
SshConnectionHeader {
|
||||
paths: self.paths.clone(),
|
||||
connection_string,
|
||||
nickname,
|
||||
}
|
||||
@@ -418,7 +378,7 @@ impl ModalView for SshConnectionModal {
|
||||
#[derive(Clone)]
|
||||
pub struct SshClientDelegate {
|
||||
window: AnyWindowHandle,
|
||||
ui: WeakView<SshPrompt>,
|
||||
ui: View<SshPrompt>,
|
||||
known_password: Option<String>,
|
||||
}
|
||||
|
||||
@@ -452,7 +412,7 @@ impl remote::SshClientDelegate for SshClientDelegate {
|
||||
&self,
|
||||
platform: SshPlatform,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> oneshot::Receiver<Result<(ServerBinary, SemanticVersion)>> {
|
||||
) -> oneshot::Receiver<Result<(PathBuf, SemanticVersion)>> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let this = self.clone();
|
||||
cx.spawn(|mut cx| async move {
|
||||
@@ -493,18 +453,10 @@ impl SshClientDelegate {
|
||||
&self,
|
||||
platform: SshPlatform,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Result<(ServerBinary, SemanticVersion)> {
|
||||
let (version, release_channel, download_binary_on_host) = cx.update(|cx| {
|
||||
let version = AppVersion::global(cx);
|
||||
let channel = ReleaseChannel::global(cx);
|
||||
|
||||
let ssh_settings = SshSettings::get_global(cx);
|
||||
let download_binary_on_host = ssh_settings
|
||||
.remote_server
|
||||
.as_ref()
|
||||
.and_then(|server| server.download_on_host)
|
||||
.unwrap_or(false);
|
||||
(version, channel, download_binary_on_host)
|
||||
) -> Result<(PathBuf, SemanticVersion)> {
|
||||
let (version, release_channel) = cx.update(|cx| {
|
||||
let global = AppVersion::global(cx);
|
||||
(global, ReleaseChannel::global(cx))
|
||||
})?;
|
||||
|
||||
// In dev mode, build the remote server binary from source
|
||||
@@ -512,55 +464,29 @@ impl SshClientDelegate {
|
||||
if release_channel == ReleaseChannel::Dev {
|
||||
let result = self.build_local(cx, platform, version).await?;
|
||||
// Fall through to a remote binary if we're not able to compile a local binary
|
||||
if let Some((path, version)) = result {
|
||||
return Ok((ServerBinary::LocalBinary(path), version));
|
||||
if let Some(result) = result {
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
|
||||
if download_binary_on_host {
|
||||
let (request_url, request_body) = AutoUpdater::get_latest_remote_server_release_url(
|
||||
self.update_status(Some("checking for latest version of remote server"), cx);
|
||||
let binary_path = AutoUpdater::get_latest_remote_server_release(
|
||||
platform.os,
|
||||
platform.arch,
|
||||
release_channel,
|
||||
cx,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
anyhow::anyhow!(
|
||||
"failed to download remote server binary (os: {}, arch: {}): {}",
|
||||
platform.os,
|
||||
platform.arch,
|
||||
release_channel,
|
||||
cx,
|
||||
e
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
anyhow!(
|
||||
"Failed to get remote server binary download url (os: {}, arch: {}): {}",
|
||||
platform.os,
|
||||
platform.arch,
|
||||
e
|
||||
)
|
||||
})?;
|
||||
})?;
|
||||
|
||||
Ok((
|
||||
ServerBinary::ReleaseUrl {
|
||||
url: request_url,
|
||||
body: request_body,
|
||||
},
|
||||
version,
|
||||
))
|
||||
} else {
|
||||
self.update_status(Some("Checking for latest version of remote server"), cx);
|
||||
let binary_path = AutoUpdater::get_latest_remote_server_release(
|
||||
platform.os,
|
||||
platform.arch,
|
||||
release_channel,
|
||||
cx,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
anyhow!(
|
||||
"Failed to download remote server binary (os: {}, arch: {}): {}",
|
||||
platform.os,
|
||||
platform.arch,
|
||||
e
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok((ServerBinary::LocalBinary(binary_path), version))
|
||||
}
|
||||
Ok((binary_path, version))
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
@@ -568,8 +494,8 @@ impl SshClientDelegate {
|
||||
&self,
|
||||
cx: &mut AsyncAppContext,
|
||||
platform: SshPlatform,
|
||||
version: gpui::SemanticVersion,
|
||||
) -> Result<Option<(PathBuf, gpui::SemanticVersion)>> {
|
||||
version: SemanticVersion,
|
||||
) -> Result<Option<(PathBuf, SemanticVersion)>> {
|
||||
use smol::process::{Command, Stdio};
|
||||
|
||||
async fn run_cmd(command: &mut Command) -> Result<()> {
|
||||
@@ -579,7 +505,7 @@ impl SshClientDelegate {
|
||||
.output()
|
||||
.await?;
|
||||
if !output.status.success() {
|
||||
Err(anyhow!("Failed to run command: {:?}", command))?;
|
||||
Err(anyhow::anyhow!("failed to run command: {:?}", command))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -688,7 +614,7 @@ pub fn connect_over_ssh(
|
||||
rx,
|
||||
Arc::new(SshClientDelegate {
|
||||
window,
|
||||
ui: ui.downgrade(),
|
||||
ui,
|
||||
known_password,
|
||||
}),
|
||||
cx,
|
||||
@@ -726,11 +652,10 @@ pub async fn open_ssh_project(
|
||||
let delegate = window.update(cx, {
|
||||
let connection_options = connection_options.clone();
|
||||
let nickname = nickname.clone();
|
||||
let paths = paths.clone();
|
||||
move |workspace, cx| {
|
||||
cx.activate_window();
|
||||
workspace.toggle_modal(cx, |cx| {
|
||||
SshConnectionModal::new(&connection_options, paths, nickname.clone(), cx)
|
||||
SshConnectionModal::new(&connection_options, nickname.clone(), cx)
|
||||
});
|
||||
|
||||
let ui = workspace
|
||||
@@ -745,7 +670,7 @@ pub async fn open_ssh_project(
|
||||
|
||||
Some(Arc::new(SshClientDelegate {
|
||||
window: cx.window_handle(),
|
||||
ui: ui.downgrade(),
|
||||
ui,
|
||||
known_password: connection_options.password.clone(),
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ use std::{
|
||||
atomic::{AtomicU32, Ordering::SeqCst},
|
||||
Arc, Weak,
|
||||
},
|
||||
time::{Duration, Instant, SystemTime, UNIX_EPOCH},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use tempfile::TempDir;
|
||||
use util::ResultExt;
|
||||
@@ -216,11 +216,6 @@ impl SshPlatform {
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ServerBinary {
|
||||
LocalBinary(PathBuf),
|
||||
ReleaseUrl { url: String, body: String },
|
||||
}
|
||||
|
||||
pub trait SshClientDelegate: Send + Sync {
|
||||
fn ask_password(
|
||||
&self,
|
||||
@@ -236,7 +231,7 @@ pub trait SshClientDelegate: Send + Sync {
|
||||
&self,
|
||||
platform: SshPlatform,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> oneshot::Receiver<Result<(ServerBinary, SemanticVersion)>>;
|
||||
) -> oneshot::Receiver<Result<(PathBuf, SemanticVersion)>>;
|
||||
fn set_status(&self, status: Option<&str>, cx: &mut AsyncAppContext);
|
||||
}
|
||||
|
||||
@@ -1364,116 +1359,6 @@ impl SshRemoteConnection {
|
||||
dst_path: &Path,
|
||||
platform: SshPlatform,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Result<()> {
|
||||
let lock_file = dst_path.with_extension("lock");
|
||||
let timestamp = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs();
|
||||
let lock_content = timestamp.to_string();
|
||||
|
||||
let lock_stale_age = Duration::from_secs(10 * 60);
|
||||
let max_wait_time = Duration::from_secs(10 * 60);
|
||||
let check_interval = Duration::from_secs(5);
|
||||
let start_time = Instant::now();
|
||||
|
||||
loop {
|
||||
let lock_acquired = self.create_lock_file(&lock_file, &lock_content).await?;
|
||||
if lock_acquired {
|
||||
let result = self
|
||||
.update_server_binary_if_needed(delegate, dst_path, platform, cx)
|
||||
.await;
|
||||
|
||||
self.remove_lock_file(&lock_file).await.ok();
|
||||
|
||||
return result;
|
||||
} else {
|
||||
if let Ok(is_stale) = self.is_lock_stale(&lock_file, &lock_stale_age).await {
|
||||
if is_stale {
|
||||
self.remove_lock_file(&lock_file).await?;
|
||||
continue;
|
||||
} else {
|
||||
if start_time.elapsed() > max_wait_time {
|
||||
return Err(anyhow!("Timeout waiting for lock to be released"));
|
||||
}
|
||||
log::info!(
|
||||
"Found lockfile: {:?}. Will check again in {:?}",
|
||||
lock_file,
|
||||
check_interval
|
||||
);
|
||||
delegate.set_status(
|
||||
Some("Waiting for another Zed instance to finish uploading binary"),
|
||||
cx,
|
||||
);
|
||||
smol::Timer::after(check_interval).await;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// Unable to check lock, assume it's valid and wait
|
||||
if start_time.elapsed() > max_wait_time {
|
||||
return Err(anyhow!("Timeout waiting for lock to be released"));
|
||||
}
|
||||
smol::Timer::after(check_interval).await;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn create_lock_file(&self, lock_file: &Path, content: &str) -> Result<bool> {
|
||||
let parent_dir = lock_file
|
||||
.parent()
|
||||
.ok_or_else(|| anyhow!("Lock file path has no parent directory"))?;
|
||||
|
||||
// Be mindful of the escaping here: we need to make sure that we have quotes
|
||||
// inside the string, so that `sh -c` gets a quoted string passed to it.
|
||||
let script = format!(
|
||||
"\"mkdir -p '{0}' && [ ! -f '{1}' ] && echo '{2}' > '{1}' && echo 'created' || echo 'exists'\"",
|
||||
parent_dir.display(),
|
||||
lock_file.display(),
|
||||
content
|
||||
);
|
||||
|
||||
let output = run_cmd(self.socket.ssh_command("sh").arg("-c").arg(&script))
|
||||
.await
|
||||
.with_context(|| format!("failed to create a lock file at {:?}", lock_file))?;
|
||||
|
||||
Ok(output.trim() == "created")
|
||||
}
|
||||
|
||||
async fn is_lock_stale(&self, lock_file: &Path, max_age: &Duration) -> Result<bool> {
|
||||
let threshold = max_age.as_secs();
|
||||
|
||||
// Be mindful of the escaping here: we need to make sure that we have quotes
|
||||
// inside the string, so that `sh -c` gets a quoted string passed to it.
|
||||
let script = format!(
|
||||
"\"[ -f '{0}' ] && [ $(( $(date +%s) - $(date -r '{0}' +%s) )) -gt {1} ] && echo 'stale' || echo 'recent'\"",
|
||||
lock_file.display(),
|
||||
threshold
|
||||
);
|
||||
|
||||
let output = run_cmd(self.socket.ssh_command("sh").arg("-c").arg(script))
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!("failed to check whether lock file {:?} is stale", lock_file)
|
||||
})?;
|
||||
|
||||
Ok(output.trim() == "stale")
|
||||
}
|
||||
|
||||
async fn remove_lock_file(&self, lock_file: &Path) -> Result<()> {
|
||||
run_cmd(self.socket.ssh_command("rm").arg("-f").arg(lock_file))
|
||||
.await
|
||||
.context("failed to remove lock file")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn update_server_binary_if_needed(
|
||||
&self,
|
||||
delegate: &Arc<dyn SshClientDelegate>,
|
||||
dst_path: &Path,
|
||||
platform: SshPlatform,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Result<()> {
|
||||
if std::env::var("ZED_USE_CACHED_REMOTE_SERVER").is_ok() {
|
||||
if let Ok(installed_version) =
|
||||
@@ -1484,7 +1369,14 @@ impl SshRemoteConnection {
|
||||
}
|
||||
}
|
||||
|
||||
let (binary, version) = delegate.get_server_binary(platform, cx).await??;
|
||||
let mut dst_path_gz = dst_path.to_path_buf();
|
||||
dst_path_gz.set_extension("gz");
|
||||
|
||||
if let Some(parent) = dst_path.parent() {
|
||||
run_cmd(self.socket.ssh_command("mkdir").arg("-p").arg(parent)).await?;
|
||||
}
|
||||
|
||||
let (src_path, version) = delegate.get_server_binary(platform, cx).await??;
|
||||
|
||||
let mut server_binary_exists = false;
|
||||
if !server_binary_exists && cfg!(not(debug_assertions)) {
|
||||
@@ -1494,7 +1386,6 @@ impl SshRemoteConnection {
|
||||
if installed_version.trim() == version.to_string() {
|
||||
server_binary_exists = true;
|
||||
}
|
||||
log::info!("checked remote server binary for version. latest version: {}. remote server version: {}", version.to_string(), installed_version.trim());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1503,82 +1394,9 @@ impl SshRemoteConnection {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
match binary {
|
||||
ServerBinary::LocalBinary(src_path) => {
|
||||
self.upload_local_server_binary(&src_path, dst_path, delegate, cx)
|
||||
.await
|
||||
}
|
||||
ServerBinary::ReleaseUrl { url, body } => {
|
||||
self.download_binary_on_server(&url, &body, dst_path, delegate, cx)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn download_binary_on_server(
|
||||
&self,
|
||||
url: &str,
|
||||
body: &str,
|
||||
dst_path: &Path,
|
||||
delegate: &Arc<dyn SshClientDelegate>,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Result<()> {
|
||||
let mut dst_path_gz = dst_path.to_path_buf();
|
||||
dst_path_gz.set_extension("gz");
|
||||
|
||||
if let Some(parent) = dst_path.parent() {
|
||||
run_cmd(self.socket.ssh_command("mkdir").arg("-p").arg(parent)).await?;
|
||||
}
|
||||
|
||||
delegate.set_status(Some("Downloading remote development server on host"), cx);
|
||||
|
||||
let script = format!(
|
||||
r#"
|
||||
if command -v wget >/dev/null 2>&1; then
|
||||
wget --max-redirect=5 --method=GET --header="Content-Type: application/json" --body-data='{}' '{}' -O '{}' && echo "wget"
|
||||
elif command -v curl >/dev/null 2>&1; then
|
||||
curl -L -X GET -H "Content-Type: application/json" -d '{}' '{}' -o '{}' && echo "curl"
|
||||
else
|
||||
echo "Neither curl nor wget is available" >&2
|
||||
exit 1
|
||||
fi
|
||||
"#,
|
||||
body.replace("'", r#"\'"#),
|
||||
url,
|
||||
dst_path_gz.display(),
|
||||
body.replace("'", r#"\'"#),
|
||||
url,
|
||||
dst_path_gz.display(),
|
||||
);
|
||||
|
||||
let output = run_cmd(self.socket.ssh_command("bash").arg("-c").arg(script))
|
||||
.await
|
||||
.context("Failed to download server binary")?;
|
||||
|
||||
if !output.contains("curl") && !output.contains("wget") {
|
||||
return Err(anyhow!("Failed to download server binary: {}", output));
|
||||
}
|
||||
|
||||
self.extract_server_binary(dst_path, &dst_path_gz, delegate, cx)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn upload_local_server_binary(
|
||||
&self,
|
||||
src_path: &Path,
|
||||
dst_path: &Path,
|
||||
delegate: &Arc<dyn SshClientDelegate>,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Result<()> {
|
||||
let mut dst_path_gz = dst_path.to_path_buf();
|
||||
dst_path_gz.set_extension("gz");
|
||||
|
||||
if let Some(parent) = dst_path.parent() {
|
||||
run_cmd(self.socket.ssh_command("mkdir").arg("-p").arg(parent)).await?;
|
||||
}
|
||||
|
||||
let src_stat = fs::metadata(&src_path).await?;
|
||||
let size = src_stat.len();
|
||||
let server_mode = 0o755;
|
||||
|
||||
let t0 = Instant::now();
|
||||
delegate.set_status(Some("Uploading remote development server"), cx);
|
||||
@@ -1588,17 +1406,6 @@ impl SshRemoteConnection {
|
||||
.context("failed to upload server binary")?;
|
||||
log::info!("uploaded remote development server in {:?}", t0.elapsed());
|
||||
|
||||
self.extract_server_binary(dst_path, &dst_path_gz, delegate, cx)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn extract_server_binary(
|
||||
&self,
|
||||
dst_path: &Path,
|
||||
dst_path_gz: &Path,
|
||||
delegate: &Arc<dyn SshClientDelegate>,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Result<()> {
|
||||
delegate.set_status(Some("Extracting remote development server"), cx);
|
||||
run_cmd(
|
||||
self.socket
|
||||
@@ -1608,7 +1415,6 @@ impl SshRemoteConnection {
|
||||
)
|
||||
.await?;
|
||||
|
||||
let server_mode = 0o755;
|
||||
delegate.set_status(Some("Marking remote development server executable"), cx);
|
||||
run_cmd(
|
||||
self.socket
|
||||
@@ -1978,8 +1784,7 @@ mod fake {
|
||||
use rpc::proto::Envelope;
|
||||
|
||||
use super::{
|
||||
ChannelClient, ServerBinary, SshClientDelegate, SshConnectionOptions, SshPlatform,
|
||||
SshRemoteProcess,
|
||||
ChannelClient, SshClientDelegate, SshConnectionOptions, SshPlatform, SshRemoteProcess,
|
||||
};
|
||||
|
||||
pub(super) struct SshRemoteConnection {
|
||||
@@ -2095,10 +1900,9 @@ mod fake {
|
||||
&self,
|
||||
_: SshPlatform,
|
||||
_: &mut AsyncAppContext,
|
||||
) -> oneshot::Receiver<Result<(ServerBinary, SemanticVersion)>> {
|
||||
) -> oneshot::Receiver<Result<(PathBuf, SemanticVersion)>> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn set_status(&self, _: Option<&str>, _: &mut AsyncAppContext) {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use fs::Fs;
|
||||
use gpui::{AppContext, AsyncAppContext, Context, Model, ModelContext, PromptLevel};
|
||||
use gpui::{AppContext, AsyncAppContext, Context, Model, ModelContext};
|
||||
use http_client::HttpClient;
|
||||
use language::{proto::serialize_operation, Buffer, BufferEvent, LanguageRegistry};
|
||||
use node_runtime::NodeRuntime;
|
||||
@@ -206,7 +206,7 @@ impl HeadlessProject {
|
||||
&mut self,
|
||||
_lsp_store: Model<LspStore>,
|
||||
event: &LspStoreEvent,
|
||||
cx: &mut ModelContext<Self>,
|
||||
_cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
match event {
|
||||
LspStoreEvent::LanguageServerUpdate {
|
||||
@@ -240,29 +240,6 @@ impl HeadlessProject {
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
LspStoreEvent::LanguageServerPrompt(prompt) => {
|
||||
let request = self.session.request(proto::LanguageServerPromptRequest {
|
||||
project_id: SSH_PROJECT_ID,
|
||||
actions: prompt
|
||||
.actions
|
||||
.iter()
|
||||
.map(|action| action.title.to_string())
|
||||
.collect(),
|
||||
level: Some(prompt_to_proto(&prompt)),
|
||||
lsp_name: prompt.lsp_name.clone(),
|
||||
message: prompt.message.clone(),
|
||||
});
|
||||
let prompt = prompt.clone();
|
||||
cx.background_executor()
|
||||
.spawn(async move {
|
||||
let response = request.await?;
|
||||
if let Some(action_response) = response.action_response {
|
||||
prompt.respond(action_response as usize).await;
|
||||
}
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -563,19 +540,3 @@ impl HeadlessProject {
|
||||
Ok(proto::Ack {})
|
||||
}
|
||||
}
|
||||
|
||||
fn prompt_to_proto(
|
||||
prompt: &project::LanguageServerPromptRequest,
|
||||
) -> proto::language_server_prompt_request::Level {
|
||||
match prompt.level {
|
||||
PromptLevel::Info => proto::language_server_prompt_request::Level::Info(
|
||||
proto::language_server_prompt_request::Info {},
|
||||
),
|
||||
PromptLevel::Warning => proto::language_server_prompt_request::Level::Warning(
|
||||
proto::language_server_prompt_request::Warning {},
|
||||
),
|
||||
PromptLevel::Critical => proto::language_server_prompt_request::Level::Critical(
|
||||
proto::language_server_prompt_request::Critical {},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ fn main() {
|
||||
}
|
||||
},
|
||||
Some(Commands::Version) => {
|
||||
println!("{}", env!("ZED_PKG_VERSION"));
|
||||
eprintln!("{}", env!("ZED_PKG_VERSION"));
|
||||
std::process::exit(0);
|
||||
}
|
||||
None => {
|
||||
|
||||
@@ -210,7 +210,7 @@ async fn test_remote_settings(cx: &mut TestAppContext, server_cx: &mut TestAppCo
|
||||
AllLanguageSettings::get_global(cx)
|
||||
.language(None, Some(&"Rust".into()), cx)
|
||||
.language_servers,
|
||||
["..."] // local settings are ignored
|
||||
["from-local-settings".to_string()]
|
||||
)
|
||||
});
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ use client::telemetry::Telemetry;
|
||||
use collections::{HashMap, HashSet};
|
||||
use editor::{
|
||||
display_map::{
|
||||
BlockContext, BlockDisposition, BlockId, BlockProperties, BlockStyle, CustomBlockId,
|
||||
BlockContext, BlockId, BlockPlacement, BlockProperties, BlockStyle, CustomBlockId,
|
||||
RenderBlock,
|
||||
},
|
||||
scroll::Autoscroll,
|
||||
@@ -90,12 +90,11 @@ impl EditorBlock {
|
||||
|
||||
let invalidation_anchor = buffer.read(cx).read(cx).anchor_before(next_row_start);
|
||||
let block = BlockProperties {
|
||||
position: code_range.end,
|
||||
placement: BlockPlacement::Below(code_range.end),
|
||||
// Take up at least one height for status, allow the editor to determine the real height based on the content from render
|
||||
height: 1,
|
||||
style: BlockStyle::Sticky,
|
||||
render: Self::create_output_area_renderer(execution_view.clone(), on_close.clone()),
|
||||
disposition: BlockDisposition::Below,
|
||||
priority: 0,
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ use crate::{
|
||||
};
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use collections::Bound;
|
||||
use feature_flags::FeatureFlagAppExt;
|
||||
use fs::Fs;
|
||||
use futures::stream::StreamExt;
|
||||
use futures_batch::ChunksTimeoutStreamExt;
|
||||
@@ -16,7 +15,6 @@ use log;
|
||||
use project::{Entry, UpdatedEntriesSet, Worktree};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use smol::channel;
|
||||
use smol::future::FutureExt;
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
future::Future,
|
||||
@@ -67,10 +65,6 @@ impl EmbeddingIndex {
|
||||
&self,
|
||||
cx: &AppContext,
|
||||
) -> impl Future<Output = Result<()>> {
|
||||
if !cx.is_staff() {
|
||||
return async move { Ok(()) }.boxed();
|
||||
}
|
||||
|
||||
let worktree = self.worktree.read(cx).snapshot();
|
||||
let worktree_abs_path = worktree.abs_path().clone();
|
||||
let scan = self.scan_entries(worktree, cx);
|
||||
@@ -81,7 +75,6 @@ impl EmbeddingIndex {
|
||||
futures::try_join!(scan.task, chunk.task, embed.task, persist)?;
|
||||
Ok(())
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
pub fn index_updated_entries(
|
||||
@@ -89,10 +82,6 @@ impl EmbeddingIndex {
|
||||
updated_entries: UpdatedEntriesSet,
|
||||
cx: &AppContext,
|
||||
) -> impl Future<Output = Result<()>> {
|
||||
if !cx.is_staff() {
|
||||
return async move { Ok(()) }.boxed();
|
||||
}
|
||||
|
||||
let worktree = self.worktree.read(cx).snapshot();
|
||||
let worktree_abs_path = worktree.abs_path().clone();
|
||||
let scan = self.scan_updated_entries(worktree, updated_entries.clone(), cx);
|
||||
@@ -103,7 +92,6 @@ impl EmbeddingIndex {
|
||||
futures::try_join!(scan.task, chunk.task, embed.task, persist)?;
|
||||
Ok(())
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn scan_entries(&self, worktree: Snapshot, cx: &AppContext) -> ScanEntries {
|
||||
|
||||
@@ -336,11 +336,6 @@ mod tests {
|
||||
|
||||
init_test(cx);
|
||||
|
||||
cx.update(|cx| {
|
||||
// This functionality is staff-flagged.
|
||||
cx.update_flags(true, vec![]);
|
||||
});
|
||||
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
|
||||
let mut semantic_index = SemanticDb::new(
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
use std::fmt::format;
|
||||
|
||||
use gpui::{
|
||||
colors, div, prelude::*, uniform_list, DefaultColor, DefaultThemeAppearance, Hsla, Render,
|
||||
View, ViewContext, WindowContext,
|
||||
};
|
||||
use story::Story;
|
||||
use strum::IntoEnumIterator;
|
||||
use ui::{
|
||||
h_flex, px, v_flex, AbsoluteLength, ActiveTheme, Color, DefiniteLength, Label, LabelCommon,
|
||||
};
|
||||
|
||||
const LENGTH: usize = 100;
|
||||
|
||||
pub struct IndentGuidesStory {
|
||||
depths: Vec<usize>,
|
||||
}
|
||||
|
||||
impl IndentGuidesStory {
|
||||
pub fn view(cx: &mut WindowContext) -> View<Self> {
|
||||
let mut depths = Vec::new();
|
||||
depths.push(0);
|
||||
depths.push(1);
|
||||
depths.push(2);
|
||||
for _ in 0..LENGTH - 6 {
|
||||
depths.push(3);
|
||||
}
|
||||
depths.push(2);
|
||||
depths.push(1);
|
||||
depths.push(0);
|
||||
|
||||
cx.new_view(|_cx| Self { depths })
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for IndentGuidesStory {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
Story::container()
|
||||
.child(Story::title("Indent guides"))
|
||||
.child(
|
||||
v_flex().size_full().child(
|
||||
uniform_list(
|
||||
cx.view().clone(),
|
||||
"some-list",
|
||||
self.depths.len(),
|
||||
|this, range, cx| {
|
||||
this.depths
|
||||
.iter()
|
||||
.enumerate()
|
||||
.skip(range.start)
|
||||
.take(range.end - range.start)
|
||||
.map(|(i, depth)| {
|
||||
div()
|
||||
.pl(DefiniteLength::Absolute(AbsoluteLength::Pixels(px(
|
||||
16. * (*depth as f32),
|
||||
))))
|
||||
.child(Label::new(format!("Item {}", i)).color(Color::Info))
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
)
|
||||
.with_sizing_behavior(gpui::ListSizingBehavior::Infer)
|
||||
.with_decoration(ui::indent_guides(
|
||||
cx.view().clone(),
|
||||
px(16.),
|
||||
ui::IndentGuideColors {
|
||||
default: Color::Info.color(cx),
|
||||
hovered: Color::Accent.color(cx),
|
||||
active: Color::Accent.color(cx),
|
||||
},
|
||||
|this, range, cx| {
|
||||
this.depths
|
||||
.iter()
|
||||
.skip(range.start)
|
||||
.take(range.end - range.start)
|
||||
.cloned()
|
||||
.collect()
|
||||
},
|
||||
)),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -147,14 +147,6 @@ impl Supermaven {
|
||||
updates_tx,
|
||||
},
|
||||
);
|
||||
// ensure the states map is max 1000 elements
|
||||
if agent.states.len() > 1000 {
|
||||
// state id is monotonic so it's sufficient to remove the first element
|
||||
agent
|
||||
.states
|
||||
.remove(&agent.states.keys().next().unwrap().clone());
|
||||
}
|
||||
|
||||
let _ = agent
|
||||
.outgoing_tx
|
||||
.unbounded_send(OutboundMessage::StateUpdate(StateUpdateMessage {
|
||||
|
||||
@@ -269,7 +269,5 @@ pub enum Shell {
|
||||
program: String,
|
||||
/// The arguments to pass to the program.
|
||||
args: Vec<String>,
|
||||
/// An optional string to override the title of the terminal tab
|
||||
title_override: Option<SharedString>,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ use smol::channel::{Receiver, Sender};
|
||||
use task::{HideStrategy, Shell, TaskId};
|
||||
use terminal_settings::{AlternateScroll, CursorShape, TerminalSettings};
|
||||
use theme::{ActiveTheme, Theme};
|
||||
use util::{paths::home_dir, truncate_and_trailoff};
|
||||
use util::truncate_and_trailoff;
|
||||
|
||||
use std::{
|
||||
cmp::{self, min},
|
||||
@@ -60,7 +60,7 @@ use thiserror::Error;
|
||||
use gpui::{
|
||||
actions, black, px, AnyWindowHandle, AppContext, Bounds, ClipboardItem, EventEmitter, Hsla,
|
||||
Keystroke, ModelContext, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
|
||||
Pixels, Point, Rgba, ScrollWheelEvent, SharedString, Size, Task, TouchPhase,
|
||||
Pixels, Point, Rgba, ScrollWheelEvent, Size, Task, TouchPhase,
|
||||
};
|
||||
|
||||
use crate::mappings::{colors::to_alac_rgb, keys::to_esc_str};
|
||||
@@ -274,21 +274,19 @@ impl TerminalError {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn shell_to_string(&self) -> String {
|
||||
match &self.shell {
|
||||
Shell::System => "<system shell>".to_string(),
|
||||
Shell::Program(p) => p.to_string(),
|
||||
Shell::WithArguments { program, args } => format!("{} {}", program, args.join(" ")),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fmt_shell(&self) -> String {
|
||||
match &self.shell {
|
||||
Shell::System => "<system defined shell>".to_string(),
|
||||
Shell::Program(s) => s.to_string(),
|
||||
Shell::WithArguments {
|
||||
program,
|
||||
args,
|
||||
title_override,
|
||||
} => {
|
||||
if let Some(title_override) = title_override {
|
||||
format!("{} {} ({})", program, args.join(" "), title_override)
|
||||
} else {
|
||||
format!("{} {}", program, args.join(" "))
|
||||
}
|
||||
}
|
||||
Shell::WithArguments { program, args } => format!("{} {}", program, args.join(" ")),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -330,7 +328,6 @@ impl TerminalBuilder {
|
||||
cursor_shape: CursorShape,
|
||||
alternate_scroll: AlternateScroll,
|
||||
max_scroll_history_lines: Option<usize>,
|
||||
is_ssh_terminal: bool,
|
||||
window: AnyWindowHandle,
|
||||
completion_tx: Sender<()>,
|
||||
cx: &AppContext,
|
||||
@@ -351,29 +348,20 @@ impl TerminalBuilder {
|
||||
release_channel::AppVersion::global(cx).to_string(),
|
||||
);
|
||||
|
||||
let mut terminal_title_override = None;
|
||||
|
||||
let pty_options = {
|
||||
let alac_shell = match shell.clone() {
|
||||
Shell::System => None,
|
||||
Shell::Program(program) => {
|
||||
Some(alacritty_terminal::tty::Shell::new(program, Vec::new()))
|
||||
}
|
||||
Shell::WithArguments {
|
||||
program,
|
||||
args,
|
||||
title_override,
|
||||
} => {
|
||||
terminal_title_override = title_override;
|
||||
Shell::WithArguments { program, args } => {
|
||||
Some(alacritty_terminal::tty::Shell::new(program, args))
|
||||
}
|
||||
};
|
||||
|
||||
alacritty_terminal::tty::Options {
|
||||
shell: alac_shell,
|
||||
working_directory: working_directory
|
||||
.clone()
|
||||
.or_else(|| Some(home_dir().to_path_buf())),
|
||||
working_directory: working_directory.clone(),
|
||||
hold: false,
|
||||
env: env.into_iter().collect(),
|
||||
}
|
||||
@@ -453,7 +441,6 @@ impl TerminalBuilder {
|
||||
completion_tx,
|
||||
term,
|
||||
term_config: config,
|
||||
title_override: terminal_title_override,
|
||||
events: VecDeque::with_capacity(10), //Should never get this high.
|
||||
last_content: Default::default(),
|
||||
last_mouse: None,
|
||||
@@ -470,7 +457,6 @@ impl TerminalBuilder {
|
||||
url_regex: RegexSearch::new(URL_REGEX).unwrap(),
|
||||
word_regex: RegexSearch::new(WORD_REGEX).unwrap(),
|
||||
vi_mode_enabled: false,
|
||||
is_ssh_terminal,
|
||||
};
|
||||
|
||||
Ok(TerminalBuilder {
|
||||
@@ -618,7 +604,6 @@ pub struct Terminal {
|
||||
pub selection_head: Option<AlacPoint>,
|
||||
pub breadcrumb_text: String,
|
||||
pub pty_info: PtyProcessInfo,
|
||||
title_override: Option<SharedString>,
|
||||
scroll_px: Pixels,
|
||||
next_link_id: usize,
|
||||
selection_phase: SelectionPhase,
|
||||
@@ -628,7 +613,6 @@ pub struct Terminal {
|
||||
word_regex: RegexSearch,
|
||||
task: Option<TaskState>,
|
||||
vi_mode_enabled: bool,
|
||||
is_ssh_terminal: bool,
|
||||
}
|
||||
|
||||
pub struct TaskState {
|
||||
@@ -737,6 +721,10 @@ impl Terminal {
|
||||
self.selection_phase == SelectionPhase::Selecting
|
||||
}
|
||||
|
||||
pub fn get_cwd(&self) -> Option<PathBuf> {
|
||||
self.pty_info.current.as_ref().map(|info| info.cwd.clone())
|
||||
}
|
||||
|
||||
///Takes events from Alacritty and translates them to behavior on this view
|
||||
fn process_terminal_event(
|
||||
&mut self,
|
||||
@@ -950,7 +938,7 @@ impl Terminal {
|
||||
} else {
|
||||
MaybeNavigationTarget::PathLike(PathLikeTarget {
|
||||
maybe_path: maybe_url_or_path,
|
||||
terminal_dir: self.working_directory(),
|
||||
terminal_dir: self.get_cwd(),
|
||||
})
|
||||
};
|
||||
cx.emit(Event::Open(target));
|
||||
@@ -1005,7 +993,7 @@ impl Terminal {
|
||||
} else {
|
||||
MaybeNavigationTarget::PathLike(PathLikeTarget {
|
||||
maybe_path: word,
|
||||
terminal_dir: self.working_directory(),
|
||||
terminal_dir: self.get_cwd(),
|
||||
})
|
||||
};
|
||||
cx.emit(Event::NewNavigationTarget(Some(navigation_target)));
|
||||
@@ -1635,23 +1623,6 @@ impl Terminal {
|
||||
}
|
||||
|
||||
pub fn working_directory(&self) -> Option<PathBuf> {
|
||||
if self.is_ssh_terminal {
|
||||
// We can't yet reliably detect the working directory of a shell on the
|
||||
// SSH host. Until we can do that, it doesn't make sense to display
|
||||
// the working directory on the client and persist that.
|
||||
None
|
||||
} else {
|
||||
self.client_side_working_directory()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the working directory of the process that's connected to the PTY.
|
||||
/// That means it returns the working directory of the local shell or program
|
||||
/// that's running inside the terminal.
|
||||
///
|
||||
/// This does *not* return the working directory of the shell that runs on the
|
||||
/// remote host, in case Zed is connected to a remote host.
|
||||
fn client_side_working_directory(&self) -> Option<PathBuf> {
|
||||
self.pty_info
|
||||
.current
|
||||
.as_ref()
|
||||
@@ -1669,42 +1640,37 @@ impl Terminal {
|
||||
}
|
||||
}
|
||||
None => self
|
||||
.title_override
|
||||
.pty_info
|
||||
.current
|
||||
.as_ref()
|
||||
.map(|title_override| title_override.to_string())
|
||||
.unwrap_or_else(|| {
|
||||
self.pty_info
|
||||
.current
|
||||
.as_ref()
|
||||
.map(|fpi| {
|
||||
let process_file = fpi
|
||||
.cwd
|
||||
.file_name()
|
||||
.map(|name| name.to_string_lossy().to_string())
|
||||
.unwrap_or_default();
|
||||
.map(|fpi| {
|
||||
let process_file = fpi
|
||||
.cwd
|
||||
.file_name()
|
||||
.map(|name| name.to_string_lossy().to_string())
|
||||
.unwrap_or_default();
|
||||
|
||||
let argv = fpi.argv.clone();
|
||||
let process_name = format!(
|
||||
"{}{}",
|
||||
fpi.name,
|
||||
if !argv.is_empty() {
|
||||
format!(" {}", (argv[1..]).join(" "))
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
);
|
||||
let (process_file, process_name) = if truncate {
|
||||
(
|
||||
truncate_and_trailoff(&process_file, MAX_CHARS),
|
||||
truncate_and_trailoff(&process_name, MAX_CHARS),
|
||||
)
|
||||
} else {
|
||||
(process_file, process_name)
|
||||
};
|
||||
format!("{process_file} — {process_name}")
|
||||
})
|
||||
.unwrap_or_else(|| "Terminal".to_string())
|
||||
}),
|
||||
let argv = fpi.argv.clone();
|
||||
let process_name = format!(
|
||||
"{}{}",
|
||||
fpi.name,
|
||||
if !argv.is_empty() {
|
||||
format!(" {}", (argv[1..]).join(" "))
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
);
|
||||
let (process_file, process_name) = if truncate {
|
||||
(
|
||||
truncate_and_trailoff(&process_file, MAX_CHARS),
|
||||
truncate_and_trailoff(&process_name, MAX_CHARS),
|
||||
)
|
||||
} else {
|
||||
(process_file, process_name)
|
||||
};
|
||||
format!("{process_file} — {process_name}")
|
||||
})
|
||||
.unwrap_or_else(|| "Terminal".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -414,7 +414,7 @@ impl TerminalPanel {
|
||||
}
|
||||
}
|
||||
Shell::Program(shell) => Some((shell, Vec::new())),
|
||||
Shell::WithArguments { program, args, .. } => Some((program, args)),
|
||||
Shell::WithArguments { program, args } => Some((program, args)),
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -1192,7 +1192,7 @@ impl SerializableItem for TerminalView {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some((cwd, workspace_id)) = terminal.working_directory().zip(self.workspace_id) {
|
||||
if let Some((cwd, workspace_id)) = terminal.get_cwd().zip(self.workspace_id) {
|
||||
Some(cx.background_executor().spawn(async move {
|
||||
TERMINAL_DB
|
||||
.save_working_directory(item_id, workspace_id, cwd)
|
||||
|
||||
@@ -59,9 +59,6 @@ impl ThemeColors {
|
||||
search_match_background: neutral().light().step_5(),
|
||||
panel_background: neutral().light().step_2(),
|
||||
panel_focused_border: blue().light().step_5(),
|
||||
panel_indent_guide: neutral().light_alpha().step_5(),
|
||||
panel_indent_guide_hover: neutral().light_alpha().step_6(),
|
||||
panel_indent_guide_active: neutral().light_alpha().step_6(),
|
||||
pane_focused_border: blue().light().step_5(),
|
||||
pane_group_border: neutral().light().step_6(),
|
||||
scrollbar_thumb_background: neutral().light_alpha().step_3(),
|
||||
@@ -165,9 +162,6 @@ impl ThemeColors {
|
||||
search_match_background: neutral().dark().step_5(),
|
||||
panel_background: neutral().dark().step_2(),
|
||||
panel_focused_border: blue().dark().step_5(),
|
||||
panel_indent_guide: neutral().dark_alpha().step_4(),
|
||||
panel_indent_guide_hover: neutral().dark_alpha().step_6(),
|
||||
panel_indent_guide_active: neutral().dark_alpha().step_6(),
|
||||
pane_focused_border: blue().dark().step_5(),
|
||||
pane_group_border: neutral().dark().step_6(),
|
||||
scrollbar_thumb_background: neutral().dark_alpha().step_3(),
|
||||
|
||||
@@ -136,9 +136,6 @@ pub(crate) fn zed_default_dark() -> Theme {
|
||||
terminal_ansi_dim_white: crate::neutral().dark().step_10(),
|
||||
panel_background: bg,
|
||||
panel_focused_border: blue,
|
||||
panel_indent_guide: hsla(228. / 360., 8. / 100., 25. / 100., 1.),
|
||||
panel_indent_guide_hover: hsla(225. / 360., 13. / 100., 12. / 100., 1.),
|
||||
panel_indent_guide_active: hsla(225. / 360., 13. / 100., 12. / 100., 1.),
|
||||
pane_focused_border: blue,
|
||||
pane_group_border: hsla(225. / 360., 13. / 100., 12. / 100., 1.),
|
||||
scrollbar_thumb_background: gpui::transparent_black(),
|
||||
|
||||
@@ -322,15 +322,6 @@ pub struct ThemeColorsContent {
|
||||
#[serde(rename = "panel.focused_border")]
|
||||
pub panel_focused_border: Option<String>,
|
||||
|
||||
#[serde(rename = "panel.indent_guide")]
|
||||
pub panel_indent_guide: Option<String>,
|
||||
|
||||
#[serde(rename = "panel.indent_guide_hover")]
|
||||
pub panel_indent_guide_hover: Option<String>,
|
||||
|
||||
#[serde(rename = "panel.indent_guide_active")]
|
||||
pub panel_indent_guide_active: Option<String>,
|
||||
|
||||
#[serde(rename = "pane.focused_border")]
|
||||
pub pane_focused_border: Option<String>,
|
||||
|
||||
@@ -719,18 +710,6 @@ impl ThemeColorsContent {
|
||||
.panel_focused_border
|
||||
.as_ref()
|
||||
.and_then(|color| try_parse_color(color).ok()),
|
||||
panel_indent_guide: self
|
||||
.panel_indent_guide
|
||||
.as_ref()
|
||||
.and_then(|color| try_parse_color(color).ok()),
|
||||
panel_indent_guide_hover: self
|
||||
.panel_indent_guide_hover
|
||||
.as_ref()
|
||||
.and_then(|color| try_parse_color(color).ok()),
|
||||
panel_indent_guide_active: self
|
||||
.panel_indent_guide_active
|
||||
.as_ref()
|
||||
.and_then(|color| try_parse_color(color).ok()),
|
||||
pane_focused_border: self
|
||||
.pane_focused_border
|
||||
.as_ref()
|
||||
|
||||
@@ -123,9 +123,6 @@ pub struct ThemeColors {
|
||||
pub search_match_background: Hsla,
|
||||
pub panel_background: Hsla,
|
||||
pub panel_focused_border: Hsla,
|
||||
pub panel_indent_guide: Hsla,
|
||||
pub panel_indent_guide_hover: Hsla,
|
||||
pub panel_indent_guide_active: Hsla,
|
||||
pub pane_focused_border: Hsla,
|
||||
pub pane_group_border: Hsla,
|
||||
/// The color of the scrollbar thumb.
|
||||
|
||||
@@ -320,6 +320,7 @@ impl TitleBar {
|
||||
div()
|
||||
.max_w_32()
|
||||
.overflow_hidden()
|
||||
.truncate()
|
||||
.text_ellipsis()
|
||||
.child(Label::new(nickname.clone()).size(LabelSize::Small)),
|
||||
)
|
||||
|
||||
@@ -8,7 +8,6 @@ mod dropdown_menu;
|
||||
mod facepile;
|
||||
mod icon;
|
||||
mod image;
|
||||
mod indent_guides;
|
||||
mod indicator;
|
||||
mod keybinding;
|
||||
mod label;
|
||||
@@ -41,7 +40,6 @@ pub use dropdown_menu::*;
|
||||
pub use facepile::*;
|
||||
pub use icon::*;
|
||||
pub use image::*;
|
||||
pub use indent_guides::*;
|
||||
pub use indicator::*;
|
||||
pub use keybinding::*;
|
||||
pub use label::*;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user