Compare commits
4 Commits
fix-git-ht
...
git-push-2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d705bdab5 | ||
|
|
a94b13ddf4 | ||
|
|
a3b08aa8da | ||
|
|
0c4224a3e0 |
25
.github/ISSUE_TEMPLATE/1_bug_report.yml
vendored
25
.github/ISSUE_TEMPLATE/1_bug_report.yml
vendored
@@ -10,39 +10,16 @@ body:
|
|||||||
value: |
|
value: |
|
||||||
<!-- Please insert a one line summary of the issue below -->
|
<!-- Please insert a one line summary of the issue below -->
|
||||||
|
|
||||||
SUMMARY_SENTENCE_HERE
|
<!-- Include all steps necessary to reproduce from a clean Zed installation. Be verbose -->
|
||||||
|
|
||||||
<!-- Be verbose: Include all steps necessary to reproduce from a clean Zed installation. -->
|
|
||||||
<!-- Code snippets are better than images, a repository link that reproduces the issue is ideal. -->
|
|
||||||
|
|
||||||
Steps to trigger the problem:
|
Steps to trigger the problem:
|
||||||
1.
|
1.
|
||||||
2.
|
2.
|
||||||
3.
|
3.
|
||||||
4.
|
|
||||||
|
|
||||||
Actual Behavior:
|
Actual Behavior:
|
||||||
|
|
||||||
Expected Behavior:
|
Expected Behavior:
|
||||||
|
|
||||||
<!--
|
|
||||||
Is there anything additional necessary to reproduce this issue?
|
|
||||||
- settings.json, keymap.json, .editorconfig etc?
|
|
||||||
- Does it happen intermittently or only with specific projects / file types?
|
|
||||||
- Have you found a workaround?
|
|
||||||
|
|
||||||
Did you check your Zed.log to see if there is any relevant details there?
|
|
||||||
- When including large items (videos, screenshots, logs, configs) please wrap with:
|
|
||||||
|
|
||||||
<details><summary>See inside for XXXXYYY</summary>
|
|
||||||
|
|
||||||
```shell
|
|
||||||
code
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
|
||||||
-->
|
|
||||||
|
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
|
|||||||
28
.github/workflows/ci.yml
vendored
28
.github/workflows/ci.yml
vendored
@@ -109,16 +109,8 @@ jobs:
|
|||||||
- name: cargo clippy
|
- name: cargo clippy
|
||||||
run: ./script/clippy
|
run: ./script/clippy
|
||||||
|
|
||||||
- name: Install cargo-machete
|
|
||||||
uses: clechasseur/rs-cargo@8435b10f6e71c2e3d4d3b7573003a8ce4bfc6386 # v2
|
|
||||||
with:
|
|
||||||
command: install
|
|
||||||
args: cargo-machete@0.7.0
|
|
||||||
|
|
||||||
- name: Check unused dependencies
|
- name: Check unused dependencies
|
||||||
uses: clechasseur/rs-cargo@8435b10f6e71c2e3d4d3b7573003a8ce4bfc6386 # v2
|
uses: bnjbvr/cargo-machete@main
|
||||||
with:
|
|
||||||
command: machete
|
|
||||||
|
|
||||||
- name: Check licenses
|
- name: Check licenses
|
||||||
run: |
|
run: |
|
||||||
@@ -240,7 +232,7 @@ jobs:
|
|||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
name: (Windows) Run Clippy and tests
|
name: (Windows) Run Clippy and tests
|
||||||
if: github.repository_owner == 'zed-industries'
|
if: github.repository_owner == 'zed-industries'
|
||||||
runs-on: hosted-windows-2
|
runs-on: hosted-windows-1
|
||||||
steps:
|
steps:
|
||||||
# more info here:- https://github.com/rust-lang/cargo/issues/13020
|
# more info here:- https://github.com/rust-lang/cargo/issues/13020
|
||||||
- name: Enable longer pathnames for git
|
- name: Enable longer pathnames for git
|
||||||
@@ -273,7 +265,8 @@ jobs:
|
|||||||
|
|
||||||
- name: cargo clippy
|
- name: cargo clippy
|
||||||
working-directory: ${{ env.ZED_WORKSPACE }}
|
working-directory: ${{ env.ZED_WORKSPACE }}
|
||||||
run: ./script/clippy.ps1
|
# Windows can't run shell scripts, so we need to use `cargo xtask`.
|
||||||
|
run: cargo xtask clippy
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
uses: ./.github/actions/run_tests_windows
|
uses: ./.github/actions/run_tests_windows
|
||||||
@@ -305,9 +298,8 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
|
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
|
||||||
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
|
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
|
||||||
APPLE_NOTARIZATION_KEY: ${{ secrets.APPLE_NOTARIZATION_KEY }}
|
APPLE_NOTARIZATION_USERNAME: ${{ secrets.APPLE_NOTARIZATION_USERNAME }}
|
||||||
APPLE_NOTARIZATION_KEY_ID: ${{ secrets.APPLE_NOTARIZATION_KEY_ID }}
|
APPLE_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_NOTARIZATION_PASSWORD }}
|
||||||
APPLE_NOTARIZATION_ISSUER_ID: ${{ secrets.APPLE_NOTARIZATION_ISSUER_ID }}
|
|
||||||
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
|
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
|
||||||
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
|
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
|
||||||
DIGITALOCEAN_SPACES_ACCESS_KEY: ${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }}
|
DIGITALOCEAN_SPACES_ACCESS_KEY: ${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }}
|
||||||
@@ -358,14 +350,14 @@ jobs:
|
|||||||
mv target/x86_64-apple-darwin/release/Zed.dmg target/x86_64-apple-darwin/release/Zed-x86_64.dmg
|
mv target/x86_64-apple-darwin/release/Zed.dmg target/x86_64-apple-darwin/release/Zed-x86_64.dmg
|
||||||
|
|
||||||
- name: Upload app bundle (aarch64) to workflow run if main branch or specific label
|
- name: Upload app bundle (aarch64) to workflow run if main branch or specific label
|
||||||
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4
|
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4
|
||||||
if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
|
if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
|
||||||
with:
|
with:
|
||||||
name: Zed_${{ github.event.pull_request.head.sha || github.sha }}-aarch64.dmg
|
name: Zed_${{ github.event.pull_request.head.sha || github.sha }}-aarch64.dmg
|
||||||
path: target/aarch64-apple-darwin/release/Zed-aarch64.dmg
|
path: target/aarch64-apple-darwin/release/Zed-aarch64.dmg
|
||||||
|
|
||||||
- name: Upload app bundle (x86_64) to workflow run if main branch or specific label
|
- name: Upload app bundle (x86_64) to workflow run if main branch or specific label
|
||||||
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4
|
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4
|
||||||
if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
|
if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
|
||||||
with:
|
with:
|
||||||
name: Zed_${{ github.event.pull_request.head.sha || github.sha }}-x86_64.dmg
|
name: Zed_${{ github.event.pull_request.head.sha || github.sha }}-x86_64.dmg
|
||||||
@@ -416,7 +408,7 @@ jobs:
|
|||||||
run: script/bundle-linux
|
run: script/bundle-linux
|
||||||
|
|
||||||
- name: Upload Linux bundle to workflow run if main branch or specific label
|
- name: Upload Linux bundle to workflow run if main branch or specific label
|
||||||
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4
|
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4
|
||||||
if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
|
if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
|
||||||
with:
|
with:
|
||||||
name: zed-${{ github.event.pull_request.head.sha || github.sha }}-x86_64-unknown-linux-gnu.tar.gz
|
name: zed-${{ github.event.pull_request.head.sha || github.sha }}-x86_64-unknown-linux-gnu.tar.gz
|
||||||
@@ -464,7 +456,7 @@ jobs:
|
|||||||
run: script/bundle-linux
|
run: script/bundle-linux
|
||||||
|
|
||||||
- name: Upload Linux bundle to workflow run if main branch or specific label
|
- name: Upload Linux bundle to workflow run if main branch or specific label
|
||||||
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4
|
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4
|
||||||
if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
|
if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
|
||||||
with:
|
with:
|
||||||
name: zed-${{ github.event.pull_request.head.sha || github.sha }}-aarch64-unknown-linux-gnu.tar.gz
|
name: zed-${{ github.event.pull_request.head.sha || github.sha }}-aarch64-unknown-linux-gnu.tar.gz
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
name: Update All Top Ranking Issues
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: "0 */12 * * *"
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
update_top_ranking_issues:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.repository == 'zed-industries/zed'
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
|
||||||
- name: Set up uv
|
|
||||||
uses: astral-sh/setup-uv@caf0cab7a618c569241d31dcd442f54681755d39 # v3
|
|
||||||
with:
|
|
||||||
version: "latest"
|
|
||||||
enable-cache: true
|
|
||||||
cache-dependency-glob: "script/update_top_ranking_issues/pyproject.toml"
|
|
||||||
- name: Install Python 3.13
|
|
||||||
run: uv python install 3.13
|
|
||||||
- name: Install dependencies
|
|
||||||
run: uv sync --project script/update_top_ranking_issues -p 3.13
|
|
||||||
- name: Run script
|
|
||||||
run: uv run --project script/update_top_ranking_issues script/update_top_ranking_issues/main.py --github-token ${{ secrets.GITHUB_TOKEN }} --issue-reference-number 5393
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
name: Update Weekly Top Ranking Issues
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: "0 15 * * *"
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
update_top_ranking_issues:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.repository == 'zed-industries/zed'
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
|
||||||
- name: Set up uv
|
|
||||||
uses: astral-sh/setup-uv@caf0cab7a618c569241d31dcd442f54681755d39 # v3
|
|
||||||
with:
|
|
||||||
version: "latest"
|
|
||||||
enable-cache: true
|
|
||||||
cache-dependency-glob: "script/update_top_ranking_issues/pyproject.toml"
|
|
||||||
- name: Install Python 3.13
|
|
||||||
run: uv python install 3.13
|
|
||||||
- name: Install dependencies
|
|
||||||
run: uv sync --project script/update_top_ranking_issues -p 3.13
|
|
||||||
- name: Run script
|
|
||||||
run: uv run --project script/update_top_ranking_issues script/update_top_ranking_issues/main.py --github-token ${{ secrets.GITHUB_TOKEN }} --issue-reference-number 6952 --query-day-interval 7
|
|
||||||
2
.github/workflows/deploy_cloudflare.yml
vendored
2
.github/workflows/deploy_cloudflare.yml
vendored
@@ -65,7 +65,7 @@ jobs:
|
|||||||
command: deploy .cloudflare/docs-proxy/src/worker.js
|
command: deploy .cloudflare/docs-proxy/src/worker.js
|
||||||
|
|
||||||
- name: Preserve Wrangler logs
|
- name: Preserve Wrangler logs
|
||||||
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4
|
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
name: wrangler_logs
|
name: wrangler_logs
|
||||||
|
|||||||
5
.github/workflows/release_nightly.yml
vendored
5
.github/workflows/release_nightly.yml
vendored
@@ -62,9 +62,8 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
|
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
|
||||||
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
|
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
|
||||||
APPLE_NOTARIZATION_KEY: ${{ secrets.APPLE_NOTARIZATION_KEY }}
|
APPLE_NOTARIZATION_USERNAME: ${{ secrets.APPLE_NOTARIZATION_USERNAME }}
|
||||||
APPLE_NOTARIZATION_KEY_ID: ${{ secrets.APPLE_NOTARIZATION_KEY_ID }}
|
APPLE_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_NOTARIZATION_PASSWORD }}
|
||||||
APPLE_NOTARIZATION_ISSUER_ID: ${{ secrets.APPLE_NOTARIZATION_ISSUER_ID }}
|
|
||||||
DIGITALOCEAN_SPACES_ACCESS_KEY: ${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }}
|
DIGITALOCEAN_SPACES_ACCESS_KEY: ${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }}
|
||||||
DIGITALOCEAN_SPACES_SECRET_KEY: ${{ secrets.DIGITALOCEAN_SPACES_SECRET_KEY }}
|
DIGITALOCEAN_SPACES_SECRET_KEY: ${{ secrets.DIGITALOCEAN_SPACES_SECRET_KEY }}
|
||||||
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
|
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
|
||||||
|
|||||||
4
.mailmap
4
.mailmap
@@ -21,8 +21,6 @@ Andrei Zvonimir Crnković <andrei@0x7f.dev>
|
|||||||
Andrei Zvonimir Crnković <andrei@0x7f.dev> <andreicek@0x7f.dev>
|
Andrei Zvonimir Crnković <andrei@0x7f.dev> <andreicek@0x7f.dev>
|
||||||
Antonio Scandurra <me@as-cii.com>
|
Antonio Scandurra <me@as-cii.com>
|
||||||
Antonio Scandurra <me@as-cii.com> <antonio@zed.dev>
|
Antonio Scandurra <me@as-cii.com> <antonio@zed.dev>
|
||||||
Ben Kunkle <ben@zed.dev>
|
|
||||||
Ben Kunkle <ben@zed.dev> <ben.kunkle@gmail.com>
|
|
||||||
Bennet Bo Fenner <bennet@zed.dev>
|
Bennet Bo Fenner <bennet@zed.dev>
|
||||||
Bennet Bo Fenner <bennet@zed.dev> <53836821+bennetbo@users.noreply.github.com>
|
Bennet Bo Fenner <bennet@zed.dev> <53836821+bennetbo@users.noreply.github.com>
|
||||||
Bennet Bo Fenner <bennet@zed.dev> <bennetbo@gmx.de>
|
Bennet Bo Fenner <bennet@zed.dev> <bennetbo@gmx.de>
|
||||||
@@ -114,8 +112,6 @@ Sebastijan Kelnerič <sebastijan.kelneric@sebba.dev> <sebastijan.kelneric@vichav
|
|||||||
Sergey Onufrienko <sergey@onufrienko.com>
|
Sergey Onufrienko <sergey@onufrienko.com>
|
||||||
Shish <webmaster@shishnet.org>
|
Shish <webmaster@shishnet.org>
|
||||||
Shish <webmaster@shishnet.org> <shish@shishnet.org>
|
Shish <webmaster@shishnet.org> <shish@shishnet.org>
|
||||||
Smit Barmase <0xtimsb@gmail.com>
|
|
||||||
Smit Barmase <0xtimsb@gmail.com> <smit@zed.dev>
|
|
||||||
Thorben Kröger <dev@thorben.net>
|
Thorben Kröger <dev@thorben.net>
|
||||||
Thorben Kröger <dev@thorben.net> <thorben.kroeger@hexagon.com>
|
Thorben Kröger <dev@thorben.net> <thorben.kroeger@hexagon.com>
|
||||||
Thorsten Ball <thorsten@zed.dev>
|
Thorsten Ball <thorsten@zed.dev>
|
||||||
|
|||||||
@@ -14,12 +14,12 @@
|
|||||||
},
|
},
|
||||||
"JSON": {
|
"JSON": {
|
||||||
"tab_size": 2,
|
"tab_size": 2,
|
||||||
"preferred_line_length": 120,
|
"preferred_line_length": 100,
|
||||||
"formatter": "prettier"
|
"formatter": "prettier"
|
||||||
},
|
},
|
||||||
"JSONC": {
|
"JSONC": {
|
||||||
"tab_size": 2,
|
"tab_size": 2,
|
||||||
"preferred_line_length": 120,
|
"preferred_line_length": 100,
|
||||||
"formatter": "prettier"
|
"formatter": "prettier"
|
||||||
},
|
},
|
||||||
"JavaScript": {
|
"JavaScript": {
|
||||||
|
|||||||
351
Cargo.lock
generated
351
Cargo.lock
generated
@@ -83,8 +83,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alacritty_terminal"
|
name = "alacritty_terminal"
|
||||||
version = "0.25.1-dev"
|
version = "0.25.0"
|
||||||
source = "git+https://github.com/zed-industries/alacritty.git?rev=03c2907b44b4189aac5fdeaea331f5aab5c7072e#03c2907b44b4189aac5fdeaea331f5aab5c7072e"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5bccc2e60c2112dc8e8a722d6d30f2bb1a6a7b5d0e65fa695e09e57415dca7f7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"bitflags 2.8.0",
|
"bitflags 2.8.0",
|
||||||
@@ -257,9 +258,9 @@ checksum = "34cd60c5e3152cef0a592f1b296f1cc93715d89d2551d85315828c3a09575ff4"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.96"
|
version = "1.0.95"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4"
|
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "approx"
|
name = "approx"
|
||||||
@@ -395,6 +396,7 @@ dependencies = [
|
|||||||
"language",
|
"language",
|
||||||
"language_model",
|
"language_model",
|
||||||
"language_model_selector",
|
"language_model_selector",
|
||||||
|
"language_models",
|
||||||
"languages",
|
"languages",
|
||||||
"log",
|
"log",
|
||||||
"lsp",
|
"lsp",
|
||||||
@@ -462,6 +464,7 @@ dependencies = [
|
|||||||
"language",
|
"language",
|
||||||
"language_model",
|
"language_model",
|
||||||
"language_model_selector",
|
"language_model_selector",
|
||||||
|
"language_models",
|
||||||
"log",
|
"log",
|
||||||
"lsp",
|
"lsp",
|
||||||
"markdown",
|
"markdown",
|
||||||
@@ -490,7 +493,6 @@ dependencies = [
|
|||||||
"ui",
|
"ui",
|
||||||
"util",
|
"util",
|
||||||
"uuid",
|
"uuid",
|
||||||
"vim_mode_setting",
|
|
||||||
"workspace",
|
"workspace",
|
||||||
"zed_actions",
|
"zed_actions",
|
||||||
]
|
]
|
||||||
@@ -517,6 +519,7 @@ dependencies = [
|
|||||||
"language",
|
"language",
|
||||||
"language_model",
|
"language_model",
|
||||||
"language_model_selector",
|
"language_model_selector",
|
||||||
|
"language_models",
|
||||||
"languages",
|
"languages",
|
||||||
"log",
|
"log",
|
||||||
"multi_buffer",
|
"multi_buffer",
|
||||||
@@ -1176,9 +1179,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aws-config"
|
name = "aws-config"
|
||||||
version = "1.5.17"
|
version = "1.5.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "490aa7465ee685b2ced076bb87ef654a47724a7844e2c7d3af4e749ce5b875dd"
|
checksum = "50236e4d60fe8458de90a71c0922c761e41755adf091b1b03de1cef537179915"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aws-credential-types",
|
"aws-credential-types",
|
||||||
"aws-runtime",
|
"aws-runtime",
|
||||||
@@ -1267,35 +1270,11 @@ dependencies = [
|
|||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aws-sdk-bedrockruntime"
|
|
||||||
version = "1.75.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2ddf7475b6f50a1a5be8edb1bcdf6e4ae00feed5b890d14a3f1f0e14d76f5a16"
|
|
||||||
dependencies = [
|
|
||||||
"aws-credential-types",
|
|
||||||
"aws-runtime",
|
|
||||||
"aws-smithy-async",
|
|
||||||
"aws-smithy-eventstream",
|
|
||||||
"aws-smithy-http",
|
|
||||||
"aws-smithy-json",
|
|
||||||
"aws-smithy-runtime",
|
|
||||||
"aws-smithy-runtime-api",
|
|
||||||
"aws-smithy-types",
|
|
||||||
"aws-types",
|
|
||||||
"bytes 1.10.0",
|
|
||||||
"fastrand 2.3.0",
|
|
||||||
"http 0.2.12",
|
|
||||||
"once_cell",
|
|
||||||
"regex-lite",
|
|
||||||
"tracing",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aws-sdk-kinesis"
|
name = "aws-sdk-kinesis"
|
||||||
version = "1.62.0"
|
version = "1.61.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e31622345afd0c35d33c1cbba73ccf9fb88e09857413d8963dea2c493e00704d"
|
checksum = "89f2163d8704e8fdcd51ec6c2e0441c418471e422ee9690451b17a1c46344e1a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aws-credential-types",
|
"aws-credential-types",
|
||||||
"aws-runtime",
|
"aws-runtime",
|
||||||
@@ -1315,9 +1294,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aws-sdk-s3"
|
name = "aws-sdk-s3"
|
||||||
version = "1.77.0"
|
version = "1.76.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "34e87342432a3de0e94e82c99a7cbd9042f99de029ae1f4e368160f9e9929264"
|
checksum = "66e83401ad7287ad15244d557e35502c2a94105ca5b41d656c391f1a4fc04ca2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aws-credential-types",
|
"aws-credential-types",
|
||||||
"aws-runtime",
|
"aws-runtime",
|
||||||
@@ -1349,9 +1328,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aws-sdk-sso"
|
name = "aws-sdk-sso"
|
||||||
version = "1.60.0"
|
version = "1.58.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "60186fab60b24376d3e33b9ff0a43485f99efd470e3b75a9160c849741d63d56"
|
checksum = "16ff718c9ee45cc1ebd4774a0e086bb80a6ab752b4902edf1c9f56b86ee1f770"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aws-credential-types",
|
"aws-credential-types",
|
||||||
"aws-runtime",
|
"aws-runtime",
|
||||||
@@ -1371,9 +1350,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aws-sdk-ssooidc"
|
name = "aws-sdk-ssooidc"
|
||||||
version = "1.61.0"
|
version = "1.59.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7033130ce1ee13e6018905b7b976c915963755aef299c1521897679d6cd4f8ef"
|
checksum = "5183e088715cc135d8d396fdd3bc02f018f0da4c511f53cb8d795b6a31c55809"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aws-credential-types",
|
"aws-credential-types",
|
||||||
"aws-runtime",
|
"aws-runtime",
|
||||||
@@ -1393,9 +1372,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aws-sdk-sts"
|
name = "aws-sdk-sts"
|
||||||
version = "1.61.0"
|
version = "1.59.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c5c1cac7677179d622b4448b0d31bcb359185295dc6fca891920cfb17e2b5156"
|
checksum = "c9f944ef032717596639cea4a2118a3a457268ef51bbb5fde9637e54c465da00"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aws-credential-types",
|
"aws-credential-types",
|
||||||
"aws-runtime",
|
"aws-runtime",
|
||||||
@@ -1456,9 +1435,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aws-smithy-checksums"
|
name = "aws-smithy-checksums"
|
||||||
version = "0.63.0"
|
version = "0.62.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "db2dc8d842d872529355c72632de49ef8c5a2949a4472f10e802f28cf925770c"
|
checksum = "f2f45a1c384d7a393026bc5f5c177105aa9fa68e4749653b985707ac27d77295"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aws-smithy-http",
|
"aws-smithy-http",
|
||||||
"aws-smithy-types",
|
"aws-smithy-types",
|
||||||
@@ -1620,17 +1599,6 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aws_http_client"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"aws-smithy-runtime-api",
|
|
||||||
"aws-smithy-types",
|
|
||||||
"futures 0.3.31",
|
|
||||||
"http_client",
|
|
||||||
"tokio",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum"
|
name = "axum"
|
||||||
version = "0.6.20"
|
version = "0.6.20"
|
||||||
@@ -1760,22 +1728,6 @@ version = "1.6.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bedrock"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"aws-sdk-bedrockruntime",
|
|
||||||
"aws-smithy-types",
|
|
||||||
"futures 0.3.31",
|
|
||||||
"schemars",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"strum",
|
|
||||||
"thiserror 1.0.69",
|
|
||||||
"tokio",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bigdecimal"
|
name = "bigdecimal"
|
||||||
version = "0.4.7"
|
version = "0.4.7"
|
||||||
@@ -1808,7 +1760,7 @@ dependencies = [
|
|||||||
"bitflags 2.8.0",
|
"bitflags 2.8.0",
|
||||||
"cexpr",
|
"cexpr",
|
||||||
"clang-sys",
|
"clang-sys",
|
||||||
"itertools 0.10.5",
|
"itertools 0.12.1",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"lazycell",
|
"lazycell",
|
||||||
"log",
|
"log",
|
||||||
@@ -1831,7 +1783,7 @@ dependencies = [
|
|||||||
"bitflags 2.8.0",
|
"bitflags 2.8.0",
|
||||||
"cexpr",
|
"cexpr",
|
||||||
"clang-sys",
|
"clang-sys",
|
||||||
"itertools 0.10.5",
|
"itertools 0.12.1",
|
||||||
"log",
|
"log",
|
||||||
"prettyplease",
|
"prettyplease",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -1966,16 +1918,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "blake3"
|
name = "blake3"
|
||||||
version = "1.6.0"
|
version = "1.5.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1230237285e3e10cde447185e8975408ae24deaa67205ce684805c25bc0c7937"
|
checksum = "b8ee0c1824c4dea5b5f81736aff91bae041d2c07ee1192bec91054e10e3e601e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayref",
|
"arrayref",
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"cc",
|
"cc",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"constant_time_eq 0.3.1",
|
"constant_time_eq 0.3.1",
|
||||||
"memmap2",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2401,6 +2352,25 @@ dependencies = [
|
|||||||
"cipher",
|
"cipher",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cbindgen"
|
||||||
|
version = "0.27.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fce8dd7fcfcbf3a0a87d8f515194b49d6135acab73e18bd380d1d93bb1a15eb"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"heck 0.4.1",
|
||||||
|
"indexmap",
|
||||||
|
"log",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"syn 2.0.90",
|
||||||
|
"tempfile",
|
||||||
|
"toml 0.8.20",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cbindgen"
|
name = "cbindgen"
|
||||||
version = "0.28.0"
|
version = "0.28.0"
|
||||||
@@ -2498,9 +2468,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.4.40"
|
version = "0.4.39"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c"
|
checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"android-tzdata",
|
"android-tzdata",
|
||||||
"iana-time-zone",
|
"iana-time-zone",
|
||||||
@@ -2508,7 +2478,7 @@ dependencies = [
|
|||||||
"num-traits",
|
"num-traits",
|
||||||
"serde",
|
"serde",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"windows-link",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2568,9 +2538,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.31"
|
version = "4.5.29"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767"
|
checksum = "8acebd8ad879283633b343856142139f2da2317c96b05b4dd6181c61e2480184"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
@@ -2578,9 +2548,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.31"
|
version = "4.5.29"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863"
|
checksum = "f6ba32cbda51c7e1dfd49acc1457ba1a7dec5b64fe360e828acb13ca8dc9c2f9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
@@ -2635,7 +2605,6 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"util",
|
"util",
|
||||||
"windows 0.58.0",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2650,7 +2619,6 @@ dependencies = [
|
|||||||
"clock",
|
"clock",
|
||||||
"cocoa 0.26.0",
|
"cocoa 0.26.0",
|
||||||
"collections",
|
"collections",
|
||||||
"credentials_provider",
|
|
||||||
"feature_flags",
|
"feature_flags",
|
||||||
"futures 0.3.31",
|
"futures 0.3.31",
|
||||||
"gpui",
|
"gpui",
|
||||||
@@ -2816,7 +2784,6 @@ dependencies = [
|
|||||||
"futures 0.3.31",
|
"futures 0.3.31",
|
||||||
"git",
|
"git",
|
||||||
"git_hosting_providers",
|
"git_hosting_providers",
|
||||||
"git_ui",
|
|
||||||
"google_ai",
|
"google_ai",
|
||||||
"gpui",
|
"gpui",
|
||||||
"hex",
|
"hex",
|
||||||
@@ -3003,6 +2970,7 @@ dependencies = [
|
|||||||
"collections",
|
"collections",
|
||||||
"gpui",
|
"gpui",
|
||||||
"linkme",
|
"linkme",
|
||||||
|
"once_cell",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"theme",
|
"theme",
|
||||||
]
|
]
|
||||||
@@ -3083,7 +3051,6 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"assistant_tool",
|
"assistant_tool",
|
||||||
"async-trait",
|
|
||||||
"collections",
|
"collections",
|
||||||
"command_palette_hooks",
|
"command_palette_hooks",
|
||||||
"context_server_settings",
|
"context_server_settings",
|
||||||
@@ -3124,9 +3091,9 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "convert_case"
|
name = "convert_case"
|
||||||
version = "0.8.0"
|
version = "0.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f"
|
checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
]
|
]
|
||||||
@@ -3144,9 +3111,7 @@ dependencies = [
|
|||||||
"clock",
|
"clock",
|
||||||
"collections",
|
"collections",
|
||||||
"command_palette_hooks",
|
"command_palette_hooks",
|
||||||
"ctor",
|
|
||||||
"editor",
|
"editor",
|
||||||
"env_logger 0.11.6",
|
|
||||||
"fs",
|
"fs",
|
||||||
"futures 0.3.31",
|
"futures 0.3.31",
|
||||||
"gpui",
|
"gpui",
|
||||||
@@ -3154,7 +3119,6 @@ dependencies = [
|
|||||||
"indoc",
|
"indoc",
|
||||||
"inline_completion",
|
"inline_completion",
|
||||||
"language",
|
"language",
|
||||||
"log",
|
|
||||||
"lsp",
|
"lsp",
|
||||||
"menu",
|
"menu",
|
||||||
"node_runtime",
|
"node_runtime",
|
||||||
@@ -3505,26 +3469,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crc64fast-nvme"
|
name = "crc64fast-nvme"
|
||||||
version = "1.2.0"
|
version = "1.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4955638f00a809894c947f85a024020a20815b65a5eea633798ea7924edab2b3"
|
checksum = "d5e2ee08013e3f228d6d2394116c4549a6df77708442c62d887d83f68ef2ee37"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"cbindgen 0.27.0",
|
||||||
"crc",
|
"crc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "credentials_provider"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"futures 0.3.31",
|
|
||||||
"gpui",
|
|
||||||
"paths",
|
|
||||||
"release_channel",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "criterion"
|
name = "criterion"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
@@ -3645,19 +3597,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ctor"
|
name = "ctor"
|
||||||
version = "0.4.0"
|
version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a7747ac3a66a06f4ee6718686c8ea976d2d05fb30ada93ebd76b3f9aef97257c"
|
checksum = "bba74424191d99c617a172ef126d429f62fe54aba1002c4d36e49403ce4b00a2"
|
||||||
dependencies = [
|
|
||||||
"ctor-proc-macro",
|
|
||||||
"dtor",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ctor-proc-macro"
|
|
||||||
version = "0.0.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4f211af61d8efdd104f96e57adf5e426ba1bc3ed7a4ead616e15e5881fd79c4d"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ctrlc"
|
name = "ctrlc"
|
||||||
@@ -3895,6 +3837,7 @@ dependencies = [
|
|||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"project",
|
"project",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"settings",
|
"settings",
|
||||||
@@ -4033,21 +3976,6 @@ dependencies = [
|
|||||||
"phf",
|
"phf",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "dtor"
|
|
||||||
version = "0.0.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8bf39a0bfd1f94d62ffdb2802a7e6244c0f34f6ebacf5d4c26547d08cd1d67a5"
|
|
||||||
dependencies = [
|
|
||||||
"dtor-proc-macro",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "dtor-proc-macro"
|
|
||||||
version = "0.0.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7454e41ff9012c00d53cf7f475c5e3afa3b91b7c90568495495e8d9bf47a1055"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dunce"
|
name = "dunce"
|
||||||
version = "1.0.5"
|
version = "1.0.5"
|
||||||
@@ -4102,7 +4030,7 @@ dependencies = [
|
|||||||
"client",
|
"client",
|
||||||
"clock",
|
"clock",
|
||||||
"collections",
|
"collections",
|
||||||
"convert_case 0.8.0",
|
"convert_case 0.7.1",
|
||||||
"ctor",
|
"ctor",
|
||||||
"db",
|
"db",
|
||||||
"emojis",
|
"emojis",
|
||||||
@@ -5345,12 +5273,10 @@ dependencies = [
|
|||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"regex",
|
"regex",
|
||||||
"rope",
|
"rope",
|
||||||
"schemars",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"smol",
|
"smol",
|
||||||
"sum_tree",
|
"sum_tree",
|
||||||
"tempfile",
|
|
||||||
"text",
|
"text",
|
||||||
"time",
|
"time",
|
||||||
"unindent",
|
"unindent",
|
||||||
@@ -5420,7 +5346,6 @@ dependencies = [
|
|||||||
"theme",
|
"theme",
|
||||||
"time",
|
"time",
|
||||||
"ui",
|
"ui",
|
||||||
"unindent",
|
|
||||||
"util",
|
"util",
|
||||||
"windows 0.58.0",
|
"windows 0.58.0",
|
||||||
"workspace",
|
"workspace",
|
||||||
@@ -5555,7 +5480,7 @@ dependencies = [
|
|||||||
"bytemuck",
|
"bytemuck",
|
||||||
"calloop",
|
"calloop",
|
||||||
"calloop-wayland-source",
|
"calloop-wayland-source",
|
||||||
"cbindgen",
|
"cbindgen 0.28.0",
|
||||||
"cocoa 0.26.0",
|
"cocoa 0.26.0",
|
||||||
"collections",
|
"collections",
|
||||||
"core-foundation 0.9.4",
|
"core-foundation 0.9.4",
|
||||||
@@ -7012,14 +6937,17 @@ dependencies = [
|
|||||||
"anthropic",
|
"anthropic",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"client",
|
|
||||||
"collections",
|
"collections",
|
||||||
|
"deepseek",
|
||||||
"futures 0.3.31",
|
"futures 0.3.31",
|
||||||
"google_ai",
|
"google_ai",
|
||||||
"gpui",
|
"gpui",
|
||||||
"http_client",
|
"http_client",
|
||||||
"image",
|
"image",
|
||||||
|
"lmstudio",
|
||||||
"log",
|
"log",
|
||||||
|
"mistral",
|
||||||
|
"ollama",
|
||||||
"open_ai",
|
"open_ai",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"proto",
|
"proto",
|
||||||
@@ -7028,7 +6956,6 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"smol",
|
"smol",
|
||||||
"strum",
|
"strum",
|
||||||
"telemetry_events",
|
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
"ui",
|
"ui",
|
||||||
"util",
|
"util",
|
||||||
@@ -7055,14 +6982,9 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anthropic",
|
"anthropic",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"aws-config",
|
|
||||||
"aws-credential-types",
|
|
||||||
"aws_http_client",
|
|
||||||
"bedrock",
|
|
||||||
"client",
|
"client",
|
||||||
"collections",
|
"collections",
|
||||||
"copilot",
|
"copilot",
|
||||||
"credentials_provider",
|
|
||||||
"deepseek",
|
"deepseek",
|
||||||
"editor",
|
"editor",
|
||||||
"feature_flags",
|
"feature_flags",
|
||||||
@@ -7070,7 +6992,6 @@ dependencies = [
|
|||||||
"futures 0.3.31",
|
"futures 0.3.31",
|
||||||
"google_ai",
|
"google_ai",
|
||||||
"gpui",
|
"gpui",
|
||||||
"gpui_tokio",
|
|
||||||
"http_client",
|
"http_client",
|
||||||
"language_model",
|
"language_model",
|
||||||
"lmstudio",
|
"lmstudio",
|
||||||
@@ -7086,9 +7007,10 @@ dependencies = [
|
|||||||
"settings",
|
"settings",
|
||||||
"smol",
|
"smol",
|
||||||
"strum",
|
"strum",
|
||||||
|
"telemetry_events",
|
||||||
"theme",
|
"theme",
|
||||||
|
"thiserror 1.0.69",
|
||||||
"tiktoken-rs",
|
"tiktoken-rs",
|
||||||
"tokio",
|
|
||||||
"ui",
|
"ui",
|
||||||
"util",
|
"util",
|
||||||
]
|
]
|
||||||
@@ -7228,9 +7150,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.170"
|
version = "0.2.169"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828"
|
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libdbus-sys"
|
name = "libdbus-sys"
|
||||||
@@ -7270,7 +7192,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
|
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.48.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -7574,9 +7496,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.26"
|
version = "0.4.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
|
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"value-bag",
|
"value-bag",
|
||||||
@@ -7947,7 +7869,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collections",
|
"collections",
|
||||||
"convert_case 0.8.0",
|
"convert_case 0.7.1",
|
||||||
"log",
|
"log",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"streaming-iterator",
|
"streaming-iterator",
|
||||||
@@ -9326,7 +9248,7 @@ name = "perplexity"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"zed_extension_api 0.3.0",
|
"zed_extension_api 0.2.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -9757,15 +9679,6 @@ dependencies = [
|
|||||||
"indexmap",
|
"indexmap",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pgvector"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e0e8871b6d7ca78348c6cd29b911b94851f3429f0cd403130ca17f26c1fb91a6"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "phf"
|
name = "phf"
|
||||||
version = "0.11.2"
|
version = "0.11.2"
|
||||||
@@ -10389,8 +10302,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4"
|
checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 1.10.0",
|
"bytes 1.10.0",
|
||||||
"heck 0.5.0",
|
"heck 0.4.1",
|
||||||
"itertools 0.10.5",
|
"itertools 0.12.1",
|
||||||
"log",
|
"log",
|
||||||
"multimap 0.10.0",
|
"multimap 0.10.0",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@@ -10423,7 +10336,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1"
|
checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"itertools 0.10.5",
|
"itertools 0.12.1",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.90",
|
"syn 2.0.90",
|
||||||
@@ -11451,9 +11364,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-embed"
|
name = "rust-embed"
|
||||||
version = "8.6.0"
|
version = "8.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b3aba5104622db5c9fc61098de54708feb732e7763d7faa2fa625899f00bf6f"
|
checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rust-embed-impl",
|
"rust-embed-impl",
|
||||||
"rust-embed-utils",
|
"rust-embed-utils",
|
||||||
@@ -11462,9 +11375,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-embed-impl"
|
name = "rust-embed-impl"
|
||||||
version = "8.6.0"
|
version = "8.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1f198c73be048d2c5aa8e12f7960ad08443e56fd39cc26336719fdb4ea0ebaae"
|
checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -11475,9 +11388,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-embed-utils"
|
name = "rust-embed-utils"
|
||||||
version = "8.6.0"
|
version = "8.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a2fcdc9f40c8dc2922842ca9add611ad19f332227fc651d015881ad1552bd9a"
|
checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"globset",
|
"globset",
|
||||||
"sha2",
|
"sha2",
|
||||||
@@ -11752,9 +11665,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "schemars"
|
name = "schemars"
|
||||||
version = "0.8.22"
|
version = "0.8.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615"
|
checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dyn-clone",
|
"dyn-clone",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
@@ -11765,9 +11678,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "schemars_derive"
|
name = "schemars_derive"
|
||||||
version = "0.8.22"
|
version = "0.8.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d"
|
checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -11830,18 +11743,17 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sea-orm"
|
name = "sea-orm"
|
||||||
version = "1.1.6"
|
version = "1.1.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "13fba7b2c749b2d0a00303d5cb13e6761e39a4172554bdf930852cac4e7aeabd"
|
checksum = "1a93194430b419da0801f404baf3b986399d6a2a4f43bc79bc96dea83f92ca43"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-stream",
|
"async-stream",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"bigdecimal",
|
"bigdecimal",
|
||||||
"chrono",
|
"chrono",
|
||||||
"futures-util",
|
"futures 0.3.31",
|
||||||
"log",
|
"log",
|
||||||
"ouroboros",
|
"ouroboros",
|
||||||
"pgvector",
|
|
||||||
"rust_decimal",
|
"rust_decimal",
|
||||||
"sea-orm-macros",
|
"sea-orm-macros",
|
||||||
"sea-query",
|
"sea-query",
|
||||||
@@ -11859,9 +11771,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sea-orm-macros"
|
name = "sea-orm-macros"
|
||||||
version = "1.1.6"
|
version = "1.1.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2568cff8d35d5150b4276cc0dd766192a587f64b6ece60ae3706e0872c4eb209"
|
checksum = "d19e8f22fb474a8a622eb516c46885a080535d8d559386188f525977eaad32b3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck 0.4.1",
|
"heck 0.4.1",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -11934,7 +11846,6 @@ dependencies = [
|
|||||||
"unindent",
|
"unindent",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
"zed_actions",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -12054,18 +11965,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.218"
|
version = "1.0.217"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60"
|
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.218"
|
version = "1.0.217"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b"
|
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -12094,9 +12005,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.139"
|
version = "1.0.138"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6"
|
checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"itoa",
|
"itoa",
|
||||||
@@ -12445,9 +12356,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.14.0"
|
version = "1.13.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
|
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
@@ -13421,9 +13332,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.17.1"
|
version = "3.16.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230"
|
checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"fastrand 2.3.0",
|
"fastrand 2.3.0",
|
||||||
@@ -14578,7 +14489,7 @@ dependencies = [
|
|||||||
name = "ui_macros"
|
name = "ui_macros"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"convert_case 0.8.0",
|
"convert_case 0.7.1",
|
||||||
"linkme",
|
"linkme",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -14781,9 +14692,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.15.1"
|
version = "1.13.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587"
|
checksum = "8c1f41ffb7cf259f1ecc2876861a17e7142e63ead296f671f81f6ae85903e0d6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.3.1",
|
"getrandom 0.3.1",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -14950,16 +14861,26 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vte"
|
name = "vte"
|
||||||
version = "0.15.0"
|
version = "0.13.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a5924018406ce0063cd67f8e008104968b74b563ee1b85dde3ed1f7cb87d3dbd"
|
checksum = "9a0b683b20ef64071ff03745b14391751f6beab06a54347885459b77a3f2caa5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
|
||||||
"bitflags 2.8.0",
|
"bitflags 2.8.0",
|
||||||
"cursor-icon",
|
"cursor-icon",
|
||||||
"log",
|
"log",
|
||||||
"memchr",
|
|
||||||
"serde",
|
"serde",
|
||||||
|
"utf8parse",
|
||||||
|
"vte_generate_state_changes",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vte_generate_state_changes"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2e369bee1b05d510a7b4ed645f5faa90619e05437111783ea5848f28d97d3c2e"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -15761,7 +15682,7 @@ version = "0.1.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -15906,12 +15827,6 @@ dependencies = [
|
|||||||
"syn 2.0.90",
|
"syn 2.0.90",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-link"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-registry"
|
name = "windows-registry"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@@ -16747,7 +16662,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zed"
|
name = "zed"
|
||||||
version = "0.177.0"
|
version = "0.176.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activity_indicator",
|
"activity_indicator",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@@ -16851,7 +16766,6 @@ dependencies = [
|
|||||||
"tasks_ui",
|
"tasks_ui",
|
||||||
"telemetry",
|
"telemetry",
|
||||||
"telemetry_events",
|
"telemetry_events",
|
||||||
"tempfile",
|
|
||||||
"terminal_view",
|
"terminal_view",
|
||||||
"theme",
|
"theme",
|
||||||
"theme_extension",
|
"theme_extension",
|
||||||
@@ -16868,7 +16782,6 @@ dependencies = [
|
|||||||
"vim",
|
"vim",
|
||||||
"vim_mode_setting",
|
"vim_mode_setting",
|
||||||
"welcome",
|
"welcome",
|
||||||
"which 6.0.3",
|
|
||||||
"windows 0.58.0",
|
"windows 0.58.0",
|
||||||
"winresource",
|
"winresource",
|
||||||
"workspace",
|
"workspace",
|
||||||
@@ -16894,7 +16807,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zed_deno"
|
name = "zed_deno"
|
||||||
version = "0.1.0"
|
version = "0.0.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zed_extension_api 0.1.0",
|
"zed_extension_api 0.1.0",
|
||||||
]
|
]
|
||||||
@@ -16903,7 +16816,7 @@ dependencies = [
|
|||||||
name = "zed_elixir"
|
name = "zed_elixir"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zed_extension_api 0.2.0",
|
"zed_extension_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -16934,8 +16847,6 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "zed_extension_api"
|
name = "zed_extension_api"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9fd16b8b30a9dc920fc1678ff852f696b5bdf5b5843bc745a128be0aac29859e"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -16944,7 +16855,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zed_extension_api"
|
name = "zed_extension_api"
|
||||||
version = "0.3.0"
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9fd16b8b30a9dc920fc1678ff852f696b5bdf5b5843bc745a128be0aac29859e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -16967,7 +16880,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zed_html"
|
name = "zed_html"
|
||||||
version = "0.1.6"
|
version = "0.1.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zed_extension_api 0.1.0",
|
"zed_extension_api 0.1.0",
|
||||||
]
|
]
|
||||||
@@ -17030,12 +16943,12 @@ dependencies = [
|
|||||||
name = "zed_test_extension"
|
name = "zed_test_extension"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zed_extension_api 0.3.0",
|
"zed_extension_api 0.2.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zed_toml"
|
name = "zed_toml"
|
||||||
version = "0.1.3"
|
version = "0.1.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zed_extension_api 0.1.0",
|
"zed_extension_api 0.1.0",
|
||||||
]
|
]
|
||||||
@@ -17212,7 +17125,7 @@ dependencies = [
|
|||||||
"indoc",
|
"indoc",
|
||||||
"inline_completion",
|
"inline_completion",
|
||||||
"language",
|
"language",
|
||||||
"language_model",
|
"language_models",
|
||||||
"log",
|
"log",
|
||||||
"menu",
|
"menu",
|
||||||
"migrator",
|
"migrator",
|
||||||
|
|||||||
28
Cargo.toml
28
Cargo.toml
@@ -15,10 +15,7 @@ members = [
|
|||||||
"crates/audio",
|
"crates/audio",
|
||||||
"crates/auto_update",
|
"crates/auto_update",
|
||||||
"crates/auto_update_ui",
|
"crates/auto_update_ui",
|
||||||
"crates/aws_http_client",
|
|
||||||
"crates/bedrock",
|
|
||||||
"crates/breadcrumbs",
|
"crates/breadcrumbs",
|
||||||
"crates/buffer_diff",
|
|
||||||
"crates/call",
|
"crates/call",
|
||||||
"crates/channel",
|
"crates/channel",
|
||||||
"crates/cli",
|
"crates/cli",
|
||||||
@@ -34,10 +31,10 @@ members = [
|
|||||||
"crates/context_server",
|
"crates/context_server",
|
||||||
"crates/context_server_settings",
|
"crates/context_server_settings",
|
||||||
"crates/copilot",
|
"crates/copilot",
|
||||||
"crates/credentials_provider",
|
|
||||||
"crates/db",
|
"crates/db",
|
||||||
"crates/deepseek",
|
"crates/deepseek",
|
||||||
"crates/diagnostics",
|
"crates/diagnostics",
|
||||||
|
"crates/buffer_diff",
|
||||||
"crates/docs_preprocessor",
|
"crates/docs_preprocessor",
|
||||||
"crates/editor",
|
"crates/editor",
|
||||||
"crates/evals",
|
"crates/evals",
|
||||||
@@ -220,8 +217,6 @@ assistant_tools = { path = "crates/assistant_tools" }
|
|||||||
audio = { path = "crates/audio" }
|
audio = { path = "crates/audio" }
|
||||||
auto_update = { path = "crates/auto_update" }
|
auto_update = { path = "crates/auto_update" }
|
||||||
auto_update_ui = { path = "crates/auto_update_ui" }
|
auto_update_ui = { path = "crates/auto_update_ui" }
|
||||||
aws_http_client = { path = "crates/aws_http_client" }
|
|
||||||
bedrock = { path = "crates/bedrock" }
|
|
||||||
breadcrumbs = { path = "crates/breadcrumbs" }
|
breadcrumbs = { path = "crates/breadcrumbs" }
|
||||||
call = { path = "crates/call" }
|
call = { path = "crates/call" }
|
||||||
channel = { path = "crates/channel" }
|
channel = { path = "crates/channel" }
|
||||||
@@ -238,7 +233,6 @@ component_preview = { path = "crates/component_preview" }
|
|||||||
context_server = { path = "crates/context_server" }
|
context_server = { path = "crates/context_server" }
|
||||||
context_server_settings = { path = "crates/context_server_settings" }
|
context_server_settings = { path = "crates/context_server_settings" }
|
||||||
copilot = { path = "crates/copilot" }
|
copilot = { path = "crates/copilot" }
|
||||||
credentials_provider = { path = "crates/credentials_provider" }
|
|
||||||
db = { path = "crates/db" }
|
db = { path = "crates/db" }
|
||||||
deepseek = { path = "crates/deepseek" }
|
deepseek = { path = "crates/deepseek" }
|
||||||
diagnostics = { path = "crates/diagnostics" }
|
diagnostics = { path = "crates/diagnostics" }
|
||||||
@@ -370,7 +364,7 @@ zeta = { path = "crates/zeta" }
|
|||||||
#
|
#
|
||||||
|
|
||||||
aho-corasick = "1.1"
|
aho-corasick = "1.1"
|
||||||
alacritty_terminal = { git = "https://github.com/zed-industries/alacritty.git", rev = "03c2907b44b4189aac5fdeaea331f5aab5c7072e" }
|
alacritty_terminal = "0.25"
|
||||||
any_vec = "0.14"
|
any_vec = "0.14"
|
||||||
anyhow = "1.0.86"
|
anyhow = "1.0.86"
|
||||||
arrayvec = { version = "0.7.4", features = ["serde"] }
|
arrayvec = { version = "0.7.4", features = ["serde"] }
|
||||||
@@ -386,11 +380,6 @@ async-trait = "0.1"
|
|||||||
async-tungstenite = "0.28"
|
async-tungstenite = "0.28"
|
||||||
async-watch = "0.3.1"
|
async-watch = "0.3.1"
|
||||||
async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
|
async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
|
||||||
aws-config = { version = "1.5.16", features = ["behavior-version-latest"] }
|
|
||||||
aws-credential-types = { version = "1.2.1", features = ["hardcoded-credentials"] }
|
|
||||||
aws-sdk-bedrockruntime = { version = "1.73.0", features = ["behavior-version-latest"] }
|
|
||||||
aws-smithy-runtime-api = { version = "1.7.3", features = ["http-1x", "client"] }
|
|
||||||
aws-smithy-types = { version = "1.2.13", features = ["http-body-1-x"] }
|
|
||||||
base64 = "0.22"
|
base64 = "0.22"
|
||||||
bitflags = "2.6.0"
|
bitflags = "2.6.0"
|
||||||
blade-graphics = { git = "https://github.com/kvark/blade", rev = "b16f5c7bd873c7126f48c82c39e7ae64602ae74f" }
|
blade-graphics = { git = "https://github.com/kvark/blade", rev = "b16f5c7bd873c7126f48c82c39e7ae64602ae74f" }
|
||||||
@@ -405,10 +394,10 @@ chrono = { version = "0.4", features = ["serde"] }
|
|||||||
clap = { version = "4.4", features = ["derive"] }
|
clap = { version = "4.4", features = ["derive"] }
|
||||||
cocoa = "0.26"
|
cocoa = "0.26"
|
||||||
cocoa-foundation = "0.2.0"
|
cocoa-foundation = "0.2.0"
|
||||||
convert_case = "0.8.0"
|
convert_case = "0.7.0"
|
||||||
core-foundation = "0.9.3"
|
core-foundation = "0.9.3"
|
||||||
core-foundation-sys = "0.8.6"
|
core-foundation-sys = "0.8.6"
|
||||||
ctor = "0.4.0"
|
ctor = "0.3.0"
|
||||||
dashmap = "6.0"
|
dashmap = "6.0"
|
||||||
derive_more = "0.99.17"
|
derive_more = "0.99.17"
|
||||||
dirs = "4.0"
|
dirs = "4.0"
|
||||||
@@ -455,6 +444,7 @@ nanoid = "0.4"
|
|||||||
nbformat = { version = "0.10.0" }
|
nbformat = { version = "0.10.0" }
|
||||||
nix = "0.29"
|
nix = "0.29"
|
||||||
num-format = "0.4.4"
|
num-format = "0.4.4"
|
||||||
|
once_cell = "1.20"
|
||||||
ordered-float = "2.1.1"
|
ordered-float = "2.1.1"
|
||||||
palette = { version = "0.7.5", default-features = false, features = ["std"] }
|
palette = { version = "0.7.5", default-features = false, features = ["std"] }
|
||||||
parking_lot = "0.12.1"
|
parking_lot = "0.12.1"
|
||||||
@@ -543,7 +533,7 @@ tree-sitter-cpp = "0.23"
|
|||||||
tree-sitter-css = "0.23"
|
tree-sitter-css = "0.23"
|
||||||
tree-sitter-elixir = "0.3"
|
tree-sitter-elixir = "0.3"
|
||||||
tree-sitter-embedded-template = "0.23.0"
|
tree-sitter-embedded-template = "0.23.0"
|
||||||
tree-sitter-gitcommit = { git = "https://github.com/zed-industries/tree-sitter-git-commit", rev = "88309716a69dd13ab83443721ba6e0b491d37ee9" }
|
tree-sitter-gitcommit = {git = "https://github.com/zed-industries/tree-sitter-git-commit", rev = "88309716a69dd13ab83443721ba6e0b491d37ee9"}
|
||||||
tree-sitter-go = "0.23"
|
tree-sitter-go = "0.23"
|
||||||
tree-sitter-go-mod = { git = "https://github.com/camdencheek/tree-sitter-go-mod", rev = "6efb59652d30e0e9cd5f3b3a669afd6f1a926d3c", package = "tree-sitter-gomod" }
|
tree-sitter-go-mod = { git = "https://github.com/camdencheek/tree-sitter-go-mod", rev = "6efb59652d30e0e9cd5f3b3a669afd6f1a926d3c", package = "tree-sitter-gomod" }
|
||||||
tree-sitter-gowork = { git = "https://github.com/zed-industries/tree-sitter-go-work", rev = "acb0617bf7f4fda02c6217676cc64acb89536dc7" }
|
tree-sitter-gowork = { git = "https://github.com/zed-industries/tree-sitter-go-work", rev = "acb0617bf7f4fda02c6217676cc64acb89536dc7" }
|
||||||
@@ -618,7 +608,6 @@ features = [
|
|||||||
"Win32_Storage_FileSystem",
|
"Win32_Storage_FileSystem",
|
||||||
"Win32_System_Com",
|
"Win32_System_Com",
|
||||||
"Win32_System_Com_StructuredStorage",
|
"Win32_System_Com_StructuredStorage",
|
||||||
"Win32_System_Console",
|
|
||||||
"Win32_System_DataExchange",
|
"Win32_System_DataExchange",
|
||||||
"Win32_System_LibraryLoader",
|
"Win32_System_LibraryLoader",
|
||||||
"Win32_System_Memory",
|
"Win32_System_Memory",
|
||||||
@@ -639,7 +628,7 @@ features = [
|
|||||||
# TODO livekit https://github.com/RustAudio/cpal/pull/891
|
# TODO livekit https://github.com/RustAudio/cpal/pull/891
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
cpal = { git = "https://github.com/zed-industries/cpal", rev = "fd8bc2fd39f1f5fdee5a0690656caff9a26d9d50" }
|
cpal = { git = "https://github.com/zed-industries/cpal", rev = "fd8bc2fd39f1f5fdee5a0690656caff9a26d9d50" }
|
||||||
real-async-tls = { git = "https://github.com/zed-industries/async-tls", rev = "1e759a4b5e370f87dc15e40756ac4f8815b61d9d", package = "async-tls" }
|
real-async-tls = { git = "https://github.com/zed-industries/async-tls", rev = "1e759a4b5e370f87dc15e40756ac4f8815b61d9d", package = "async-tls"}
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
split-debuginfo = "unpacked"
|
split-debuginfo = "unpacked"
|
||||||
@@ -709,9 +698,6 @@ debug = "full"
|
|||||||
lto = false
|
lto = false
|
||||||
codegen-units = 16
|
codegen-units = 16
|
||||||
|
|
||||||
[workspace.lints.rust]
|
|
||||||
unexpected_cfgs = { level = "allow" }
|
|
||||||
|
|
||||||
[workspace.lints.clippy]
|
[workspace.lints.clippy]
|
||||||
dbg_macro = "deny"
|
dbg_macro = "deny"
|
||||||
todo = "deny"
|
todo = "deny"
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="283.6413 127.3453 56 55.9999" width="16px" height="16px">
|
|
||||||
<path d="M 808.592 158.131 C 807.489 158.131 806.592 157.234 806.592 156.131 C 806.592 155.028 807.489 154.131 808.592 154.131 C 809.695 154.131 810.592 155.028 810.592 156.131 C 810.592 157.234 809.695 158.131 808.592 158.131 Z M 776.705 185.039 L 773.457 183.145 L 780.122 178.979 L 779.062 177.283 L 771.505 182.006 L 765.592 178.557 L 765.592 169.666 L 771.147 165.963 L 770.037 164.299 L 764.551 167.956 L 758.592 164.551 L 758.592 159.711 L 765.088 155.999 L 764.096 154.263 L 758.592 157.408 L 758.592 153.711 L 764.592 150.283 L 770.592 153.711 L 770.592 157.565 L 766.077 160.274 L 767.107 161.988 L 771.592 159.297 L 776.077 161.988 L 777.107 160.274 L 772.592 157.565 L 772.592 153.666 L 778.147 149.963 C 778.425 149.777 778.592 149.465 778.592 149.131 L 778.592 142.131 L 776.592 142.131 L 776.592 148.596 L 771.551 151.956 L 765.592 148.551 L 765.592 139.705 L 770.592 136.789 L 770.592 145.131 L 772.592 145.131 L 772.592 135.622 L 776.705 133.223 L 784.592 135.852 L 784.592 164.565 L 770.077 173.274 L 771.107 174.988 L 784.592 166.897 L 784.592 182.41 L 776.705 185.039 Z M 806.592 169.131 C 806.592 170.234 805.695 171.131 804.592 171.131 C 803.489 171.131 802.592 170.234 802.592 169.131 C 802.592 168.028 803.489 167.131 804.592 167.131 C 805.695 167.131 806.592 168.028 806.592 169.131 Z M 796.592 179.131 C 796.592 180.234 795.695 181.131 794.592 181.131 C 793.489 181.131 792.592 180.234 792.592 179.131 C 792.592 178.028 793.489 177.131 794.592 177.131 C 795.695 177.131 796.592 178.028 796.592 179.131 Z M 795.592 139.131 C 795.592 138.028 796.489 137.131 797.592 137.131 C 798.695 137.131 799.592 138.028 799.592 139.131 C 799.592 140.234 798.695 141.131 797.592 141.131 C 796.489 141.131 795.592 140.234 795.592 139.131 Z M 808.592 152.131 C 806.733 152.131 805.181 153.411 804.734 155.131 L 786.592 155.131 L 786.592 150.131 L 797.592 150.131 C 798.145 150.131 798.592 149.683 798.592 149.131 L 798.592 142.989 C 800.312 142.542 801.592 140.989 801.592 139.131 C 801.592 136.925 799.798 135.131 797.592 135.131 C 795.386 135.131 793.592 136.925 793.592 139.131 C 793.592 140.989 794.872 142.542 796.592 142.989 L 796.592 148.131 L 786.592 148.131 L 786.592 135.131 C 786.592 134.7 786.317 134.319 785.908 134.182 L 776.908 131.182 C 776.634 131.092 776.336 131.122 776.088 131.267 L 764.088 138.267 C 763.78 138.446 763.592 138.776 763.592 139.131 L 763.592 148.551 L 757.096 152.263 C 756.784 152.441 756.592 152.772 756.592 153.131 L 756.592 165.131 C 756.592 165.49 756.784 165.821 757.096 165.999 L 763.592 169.711 L 763.592 179.131 C 763.592 179.486 763.78 179.816 764.088 179.995 L 776.088 186.995 C 776.242 187.085 776.417 187.131 776.592 187.131 C 776.698 187.131 776.805 187.114 776.908 187.08 L 785.908 184.08 C 786.317 183.943 786.592 183.562 786.592 183.131 L 786.592 171.131 L 793.592 171.131 L 793.592 175.273 C 791.872 175.72 790.592 177.273 790.592 179.131 C 790.592 181.337 792.386 183.131 794.592 183.131 C 796.798 183.131 798.592 181.337 798.592 179.131 C 798.592 177.273 797.312 175.72 795.592 175.273 L 795.592 170.131 C 795.592 169.579 795.145 169.131 794.592 169.131 L 786.592 169.131 L 786.592 164.131 L 799.092 164.131 L 801.23 166.981 C 800.831 167.603 800.592 168.338 800.592 169.131 C 800.592 171.337 802.386 173.131 804.592 173.131 C 806.798 173.131 808.592 171.337 808.592 169.131 C 808.592 166.925 806.798 165.131 804.592 165.131 C 803.908 165.131 803.274 165.319 802.711 165.623 L 800.392 162.531 C 800.203 162.279 799.906 162.131 799.592 162.131 L 786.592 162.131 L 786.592 157.131 L 804.734 157.131 C 805.181 158.851 806.733 160.131 808.592 160.131 C 810.798 160.131 812.592 158.337 812.592 156.131 C 812.592 153.925 810.798 152.131 808.592 152.131 Z" fill-rule="evenodd" fill-opacity="1" style="" id="object-0" transform="matrix(1, 0, 0, 1, -472.9506530761719, -3.7858259677886963)"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 3.9 KiB |
@@ -84,12 +84,12 @@
|
|||||||
"pageup": "editor::MovePageUp",
|
"pageup": "editor::MovePageUp",
|
||||||
"alt-pageup": "editor::PageUp",
|
"alt-pageup": "editor::PageUp",
|
||||||
"shift-pageup": "editor::SelectPageUp",
|
"shift-pageup": "editor::SelectPageUp",
|
||||||
"home": ["editor::MoveToBeginningOfLine", { "stop_at_soft_wraps": true, "stop_at_indent": true }],
|
"home": "editor::MoveToBeginningOfLine",
|
||||||
"down": "editor::MoveDown",
|
"down": "editor::MoveDown",
|
||||||
"pagedown": "editor::MovePageDown",
|
"pagedown": "editor::MovePageDown",
|
||||||
"alt-pagedown": "editor::PageDown",
|
"alt-pagedown": "editor::PageDown",
|
||||||
"shift-pagedown": "editor::SelectPageDown",
|
"shift-pagedown": "editor::SelectPageDown",
|
||||||
"end": ["editor::MoveToEndOfLine", { "stop_at_soft_wraps": true }],
|
"end": "editor::MoveToEndOfLine",
|
||||||
"left": "editor::MoveLeft",
|
"left": "editor::MoveLeft",
|
||||||
"right": "editor::MoveRight",
|
"right": "editor::MoveRight",
|
||||||
"ctrl-left": "editor::MoveToPreviousWordStart",
|
"ctrl-left": "editor::MoveToPreviousWordStart",
|
||||||
@@ -107,9 +107,9 @@
|
|||||||
"ctrl-a": "editor::SelectAll",
|
"ctrl-a": "editor::SelectAll",
|
||||||
"ctrl-l": "editor::SelectLine",
|
"ctrl-l": "editor::SelectLine",
|
||||||
"ctrl-shift-i": "editor::Format",
|
"ctrl-shift-i": "editor::Format",
|
||||||
// "cmd-shift-left": ["editor::SelectToBeginningOfLine", {"stop_at_soft_wraps": true, "stop_at_indent": true }],
|
// "cmd-shift-left": ["editor::SelectToBeginningOfLine", {"stop_at_soft_wraps": true }],
|
||||||
// "ctrl-shift-a": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true, "stop_at_indent": true }],
|
// "ctrl-shift-a": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true }],
|
||||||
"shift-home": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true, "stop_at_indent": true }],
|
"shift-home": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true }],
|
||||||
// "cmd-shift-right": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": true }],
|
// "cmd-shift-right": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": true }],
|
||||||
// "ctrl-shift-e": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": true }],
|
// "ctrl-shift-e": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": true }],
|
||||||
"shift-end": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": true }],
|
"shift-end": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": true }],
|
||||||
@@ -118,7 +118,7 @@
|
|||||||
"ctrl-;": "editor::ToggleLineNumbers",
|
"ctrl-;": "editor::ToggleLineNumbers",
|
||||||
"ctrl-k ctrl-r": "git::Restore",
|
"ctrl-k ctrl-r": "git::Restore",
|
||||||
"ctrl-'": "editor::ToggleSelectedDiffHunks",
|
"ctrl-'": "editor::ToggleSelectedDiffHunks",
|
||||||
"ctrl-\"": "editor::ExpandAllDiffHunks",
|
"ctrl-\"": "editor::ExpandAllHunkDiffs",
|
||||||
"ctrl-i": "editor::ShowSignatureHelp",
|
"ctrl-i": "editor::ShowSignatureHelp",
|
||||||
"alt-g b": "editor::ToggleGitBlame",
|
"alt-g b": "editor::ToggleGitBlame",
|
||||||
"menu": "editor::OpenContextMenu",
|
"menu": "editor::OpenContextMenu",
|
||||||
@@ -184,9 +184,8 @@
|
|||||||
"ctrl-alt-/": "assistant::ToggleModelSelector",
|
"ctrl-alt-/": "assistant::ToggleModelSelector",
|
||||||
"ctrl-k h": "assistant::DeployHistory",
|
"ctrl-k h": "assistant::DeployHistory",
|
||||||
"ctrl-k l": "assistant::DeployPromptLibrary",
|
"ctrl-k l": "assistant::DeployPromptLibrary",
|
||||||
"new": "assistant::NewChat",
|
"new": "assistant::NewContext",
|
||||||
"ctrl-t": "assistant::NewChat",
|
"ctrl-n": "assistant::NewContext"
|
||||||
"ctrl-n": "assistant::NewChat"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -344,9 +343,9 @@
|
|||||||
"alt-ctrl-f12": "editor::GoToTypeDefinitionSplit",
|
"alt-ctrl-f12": "editor::GoToTypeDefinitionSplit",
|
||||||
"alt-shift-f12": "editor::FindAllReferences",
|
"alt-shift-f12": "editor::FindAllReferences",
|
||||||
"ctrl-m": "editor::MoveToEnclosingBracket",
|
"ctrl-m": "editor::MoveToEnclosingBracket",
|
||||||
"ctrl-|": "editor::MoveToEnclosingBracket",
|
"ctrl-shift-\\": "editor::MoveToEnclosingBracket",
|
||||||
"ctrl-{": "editor::Fold",
|
"ctrl-shift-[": "editor::Fold",
|
||||||
"ctrl-}": "editor::UnfoldLines",
|
"ctrl-shift-]": "editor::UnfoldLines",
|
||||||
"ctrl-k ctrl-l": "editor::ToggleFold",
|
"ctrl-k ctrl-l": "editor::ToggleFold",
|
||||||
"ctrl-k ctrl-[": "editor::FoldRecursive",
|
"ctrl-k ctrl-[": "editor::FoldRecursive",
|
||||||
"ctrl-k ctrl-]": "editor::UnfoldRecursive",
|
"ctrl-k ctrl-]": "editor::UnfoldRecursive",
|
||||||
@@ -368,12 +367,7 @@
|
|||||||
"ctrl-\\": "pane::SplitRight",
|
"ctrl-\\": "pane::SplitRight",
|
||||||
"ctrl-k v": "markdown::OpenPreviewToTheSide",
|
"ctrl-k v": "markdown::OpenPreviewToTheSide",
|
||||||
"ctrl-shift-v": "markdown::OpenPreview",
|
"ctrl-shift-v": "markdown::OpenPreview",
|
||||||
"ctrl-alt-shift-c": "editor::DisplayCursorNames",
|
"ctrl-alt-shift-c": "editor::DisplayCursorNames"
|
||||||
"ctrl-alt-y": "git::ToggleStaged",
|
|
||||||
"alt-y": "git::StageAndNext",
|
|
||||||
"alt-shift-y": "git::UnstageAndNext",
|
|
||||||
"alt-.": "editor::GoToHunk",
|
|
||||||
"alt-,": "editor::GoToPrevHunk"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -606,7 +600,7 @@
|
|||||||
"ctrl-n": "assistant2::NewThread",
|
"ctrl-n": "assistant2::NewThread",
|
||||||
"new": "assistant2::NewThread",
|
"new": "assistant2::NewThread",
|
||||||
"ctrl-shift-h": "assistant2::OpenHistory",
|
"ctrl-shift-h": "assistant2::OpenHistory",
|
||||||
"ctrl-alt-/": "assistant::ToggleModelSelector",
|
"ctrl-alt-/": "assistant2::ToggleModelSelector",
|
||||||
"ctrl-shift-a": "assistant2::ToggleContextPicker",
|
"ctrl-shift-a": "assistant2::ToggleContextPicker",
|
||||||
"ctrl-e": "assistant2::ChatMode",
|
"ctrl-e": "assistant2::ChatMode",
|
||||||
"ctrl-alt-e": "assistant2::RemoveAllContext"
|
"ctrl-alt-e": "assistant2::RemoveAllContext"
|
||||||
@@ -710,6 +704,12 @@
|
|||||||
"space": "project_panel::Open"
|
"space": "project_panel::Open"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"context": "GitPanel && !CommitEditor",
|
||||||
|
"bindings": {
|
||||||
|
"escape": "git_panel::Close"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"context": "GitPanel && ChangesList",
|
"context": "GitPanel && ChangesList",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
@@ -721,42 +721,19 @@
|
|||||||
"ctrl-shift-space": "git::UnstageAll",
|
"ctrl-shift-space": "git::UnstageAll",
|
||||||
"tab": "git_panel::FocusEditor",
|
"tab": "git_panel::FocusEditor",
|
||||||
"shift-tab": "git_panel::FocusEditor",
|
"shift-tab": "git_panel::FocusEditor",
|
||||||
"escape": "git_panel::ToggleFocus",
|
"escape": "git_panel::ToggleFocus"
|
||||||
"ctrl-enter": "git::Commit",
|
|
||||||
"alt-enter": "menu::SecondaryConfirm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"context": "GitCommit > Editor",
|
|
||||||
"bindings": {
|
|
||||||
"enter": "editor::Newline",
|
|
||||||
"ctrl-enter": "git::Commit"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"context": "GitDiff > Editor",
|
|
||||||
"bindings": {
|
|
||||||
"ctrl-enter": "git::Commit"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"context": "GitPanel > Editor",
|
"context": "GitPanel > Editor",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"escape": "git_panel::FocusChanges",
|
"escape": "git_panel::FocusChanges",
|
||||||
|
"ctrl-enter": "git::Commit",
|
||||||
"tab": "git_panel::FocusChanges",
|
"tab": "git_panel::FocusChanges",
|
||||||
"shift-tab": "git_panel::FocusChanges",
|
"shift-tab": "git_panel::FocusChanges",
|
||||||
"ctrl-enter": "git::Commit",
|
|
||||||
"alt-up": "git_panel::FocusChanges"
|
"alt-up": "git_panel::FocusChanges"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"context": "GitCommit > Editor",
|
|
||||||
"use_key_equivalents": true,
|
|
||||||
"bindings": {
|
|
||||||
"enter": "editor::Newline",
|
|
||||||
"ctrl-enter": "git::Commit"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"context": "CollabPanel && not_editing",
|
"context": "CollabPanel && not_editing",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
@@ -835,7 +812,6 @@
|
|||||||
"pagedown": ["terminal::SendKeystroke", "pagedown"],
|
"pagedown": ["terminal::SendKeystroke", "pagedown"],
|
||||||
"escape": ["terminal::SendKeystroke", "escape"],
|
"escape": ["terminal::SendKeystroke", "escape"],
|
||||||
"enter": ["terminal::SendKeystroke", "enter"],
|
"enter": ["terminal::SendKeystroke", "enter"],
|
||||||
"ctrl-b": ["terminal::SendKeystroke", "ctrl-b"],
|
|
||||||
"ctrl-c": ["terminal::SendKeystroke", "ctrl-c"],
|
"ctrl-c": ["terminal::SendKeystroke", "ctrl-c"],
|
||||||
"shift-pageup": "terminal::ScrollPageUp",
|
"shift-pageup": "terminal::ScrollPageUp",
|
||||||
"shift-pagedown": "terminal::ScrollPageDown",
|
"shift-pagedown": "terminal::ScrollPageDown",
|
||||||
|
|||||||
@@ -91,16 +91,14 @@
|
|||||||
"ctrl-l": "editor::ScrollCursorCenter",
|
"ctrl-l": "editor::ScrollCursorCenter",
|
||||||
"alt-left": "editor::MoveToPreviousWordStart",
|
"alt-left": "editor::MoveToPreviousWordStart",
|
||||||
"alt-right": "editor::MoveToNextWordEnd",
|
"alt-right": "editor::MoveToNextWordEnd",
|
||||||
"cmd-left": ["editor::MoveToBeginningOfLine", { "stop_at_soft_wraps": true, "stop_at_indent": true }],
|
"cmd-left": "editor::MoveToBeginningOfLine",
|
||||||
"ctrl-a": ["editor::MoveToBeginningOfLine", { "stop_at_soft_wraps": false, "stop_at_indent": true }],
|
"ctrl-a": "editor::MoveToBeginningOfLine",
|
||||||
"home": ["editor::MoveToBeginningOfLine", { "stop_at_soft_wraps": true, "stop_at_indent": true }],
|
"home": "editor::MoveToBeginningOfLine",
|
||||||
"cmd-right": ["editor::MoveToEndOfLine", { "stop_at_soft_wraps": true }],
|
"cmd-right": "editor::MoveToEndOfLine",
|
||||||
"ctrl-e": ["editor::MoveToEndOfLine", { "stop_at_soft_wraps": false }],
|
"ctrl-e": "editor::MoveToEndOfLine",
|
||||||
"end": ["editor::MoveToEndOfLine", { "stop_at_soft_wraps": true }],
|
"end": "editor::MoveToEndOfLine",
|
||||||
"cmd-up": "editor::MoveToStartOfExcerpt",
|
"cmd-up": "editor::MoveToBeginning",
|
||||||
"cmd-down": "editor::MoveToEndOfExcerpt",
|
"cmd-down": "editor::MoveToEnd",
|
||||||
"cmd-home": "editor::MoveToBeginning", // Typed via `cmd-fn-left`
|
|
||||||
"cmd-end": "editor::MoveToEnd", // Typed via `cmd-fn-right`
|
|
||||||
"shift-up": "editor::SelectUp",
|
"shift-up": "editor::SelectUp",
|
||||||
"ctrl-shift-p": "editor::SelectUp",
|
"ctrl-shift-p": "editor::SelectUp",
|
||||||
"shift-down": "editor::SelectDown",
|
"shift-down": "editor::SelectDown",
|
||||||
@@ -113,14 +111,14 @@
|
|||||||
"alt-shift-right": "editor::SelectToNextWordEnd", // cursorWordRightSelect
|
"alt-shift-right": "editor::SelectToNextWordEnd", // cursorWordRightSelect
|
||||||
"ctrl-shift-up": "editor::SelectToStartOfParagraph",
|
"ctrl-shift-up": "editor::SelectToStartOfParagraph",
|
||||||
"ctrl-shift-down": "editor::SelectToEndOfParagraph",
|
"ctrl-shift-down": "editor::SelectToEndOfParagraph",
|
||||||
"cmd-shift-up": "editor::SelectToStartOfExcerpt",
|
"cmd-shift-up": "editor::SelectToBeginning",
|
||||||
"cmd-shift-down": "editor::SelectToEndOfExcerpt",
|
"cmd-shift-down": "editor::SelectToEnd",
|
||||||
"cmd-a": "editor::SelectAll",
|
"cmd-a": "editor::SelectAll",
|
||||||
"cmd-l": "editor::SelectLine",
|
"cmd-l": "editor::SelectLine",
|
||||||
"cmd-shift-i": "editor::Format",
|
"cmd-shift-i": "editor::Format",
|
||||||
"cmd-shift-left": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true, "stop_at_indent": true }],
|
"cmd-shift-left": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true }],
|
||||||
"shift-home": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true, "stop_at_indent": true }],
|
"shift-home": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true }],
|
||||||
"ctrl-shift-a": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true, "stop_at_indent": true }],
|
"ctrl-shift-a": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true }],
|
||||||
"cmd-shift-right": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": true }],
|
"cmd-shift-right": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": true }],
|
||||||
"shift-end": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": true }],
|
"shift-end": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": true }],
|
||||||
"ctrl-shift-e": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": true }],
|
"ctrl-shift-e": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": true }],
|
||||||
@@ -133,7 +131,7 @@
|
|||||||
"cmd-y": "git::StageAndNext",
|
"cmd-y": "git::StageAndNext",
|
||||||
"cmd-shift-y": "git::UnstageAndNext",
|
"cmd-shift-y": "git::UnstageAndNext",
|
||||||
"cmd-'": "editor::ToggleSelectedDiffHunks",
|
"cmd-'": "editor::ToggleSelectedDiffHunks",
|
||||||
"cmd-\"": "editor::ExpandAllDiffHunks",
|
"cmd-\"": "editor::ExpandAllHunkDiffs",
|
||||||
"cmd-alt-g b": "editor::ToggleGitBlame",
|
"cmd-alt-g b": "editor::ToggleGitBlame",
|
||||||
"cmd-i": "editor::ShowSignatureHelp",
|
"cmd-i": "editor::ShowSignatureHelp",
|
||||||
"ctrl-f12": "editor::GoToDeclaration",
|
"ctrl-f12": "editor::GoToDeclaration",
|
||||||
@@ -211,8 +209,7 @@
|
|||||||
"cmd-alt-/": "assistant::ToggleModelSelector",
|
"cmd-alt-/": "assistant::ToggleModelSelector",
|
||||||
"cmd-k h": "assistant::DeployHistory",
|
"cmd-k h": "assistant::DeployHistory",
|
||||||
"cmd-k l": "assistant::DeployPromptLibrary",
|
"cmd-k l": "assistant::DeployPromptLibrary",
|
||||||
"cmd-t": "assistant::NewChat",
|
"cmd-n": "assistant::NewContext"
|
||||||
"cmd-n": "assistant::NewChat"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -238,7 +235,7 @@
|
|||||||
"cmd-n": "assistant2::NewThread",
|
"cmd-n": "assistant2::NewThread",
|
||||||
"cmd-alt-p": "assistant2::NewPromptEditor",
|
"cmd-alt-p": "assistant2::NewPromptEditor",
|
||||||
"cmd-shift-h": "assistant2::OpenHistory",
|
"cmd-shift-h": "assistant2::OpenHistory",
|
||||||
"cmd-alt-/": "assistant::ToggleModelSelector",
|
"cmd-alt-/": "assistant2::ToggleModelSelector",
|
||||||
"cmd-shift-a": "assistant2::ToggleContextPicker",
|
"cmd-shift-a": "assistant2::ToggleContextPicker",
|
||||||
"cmd-e": "assistant2::ChatMode",
|
"cmd-e": "assistant2::ChatMode",
|
||||||
"cmd-alt-e": "assistant2::RemoveAllContext"
|
"cmd-alt-e": "assistant2::RemoveAllContext"
|
||||||
@@ -289,8 +286,7 @@
|
|||||||
"alt-enter": "search::SelectAllMatches",
|
"alt-enter": "search::SelectAllMatches",
|
||||||
"cmd-f": "search::FocusSearch",
|
"cmd-f": "search::FocusSearch",
|
||||||
"cmd-alt-f": "search::ToggleReplace",
|
"cmd-alt-f": "search::ToggleReplace",
|
||||||
"cmd-alt-l": "search::ToggleSelection",
|
"cmd-alt-l": "search::ToggleSelection"
|
||||||
"cmd-shift-o": "outline::Toggle"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -468,7 +464,7 @@
|
|||||||
"ctrl-9": ["pane::ActivateItem", 8],
|
"ctrl-9": ["pane::ActivateItem", 8],
|
||||||
"ctrl-0": "pane::ActivateLastItem",
|
"ctrl-0": "pane::ActivateLastItem",
|
||||||
"ctrl--": "pane::GoBack",
|
"ctrl--": "pane::GoBack",
|
||||||
"ctrl-_": "pane::GoForward",
|
"ctrl-shift--": "pane::GoForward",
|
||||||
"cmd-shift-f": "pane::DeploySearch"
|
"cmd-shift-f": "pane::DeploySearch"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -658,7 +654,7 @@
|
|||||||
"use_key_equivalents": true,
|
"use_key_equivalents": true,
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"cmd-shift-a": "assistant2::ToggleContextPicker",
|
"cmd-shift-a": "assistant2::ToggleContextPicker",
|
||||||
"cmd-alt-/": "assistant::ToggleModelSelector",
|
"cmd-alt-/": "assistant2::ToggleModelSelector",
|
||||||
"cmd-alt-e": "assistant2::RemoveAllContext",
|
"cmd-alt-e": "assistant2::RemoveAllContext",
|
||||||
"ctrl-[": "assistant::CyclePreviousInlineAssist",
|
"ctrl-[": "assistant::CyclePreviousInlineAssist",
|
||||||
"ctrl-]": "assistant::CycleNextInlineAssist"
|
"ctrl-]": "assistant::CycleNextInlineAssist"
|
||||||
@@ -739,15 +735,7 @@
|
|||||||
"alt-down": "git_panel::FocusEditor",
|
"alt-down": "git_panel::FocusEditor",
|
||||||
"tab": "git_panel::FocusEditor",
|
"tab": "git_panel::FocusEditor",
|
||||||
"shift-tab": "git_panel::FocusEditor",
|
"shift-tab": "git_panel::FocusEditor",
|
||||||
"escape": "git_panel::ToggleFocus",
|
"escape": "git_panel::ToggleFocus"
|
||||||
"cmd-enter": "git::Commit"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"context": "GitDiff > Editor",
|
|
||||||
"use_key_equivalents": true,
|
|
||||||
"bindings": {
|
|
||||||
"cmd-enter": "git::Commit"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -761,14 +749,6 @@
|
|||||||
"alt-up": "git_panel::FocusChanges"
|
"alt-up": "git_panel::FocusChanges"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"context": "GitCommit > Editor",
|
|
||||||
"use_key_equivalents": true,
|
|
||||||
"bindings": {
|
|
||||||
"enter": "editor::Newline",
|
|
||||||
"cmd-enter": "git::Commit"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"context": "CollabPanel && not_editing",
|
"context": "CollabPanel && not_editing",
|
||||||
"use_key_equivalents": true,
|
"use_key_equivalents": true,
|
||||||
@@ -839,7 +819,6 @@
|
|||||||
"cmd-k": "terminal::Clear",
|
"cmd-k": "terminal::Clear",
|
||||||
"cmd-n": "workspace::NewTerminal",
|
"cmd-n": "workspace::NewTerminal",
|
||||||
"ctrl-enter": "assistant::InlineAssist",
|
"ctrl-enter": "assistant::InlineAssist",
|
||||||
"ctrl-_": null, // emacs undo
|
|
||||||
// Some nice conveniences
|
// Some nice conveniences
|
||||||
"cmd-backspace": ["terminal::SendText", "\u0015"],
|
"cmd-backspace": ["terminal::SendText", "\u0015"],
|
||||||
"cmd-right": ["terminal::SendText", "\u0005"],
|
"cmd-right": ["terminal::SendText", "\u0005"],
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
"ctrl-e": ["editor::MoveToEndOfLine", { "stop_at_soft_wraps": false }], // move-end-of-line
|
"ctrl-e": ["editor::MoveToEndOfLine", { "stop_at_soft_wraps": false }], // move-end-of-line
|
||||||
"shift-home": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": false }], // move-beginning-of-line
|
"shift-home": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": false }], // move-beginning-of-line
|
||||||
"shift-end": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": false }], // move-end-of-line
|
"shift-end": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": false }], // move-end-of-line
|
||||||
"alt-m": ["editor::MoveToBeginningOfLine", { "stop_at_soft_wraps": false, "stop_at_indent": true }], // back-to-indentation
|
|
||||||
"alt-f": "editor::MoveToNextSubwordEnd", // forward-word
|
"alt-f": "editor::MoveToNextSubwordEnd", // forward-word
|
||||||
"alt-b": "editor::MoveToPreviousSubwordStart", // backward-word
|
"alt-b": "editor::MoveToPreviousSubwordStart", // backward-word
|
||||||
"alt-u": "editor::ConvertToUpperCase", // upcase-word
|
"alt-u": "editor::ConvertToUpperCase", // upcase-word
|
||||||
@@ -49,8 +48,6 @@
|
|||||||
"ctrl-_": "editor::Undo", // undo
|
"ctrl-_": "editor::Undo", // undo
|
||||||
"ctrl-/": "editor::Undo", // undo
|
"ctrl-/": "editor::Undo", // undo
|
||||||
"ctrl-x u": "editor::Undo", // undo
|
"ctrl-x u": "editor::Undo", // undo
|
||||||
"alt-{": "editor::MoveToStartOfParagraph", // backward-paragraph
|
|
||||||
"alt-}": "editor::MoveToEndOfParagraph", // forward-paragraph
|
|
||||||
"ctrl-v": "editor::MovePageDown", // scroll-up
|
"ctrl-v": "editor::MovePageDown", // scroll-up
|
||||||
"alt-v": "editor::MovePageUp", // scroll-down
|
"alt-v": "editor::MovePageUp", // scroll-down
|
||||||
"ctrl-x [": "editor::MoveToBeginning", // beginning-of-buffer
|
"ctrl-x [": "editor::MoveToBeginning", // beginning-of-buffer
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
{
|
{
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"ctrl-alt-s": "zed::OpenSettings",
|
"ctrl-alt-s": "zed::OpenSettings",
|
||||||
"ctrl-{": "pane::ActivatePrevItem",
|
"ctrl-shift-[": "pane::ActivatePrevItem",
|
||||||
"ctrl-}": "pane::ActivateNextItem"
|
"ctrl-shift-]": "pane::ActivateNextItem"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"ctrl-{": "pane::ActivatePrevItem",
|
"ctrl-shift-[": "pane::ActivatePrevItem",
|
||||||
"ctrl-}": "pane::ActivateNextItem",
|
"ctrl-shift-]": "pane::ActivateNextItem",
|
||||||
"ctrl-pageup": "pane::ActivatePrevItem",
|
"ctrl-pageup": "pane::ActivatePrevItem",
|
||||||
"ctrl-pagedown": "pane::ActivateNextItem",
|
"ctrl-pagedown": "pane::ActivateNextItem",
|
||||||
"ctrl-1": ["workspace::ActivatePane", 0],
|
"ctrl-1": ["workspace::ActivatePane", 0],
|
||||||
@@ -14,15 +14,15 @@
|
|||||||
"ctrl-7": ["workspace::ActivatePane", 6],
|
"ctrl-7": ["workspace::ActivatePane", 6],
|
||||||
"ctrl-8": ["workspace::ActivatePane", 7],
|
"ctrl-8": ["workspace::ActivatePane", 7],
|
||||||
"ctrl-9": ["workspace::ActivatePane", 8],
|
"ctrl-9": ["workspace::ActivatePane", 8],
|
||||||
"ctrl-!": ["workspace::MoveItemToPane", { "destination": 0, "focus": true }],
|
"ctrl-shift-1": ["workspace::MoveItemToPane", { "destination": 0, "focus": true }],
|
||||||
"ctrl-@": ["workspace::MoveItemToPane", { "destination": 1 }],
|
"ctrl-shift-2": ["workspace::MoveItemToPane", { "destination": 1 }],
|
||||||
"ctrl-#": ["workspace::MoveItemToPane", { "destination": 2 }],
|
"ctrl-shift-3": ["workspace::MoveItemToPane", { "destination": 2 }],
|
||||||
"ctrl-$": ["workspace::MoveItemToPane", { "destination": 3 }],
|
"ctrl-shift-4": ["workspace::MoveItemToPane", { "destination": 3 }],
|
||||||
"ctrl-%": ["workspace::MoveItemToPane", { "destination": 4 }],
|
"ctrl-shift-5": ["workspace::MoveItemToPane", { "destination": 4 }],
|
||||||
"ctrl-^": ["workspace::MoveItemToPane", { "destination": 5 }],
|
"ctrl-shift-6": ["workspace::MoveItemToPane", { "destination": 5 }],
|
||||||
"ctrl-&": ["workspace::MoveItemToPane", { "destination": 6 }],
|
"ctrl-shift-7": ["workspace::MoveItemToPane", { "destination": 6 }],
|
||||||
"ctrl-*": ["workspace::MoveItemToPane", { "destination": 7 }],
|
"ctrl-shift-8": ["workspace::MoveItemToPane", { "destination": 7 }],
|
||||||
"ctrl-(": ["workspace::MoveItemToPane", { "destination": 8 }]
|
"ctrl-shift-9": ["workspace::MoveItemToPane", { "destination": 8 }]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
"ctrl-e": ["editor::MoveToEndOfLine", { "stop_at_soft_wraps": false }], // move-end-of-line
|
"ctrl-e": ["editor::MoveToEndOfLine", { "stop_at_soft_wraps": false }], // move-end-of-line
|
||||||
"shift-home": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": false }], // move-beginning-of-line
|
"shift-home": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": false }], // move-beginning-of-line
|
||||||
"shift-end": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": false }], // move-end-of-line
|
"shift-end": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": false }], // move-end-of-line
|
||||||
"alt-m": ["editor::MoveToBeginningOfLine", { "stop_at_soft_wraps": false, "stop_at_indent": true }], // back-to-indentation
|
|
||||||
"alt-f": "editor::MoveToNextSubwordEnd", // forward-word
|
"alt-f": "editor::MoveToNextSubwordEnd", // forward-word
|
||||||
"alt-b": "editor::MoveToPreviousSubwordStart", // backward-word
|
"alt-b": "editor::MoveToPreviousSubwordStart", // backward-word
|
||||||
"alt-u": "editor::ConvertToUpperCase", // upcase-word
|
"alt-u": "editor::ConvertToUpperCase", // upcase-word
|
||||||
@@ -49,8 +48,6 @@
|
|||||||
"ctrl-_": "editor::Undo", // undo
|
"ctrl-_": "editor::Undo", // undo
|
||||||
"ctrl-/": "editor::Undo", // undo
|
"ctrl-/": "editor::Undo", // undo
|
||||||
"ctrl-x u": "editor::Undo", // undo
|
"ctrl-x u": "editor::Undo", // undo
|
||||||
"alt-{": "editor::MoveToStartOfParagraph", // backward-paragraph
|
|
||||||
"alt-}": "editor::MoveToEndOfParagraph", // forward-paragraph
|
|
||||||
"ctrl-v": "editor::MovePageDown", // scroll-up
|
"ctrl-v": "editor::MovePageDown", // scroll-up
|
||||||
"alt-v": "editor::MovePageUp", // scroll-down
|
"alt-v": "editor::MovePageUp", // scroll-down
|
||||||
"ctrl-x [": "editor::MoveToBeginning", // beginning-of-buffer
|
"ctrl-x [": "editor::MoveToBeginning", // beginning-of-buffer
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"cmd-{": "pane::ActivatePrevItem",
|
"cmd-shift-[": "pane::ActivatePrevItem",
|
||||||
"cmd-}": "pane::ActivateNextItem"
|
"cmd-shift-]": "pane::ActivateNextItem"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"cmd-{": "pane::ActivatePrevItem",
|
"cmd-shift-[": "pane::ActivatePrevItem",
|
||||||
"cmd-}": "pane::ActivateNextItem",
|
"cmd-shift-]": "pane::ActivateNextItem",
|
||||||
"ctrl-pageup": "pane::ActivatePrevItem",
|
"ctrl-pageup": "pane::ActivatePrevItem",
|
||||||
"ctrl-pagedown": "pane::ActivateNextItem",
|
"ctrl-pagedown": "pane::ActivateNextItem",
|
||||||
"ctrl-1": ["workspace::ActivatePane", 0],
|
"ctrl-1": ["workspace::ActivatePane", 0],
|
||||||
@@ -14,15 +14,15 @@
|
|||||||
"ctrl-7": ["workspace::ActivatePane", 6],
|
"ctrl-7": ["workspace::ActivatePane", 6],
|
||||||
"ctrl-8": ["workspace::ActivatePane", 7],
|
"ctrl-8": ["workspace::ActivatePane", 7],
|
||||||
"ctrl-9": ["workspace::ActivatePane", 8],
|
"ctrl-9": ["workspace::ActivatePane", 8],
|
||||||
"ctrl-!": ["workspace::MoveItemToPane", { "destination": 0, "focus": true }],
|
"ctrl-shift-1": ["workspace::MoveItemToPane", { "destination": 0, "focus": true }],
|
||||||
"ctrl-@": ["workspace::MoveItemToPane", { "destination": 1 }],
|
"ctrl-shift-2": ["workspace::MoveItemToPane", { "destination": 1 }],
|
||||||
"ctrl-#": ["workspace::MoveItemToPane", { "destination": 2 }],
|
"ctrl-shift-3": ["workspace::MoveItemToPane", { "destination": 2 }],
|
||||||
"ctrl-$": ["workspace::MoveItemToPane", { "destination": 3 }],
|
"ctrl-shift-4": ["workspace::MoveItemToPane", { "destination": 3 }],
|
||||||
"ctrl-%": ["workspace::MoveItemToPane", { "destination": 4 }],
|
"ctrl-shift-5": ["workspace::MoveItemToPane", { "destination": 4 }],
|
||||||
"ctrl-^": ["workspace::MoveItemToPane", { "destination": 5 }],
|
"ctrl-shift-6": ["workspace::MoveItemToPane", { "destination": 5 }],
|
||||||
"ctrl-&": ["workspace::MoveItemToPane", { "destination": 6 }],
|
"ctrl-shift-7": ["workspace::MoveItemToPane", { "destination": 6 }],
|
||||||
"ctrl-*": ["workspace::MoveItemToPane", { "destination": 7 }],
|
"ctrl-shift-8": ["workspace::MoveItemToPane", { "destination": 7 }],
|
||||||
"ctrl-(": ["workspace::MoveItemToPane", { "destination": 8 }]
|
"ctrl-shift-9": ["workspace::MoveItemToPane", { "destination": 8 }]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -293,7 +293,6 @@
|
|||||||
"!": "vim::ShellCommand",
|
"!": "vim::ShellCommand",
|
||||||
"i": ["vim::PushObject", { "around": false }],
|
"i": ["vim::PushObject", { "around": false }],
|
||||||
"a": ["vim::PushObject", { "around": true }],
|
"a": ["vim::PushObject", { "around": true }],
|
||||||
"g r": ["vim::Paste", { "preserve_clipboard": true }],
|
|
||||||
"g c": "vim::ToggleComments",
|
"g c": "vim::ToggleComments",
|
||||||
"g q": "vim::Rewrap",
|
"g q": "vim::Rewrap",
|
||||||
"\"": "vim::PushRegister",
|
"\"": "vim::PushRegister",
|
||||||
@@ -437,7 +436,6 @@
|
|||||||
"context": "vim_operator == c",
|
"context": "vim_operator == c",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"c": "vim::CurrentLine",
|
"c": "vim::CurrentLine",
|
||||||
"x": "vim::Exchange",
|
|
||||||
"d": "editor::Rename", // zed specific
|
"d": "editor::Rename", // zed specific
|
||||||
"s": ["vim::PushChangeSurrounds", {}]
|
"s": ["vim::PushChangeSurrounds", {}]
|
||||||
}
|
}
|
||||||
@@ -524,19 +522,6 @@
|
|||||||
"c": "vim::CurrentLine"
|
"c": "vim::CurrentLine"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"context": "vim_operator == gr",
|
|
||||||
"bindings": {
|
|
||||||
"r": "vim::CurrentLine"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"context": "vim_operator == cx",
|
|
||||||
"bindings": {
|
|
||||||
"x": "vim::CurrentLine",
|
|
||||||
"c": "vim::ClearExchange"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"context": "vim_mode == literal",
|
"context": "vim_mode == literal",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
|
|||||||
@@ -176,8 +176,8 @@
|
|||||||
"show_completion_documentation": true,
|
"show_completion_documentation": true,
|
||||||
// Show method signatures in the editor, when inside parentheses.
|
// Show method signatures in the editor, when inside parentheses.
|
||||||
"auto_signature_help": false,
|
"auto_signature_help": false,
|
||||||
// Whether to show the signature help after completion or a bracket pair inserted.
|
/// Whether to show the signature help after completion or a bracket pair inserted.
|
||||||
// If `auto_signature_help` is enabled, this setting will be treated as enabled also.
|
/// If `auto_signature_help` is enabled, this setting will be treated as enabled also.
|
||||||
"show_signature_help_after_edits": false,
|
"show_signature_help_after_edits": false,
|
||||||
// Whether to show wrap guides (vertical rulers) in the editor.
|
// Whether to show wrap guides (vertical rulers) in the editor.
|
||||||
// Setting this to true will show a guide at the 'preferred_line_length' value
|
// Setting this to true will show a guide at the 'preferred_line_length' value
|
||||||
@@ -298,11 +298,11 @@
|
|||||||
// - "information": show only errors, warnings, and information
|
// - "information": show only errors, warnings, and information
|
||||||
// - "all" or true: show all diagnostics
|
// - "all" or true: show all diagnostics
|
||||||
"diagnostics": "all",
|
"diagnostics": "all",
|
||||||
// Forcefully enable or disable the scrollbar for each axis
|
/// Forcefully enable or disable the scrollbar for each axis
|
||||||
"axes": {
|
"axes": {
|
||||||
// When false, forcefully disables the horizontal scrollbar. Otherwise, obey other settings.
|
/// When false, forcefully disables the horizontal scrollbar. Otherwise, obey other settings.
|
||||||
"horizontal": true,
|
"horizontal": true,
|
||||||
// When false, forcefully disables the vertical scrollbar. Otherwise, obey other settings.
|
/// When false, forcefully disables the vertical scrollbar. Otherwise, obey other settings.
|
||||||
"vertical": true
|
"vertical": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -328,24 +328,24 @@
|
|||||||
"folds": true
|
"folds": true
|
||||||
},
|
},
|
||||||
"indent_guides": {
|
"indent_guides": {
|
||||||
// Whether to show indent guides in the editor.
|
/// Whether to show indent guides in the editor.
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
// The width of the indent guides in pixels, between 1 and 10.
|
/// The width of the indent guides in pixels, between 1 and 10.
|
||||||
"line_width": 1,
|
"line_width": 1,
|
||||||
// The width of the active indent guide in pixels, between 1 and 10.
|
/// The width of the active indent guide in pixels, between 1 and 10.
|
||||||
"active_line_width": 1,
|
"active_line_width": 1,
|
||||||
// Determines how indent guides are colored.
|
/// Determines how indent guides are colored.
|
||||||
// This setting can take the following three values:
|
/// This setting can take the following three values:
|
||||||
///
|
///
|
||||||
// 1. "disabled"
|
/// 1. "disabled"
|
||||||
// 2. "fixed"
|
/// 2. "fixed"
|
||||||
// 3. "indent_aware"
|
/// 3. "indent_aware"
|
||||||
"coloring": "fixed",
|
"coloring": "fixed",
|
||||||
// Determines how indent guide backgrounds are colored.
|
/// Determines how indent guide backgrounds are colored.
|
||||||
// This setting can take the following two values:
|
/// This setting can take the following two values:
|
||||||
///
|
///
|
||||||
// 1. "disabled"
|
/// 1. "disabled"
|
||||||
// 2. "indent_aware"
|
/// 2. "indent_aware"
|
||||||
"background_coloring": "disabled"
|
"background_coloring": "disabled"
|
||||||
},
|
},
|
||||||
// Whether the editor will scroll beyond the last line.
|
// Whether the editor will scroll beyond the last line.
|
||||||
@@ -379,9 +379,6 @@
|
|||||||
// 3. Never populate the search query
|
// 3. Never populate the search query
|
||||||
// "never"
|
// "never"
|
||||||
"seed_search_query_from_cursor": "always",
|
"seed_search_query_from_cursor": "always",
|
||||||
// When enabled, automatically adjusts search case sensitivity based on your query.
|
|
||||||
// If your search query contains any uppercase letters, the search becomes case-sensitive;
|
|
||||||
// if it contains only lowercase letters, the search becomes case-insensitive.
|
|
||||||
"use_smartcase_search": false,
|
"use_smartcase_search": false,
|
||||||
// Inlay hint related settings
|
// Inlay hint related settings
|
||||||
"inlay_hints": {
|
"inlay_hints": {
|
||||||
@@ -401,16 +398,7 @@
|
|||||||
"edit_debounce_ms": 700,
|
"edit_debounce_ms": 700,
|
||||||
// Time to wait after scrolling the buffer, before requesting the hints,
|
// Time to wait after scrolling the buffer, before requesting the hints,
|
||||||
// set to 0 to disable debouncing.
|
// set to 0 to disable debouncing.
|
||||||
"scroll_debounce_ms": 50,
|
"scroll_debounce_ms": 50
|
||||||
/// A set of modifiers which, when pressed, will toggle the visibility of inlay hints.
|
|
||||||
/// If the set if empty or not all the modifiers specified are pressed, inlay hints will not be toggled.
|
|
||||||
"toggle_on_modifiers_press": {
|
|
||||||
"control": false,
|
|
||||||
"shift": false,
|
|
||||||
"alt": false,
|
|
||||||
"platform": false,
|
|
||||||
"function": false
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"project_panel": {
|
"project_panel": {
|
||||||
// Whether to show the project panel button in the status bar
|
// Whether to show the project panel button in the status bar
|
||||||
@@ -436,32 +424,32 @@
|
|||||||
// Whether to fold directories automatically and show compact folders
|
// Whether to fold directories automatically and show compact folders
|
||||||
// (e.g. "a/b/c" ) when a directory has only one subdirectory inside.
|
// (e.g. "a/b/c" ) when a directory has only one subdirectory inside.
|
||||||
"auto_fold_dirs": true,
|
"auto_fold_dirs": true,
|
||||||
// Scrollbar-related settings
|
/// Scrollbar-related settings
|
||||||
"scrollbar": {
|
"scrollbar": {
|
||||||
// When to show the scrollbar in the project panel.
|
/// When to show the scrollbar in the project panel.
|
||||||
// This setting can take five values:
|
/// This setting can take five values:
|
||||||
///
|
///
|
||||||
// 1. null (default): Inherit editor settings
|
/// 1. null (default): Inherit editor settings
|
||||||
// 2. Show the scrollbar if there's important information or
|
/// 2. Show the scrollbar if there's important information or
|
||||||
// follow the system's configured behavior (default):
|
/// follow the system's configured behavior (default):
|
||||||
// "auto"
|
/// "auto"
|
||||||
// 3. Match the system's configured behavior:
|
/// 3. Match the system's configured behavior:
|
||||||
// "system"
|
/// "system"
|
||||||
// 4. Always show the scrollbar:
|
/// 4. Always show the scrollbar:
|
||||||
// "always"
|
/// "always"
|
||||||
// 5. Never show the scrollbar:
|
/// 5. Never show the scrollbar:
|
||||||
// "never"
|
/// "never"
|
||||||
"show": null
|
"show": null
|
||||||
},
|
},
|
||||||
// Which files containing diagnostic errors/warnings to mark in the project panel.
|
/// Which files containing diagnostic errors/warnings to mark in the project panel.
|
||||||
// This setting can take the following three values:
|
/// This setting can take the following three values:
|
||||||
///
|
///
|
||||||
// 1. Do not mark any files:
|
/// 1. Do not mark any files:
|
||||||
// "off"
|
/// "off"
|
||||||
// 2. Only mark files with errors:
|
/// 2. Only mark files with errors:
|
||||||
// "errors"
|
/// "errors"
|
||||||
// 3. Mark files with errors and warnings:
|
/// 3. Mark files with errors and warnings:
|
||||||
// "all"
|
/// "all"
|
||||||
"show_diagnostics": "all",
|
"show_diagnostics": "all",
|
||||||
// Settings related to indent guides in the project panel.
|
// Settings related to indent guides in the project panel.
|
||||||
"indent_guides": {
|
"indent_guides": {
|
||||||
@@ -494,8 +482,8 @@
|
|||||||
// when a corresponding outline entry becomes active.
|
// when a corresponding outline entry becomes active.
|
||||||
// Gitignored entries are never auto revealed.
|
// Gitignored entries are never auto revealed.
|
||||||
"auto_reveal_entries": true,
|
"auto_reveal_entries": true,
|
||||||
// Whether to fold directories automatically
|
/// Whether to fold directories automatically
|
||||||
// when a directory has only one directory inside.
|
/// when a directory has only one directory inside.
|
||||||
"auto_fold_dirs": true,
|
"auto_fold_dirs": true,
|
||||||
// Settings related to indent guides in the outline panel.
|
// Settings related to indent guides in the outline panel.
|
||||||
"indent_guides": {
|
"indent_guides": {
|
||||||
@@ -508,21 +496,21 @@
|
|||||||
// "never"
|
// "never"
|
||||||
"show": "always"
|
"show": "always"
|
||||||
},
|
},
|
||||||
// Scrollbar-related settings
|
/// Scrollbar-related settings
|
||||||
"scrollbar": {
|
"scrollbar": {
|
||||||
// When to show the scrollbar in the project panel.
|
/// When to show the scrollbar in the project panel.
|
||||||
// This setting can take five values:
|
/// This setting can take five values:
|
||||||
///
|
///
|
||||||
// 1. null (default): Inherit editor settings
|
/// 1. null (default): Inherit editor settings
|
||||||
// 2. Show the scrollbar if there's important information or
|
/// 2. Show the scrollbar if there's important information or
|
||||||
// follow the system's configured behavior (default):
|
/// follow the system's configured behavior (default):
|
||||||
// "auto"
|
/// "auto"
|
||||||
// 3. Match the system's configured behavior:
|
/// 3. Match the system's configured behavior:
|
||||||
// "system"
|
/// "system"
|
||||||
// 4. Always show the scrollbar:
|
/// 4. Always show the scrollbar:
|
||||||
// "always"
|
/// "always"
|
||||||
// 5. Never show the scrollbar:
|
/// 5. Never show the scrollbar:
|
||||||
// "never"
|
/// "never"
|
||||||
"show": null
|
"show": null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -593,7 +581,7 @@
|
|||||||
// The provider to use.
|
// The provider to use.
|
||||||
"provider": "zed.dev",
|
"provider": "zed.dev",
|
||||||
// The model to use.
|
// The model to use.
|
||||||
"model": "claude-3-5-sonnet-latest"
|
"model": "claude-3-5-sonnet"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// The settings for slash commands.
|
// The settings for slash commands.
|
||||||
@@ -640,7 +628,7 @@
|
|||||||
"show": true,
|
"show": true,
|
||||||
// Whether or not to show the navigation history buttons.
|
// Whether or not to show the navigation history buttons.
|
||||||
"show_nav_history_buttons": true,
|
"show_nav_history_buttons": true,
|
||||||
// Whether or not to show the tab bar buttons.
|
/// Whether or not to show the tab bar buttons.
|
||||||
"show_tab_bar_buttons": true
|
"show_tab_bar_buttons": true
|
||||||
},
|
},
|
||||||
// Settings related to the editor's tabs
|
// Settings related to the editor's tabs
|
||||||
@@ -662,16 +650,16 @@
|
|||||||
// 3. Activate the left neighbour tab if present
|
// 3. Activate the left neighbour tab if present
|
||||||
// "left_neighbour"
|
// "left_neighbour"
|
||||||
"activate_on_close": "history",
|
"activate_on_close": "history",
|
||||||
// Which files containing diagnostic errors/warnings to mark in the tabs.
|
/// Which files containing diagnostic errors/warnings to mark in the tabs.
|
||||||
// Diagnostics are only shown when file icons are also active.
|
/// Diagnostics are only shown when file icons are also active.
|
||||||
// This setting only works when can take the following three values:
|
/// This setting only works when can take the following three values:
|
||||||
///
|
///
|
||||||
// 1. Do not mark any files:
|
/// 1. Do not mark any files:
|
||||||
// "off"
|
/// "off"
|
||||||
// 2. Only mark files with errors:
|
/// 2. Only mark files with errors:
|
||||||
// "errors"
|
/// "errors"
|
||||||
// 3. Mark files with errors and warnings:
|
/// 3. Mark files with errors and warnings:
|
||||||
// "all"
|
/// "all"
|
||||||
"show_diagnostics": "off"
|
"show_diagnostics": "off"
|
||||||
},
|
},
|
||||||
// Settings related to preview tabs.
|
// Settings related to preview tabs.
|
||||||
@@ -770,25 +758,7 @@
|
|||||||
// Diagnostics configuration.
|
// Diagnostics configuration.
|
||||||
"diagnostics": {
|
"diagnostics": {
|
||||||
// Whether to show warnings or not by default.
|
// Whether to show warnings or not by default.
|
||||||
"include_warnings": true,
|
"include_warnings": true
|
||||||
// Settings for inline diagnostics
|
|
||||||
"inline": {
|
|
||||||
// Whether to show diagnostics inline or not
|
|
||||||
"enabled": false,
|
|
||||||
// The delay in milliseconds to show inline diagnostics after the
|
|
||||||
// last diagnostic update.
|
|
||||||
"update_debounce_ms": 150,
|
|
||||||
// The amount of padding between the end of the source line and the start
|
|
||||||
// of the inline diagnostic in units of em widths.
|
|
||||||
"padding": 4,
|
|
||||||
// The minimum column to display inline diagnostics. This setting can be
|
|
||||||
// used to horizontally align inline diagnostics at some column. Lines
|
|
||||||
// longer than this value will still push diagnostics further to the right.
|
|
||||||
"min_column": 0,
|
|
||||||
// The minimum severity of the diagnostics to show inline.
|
|
||||||
// Shows all diagnostics when not specified.
|
|
||||||
"max_severity": null
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
// Files or globs of files that will be excluded by Zed entirely. They will be skipped during file
|
// Files or globs of files that will be excluded by Zed entirely. They will be skipped during file
|
||||||
// scans, file searches, and not be displayed in the project file tree. Takes precedence over `file_scan_inclusions`.
|
// scans, file searches, and not be displayed in the project file tree. Takes precedence over `file_scan_inclusions`.
|
||||||
@@ -841,8 +811,6 @@
|
|||||||
// A list of globs representing files that edit predictions should be disabled for.
|
// A list of globs representing files that edit predictions should be disabled for.
|
||||||
// There's a sensible default list of globs already included.
|
// There's a sensible default list of globs already included.
|
||||||
// Any addition to this list will be merged with the default list.
|
// Any addition to this list will be merged with the default list.
|
||||||
// Globs are matched relative to the worktree root,
|
|
||||||
// except when starting with a slash (/) or equivalent in Windows.
|
|
||||||
"disabled_globs": [
|
"disabled_globs": [
|
||||||
"**/.env*",
|
"**/.env*",
|
||||||
"**/*.pem",
|
"**/*.pem",
|
||||||
@@ -854,14 +822,11 @@
|
|||||||
],
|
],
|
||||||
// When to show edit predictions previews in buffer.
|
// When to show edit predictions previews in buffer.
|
||||||
// This setting takes two possible values:
|
// This setting takes two possible values:
|
||||||
// 1. Display predictions inline when there are no language server completions available.
|
// 1. Display inline when there are no language server completions available.
|
||||||
// "mode": "eager"
|
// "mode": "eager_preview"
|
||||||
// 2. Display predictions inline only when holding a modifier key (alt by default).
|
// 2. Display inline when holding modifier key (alt by default).
|
||||||
// "mode": "subtle"
|
// "mode": "auto"
|
||||||
"mode": "eager",
|
"mode": "eager_preview"
|
||||||
// Whether edit predictions are enabled in the assistant panel.
|
|
||||||
// This setting has no effect if globally disabled.
|
|
||||||
"enabled_in_assistant": true
|
|
||||||
},
|
},
|
||||||
// Settings specific to journaling
|
// Settings specific to journaling
|
||||||
"journal": {
|
"journal": {
|
||||||
@@ -997,21 +962,21 @@
|
|||||||
// Example: `echo -e "\e]2;New Title\007";`
|
// Example: `echo -e "\e]2;New Title\007";`
|
||||||
"breadcrumbs": true
|
"breadcrumbs": true
|
||||||
},
|
},
|
||||||
// Scrollbar-related settings
|
/// Scrollbar-related settings
|
||||||
"scrollbar": {
|
"scrollbar": {
|
||||||
// When to show the scrollbar in the terminal.
|
/// When to show the scrollbar in the terminal.
|
||||||
// This setting can take five values:
|
/// This setting can take five values:
|
||||||
///
|
///
|
||||||
// 1. null (default): Inherit editor settings
|
/// 1. null (default): Inherit editor settings
|
||||||
// 2. Show the scrollbar if there's important information or
|
/// 2. Show the scrollbar if there's important information or
|
||||||
// follow the system's configured behavior (default):
|
/// follow the system's configured behavior (default):
|
||||||
// "auto"
|
/// "auto"
|
||||||
// 3. Match the system's configured behavior:
|
/// 3. Match the system's configured behavior:
|
||||||
// "system"
|
/// "system"
|
||||||
// 4. Always show the scrollbar:
|
/// 4. Always show the scrollbar:
|
||||||
// "always"
|
/// "always"
|
||||||
// 5. Never show the scrollbar:
|
/// 5. Never show the scrollbar:
|
||||||
// "never"
|
/// "never"
|
||||||
"show": null
|
"show": null
|
||||||
}
|
}
|
||||||
// Set the terminal's font size. If this option is not included,
|
// Set the terminal's font size. If this option is not included,
|
||||||
@@ -1030,7 +995,7 @@
|
|||||||
// "max_scroll_history_lines": 10000,
|
// "max_scroll_history_lines": 10000,
|
||||||
},
|
},
|
||||||
"code_actions_on_format": {},
|
"code_actions_on_format": {},
|
||||||
// Settings related to running tasks.
|
/// Settings related to running tasks.
|
||||||
"tasks": {
|
"tasks": {
|
||||||
"variables": {}
|
"variables": {}
|
||||||
},
|
},
|
||||||
@@ -1051,20 +1016,20 @@
|
|||||||
"JSONC": ["**/.zed/**/*.json", "**/zed/**/*.json", "**/Zed/**/*.json", "**/.vscode/**/*.json"],
|
"JSONC": ["**/.zed/**/*.json", "**/zed/**/*.json", "**/Zed/**/*.json", "**/.vscode/**/*.json"],
|
||||||
"Shell Script": [".env.*"]
|
"Shell Script": [".env.*"]
|
||||||
},
|
},
|
||||||
// By default use a recent system version of node, or install our own.
|
/// By default use a recent system version of node, or install our own.
|
||||||
// You can override this to use a version of node that is not in $PATH with:
|
/// You can override this to use a version of node that is not in $PATH with:
|
||||||
// {
|
/// {
|
||||||
// "node": {
|
/// "node": {
|
||||||
// "path": "/path/to/node"
|
/// "path": "/path/to/node"
|
||||||
// "npm_path": "/path/to/npm" (defaults to node_path/../npm)
|
/// "npm_path": "/path/to/npm" (defaults to node_path/../npm)
|
||||||
// }
|
/// }
|
||||||
// }
|
/// }
|
||||||
// or to ensure Zed always downloads and installs an isolated version of node:
|
/// or to ensure Zed always downloads and installs an isolated version of node:
|
||||||
// {
|
/// {
|
||||||
// "node": {
|
/// "node": {
|
||||||
// "ignore_system_version": true,
|
/// "ignore_system_version": true,
|
||||||
// }
|
/// }
|
||||||
// NOTE: changing this setting currently requires restarting Zed.
|
/// NOTE: changing this setting currently requires restarting Zed.
|
||||||
"node": {},
|
"node": {},
|
||||||
// The extensions that Zed should automatically install on startup.
|
// The extensions that Zed should automatically install on startup.
|
||||||
//
|
//
|
||||||
@@ -1110,7 +1075,6 @@
|
|||||||
"tab_size": 2
|
"tab_size": 2
|
||||||
},
|
},
|
||||||
"Diff": {
|
"Diff": {
|
||||||
"show_edit_predictions": false,
|
|
||||||
"remove_trailing_whitespace_on_save": false,
|
"remove_trailing_whitespace_on_save": false,
|
||||||
"ensure_final_newline_on_save": false
|
"ensure_final_newline_on_save": false
|
||||||
},
|
},
|
||||||
@@ -1120,9 +1084,6 @@
|
|||||||
"Erlang": {
|
"Erlang": {
|
||||||
"language_servers": ["erlang-ls", "!elp", "..."]
|
"language_servers": ["erlang-ls", "!elp", "..."]
|
||||||
},
|
},
|
||||||
"Git Commit": {
|
|
||||||
"allow_rewrap": "anywhere"
|
|
||||||
},
|
|
||||||
"Go": {
|
"Go": {
|
||||||
"code_actions_on_format": {
|
"code_actions_on_format": {
|
||||||
"source.organizeImports": true
|
"source.organizeImports": true
|
||||||
@@ -1313,7 +1274,6 @@
|
|||||||
},
|
},
|
||||||
// Vim settings
|
// Vim settings
|
||||||
"vim": {
|
"vim": {
|
||||||
"default_mode": "normal",
|
|
||||||
"toggle_relative_line_numbers": false,
|
"toggle_relative_line_numbers": false,
|
||||||
"use_system_clipboard": "always",
|
"use_system_clipboard": "always",
|
||||||
"use_multiline_find": false,
|
"use_multiline_find": false,
|
||||||
|
|||||||
@@ -379,7 +379,7 @@
|
|||||||
"font_weight": null
|
"font_weight": null
|
||||||
},
|
},
|
||||||
"variable": {
|
"variable": {
|
||||||
"color": "#ebdbb2ff",
|
"color": "#83a598ff",
|
||||||
"font_style": null,
|
"font_style": null,
|
||||||
"font_weight": null
|
"font_weight": null
|
||||||
},
|
},
|
||||||
@@ -767,7 +767,7 @@
|
|||||||
"font_weight": null
|
"font_weight": null
|
||||||
},
|
},
|
||||||
"variable": {
|
"variable": {
|
||||||
"color": "#ebdbb2ff",
|
"color": "#83a598ff",
|
||||||
"font_style": null,
|
"font_style": null,
|
||||||
"font_weight": null
|
"font_weight": null
|
||||||
},
|
},
|
||||||
@@ -1155,7 +1155,7 @@
|
|||||||
"font_weight": null
|
"font_weight": null
|
||||||
},
|
},
|
||||||
"variable": {
|
"variable": {
|
||||||
"color": "#ebdbb2ff",
|
"color": "#83a598ff",
|
||||||
"font_style": null,
|
"font_style": null,
|
||||||
"font_weight": null
|
"font_weight": null
|
||||||
},
|
},
|
||||||
@@ -1543,7 +1543,7 @@
|
|||||||
"font_weight": null
|
"font_weight": null
|
||||||
},
|
},
|
||||||
"variable": {
|
"variable": {
|
||||||
"color": "#282828ff",
|
"color": "#066578ff",
|
||||||
"font_style": null,
|
"font_style": null,
|
||||||
"font_weight": null
|
"font_weight": null
|
||||||
},
|
},
|
||||||
@@ -1931,7 +1931,7 @@
|
|||||||
"font_weight": null
|
"font_weight": null
|
||||||
},
|
},
|
||||||
"variable": {
|
"variable": {
|
||||||
"color": "#282828ff",
|
"color": "#066578ff",
|
||||||
"font_style": null,
|
"font_style": null,
|
||||||
"font_weight": null
|
"font_weight": null
|
||||||
},
|
},
|
||||||
@@ -2319,7 +2319,7 @@
|
|||||||
"font_weight": null
|
"font_weight": null
|
||||||
},
|
},
|
||||||
"variable": {
|
"variable": {
|
||||||
"color": "#282828ff",
|
"color": "#066578ff",
|
||||||
"font_style": null,
|
"font_style": null,
|
||||||
"font_weight": null
|
"font_weight": null
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -365,7 +365,7 @@
|
|||||||
"font_weight": null
|
"font_weight": null
|
||||||
},
|
},
|
||||||
"variable": {
|
"variable": {
|
||||||
"color": "#acb2beff",
|
"color": "#dce0e5ff",
|
||||||
"font_style": null,
|
"font_style": null,
|
||||||
"font_weight": null
|
"font_weight": null
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -30,8 +30,6 @@ pub enum Model {
|
|||||||
#[default]
|
#[default]
|
||||||
#[serde(rename = "claude-3-5-sonnet", alias = "claude-3-5-sonnet-latest")]
|
#[serde(rename = "claude-3-5-sonnet", alias = "claude-3-5-sonnet-latest")]
|
||||||
Claude3_5Sonnet,
|
Claude3_5Sonnet,
|
||||||
#[serde(rename = "claude-3-7-sonnet", alias = "claude-3-7-sonnet-latest")]
|
|
||||||
Claude3_7Sonnet,
|
|
||||||
#[serde(rename = "claude-3-5-haiku", alias = "claude-3-5-haiku-latest")]
|
#[serde(rename = "claude-3-5-haiku", alias = "claude-3-5-haiku-latest")]
|
||||||
Claude3_5Haiku,
|
Claude3_5Haiku,
|
||||||
#[serde(rename = "claude-3-opus", alias = "claude-3-opus-latest")]
|
#[serde(rename = "claude-3-opus", alias = "claude-3-opus-latest")]
|
||||||
@@ -61,8 +59,6 @@ impl Model {
|
|||||||
pub fn from_id(id: &str) -> Result<Self> {
|
pub fn from_id(id: &str) -> Result<Self> {
|
||||||
if id.starts_with("claude-3-5-sonnet") {
|
if id.starts_with("claude-3-5-sonnet") {
|
||||||
Ok(Self::Claude3_5Sonnet)
|
Ok(Self::Claude3_5Sonnet)
|
||||||
} else if id.starts_with("claude-3-7-sonnet") {
|
|
||||||
Ok(Self::Claude3_7Sonnet)
|
|
||||||
} else if id.starts_with("claude-3-5-haiku") {
|
} else if id.starts_with("claude-3-5-haiku") {
|
||||||
Ok(Self::Claude3_5Haiku)
|
Ok(Self::Claude3_5Haiku)
|
||||||
} else if id.starts_with("claude-3-opus") {
|
} else if id.starts_with("claude-3-opus") {
|
||||||
@@ -79,7 +75,6 @@ impl Model {
|
|||||||
pub fn id(&self) -> &str {
|
pub fn id(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
Model::Claude3_5Sonnet => "claude-3-5-sonnet-latest",
|
Model::Claude3_5Sonnet => "claude-3-5-sonnet-latest",
|
||||||
Model::Claude3_7Sonnet => "claude-3-7-sonnet-latest",
|
|
||||||
Model::Claude3_5Haiku => "claude-3-5-haiku-latest",
|
Model::Claude3_5Haiku => "claude-3-5-haiku-latest",
|
||||||
Model::Claude3Opus => "claude-3-opus-latest",
|
Model::Claude3Opus => "claude-3-opus-latest",
|
||||||
Model::Claude3Sonnet => "claude-3-sonnet-20240229",
|
Model::Claude3Sonnet => "claude-3-sonnet-20240229",
|
||||||
@@ -90,7 +85,6 @@ impl Model {
|
|||||||
|
|
||||||
pub fn display_name(&self) -> &str {
|
pub fn display_name(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
Self::Claude3_7Sonnet => "Claude 3.7 Sonnet",
|
|
||||||
Self::Claude3_5Sonnet => "Claude 3.5 Sonnet",
|
Self::Claude3_5Sonnet => "Claude 3.5 Sonnet",
|
||||||
Self::Claude3_5Haiku => "Claude 3.5 Haiku",
|
Self::Claude3_5Haiku => "Claude 3.5 Haiku",
|
||||||
Self::Claude3Opus => "Claude 3 Opus",
|
Self::Claude3Opus => "Claude 3 Opus",
|
||||||
@@ -104,14 +98,13 @@ impl Model {
|
|||||||
|
|
||||||
pub fn cache_configuration(&self) -> Option<AnthropicModelCacheConfiguration> {
|
pub fn cache_configuration(&self) -> Option<AnthropicModelCacheConfiguration> {
|
||||||
match self {
|
match self {
|
||||||
Self::Claude3_5Sonnet
|
Self::Claude3_5Sonnet | Self::Claude3_5Haiku | Self::Claude3Haiku => {
|
||||||
| Self::Claude3_5Haiku
|
Some(AnthropicModelCacheConfiguration {
|
||||||
| Self::Claude3_7Sonnet
|
|
||||||
| Self::Claude3Haiku => Some(AnthropicModelCacheConfiguration {
|
|
||||||
min_total_token: 2_048,
|
min_total_token: 2_048,
|
||||||
should_speculate: true,
|
should_speculate: true,
|
||||||
max_cache_anchors: 4,
|
max_cache_anchors: 4,
|
||||||
}),
|
})
|
||||||
|
}
|
||||||
Self::Custom {
|
Self::Custom {
|
||||||
cache_configuration,
|
cache_configuration,
|
||||||
..
|
..
|
||||||
@@ -124,7 +117,6 @@ impl Model {
|
|||||||
match self {
|
match self {
|
||||||
Self::Claude3_5Sonnet
|
Self::Claude3_5Sonnet
|
||||||
| Self::Claude3_5Haiku
|
| Self::Claude3_5Haiku
|
||||||
| Self::Claude3_7Sonnet
|
|
||||||
| Self::Claude3Opus
|
| Self::Claude3Opus
|
||||||
| Self::Claude3Sonnet
|
| Self::Claude3Sonnet
|
||||||
| Self::Claude3Haiku => 200_000,
|
| Self::Claude3Haiku => 200_000,
|
||||||
@@ -135,7 +127,7 @@ impl Model {
|
|||||||
pub fn max_output_tokens(&self) -> u32 {
|
pub fn max_output_tokens(&self) -> u32 {
|
||||||
match self {
|
match self {
|
||||||
Self::Claude3Opus | Self::Claude3Sonnet | Self::Claude3Haiku => 4_096,
|
Self::Claude3Opus | Self::Claude3Sonnet | Self::Claude3Haiku => 4_096,
|
||||||
Self::Claude3_5Sonnet | Self::Claude3_7Sonnet | Self::Claude3_5Haiku => 8_192,
|
Self::Claude3_5Sonnet | Self::Claude3_5Haiku => 8_192,
|
||||||
Self::Custom {
|
Self::Custom {
|
||||||
max_output_tokens, ..
|
max_output_tokens, ..
|
||||||
} => max_output_tokens.unwrap_or(4_096),
|
} => max_output_tokens.unwrap_or(4_096),
|
||||||
@@ -145,7 +137,6 @@ impl Model {
|
|||||||
pub fn default_temperature(&self) -> f32 {
|
pub fn default_temperature(&self) -> f32 {
|
||||||
match self {
|
match self {
|
||||||
Self::Claude3_5Sonnet
|
Self::Claude3_5Sonnet
|
||||||
| Self::Claude3_7Sonnet
|
|
||||||
| Self::Claude3_5Haiku
|
| Self::Claude3_5Haiku
|
||||||
| Self::Claude3Opus
|
| Self::Claude3Opus
|
||||||
| Self::Claude3Sonnet
|
| Self::Claude3Sonnet
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ indoc.workspace = true
|
|||||||
language.workspace = true
|
language.workspace = true
|
||||||
language_model.workspace = true
|
language_model.workspace = true
|
||||||
language_model_selector.workspace = true
|
language_model_selector.workspace = true
|
||||||
|
language_models.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
lsp.workspace = true
|
lsp.workspace = true
|
||||||
menu.workspace = true
|
menu.workspace = true
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ actions!(
|
|||||||
[
|
[
|
||||||
InsertActivePrompt,
|
InsertActivePrompt,
|
||||||
DeployHistory,
|
DeployHistory,
|
||||||
NewChat,
|
NewContext,
|
||||||
CycleNextInlineAssist,
|
CycleNextInlineAssist,
|
||||||
CyclePreviousInlineAssist
|
CyclePreviousInlineAssist
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::assistant_configuration::{ConfigurationView, ConfigurationViewEvent};
|
use crate::assistant_configuration::{ConfigurationView, ConfigurationViewEvent};
|
||||||
use crate::{
|
use crate::{
|
||||||
terminal_inline_assistant::TerminalInlineAssistant, DeployHistory, InlineAssistant, NewChat,
|
terminal_inline_assistant::TerminalInlineAssistant, DeployHistory, InlineAssistant, NewContext,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use assistant_context_editor::{
|
use assistant_context_editor::{
|
||||||
@@ -129,7 +129,7 @@ impl AssistantPanel {
|
|||||||
workspace.project().clone(),
|
workspace.project().clone(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
None,
|
None,
|
||||||
NewChat.boxed_clone(),
|
NewContext.boxed_clone(),
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
@@ -228,12 +228,12 @@ impl AssistantPanel {
|
|||||||
IconButton::new("new-chat", IconName::Plus)
|
IconButton::new("new-chat", IconName::Plus)
|
||||||
.icon_size(IconSize::Small)
|
.icon_size(IconSize::Small)
|
||||||
.on_click(cx.listener(|_, _, window, cx| {
|
.on_click(cx.listener(|_, _, window, cx| {
|
||||||
window.dispatch_action(NewChat.boxed_clone(), cx)
|
window.dispatch_action(NewContext.boxed_clone(), cx)
|
||||||
}))
|
}))
|
||||||
.tooltip(move |window, cx| {
|
.tooltip(move |window, cx| {
|
||||||
Tooltip::for_action_in(
|
Tooltip::for_action_in(
|
||||||
"New Chat",
|
"New Chat",
|
||||||
&NewChat,
|
&NewContext,
|
||||||
&focus_handle,
|
&focus_handle,
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
@@ -256,7 +256,7 @@ impl AssistantPanel {
|
|||||||
let focus_handle = _pane.focus_handle(cx);
|
let focus_handle = _pane.focus_handle(cx);
|
||||||
Some(ContextMenu::build(window, cx, move |menu, _, _| {
|
Some(ContextMenu::build(window, cx, move |menu, _, _| {
|
||||||
menu.context(focus_handle.clone())
|
menu.context(focus_handle.clone())
|
||||||
.action("New Chat", Box::new(NewChat))
|
.action("New Chat", Box::new(NewContext))
|
||||||
.action("History", Box::new(DeployHistory))
|
.action("History", Box::new(DeployHistory))
|
||||||
.action("Prompt Library", Box::new(DeployPromptLibrary))
|
.action("Prompt Library", Box::new(DeployPromptLibrary))
|
||||||
.action("Configure", Box::new(ShowConfiguration))
|
.action("Configure", Box::new(ShowConfiguration))
|
||||||
@@ -760,7 +760,7 @@ impl AssistantPanel {
|
|||||||
|
|
||||||
pub fn create_new_context(
|
pub fn create_new_context(
|
||||||
workspace: &mut Workspace,
|
workspace: &mut Workspace,
|
||||||
_: &NewChat,
|
_: &NewContext,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Workspace>,
|
cx: &mut Context<Workspace>,
|
||||||
) {
|
) {
|
||||||
@@ -978,7 +978,7 @@ impl AssistantPanel {
|
|||||||
.active_provider()
|
.active_provider()
|
||||||
.map_or(true, |p| p.id() != provider.id())
|
.map_or(true, |p| p.id() != provider.id())
|
||||||
{
|
{
|
||||||
if let Some(model) = provider.default_model(cx) {
|
if let Some(model) = provider.provided_models(cx).first().cloned() {
|
||||||
update_settings_file::<AssistantSettings>(
|
update_settings_file::<AssistantSettings>(
|
||||||
this.fs.clone(),
|
this.fs.clone(),
|
||||||
cx,
|
cx,
|
||||||
@@ -1206,7 +1206,7 @@ impl Render for AssistantPanel {
|
|||||||
v_flex()
|
v_flex()
|
||||||
.key_context("AssistantPanel")
|
.key_context("AssistantPanel")
|
||||||
.size_full()
|
.size_full()
|
||||||
.on_action(cx.listener(|this, _: &NewChat, window, cx| {
|
.on_action(cx.listener(|this, _: &NewContext, window, cx| {
|
||||||
this.new_context(window, cx);
|
this.new_context(window, cx);
|
||||||
}))
|
}))
|
||||||
.on_action(cx.listener(|this, _: &ShowConfiguration, window, cx| {
|
.on_action(cx.listener(|this, _: &ShowConfiguration, window, cx| {
|
||||||
|
|||||||
@@ -32,10 +32,11 @@ use gpui::{
|
|||||||
};
|
};
|
||||||
use language::{line_diff, Buffer, IndentKind, Point, Selection, TransactionId};
|
use language::{line_diff, Buffer, IndentKind, Point, Selection, TransactionId};
|
||||||
use language_model::{
|
use language_model::{
|
||||||
report_assistant_event, LanguageModel, LanguageModelRegistry, LanguageModelRequest,
|
LanguageModel, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage,
|
||||||
LanguageModelRequestMessage, LanguageModelTextStream, Role,
|
LanguageModelTextStream, Role,
|
||||||
};
|
};
|
||||||
use language_model_selector::{InlineLanguageModelSelector, LanguageModelSelector};
|
use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};
|
||||||
|
use language_models::report_assistant_event;
|
||||||
use multi_buffer::MultiBufferRow;
|
use multi_buffer::MultiBufferRow;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use project::{CodeAction, ProjectTransaction};
|
use project::{CodeAction, ProjectTransaction};
|
||||||
@@ -1589,10 +1590,29 @@ impl Render for PromptEditor {
|
|||||||
.w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0))
|
.w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0))
|
||||||
.justify_center()
|
.justify_center()
|
||||||
.gap_2()
|
.gap_2()
|
||||||
.child(
|
.child(LanguageModelSelectorPopoverMenu::new(
|
||||||
InlineLanguageModelSelector::new(self.language_model_selector.clone())
|
self.language_model_selector.clone(),
|
||||||
.render(window, cx),
|
IconButton::new("context", IconName::SettingsAlt)
|
||||||
|
.shape(IconButtonShape::Square)
|
||||||
|
.icon_size(IconSize::Small)
|
||||||
|
.icon_color(Color::Muted),
|
||||||
|
move |window, cx| {
|
||||||
|
Tooltip::with_meta(
|
||||||
|
format!(
|
||||||
|
"Using {}",
|
||||||
|
LanguageModelRegistry::read_global(cx)
|
||||||
|
.active_model()
|
||||||
|
.map(|model| model.name().0)
|
||||||
|
.unwrap_or_else(|| "No model selected".into()),
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
"Change Model",
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
)
|
)
|
||||||
|
},
|
||||||
|
gpui::Corner::TopRight,
|
||||||
|
))
|
||||||
.map(|el| {
|
.map(|el| {
|
||||||
let CodegenStatus::Error(error) = self.codegen.read(cx).status(cx) else {
|
let CodegenStatus::Error(error) = self.codegen.read(cx).status(cx) else {
|
||||||
return el;
|
return el;
|
||||||
@@ -2207,7 +2227,7 @@ impl PromptEditor {
|
|||||||
},
|
},
|
||||||
font_family: settings.buffer_font.family.clone(),
|
font_family: settings.buffer_font.family.clone(),
|
||||||
font_fallbacks: settings.buffer_font.fallbacks.clone(),
|
font_fallbacks: settings.buffer_font.fallbacks.clone(),
|
||||||
font_size: settings.buffer_font_size(cx).into(),
|
font_size: settings.buffer_font_size.into(),
|
||||||
font_weight: settings.buffer_font.weight,
|
font_weight: settings.buffer_font.weight,
|
||||||
line_height: relative(settings.buffer_line_height.value()),
|
line_height: relative(settings.buffer_line_height.value()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ use gpui::{
|
|||||||
};
|
};
|
||||||
use language::Buffer;
|
use language::Buffer;
|
||||||
use language_model::{
|
use language_model::{
|
||||||
report_assistant_event, LanguageModelRegistry, LanguageModelRequest,
|
LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage, Role,
|
||||||
LanguageModelRequestMessage, Role,
|
|
||||||
};
|
};
|
||||||
use language_model_selector::{InlineLanguageModelSelector, LanguageModelSelector};
|
use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};
|
||||||
|
use language_models::report_assistant_event;
|
||||||
use prompt_library::PromptBuilder;
|
use prompt_library::PromptBuilder;
|
||||||
use settings::{update_settings_file, Settings};
|
use settings::{update_settings_file, Settings};
|
||||||
use std::{
|
use std::{
|
||||||
@@ -506,7 +506,7 @@ struct PromptEditor {
|
|||||||
impl EventEmitter<PromptEditorEvent> for PromptEditor {}
|
impl EventEmitter<PromptEditorEvent> for PromptEditor {}
|
||||||
|
|
||||||
impl Render for PromptEditor {
|
impl Render for PromptEditor {
|
||||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
let status = &self.codegen.read(cx).status;
|
let status = &self.codegen.read(cx).status;
|
||||||
let buttons = match status {
|
let buttons = match status {
|
||||||
CodegenStatus::Idle => {
|
CodegenStatus::Idle => {
|
||||||
@@ -641,10 +641,29 @@ impl Render for PromptEditor {
|
|||||||
.w_12()
|
.w_12()
|
||||||
.justify_center()
|
.justify_center()
|
||||||
.gap_2()
|
.gap_2()
|
||||||
.child(
|
.child(LanguageModelSelectorPopoverMenu::new(
|
||||||
InlineLanguageModelSelector::new(self.language_model_selector.clone())
|
self.language_model_selector.clone(),
|
||||||
.render(window, cx),
|
IconButton::new("context", IconName::SettingsAlt)
|
||||||
|
.shape(IconButtonShape::Square)
|
||||||
|
.icon_size(IconSize::Small)
|
||||||
|
.icon_color(Color::Muted),
|
||||||
|
move |window, cx| {
|
||||||
|
Tooltip::with_meta(
|
||||||
|
format!(
|
||||||
|
"Using {}",
|
||||||
|
LanguageModelRegistry::read_global(cx)
|
||||||
|
.active_model()
|
||||||
|
.map(|model| model.name().0)
|
||||||
|
.unwrap_or_else(|| "No model selected".into()),
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
"Change Model",
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
)
|
)
|
||||||
|
},
|
||||||
|
gpui::Corner::TopRight,
|
||||||
|
))
|
||||||
.children(
|
.children(
|
||||||
if let CodegenStatus::Error(error) = &self.codegen.read(cx).status {
|
if let CodegenStatus::Error(error) = &self.codegen.read(cx).status {
|
||||||
let error_message = SharedString::from(error.to_string());
|
let error_message = SharedString::from(error.to_string());
|
||||||
@@ -1030,7 +1049,7 @@ impl PromptEditor {
|
|||||||
},
|
},
|
||||||
font_family: settings.buffer_font.family.clone(),
|
font_family: settings.buffer_font.family.clone(),
|
||||||
font_fallbacks: settings.buffer_font.fallbacks.clone(),
|
font_fallbacks: settings.buffer_font.fallbacks.clone(),
|
||||||
font_size: settings.buffer_font_size(cx).into(),
|
font_size: settings.buffer_font_size.into(),
|
||||||
font_weight: settings.buffer_font.weight,
|
font_weight: settings.buffer_font.weight,
|
||||||
line_height: relative(settings.buffer_line_height.value()),
|
line_height: relative(settings.buffer_line_height.value()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -1054,10 +1073,7 @@ pub enum CodegenEvent {
|
|||||||
|
|
||||||
impl EventEmitter<CodegenEvent> for Codegen {}
|
impl EventEmitter<CodegenEvent> for Codegen {}
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
const CLEAR_INPUT: &str = "\x15";
|
const CLEAR_INPUT: &str = "\x15";
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
const CLEAR_INPUT: &str = "\x03";
|
|
||||||
const CARRIAGE_RETURN: &str = "\x0d";
|
const CARRIAGE_RETURN: &str = "\x0d";
|
||||||
|
|
||||||
struct TerminalTransaction {
|
struct TerminalTransaction {
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ itertools.workspace = true
|
|||||||
language.workspace = true
|
language.workspace = true
|
||||||
language_model.workspace = true
|
language_model.workspace = true
|
||||||
language_model_selector.workspace = true
|
language_model_selector.workspace = true
|
||||||
|
language_models.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
lsp.workspace = true
|
lsp.workspace = true
|
||||||
markdown.workspace = true
|
markdown.workspace = true
|
||||||
@@ -73,7 +74,6 @@ time_format.workspace = true
|
|||||||
ui.workspace = true
|
ui.workspace = true
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
uuid.workspace = true
|
uuid.workspace = true
|
||||||
vim_mode_setting.workspace = true
|
|
||||||
workspace.workspace = true
|
workspace.workspace = true
|
||||||
zed_actions.workspace = true
|
zed_actions.workspace = true
|
||||||
|
|
||||||
|
|||||||
@@ -8,16 +8,15 @@ use gpui::{
|
|||||||
UnderlineStyle, WeakEntity,
|
UnderlineStyle, WeakEntity,
|
||||||
};
|
};
|
||||||
use language::LanguageRegistry;
|
use language::LanguageRegistry;
|
||||||
use language_model::{LanguageModelRegistry, LanguageModelToolUseId, Role};
|
use language_model::Role;
|
||||||
use markdown::{Markdown, MarkdownStyle};
|
use markdown::{Markdown, MarkdownStyle};
|
||||||
use settings::Settings as _;
|
use settings::Settings as _;
|
||||||
use theme::ThemeSettings;
|
use theme::ThemeSettings;
|
||||||
use ui::{prelude::*, Disclosure};
|
use ui::prelude::*;
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
use crate::thread::{MessageId, RequestKind, Thread, ThreadError, ThreadEvent};
|
use crate::thread::{MessageId, Thread, ThreadError, ThreadEvent};
|
||||||
use crate::thread_store::ThreadStore;
|
use crate::thread_store::ThreadStore;
|
||||||
use crate::tool_use::{ToolUse, ToolUseStatus};
|
|
||||||
use crate::ui::ContextPill;
|
use crate::ui::ContextPill;
|
||||||
|
|
||||||
pub struct ActiveThread {
|
pub struct ActiveThread {
|
||||||
@@ -29,7 +28,6 @@ pub struct ActiveThread {
|
|||||||
messages: Vec<MessageId>,
|
messages: Vec<MessageId>,
|
||||||
list_state: ListState,
|
list_state: ListState,
|
||||||
rendered_messages_by_id: HashMap<MessageId, Entity<Markdown>>,
|
rendered_messages_by_id: HashMap<MessageId, Entity<Markdown>>,
|
||||||
expanded_tool_uses: HashMap<LanguageModelToolUseId, bool>,
|
|
||||||
last_error: Option<ThreadError>,
|
last_error: Option<ThreadError>,
|
||||||
_subscriptions: Vec<Subscription>,
|
_subscriptions: Vec<Subscription>,
|
||||||
}
|
}
|
||||||
@@ -57,7 +55,6 @@ impl ActiveThread {
|
|||||||
thread: thread.clone(),
|
thread: thread.clone(),
|
||||||
messages: Vec::new(),
|
messages: Vec::new(),
|
||||||
rendered_messages_by_id: HashMap::default(),
|
rendered_messages_by_id: HashMap::default(),
|
||||||
expanded_tool_uses: HashMap::default(),
|
|
||||||
list_state: ListState::new(0, ListAlignment::Bottom, px(1024.), {
|
list_state: ListState::new(0, ListAlignment::Bottom, px(1024.), {
|
||||||
let this = cx.entity().downgrade();
|
let this = cx.entity().downgrade();
|
||||||
move |ix, _: &mut Window, cx: &mut App| {
|
move |ix, _: &mut Window, cx: &mut App| {
|
||||||
@@ -254,29 +251,17 @@ impl ActiveThread {
|
|||||||
let task = tool.run(tool_use.input, self.workspace.clone(), window, cx);
|
let task = tool.run(tool_use.input, self.workspace.clone(), window, cx);
|
||||||
|
|
||||||
self.thread.update(cx, |thread, cx| {
|
self.thread.update(cx, |thread, cx| {
|
||||||
thread.insert_tool_output(tool_use.id.clone(), task, cx);
|
thread.insert_tool_output(
|
||||||
});
|
tool_use.assistant_message_id,
|
||||||
}
|
tool_use.id.clone(),
|
||||||
}
|
task,
|
||||||
}
|
cx,
|
||||||
ThreadEvent::ToolFinished { .. } => {
|
);
|
||||||
let all_tools_finished = self
|
|
||||||
.thread
|
|
||||||
.read(cx)
|
|
||||||
.pending_tool_uses()
|
|
||||||
.into_iter()
|
|
||||||
.all(|tool_use| tool_use.status.is_error());
|
|
||||||
if all_tools_finished {
|
|
||||||
let model_registry = LanguageModelRegistry::read_global(cx);
|
|
||||||
if let Some(model) = model_registry.active_model() {
|
|
||||||
self.thread.update(cx, |thread, cx| {
|
|
||||||
// Insert an empty user message to contain the tool results.
|
|
||||||
thread.insert_user_message("", Vec::new(), cx);
|
|
||||||
thread.send_to_model(model, RequestKind::Chat, true, cx);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ThreadEvent::ToolFinished { .. } => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,17 +276,8 @@ impl ActiveThread {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let context = self.thread.read(cx).context_for_message(message_id);
|
let context = self.thread.read(cx).context_for_message(message_id);
|
||||||
let tool_uses = self.thread.read(cx).tool_uses_for_message(message_id);
|
|
||||||
let colors = cx.theme().colors();
|
let colors = cx.theme().colors();
|
||||||
|
|
||||||
// Don't render user messages that are just there for returning tool results.
|
|
||||||
if message.role == Role::User
|
|
||||||
&& message.text.is_empty()
|
|
||||||
&& self.thread.read(cx).message_has_tool_results(message_id)
|
|
||||||
{
|
|
||||||
return Empty.into_any();
|
|
||||||
}
|
|
||||||
|
|
||||||
let message_content = v_flex()
|
let message_content = v_flex()
|
||||||
.child(div().p_2p5().text_ui(cx).child(markdown.clone()))
|
.child(div().p_2p5().text_ui(cx).child(markdown.clone()))
|
||||||
.when_some(context, |parent, context| {
|
.when_some(context, |parent, context| {
|
||||||
@@ -356,22 +332,7 @@ impl ActiveThread {
|
|||||||
)
|
)
|
||||||
.child(message_content),
|
.child(message_content),
|
||||||
),
|
),
|
||||||
Role::Assistant => div()
|
Role::Assistant => div().id(("message-container", ix)).child(message_content),
|
||||||
.id(("message-container", ix))
|
|
||||||
.child(message_content)
|
|
||||||
.map(|parent| {
|
|
||||||
if tool_uses.is_empty() {
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
parent.child(
|
|
||||||
v_flex().children(
|
|
||||||
tool_uses
|
|
||||||
.into_iter()
|
|
||||||
.map(|tool_use| self.render_tool_use(tool_use, cx)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
Role::System => div().id(("message-container", ix)).py_1().px_2().child(
|
Role::System => div().id(("message-container", ix)).py_1().px_2().child(
|
||||||
v_flex()
|
v_flex()
|
||||||
.bg(colors.editor_background)
|
.bg(colors.editor_background)
|
||||||
@@ -382,102 +343,6 @@ impl ActiveThread {
|
|||||||
|
|
||||||
styled_message.into_any()
|
styled_message.into_any()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_tool_use(&self, tool_use: ToolUse, cx: &mut Context<Self>) -> impl IntoElement {
|
|
||||||
let is_open = self
|
|
||||||
.expanded_tool_uses
|
|
||||||
.get(&tool_use.id)
|
|
||||||
.copied()
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
div().px_2p5().child(
|
|
||||||
v_flex()
|
|
||||||
.gap_1()
|
|
||||||
.rounded_lg()
|
|
||||||
.border_1()
|
|
||||||
.border_color(cx.theme().colors().border)
|
|
||||||
.child(
|
|
||||||
h_flex()
|
|
||||||
.justify_between()
|
|
||||||
.py_0p5()
|
|
||||||
.pl_1()
|
|
||||||
.pr_2()
|
|
||||||
.bg(cx.theme().colors().editor_foreground.opacity(0.02))
|
|
||||||
.when(is_open, |element| element.border_b_1().rounded_t(px(6.)))
|
|
||||||
.when(!is_open, |element| element.rounded(px(6.)))
|
|
||||||
.border_color(cx.theme().colors().border)
|
|
||||||
.child(
|
|
||||||
h_flex()
|
|
||||||
.gap_1()
|
|
||||||
.child(Disclosure::new("tool-use-disclosure", is_open).on_click(
|
|
||||||
cx.listener({
|
|
||||||
let tool_use_id = tool_use.id.clone();
|
|
||||||
move |this, _event, _window, _cx| {
|
|
||||||
let is_open = this
|
|
||||||
.expanded_tool_uses
|
|
||||||
.entry(tool_use_id.clone())
|
|
||||||
.or_insert(false);
|
|
||||||
|
|
||||||
*is_open = !*is_open;
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
))
|
|
||||||
.child(Label::new(tool_use.name)),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Label::new(match tool_use.status {
|
|
||||||
ToolUseStatus::Pending => "Pending",
|
|
||||||
ToolUseStatus::Running => "Running",
|
|
||||||
ToolUseStatus::Finished(_) => "Finished",
|
|
||||||
ToolUseStatus::Error(_) => "Error",
|
|
||||||
})
|
|
||||||
.size(LabelSize::XSmall)
|
|
||||||
.buffer_font(cx),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.map(|parent| {
|
|
||||||
if !is_open {
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
parent.child(
|
|
||||||
v_flex()
|
|
||||||
.child(
|
|
||||||
v_flex()
|
|
||||||
.gap_0p5()
|
|
||||||
.py_1()
|
|
||||||
.px_2p5()
|
|
||||||
.border_b_1()
|
|
||||||
.border_color(cx.theme().colors().border)
|
|
||||||
.child(Label::new("Input:"))
|
|
||||||
.child(Label::new(
|
|
||||||
serde_json::to_string_pretty(&tool_use.input)
|
|
||||||
.unwrap_or_default(),
|
|
||||||
)),
|
|
||||||
)
|
|
||||||
.map(|parent| match tool_use.status {
|
|
||||||
ToolUseStatus::Finished(output) => parent.child(
|
|
||||||
v_flex()
|
|
||||||
.gap_0p5()
|
|
||||||
.py_1()
|
|
||||||
.px_2p5()
|
|
||||||
.child(Label::new("Result:"))
|
|
||||||
.child(Label::new(output)),
|
|
||||||
),
|
|
||||||
ToolUseStatus::Error(err) => parent.child(
|
|
||||||
v_flex()
|
|
||||||
.gap_0p5()
|
|
||||||
.py_1()
|
|
||||||
.px_2p5()
|
|
||||||
.child(Label::new("Error:"))
|
|
||||||
.child(Label::new(err)),
|
|
||||||
),
|
|
||||||
ToolUseStatus::Pending | ToolUseStatus::Running => parent,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for ActiveThread {
|
impl Render for ActiveThread {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ mod context;
|
|||||||
mod context_picker;
|
mod context_picker;
|
||||||
mod context_store;
|
mod context_store;
|
||||||
mod context_strip;
|
mod context_strip;
|
||||||
mod history_store;
|
|
||||||
mod inline_assistant;
|
mod inline_assistant;
|
||||||
mod inline_prompt_editor;
|
mod inline_prompt_editor;
|
||||||
mod message_editor;
|
mod message_editor;
|
||||||
@@ -16,7 +15,6 @@ mod terminal_inline_assistant;
|
|||||||
mod thread;
|
mod thread;
|
||||||
mod thread_history;
|
mod thread_history;
|
||||||
mod thread_store;
|
mod thread_store;
|
||||||
mod tool_use;
|
|
||||||
mod ui;
|
mod ui;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -39,8 +37,10 @@ actions!(
|
|||||||
NewThread,
|
NewThread,
|
||||||
NewPromptEditor,
|
NewPromptEditor,
|
||||||
ToggleContextPicker,
|
ToggleContextPicker,
|
||||||
|
ToggleModelSelector,
|
||||||
RemoveAllContext,
|
RemoveAllContext,
|
||||||
OpenHistory,
|
OpenHistory,
|
||||||
|
OpenPromptEditorHistory,
|
||||||
OpenConfiguration,
|
OpenConfiguration,
|
||||||
RemoveSelectedThread,
|
RemoveSelectedThread,
|
||||||
Chat,
|
Chat,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use gpui::{Action, AnyView, App, EventEmitter, FocusHandle, Focusable, Subscription};
|
use gpui::{AnyView, App, EventEmitter, FocusHandle, Focusable, Subscription};
|
||||||
use language_model::{LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry};
|
use language_model::{LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry};
|
||||||
use ui::{prelude::*, Divider, DividerColor, ElevationIndex};
|
use ui::{prelude::*, Divider, DividerColor, ElevationIndex};
|
||||||
use zed_actions::assistant::DeployPromptLibrary;
|
use zed_actions::assistant::DeployPromptLibrary;
|
||||||
@@ -158,16 +158,8 @@ impl Render for AssistantConfiguration {
|
|||||||
.child(
|
.child(
|
||||||
v_flex()
|
v_flex()
|
||||||
.p(DynamicSpacing::Base16.rems(cx))
|
.p(DynamicSpacing::Base16.rems(cx))
|
||||||
.gap_2()
|
.gap_1()
|
||||||
.child(
|
|
||||||
v_flex()
|
|
||||||
.gap_0p5()
|
|
||||||
.child(Headline::new("Prompt Library").size(HeadlineSize::Small))
|
.child(Headline::new("Prompt Library").size(HeadlineSize::Small))
|
||||||
.child(
|
|
||||||
Label::new("Create reusable prompts and tag which ones you want sent in every LLM interaction.")
|
|
||||||
.color(Color::Muted),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
.child(
|
||||||
Button::new("open-prompt-library", "Open Prompt Library")
|
Button::new("open-prompt-library", "Open Prompt Library")
|
||||||
.style(ButtonStyle::Filled)
|
.style(ButtonStyle::Filled)
|
||||||
@@ -176,8 +168,8 @@ impl Render for AssistantConfiguration {
|
|||||||
.icon(IconName::Book)
|
.icon(IconName::Book)
|
||||||
.icon_size(IconSize::Small)
|
.icon_size(IconSize::Small)
|
||||||
.icon_position(IconPosition::Start)
|
.icon_position(IconPosition::Start)
|
||||||
.on_click(|_event, window, cx| {
|
.on_click(|_event, _window, cx| {
|
||||||
window.dispatch_action(DeployPromptLibrary.boxed_clone(), cx)
|
cx.dispatch_action(&DeployPromptLibrary)
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,19 +1,24 @@
|
|||||||
use assistant_settings::AssistantSettings;
|
use assistant_settings::AssistantSettings;
|
||||||
use fs::Fs;
|
use fs::Fs;
|
||||||
use gpui::{Entity, FocusHandle};
|
use gpui::{Entity, FocusHandle, SharedString};
|
||||||
use language_model_selector::{AssistantLanguageModelSelector, LanguageModelSelector};
|
use language_model::LanguageModelRegistry;
|
||||||
|
use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};
|
||||||
use settings::update_settings_file;
|
use settings::update_settings_file;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use ui::prelude::*;
|
use ui::{prelude::*, ButtonLike, PopoverMenuHandle, Tooltip};
|
||||||
|
|
||||||
|
use crate::ToggleModelSelector;
|
||||||
|
|
||||||
pub struct AssistantModelSelector {
|
pub struct AssistantModelSelector {
|
||||||
pub selector: Entity<LanguageModelSelector>,
|
selector: Entity<LanguageModelSelector>,
|
||||||
|
menu_handle: PopoverMenuHandle<LanguageModelSelector>,
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AssistantModelSelector {
|
impl AssistantModelSelector {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
fs: Arc<dyn Fs>,
|
fs: Arc<dyn Fs>,
|
||||||
|
menu_handle: PopoverMenuHandle<LanguageModelSelector>,
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
@@ -33,14 +38,50 @@ impl AssistantModelSelector {
|
|||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
menu_handle,
|
||||||
focus_handle,
|
focus_handle,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for AssistantModelSelector {
|
impl Render for AssistantModelSelector {
|
||||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
AssistantLanguageModelSelector::new(self.focus_handle.clone(), self.selector.clone())
|
let active_model = LanguageModelRegistry::read_global(cx).active_model();
|
||||||
.render(window, cx)
|
let focus_handle = self.focus_handle.clone();
|
||||||
|
let model_name = match active_model {
|
||||||
|
Some(model) => model.name().0,
|
||||||
|
_ => SharedString::from("No model selected"),
|
||||||
|
};
|
||||||
|
|
||||||
|
LanguageModelSelectorPopoverMenu::new(
|
||||||
|
self.selector.clone(),
|
||||||
|
ButtonLike::new("active-model")
|
||||||
|
.style(ButtonStyle::Subtle)
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.gap_0p5()
|
||||||
|
.child(
|
||||||
|
Label::new(model_name)
|
||||||
|
.size(LabelSize::Small)
|
||||||
|
.color(Color::Muted),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Icon::new(IconName::ChevronDown)
|
||||||
|
.color(Color::Muted)
|
||||||
|
.size(IconSize::XSmall),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
move |window, cx| {
|
||||||
|
Tooltip::for_action_in(
|
||||||
|
"Change Model",
|
||||||
|
&ToggleModelSelector,
|
||||||
|
&focus_handle,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
gpui::Corner::BottomRight,
|
||||||
|
)
|
||||||
|
.with_handle(self.menu_handle.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use std::sync::Arc;
|
|||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use assistant_context_editor::{
|
use assistant_context_editor::{
|
||||||
make_lsp_adapter_delegate, render_remaining_tokens, AssistantPanelDelegate, ConfigurationError,
|
make_lsp_adapter_delegate, render_remaining_tokens, AssistantPanelDelegate, ConfigurationError,
|
||||||
ContextEditor, SlashCommandCompletionProvider,
|
ContextEditor, ContextHistory, SlashCommandCompletionProvider,
|
||||||
};
|
};
|
||||||
use assistant_settings::{AssistantDockPosition, AssistantSettings};
|
use assistant_settings::{AssistantDockPosition, AssistantSettings};
|
||||||
use assistant_slash_command::SlashCommandWorkingSet;
|
use assistant_slash_command::SlashCommandWorkingSet;
|
||||||
@@ -14,7 +14,7 @@ use client::zed_urls;
|
|||||||
use editor::Editor;
|
use editor::Editor;
|
||||||
use fs::Fs;
|
use fs::Fs;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
prelude::*, Action, AnyElement, App, AsyncWindowContext, Corner, Entity, EventEmitter,
|
prelude::*, px, svg, Action, AnyElement, App, AsyncWindowContext, Corner, Entity, EventEmitter,
|
||||||
FocusHandle, Focusable, FontWeight, Pixels, Subscription, Task, UpdateGlobal, WeakEntity,
|
FocusHandle, Focusable, FontWeight, Pixels, Subscription, Task, UpdateGlobal, WeakEntity,
|
||||||
};
|
};
|
||||||
use language::LanguageRegistry;
|
use language::LanguageRegistry;
|
||||||
@@ -31,12 +31,14 @@ use zed_actions::assistant::{DeployPromptLibrary, ToggleFocus};
|
|||||||
|
|
||||||
use crate::active_thread::ActiveThread;
|
use crate::active_thread::ActiveThread;
|
||||||
use crate::assistant_configuration::{AssistantConfiguration, AssistantConfigurationEvent};
|
use crate::assistant_configuration::{AssistantConfiguration, AssistantConfigurationEvent};
|
||||||
use crate::history_store::{HistoryEntry, HistoryStore};
|
|
||||||
use crate::message_editor::MessageEditor;
|
use crate::message_editor::MessageEditor;
|
||||||
use crate::thread::{Thread, ThreadError, ThreadId};
|
use crate::thread::{Thread, ThreadError, ThreadId};
|
||||||
use crate::thread_history::{PastContext, PastThread, ThreadHistory};
|
use crate::thread_history::{PastThread, ThreadHistory};
|
||||||
use crate::thread_store::ThreadStore;
|
use crate::thread_store::ThreadStore;
|
||||||
use crate::{InlineAssistant, NewPromptEditor, NewThread, OpenConfiguration, OpenHistory};
|
use crate::{
|
||||||
|
InlineAssistant, NewPromptEditor, NewThread, OpenConfiguration, OpenHistory,
|
||||||
|
OpenPromptEditorHistory,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn init(cx: &mut App) {
|
pub fn init(cx: &mut App) {
|
||||||
cx.observe_new(
|
cx.observe_new(
|
||||||
@@ -60,6 +62,12 @@ pub fn init(cx: &mut App) {
|
|||||||
panel.update(cx, |panel, cx| panel.new_prompt_editor(window, cx));
|
panel.update(cx, |panel, cx| panel.new_prompt_editor(window, cx));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.register_action(|workspace, _: &OpenPromptEditorHistory, window, cx| {
|
||||||
|
if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {
|
||||||
|
workspace.focus_panel::<AssistantPanel>(window, cx);
|
||||||
|
panel.update(cx, |panel, cx| panel.open_prompt_editor_history(window, cx));
|
||||||
|
}
|
||||||
|
})
|
||||||
.register_action(|workspace, _: &OpenConfiguration, window, cx| {
|
.register_action(|workspace, _: &OpenConfiguration, window, cx| {
|
||||||
if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {
|
if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {
|
||||||
workspace.focus_panel::<AssistantPanel>(window, cx);
|
workspace.focus_panel::<AssistantPanel>(window, cx);
|
||||||
@@ -75,6 +83,7 @@ enum ActiveView {
|
|||||||
Thread,
|
Thread,
|
||||||
PromptEditor,
|
PromptEditor,
|
||||||
History,
|
History,
|
||||||
|
PromptEditorHistory,
|
||||||
Configuration,
|
Configuration,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,14 +97,15 @@ pub struct AssistantPanel {
|
|||||||
message_editor: Entity<MessageEditor>,
|
message_editor: Entity<MessageEditor>,
|
||||||
context_store: Entity<assistant_context_editor::ContextStore>,
|
context_store: Entity<assistant_context_editor::ContextStore>,
|
||||||
context_editor: Option<Entity<ContextEditor>>,
|
context_editor: Option<Entity<ContextEditor>>,
|
||||||
|
context_history: Option<Entity<ContextHistory>>,
|
||||||
configuration: Option<Entity<AssistantConfiguration>>,
|
configuration: Option<Entity<AssistantConfiguration>>,
|
||||||
configuration_subscription: Option<Subscription>,
|
configuration_subscription: Option<Subscription>,
|
||||||
tools: Arc<ToolWorkingSet>,
|
tools: Arc<ToolWorkingSet>,
|
||||||
local_timezone: UtcOffset,
|
local_timezone: UtcOffset,
|
||||||
active_view: ActiveView,
|
active_view: ActiveView,
|
||||||
history_store: Entity<HistoryStore>,
|
|
||||||
history: Entity<ThreadHistory>,
|
history: Entity<ThreadHistory>,
|
||||||
new_item_context_menu_handle: PopoverMenuHandle<ContextMenu>,
|
new_item_context_menu_handle: PopoverMenuHandle<ContextMenu>,
|
||||||
|
open_history_context_menu_handle: PopoverMenuHandle<ContextMenu>,
|
||||||
width: Option<Pixels>,
|
width: Option<Pixels>,
|
||||||
height: Option<Pixels>,
|
height: Option<Pixels>,
|
||||||
}
|
}
|
||||||
@@ -163,9 +173,6 @@ impl AssistantPanel {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
let history_store =
|
|
||||||
cx.new(|cx| HistoryStore::new(thread_store.clone(), context_store.clone(), cx));
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
active_view: ActiveView::Thread,
|
active_view: ActiveView::Thread,
|
||||||
workspace: workspace.clone(),
|
workspace: workspace.clone(),
|
||||||
@@ -187,6 +194,7 @@ impl AssistantPanel {
|
|||||||
message_editor,
|
message_editor,
|
||||||
context_store,
|
context_store,
|
||||||
context_editor: None,
|
context_editor: None,
|
||||||
|
context_history: None,
|
||||||
configuration: None,
|
configuration: None,
|
||||||
configuration_subscription: None,
|
configuration_subscription: None,
|
||||||
tools,
|
tools,
|
||||||
@@ -194,9 +202,9 @@ impl AssistantPanel {
|
|||||||
chrono::Local::now().offset().local_minus_utc(),
|
chrono::Local::now().offset().local_minus_utc(),
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
history_store: history_store.clone(),
|
history: cx.new(|cx| ThreadHistory::new(weak_self, thread_store, cx)),
|
||||||
history: cx.new(|cx| ThreadHistory::new(weak_self, history_store, cx)),
|
|
||||||
new_item_context_menu_handle: PopoverMenuHandle::default(),
|
new_item_context_menu_handle: PopoverMenuHandle::default(),
|
||||||
|
open_history_context_menu_handle: PopoverMenuHandle::default(),
|
||||||
width: None,
|
width: None,
|
||||||
height: None,
|
height: None,
|
||||||
}
|
}
|
||||||
@@ -323,7 +331,26 @@ impl AssistantPanel {
|
|||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn open_saved_prompt_editor(
|
fn open_prompt_editor_history(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
self.active_view = ActiveView::PromptEditorHistory;
|
||||||
|
self.context_history = Some(cx.new(|cx| {
|
||||||
|
ContextHistory::new(
|
||||||
|
self.project.clone(),
|
||||||
|
self.context_store.clone(),
|
||||||
|
self.workspace.clone(),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}));
|
||||||
|
|
||||||
|
if let Some(context_history) = self.context_history.as_ref() {
|
||||||
|
context_history.focus_handle(cx).focus(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open_saved_prompt_editor(
|
||||||
&mut self,
|
&mut self,
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
@@ -431,7 +458,7 @@ impl AssistantPanel {
|
|||||||
active_provider.id() != provider.id()
|
active_provider.id() != provider.id()
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
if let Some(model) = provider.default_model(cx) {
|
if let Some(model) = provider.provided_models(cx).first().cloned() {
|
||||||
update_settings_file::<AssistantSettings>(
|
update_settings_file::<AssistantSettings>(
|
||||||
self.fs.clone(),
|
self.fs.clone(),
|
||||||
cx,
|
cx,
|
||||||
@@ -458,12 +485,6 @@ impl AssistantPanel {
|
|||||||
pub(crate) fn active_context_editor(&self) -> Option<Entity<ContextEditor>> {
|
pub(crate) fn active_context_editor(&self) -> Option<Entity<ContextEditor>> {
|
||||||
self.context_editor.clone()
|
self.context_editor.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn delete_context(&mut self, path: PathBuf, cx: &mut Context<Self>) {
|
|
||||||
self.context_store
|
|
||||||
.update(cx, |this, cx| this.delete_local_context(path, cx))
|
|
||||||
.detach_and_log_err(cx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Focusable for AssistantPanel {
|
impl Focusable for AssistantPanel {
|
||||||
@@ -478,6 +499,13 @@ impl Focusable for AssistantPanel {
|
|||||||
cx.focus_handle()
|
cx.focus_handle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ActiveView::PromptEditorHistory => {
|
||||||
|
if let Some(context_history) = self.context_history.as_ref() {
|
||||||
|
context_history.focus_handle(cx)
|
||||||
|
} else {
|
||||||
|
cx.focus_handle()
|
||||||
|
}
|
||||||
|
}
|
||||||
ActiveView::Configuration => {
|
ActiveView::Configuration => {
|
||||||
if let Some(configuration) = self.configuration.as_ref() {
|
if let Some(configuration) = self.configuration.as_ref() {
|
||||||
configuration.focus_handle(cx)
|
configuration.focus_handle(cx)
|
||||||
@@ -590,12 +618,21 @@ impl AssistantPanel {
|
|||||||
SharedString::from(context_editor.read(cx).title(cx).to_string())
|
SharedString::from(context_editor.read(cx).title(cx).to_string())
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| SharedString::from("Loading Summary…")),
|
.unwrap_or_else(|| SharedString::from("Loading Summary…")),
|
||||||
ActiveView::History => "History".into(),
|
ActiveView::History | ActiveView::PromptEditorHistory => "History".into(),
|
||||||
ActiveView::Configuration => "Assistant Settings".into(),
|
ActiveView::Configuration => "Assistant Settings".into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let sub_title = match self.active_view {
|
||||||
|
ActiveView::Thread => None,
|
||||||
|
ActiveView::PromptEditor => None,
|
||||||
|
ActiveView::History => Some("Thread"),
|
||||||
|
ActiveView::PromptEditorHistory => Some("Prompt Editor"),
|
||||||
|
ActiveView::Configuration => None,
|
||||||
|
};
|
||||||
|
|
||||||
h_flex()
|
h_flex()
|
||||||
.id("assistant-toolbar")
|
.id("assistant-toolbar")
|
||||||
|
.px(DynamicSpacing::Base08.rems(cx))
|
||||||
.h(Tab::container_height(cx))
|
.h(Tab::container_height(cx))
|
||||||
.flex_none()
|
.flex_none()
|
||||||
.justify_between()
|
.justify_between()
|
||||||
@@ -604,29 +641,40 @@ impl AssistantPanel {
|
|||||||
.border_b_1()
|
.border_b_1()
|
||||||
.border_color(cx.theme().colors().border)
|
.border_color(cx.theme().colors().border)
|
||||||
.child(
|
.child(
|
||||||
div()
|
h_flex()
|
||||||
.id("title")
|
.w_full()
|
||||||
.overflow_x_scroll()
|
.gap_1()
|
||||||
.px(DynamicSpacing::Base08.rems(cx))
|
.justify_between()
|
||||||
.child(Label::new(title).text_ellipsis()),
|
|
||||||
)
|
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
.h_full()
|
.child(Label::new(title))
|
||||||
.pl_2()
|
.when(sub_title.is_some(), |this| {
|
||||||
.gap_2()
|
this.child(
|
||||||
.bg(cx.theme().colors().tab_bar_background)
|
h_flex()
|
||||||
|
.pl_1p5()
|
||||||
|
.gap_1p5()
|
||||||
|
.child(
|
||||||
|
Label::new("/")
|
||||||
|
.size(LabelSize::Small)
|
||||||
|
.color(Color::Disabled)
|
||||||
|
.alpha(0.5),
|
||||||
|
)
|
||||||
|
.child(Label::new(sub_title.unwrap())),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
)
|
||||||
.children(if matches!(self.active_view, ActiveView::PromptEditor) {
|
.children(if matches!(self.active_view, ActiveView::PromptEditor) {
|
||||||
self.context_editor
|
self.context_editor
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|editor| render_remaining_tokens(editor, cx))
|
.and_then(|editor| render_remaining_tokens(editor, cx))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
})
|
}),
|
||||||
|
)
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
.h_full()
|
.h_full()
|
||||||
.px(DynamicSpacing::Base08.rems(cx))
|
.pl_1p5()
|
||||||
.border_l_1()
|
.border_l_1()
|
||||||
.border_color(cx.theme().colors().border)
|
.border_color(cx.theme().colors().border)
|
||||||
.gap(DynamicSpacing::Base02.rems(cx))
|
.gap(DynamicSpacing::Base02.rems(cx))
|
||||||
@@ -641,37 +689,30 @@ impl AssistantPanel {
|
|||||||
.anchor(Corner::TopRight)
|
.anchor(Corner::TopRight)
|
||||||
.with_handle(self.new_item_context_menu_handle.clone())
|
.with_handle(self.new_item_context_menu_handle.clone())
|
||||||
.menu(move |window, cx| {
|
.menu(move |window, cx| {
|
||||||
Some(ContextMenu::build(
|
Some(ContextMenu::build(window, cx, |menu, _window, _cx| {
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
|menu, _window, _cx| {
|
|
||||||
menu.action("New Thread", NewThread.boxed_clone())
|
menu.action("New Thread", NewThread.boxed_clone())
|
||||||
.action(
|
.action("New Prompt Editor", NewPromptEditor.boxed_clone())
|
||||||
"New Prompt Editor",
|
}))
|
||||||
NewPromptEditor.boxed_clone(),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
|
PopoverMenu::new("assistant-toolbar-history-popover-menu")
|
||||||
|
.trigger_with_tooltip(
|
||||||
IconButton::new("open-history", IconName::HistoryRerun)
|
IconButton::new("open-history", IconName::HistoryRerun)
|
||||||
.icon_size(IconSize::Small)
|
.icon_size(IconSize::Small)
|
||||||
.style(ButtonStyle::Subtle)
|
.style(ButtonStyle::Subtle),
|
||||||
.tooltip({
|
Tooltip::text("History…"),
|
||||||
let focus_handle = self.focus_handle(cx);
|
|
||||||
move |window, cx| {
|
|
||||||
Tooltip::for_action_in(
|
|
||||||
"History",
|
|
||||||
&OpenHistory,
|
|
||||||
&focus_handle,
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
)
|
)
|
||||||
}
|
.anchor(Corner::TopRight)
|
||||||
})
|
.with_handle(self.open_history_context_menu_handle.clone())
|
||||||
.on_click(move |_event, window, cx| {
|
.menu(move |window, cx| {
|
||||||
window.dispatch_action(OpenHistory.boxed_clone(), cx);
|
Some(ContextMenu::build(window, cx, |menu, _window, _cx| {
|
||||||
|
menu.action("Thread History", OpenHistory.boxed_clone())
|
||||||
|
.action(
|
||||||
|
"Prompt Editor History",
|
||||||
|
OpenPromptEditorHistory.boxed_clone(),
|
||||||
|
)
|
||||||
|
}))
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
@@ -683,7 +724,6 @@ impl AssistantPanel {
|
|||||||
window.dispatch_action(OpenConfiguration.boxed_clone(), cx);
|
window.dispatch_action(OpenConfiguration.boxed_clone(), cx);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -722,13 +762,14 @@ impl AssistantPanel {
|
|||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement {
|
||||||
let recent_history = self
|
let recent_threads = self
|
||||||
.history_store
|
.thread_store
|
||||||
.update(cx, |this, cx| this.recent_entries(6, cx));
|
.update(cx, |this, _cx| this.recent_threads(3));
|
||||||
|
|
||||||
let create_welcome_heading = || {
|
let create_welcome_heading = || {
|
||||||
h_flex()
|
h_flex()
|
||||||
.w_full()
|
.w_full()
|
||||||
|
.justify_center()
|
||||||
.child(Headline::new("Welcome to the Assistant Panel").size(HeadlineSize::Small))
|
.child(Headline::new("Welcome to the Assistant Panel").size(HeadlineSize::Small))
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -736,27 +777,35 @@ impl AssistantPanel {
|
|||||||
let no_error = configuration_error.is_none();
|
let no_error = configuration_error.is_none();
|
||||||
|
|
||||||
v_flex()
|
v_flex()
|
||||||
.p_1p5()
|
.gap_2()
|
||||||
.size_full()
|
.child(
|
||||||
.justify_end()
|
v_flex().w_full().child(
|
||||||
.gap_1()
|
svg()
|
||||||
|
.path("icons/logo_96.svg")
|
||||||
|
.text_color(cx.theme().colors().text)
|
||||||
|
.w(px(40.))
|
||||||
|
.h(px(40.))
|
||||||
|
.mx_auto()
|
||||||
|
.mb_4(),
|
||||||
|
),
|
||||||
|
)
|
||||||
.map(|parent| {
|
.map(|parent| {
|
||||||
match configuration_error {
|
match configuration_error {
|
||||||
Some(ConfigurationError::ProviderNotAuthenticated)
|
Some(ConfigurationError::ProviderNotAuthenticated) | Some(ConfigurationError::NoProvider) => {
|
||||||
| Some(ConfigurationError::NoProvider) => {
|
|
||||||
parent.child(
|
parent.child(
|
||||||
v_flex()
|
v_flex()
|
||||||
.px_1p5()
|
|
||||||
.gap_0p5()
|
.gap_0p5()
|
||||||
.child(create_welcome_heading())
|
.child(create_welcome_heading())
|
||||||
.child(
|
.child(
|
||||||
|
h_flex().mb_2().w_full().justify_center().child(
|
||||||
Label::new(
|
Label::new(
|
||||||
"To start using the assistant, configure at least one LLM provider.",
|
"To start using the assistant, configure at least one LLM provider.",
|
||||||
)
|
)
|
||||||
.color(Color::Muted),
|
.color(Color::Muted),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
h_flex().mt_1().w_full().child(
|
h_flex().w_full().justify_center().child(
|
||||||
Button::new("open-configuration", "Configure a Provider")
|
Button::new("open-configuration", "Configure a Provider")
|
||||||
.size(ButtonSize::Compact)
|
.size(ButtonSize::Compact)
|
||||||
.icon(Some(IconName::Sliders))
|
.icon(Some(IconName::Sliders))
|
||||||
@@ -769,66 +818,64 @@ impl AssistantPanel {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Some(ConfigurationError::ProviderPendingTermsAcceptance(provider)) => parent
|
Some(ConfigurationError::ProviderPendingTermsAcceptance(provider)) => {
|
||||||
.child(v_flex().px_1p5().gap_0p5().child(create_welcome_heading()).children(
|
parent.child(
|
||||||
provider.render_accept_terms(
|
v_flex()
|
||||||
|
.gap_0p5()
|
||||||
|
.child(create_welcome_heading())
|
||||||
|
.children(provider.render_accept_terms(
|
||||||
LanguageModelProviderTosView::ThreadEmptyState,
|
LanguageModelProviderTosView::ThreadEmptyState,
|
||||||
cx,
|
cx,
|
||||||
),
|
|
||||||
)),
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
None => parent,
|
None => parent,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.when(recent_history.is_empty() && no_error, |parent| {
|
.when(
|
||||||
parent.child(v_flex().gap_0p5().child(create_welcome_heading()).child(
|
recent_threads.is_empty() && no_error,
|
||||||
Label::new("Start typing to chat with your codebase").color(Color::Muted),
|
|parent| {
|
||||||
))
|
parent.child(
|
||||||
})
|
v_flex().gap_0p5().child(create_welcome_heading()).child(
|
||||||
.when(!recent_history.is_empty(), |parent| {
|
h_flex().w_full().justify_center().child(
|
||||||
|
Label::new("Start typing to chat with your codebase")
|
||||||
|
.color(Color::Muted),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.when(!recent_threads.is_empty(), |parent| {
|
||||||
parent
|
parent
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex().w_full().justify_center().child(
|
||||||
.pl_1p5()
|
Label::new("Recent Threads:")
|
||||||
.pb_1()
|
|
||||||
.w_full()
|
|
||||||
.justify_between()
|
|
||||||
.border_b_1()
|
|
||||||
.border_color(cx.theme().colors().border_variant)
|
|
||||||
.child(
|
|
||||||
Label::new("Past Interactions")
|
|
||||||
.size(LabelSize::Small)
|
.size(LabelSize::Small)
|
||||||
.color(Color::Muted),
|
.color(Color::Muted),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
.child(v_flex().mx_auto().w_4_5().gap_2().children(
|
||||||
|
recent_threads.into_iter().map(|thread| {
|
||||||
|
// TODO: keyboard navigation
|
||||||
|
PastThread::new(thread, cx.entity().downgrade(), false)
|
||||||
|
}),
|
||||||
|
))
|
||||||
.child(
|
.child(
|
||||||
Button::new("view-history", "View All")
|
h_flex().w_full().justify_center().child(
|
||||||
|
Button::new("view-all-past-threads", "View All Past Threads")
|
||||||
.style(ButtonStyle::Subtle)
|
.style(ButtonStyle::Subtle)
|
||||||
.label_size(LabelSize::Small)
|
.label_size(LabelSize::Small)
|
||||||
.key_binding(KeyBinding::for_action_in(
|
.key_binding(KeyBinding::for_action_in(
|
||||||
&OpenHistory,
|
&OpenHistory,
|
||||||
&self.focus_handle(cx),
|
&self.focus_handle(cx),
|
||||||
window,
|
window,
|
||||||
cx,
|
cx
|
||||||
))
|
))
|
||||||
.on_click(move |_event, window, cx| {
|
.on_click(move |_event, window, cx| {
|
||||||
window.dispatch_action(OpenHistory.boxed_clone(), cx);
|
window.dispatch_action(OpenHistory.boxed_clone(), cx);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.child(v_flex().gap_1().children(
|
|
||||||
recent_history.into_iter().map(|entry| {
|
|
||||||
// TODO: Add keyboard navigation.
|
|
||||||
match entry {
|
|
||||||
HistoryEntry::Thread(thread) => {
|
|
||||||
PastThread::new(thread, cx.entity().downgrade(), false)
|
|
||||||
.into_any_element()
|
|
||||||
}
|
|
||||||
HistoryEntry::Context(context) => {
|
|
||||||
PastContext::new(context, cx.entity().downgrade(), false)
|
|
||||||
.into_any_element()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
))
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1021,6 +1068,7 @@ impl Render for AssistantPanel {
|
|||||||
.children(self.render_last_error(cx)),
|
.children(self.render_last_error(cx)),
|
||||||
ActiveView::History => parent.child(self.history.clone()),
|
ActiveView::History => parent.child(self.history.clone()),
|
||||||
ActiveView::PromptEditor => parent.children(self.context_editor.clone()),
|
ActiveView::PromptEditor => parent.children(self.context_editor.clone()),
|
||||||
|
ActiveView::PromptEditorHistory => parent.children(self.context_history.clone()),
|
||||||
ActiveView::Configuration => parent.children(self.configuration.clone()),
|
ActiveView::Configuration => parent.children(self.configuration.clone()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,10 @@ use futures::{channel::mpsc, future::LocalBoxFuture, join, SinkExt, Stream, Stre
|
|||||||
use gpui::{App, AppContext as _, Context, Entity, EventEmitter, Subscription, Task};
|
use gpui::{App, AppContext as _, Context, Entity, EventEmitter, Subscription, Task};
|
||||||
use language::{line_diff, Buffer, IndentKind, Point, TransactionId};
|
use language::{line_diff, Buffer, IndentKind, Point, TransactionId};
|
||||||
use language_model::{
|
use language_model::{
|
||||||
report_assistant_event, LanguageModel, LanguageModelRegistry, LanguageModelRequest,
|
LanguageModel, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage,
|
||||||
LanguageModelRequestMessage, LanguageModelTextStream, Role,
|
LanguageModelTextStream, Role,
|
||||||
};
|
};
|
||||||
|
use language_models::report_assistant_event;
|
||||||
use multi_buffer::MultiBufferRow;
|
use multi_buffer::MultiBufferRow;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use prompt_library::PromptBuilder;
|
use prompt_library::PromptBuilder;
|
||||||
|
|||||||
@@ -7,19 +7,19 @@ use std::sync::Arc;
|
|||||||
use editor::actions::FoldAt;
|
use editor::actions::FoldAt;
|
||||||
use editor::display_map::{Crease, FoldId};
|
use editor::display_map::{Crease, FoldId};
|
||||||
use editor::scroll::Autoscroll;
|
use editor::scroll::Autoscroll;
|
||||||
use editor::{Anchor, AnchorRangeExt, Editor, FoldPlaceholder, ToPoint};
|
use editor::{Anchor, Editor, FoldPlaceholder, ToPoint};
|
||||||
use file_icons::FileIcons;
|
use file_icons::FileIcons;
|
||||||
use fuzzy::PathMatch;
|
use fuzzy::PathMatch;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
AnyElement, App, AppContext, DismissEvent, Empty, Entity, FocusHandle, Focusable, Stateful,
|
AnyElement, App, DismissEvent, Empty, Entity, FocusHandle, Focusable, Stateful, Task,
|
||||||
Task, WeakEntity,
|
WeakEntity,
|
||||||
};
|
};
|
||||||
use multi_buffer::{MultiBufferPoint, MultiBufferRow};
|
use multi_buffer::{MultiBufferPoint, MultiBufferRow};
|
||||||
use picker::{Picker, PickerDelegate};
|
use picker::{Picker, PickerDelegate};
|
||||||
use project::{PathMatchCandidateSet, ProjectPath, WorktreeId};
|
use project::{PathMatchCandidateSet, ProjectPath, WorktreeId};
|
||||||
use rope::Point;
|
use rope::Point;
|
||||||
use text::SelectionGoal;
|
use text::SelectionGoal;
|
||||||
use ui::{prelude::*, ButtonLike, Disclosure, ListItem, TintColor, Tooltip};
|
use ui::{prelude::*, ButtonLike, Disclosure, ElevationIndex, ListItem, Tooltip};
|
||||||
use util::ResultExt as _;
|
use util::ResultExt as _;
|
||||||
use workspace::{notifications::NotifyResultExt, Workspace};
|
use workspace::{notifications::NotifyResultExt, Workspace};
|
||||||
|
|
||||||
@@ -238,11 +238,11 @@ impl PickerDelegate for FileContextPickerDelegate {
|
|||||||
path: mat.path.clone(),
|
path: mat.path.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(editor_entity) = self.editor.upgrade() else {
|
let Some(editor) = self.editor.upgrade() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
editor_entity.update(cx, |editor, cx| {
|
editor.update(cx, |editor, cx| {
|
||||||
editor.transact(window, cx, |editor, window, cx| {
|
editor.transact(window, cx, |editor, window, cx| {
|
||||||
// Move empty selections left by 1 column to select the `@`s, so they get overwritten when we insert.
|
// Move empty selections left by 1 column to select the `@`s, so they get overwritten when we insert.
|
||||||
{
|
{
|
||||||
@@ -292,11 +292,7 @@ impl PickerDelegate for FileContextPickerDelegate {
|
|||||||
.unwrap_or_else(|| SharedString::new(""));
|
.unwrap_or_else(|| SharedString::new(""));
|
||||||
|
|
||||||
let placeholder = FoldPlaceholder {
|
let placeholder = FoldPlaceholder {
|
||||||
render: render_fold_icon_button(
|
render: render_fold_icon_button(file_icon, file_name.into()),
|
||||||
file_icon,
|
|
||||||
file_name.into(),
|
|
||||||
editor_entity.downgrade(),
|
|
||||||
),
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -468,50 +464,11 @@ pub fn render_file_context_entry(
|
|||||||
fn render_fold_icon_button(
|
fn render_fold_icon_button(
|
||||||
icon: SharedString,
|
icon: SharedString,
|
||||||
label: SharedString,
|
label: SharedString,
|
||||||
editor: WeakEntity<Editor>,
|
) -> Arc<dyn Send + Sync + Fn(FoldId, Range<Anchor>, &mut Window, &mut App) -> AnyElement> {
|
||||||
) -> Arc<dyn Send + Sync + Fn(FoldId, Range<Anchor>, &mut App) -> AnyElement> {
|
Arc::new(move |fold_id, _fold_range, _window, _cx| {
|
||||||
Arc::new(move |fold_id, fold_range, cx| {
|
|
||||||
let is_in_text_selection = editor.upgrade().is_some_and(|editor| {
|
|
||||||
editor.update(cx, |editor, cx| {
|
|
||||||
let snapshot = editor
|
|
||||||
.buffer()
|
|
||||||
.update(cx, |multi_buffer, cx| multi_buffer.snapshot(cx));
|
|
||||||
|
|
||||||
let is_in_pending_selection = || {
|
|
||||||
editor
|
|
||||||
.selections
|
|
||||||
.pending
|
|
||||||
.as_ref()
|
|
||||||
.is_some_and(|pending_selection| {
|
|
||||||
pending_selection
|
|
||||||
.selection
|
|
||||||
.range()
|
|
||||||
.includes(&fold_range, &snapshot)
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut is_in_complete_selection = || {
|
|
||||||
editor
|
|
||||||
.selections
|
|
||||||
.disjoint_in_range::<usize>(fold_range.clone(), cx)
|
|
||||||
.into_iter()
|
|
||||||
.any(|selection| {
|
|
||||||
// This is needed to cover a corner case, if we just check for an existing
|
|
||||||
// selection in the fold range, having a cursor at the start of the fold
|
|
||||||
// marks it as selected. Non-empty selections don't cause this.
|
|
||||||
let length = selection.end - selection.start;
|
|
||||||
length > 0
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
is_in_pending_selection() || is_in_complete_selection()
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
ButtonLike::new(fold_id)
|
ButtonLike::new(fold_id)
|
||||||
.style(ButtonStyle::Filled)
|
.style(ButtonStyle::Filled)
|
||||||
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
|
.layer(ElevationIndex::ElevatedSurface)
|
||||||
.toggle_state(is_in_text_selection)
|
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
.gap_1()
|
.gap_1()
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
use assistant_context_editor::SavedContextMetadata;
|
|
||||||
use chrono::{DateTime, Utc};
|
|
||||||
use gpui::{prelude::*, Entity};
|
|
||||||
|
|
||||||
use crate::thread_store::{SavedThreadMetadata, ThreadStore};
|
|
||||||
|
|
||||||
pub enum HistoryEntry {
|
|
||||||
Thread(SavedThreadMetadata),
|
|
||||||
Context(SavedContextMetadata),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HistoryEntry {
|
|
||||||
pub fn updated_at(&self) -> DateTime<Utc> {
|
|
||||||
match self {
|
|
||||||
HistoryEntry::Thread(thread) => thread.updated_at,
|
|
||||||
HistoryEntry::Context(context) => context.mtime.to_utc(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct HistoryStore {
|
|
||||||
thread_store: Entity<ThreadStore>,
|
|
||||||
context_store: Entity<assistant_context_editor::ContextStore>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HistoryStore {
|
|
||||||
pub fn new(
|
|
||||||
thread_store: Entity<ThreadStore>,
|
|
||||||
context_store: Entity<assistant_context_editor::ContextStore>,
|
|
||||||
_cx: &mut Context<Self>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
thread_store,
|
|
||||||
context_store,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the number of history entries.
|
|
||||||
pub fn entry_count(&self, cx: &mut Context<Self>) -> usize {
|
|
||||||
self.entries(cx).len()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn entries(&self, cx: &mut Context<Self>) -> Vec<HistoryEntry> {
|
|
||||||
let mut history_entries = Vec::new();
|
|
||||||
|
|
||||||
for thread in self.thread_store.update(cx, |this, _cx| this.threads()) {
|
|
||||||
history_entries.push(HistoryEntry::Thread(thread));
|
|
||||||
}
|
|
||||||
|
|
||||||
for context in self.context_store.update(cx, |this, _cx| this.contexts()) {
|
|
||||||
history_entries.push(HistoryEntry::Context(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
history_entries.sort_unstable_by_key(|entry| std::cmp::Reverse(entry.updated_at()));
|
|
||||||
history_entries
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn recent_entries(&self, limit: usize, cx: &mut Context<Self>) -> Vec<HistoryEntry> {
|
|
||||||
self.entries(cx).into_iter().take(limit).collect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -24,7 +24,8 @@ use gpui::{
|
|||||||
UpdateGlobal, WeakEntity, Window,
|
UpdateGlobal, WeakEntity, Window,
|
||||||
};
|
};
|
||||||
use language::{Buffer, Point, Selection, TransactionId};
|
use language::{Buffer, Point, Selection, TransactionId};
|
||||||
use language_model::{report_assistant_event, LanguageModelRegistry};
|
use language_model::LanguageModelRegistry;
|
||||||
|
use language_models::report_assistant_event;
|
||||||
use multi_buffer::MultiBufferRow;
|
use multi_buffer::MultiBufferRow;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use project::{CodeAction, ProjectTransaction};
|
use project::{CodeAction, ProjectTransaction};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use crate::context_strip::{ContextStrip, ContextStripEvent, SuggestContextKind};
|
|||||||
use crate::terminal_codegen::TerminalCodegen;
|
use crate::terminal_codegen::TerminalCodegen;
|
||||||
use crate::thread_store::ThreadStore;
|
use crate::thread_store::ThreadStore;
|
||||||
use crate::{CycleNextInlineAssist, CyclePreviousInlineAssist};
|
use crate::{CycleNextInlineAssist, CyclePreviousInlineAssist};
|
||||||
use crate::{RemoveAllContext, ToggleContextPicker};
|
use crate::{RemoveAllContext, ToggleContextPicker, ToggleModelSelector};
|
||||||
use client::ErrorExt;
|
use client::ErrorExt;
|
||||||
use collections::VecDeque;
|
use collections::VecDeque;
|
||||||
use editor::{
|
use editor::{
|
||||||
@@ -20,6 +20,7 @@ use gpui::{
|
|||||||
EventEmitter, FocusHandle, Focusable, FontWeight, Subscription, TextStyle, WeakEntity, Window,
|
EventEmitter, FocusHandle, Focusable, FontWeight, Subscription, TextStyle, WeakEntity, Window,
|
||||||
};
|
};
|
||||||
use language_model::{LanguageModel, LanguageModelRegistry};
|
use language_model::{LanguageModel, LanguageModelRegistry};
|
||||||
|
use language_model_selector::LanguageModelSelector;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
@@ -39,6 +40,7 @@ pub struct PromptEditor<T> {
|
|||||||
context_strip: Entity<ContextStrip>,
|
context_strip: Entity<ContextStrip>,
|
||||||
context_picker_menu_handle: PopoverMenuHandle<ContextPicker>,
|
context_picker_menu_handle: PopoverMenuHandle<ContextPicker>,
|
||||||
model_selector: Entity<AssistantModelSelector>,
|
model_selector: Entity<AssistantModelSelector>,
|
||||||
|
model_selector_menu_handle: PopoverMenuHandle<LanguageModelSelector>,
|
||||||
edited_since_done: bool,
|
edited_since_done: bool,
|
||||||
prompt_history: VecDeque<String>,
|
prompt_history: VecDeque<String>,
|
||||||
prompt_history_ix: Option<usize>,
|
prompt_history_ix: Option<usize>,
|
||||||
@@ -54,7 +56,7 @@ impl<T: 'static> EventEmitter<PromptEditorEvent> for PromptEditor<T> {}
|
|||||||
|
|
||||||
impl<T: 'static> Render for PromptEditor<T> {
|
impl<T: 'static> Render for PromptEditor<T> {
|
||||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
let ui_font_size = ThemeSettings::get_global(cx).ui_font_size(cx);
|
let ui_font_size = ThemeSettings::get_global(cx).ui_font_size;
|
||||||
let mut buttons = Vec::new();
|
let mut buttons = Vec::new();
|
||||||
|
|
||||||
let left_gutter_width = match &self.mode {
|
let left_gutter_width = match &self.mode {
|
||||||
@@ -102,12 +104,7 @@ impl<T: 'static> Render for PromptEditor<T> {
|
|||||||
.items_start()
|
.items_start()
|
||||||
.cursor(CursorStyle::Arrow)
|
.cursor(CursorStyle::Arrow)
|
||||||
.on_action(cx.listener(Self::toggle_context_picker))
|
.on_action(cx.listener(Self::toggle_context_picker))
|
||||||
.on_action(cx.listener(|this, action, window, cx| {
|
.on_action(cx.listener(Self::toggle_model_selector))
|
||||||
let selector = this.model_selector.read(cx).selector.clone();
|
|
||||||
selector.update(cx, |selector, cx| {
|
|
||||||
selector.toggle_model_selector(action, window, cx);
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
.on_action(cx.listener(Self::confirm))
|
.on_action(cx.listener(Self::confirm))
|
||||||
.on_action(cx.listener(Self::cancel))
|
.on_action(cx.listener(Self::cancel))
|
||||||
.on_action(cx.listener(Self::move_up))
|
.on_action(cx.listener(Self::move_up))
|
||||||
@@ -350,6 +347,15 @@ impl<T: 'static> PromptEditor<T> {
|
|||||||
self.context_picker_menu_handle.toggle(window, cx);
|
self.context_picker_menu_handle.toggle(window, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn toggle_model_selector(
|
||||||
|
&mut self,
|
||||||
|
_: &ToggleModelSelector,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) {
|
||||||
|
self.model_selector_menu_handle.toggle(window, cx);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn remove_all_context(
|
pub fn remove_all_context(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: &RemoveAllContext,
|
_: &RemoveAllContext,
|
||||||
@@ -858,6 +864,7 @@ impl PromptEditor<BufferCodegen> {
|
|||||||
editor
|
editor
|
||||||
});
|
});
|
||||||
let context_picker_menu_handle = PopoverMenuHandle::default();
|
let context_picker_menu_handle = PopoverMenuHandle::default();
|
||||||
|
let model_selector_menu_handle = PopoverMenuHandle::default();
|
||||||
|
|
||||||
let context_strip = cx.new(|cx| {
|
let context_strip = cx.new(|cx| {
|
||||||
ContextStrip::new(
|
ContextStrip::new(
|
||||||
@@ -881,8 +888,15 @@ impl PromptEditor<BufferCodegen> {
|
|||||||
context_strip,
|
context_strip,
|
||||||
context_picker_menu_handle,
|
context_picker_menu_handle,
|
||||||
model_selector: cx.new(|cx| {
|
model_selector: cx.new(|cx| {
|
||||||
AssistantModelSelector::new(fs, prompt_editor.focus_handle(cx), window, cx)
|
AssistantModelSelector::new(
|
||||||
|
fs,
|
||||||
|
model_selector_menu_handle.clone(),
|
||||||
|
prompt_editor.focus_handle(cx),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
|
model_selector_menu_handle,
|
||||||
edited_since_done: false,
|
edited_since_done: false,
|
||||||
prompt_history,
|
prompt_history,
|
||||||
prompt_history_ix: None,
|
prompt_history_ix: None,
|
||||||
@@ -1006,6 +1020,7 @@ impl PromptEditor<TerminalCodegen> {
|
|||||||
editor
|
editor
|
||||||
});
|
});
|
||||||
let context_picker_menu_handle = PopoverMenuHandle::default();
|
let context_picker_menu_handle = PopoverMenuHandle::default();
|
||||||
|
let model_selector_menu_handle = PopoverMenuHandle::default();
|
||||||
|
|
||||||
let context_strip = cx.new(|cx| {
|
let context_strip = cx.new(|cx| {
|
||||||
ContextStrip::new(
|
ContextStrip::new(
|
||||||
@@ -1029,8 +1044,15 @@ impl PromptEditor<TerminalCodegen> {
|
|||||||
context_strip,
|
context_strip,
|
||||||
context_picker_menu_handle,
|
context_picker_menu_handle,
|
||||||
model_selector: cx.new(|cx| {
|
model_selector: cx.new(|cx| {
|
||||||
AssistantModelSelector::new(fs, prompt_editor.focus_handle(cx), window, cx)
|
AssistantModelSelector::new(
|
||||||
|
fs,
|
||||||
|
model_selector_menu_handle.clone(),
|
||||||
|
prompt_editor.focus_handle(cx),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
|
model_selector_menu_handle,
|
||||||
edited_since_done: false,
|
edited_since_done: false,
|
||||||
prompt_history,
|
prompt_history,
|
||||||
prompt_history_ix: None,
|
prompt_history_ix: None,
|
||||||
|
|||||||
@@ -7,17 +7,16 @@ use gpui::{
|
|||||||
pulsating_between, Animation, AnimationExt, App, DismissEvent, Entity, Focusable, Subscription,
|
pulsating_between, Animation, AnimationExt, App, DismissEvent, Entity, Focusable, Subscription,
|
||||||
TextStyle, WeakEntity,
|
TextStyle, WeakEntity,
|
||||||
};
|
};
|
||||||
use language_model::LanguageModelRegistry;
|
use language_model::{LanguageModelRegistry, LanguageModelRequestTool};
|
||||||
|
use language_model_selector::LanguageModelSelector;
|
||||||
use rope::Point;
|
use rope::Point;
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use text::Bias;
|
use text::Bias;
|
||||||
use theme::ThemeSettings;
|
use theme::{get_ui_font_size, ThemeSettings};
|
||||||
use ui::{
|
use ui::{
|
||||||
prelude::*, ButtonLike, KeyBinding, PlatformStyle, PopoverMenu, PopoverMenuHandle, Switch,
|
prelude::*, ButtonLike, KeyBinding, PopoverMenu, PopoverMenuHandle, Switch, TintColor, Tooltip,
|
||||||
TintColor, Tooltip,
|
|
||||||
};
|
};
|
||||||
use vim_mode_setting::VimModeSetting;
|
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
use crate::assistant_model_selector::AssistantModelSelector;
|
use crate::assistant_model_selector::AssistantModelSelector;
|
||||||
@@ -26,7 +25,7 @@ use crate::context_store::{refresh_context_store_text, ContextStore};
|
|||||||
use crate::context_strip::{ContextStrip, ContextStripEvent, SuggestContextKind};
|
use crate::context_strip::{ContextStrip, ContextStripEvent, SuggestContextKind};
|
||||||
use crate::thread::{RequestKind, Thread};
|
use crate::thread::{RequestKind, Thread};
|
||||||
use crate::thread_store::ThreadStore;
|
use crate::thread_store::ThreadStore;
|
||||||
use crate::{Chat, ChatMode, RemoveAllContext, ToggleContextPicker};
|
use crate::{Chat, ChatMode, RemoveAllContext, ToggleContextPicker, ToggleModelSelector};
|
||||||
|
|
||||||
pub struct MessageEditor {
|
pub struct MessageEditor {
|
||||||
thread: Entity<Thread>,
|
thread: Entity<Thread>,
|
||||||
@@ -37,6 +36,7 @@ pub struct MessageEditor {
|
|||||||
inline_context_picker: Entity<ContextPicker>,
|
inline_context_picker: Entity<ContextPicker>,
|
||||||
inline_context_picker_menu_handle: PopoverMenuHandle<ContextPicker>,
|
inline_context_picker_menu_handle: PopoverMenuHandle<ContextPicker>,
|
||||||
model_selector: Entity<AssistantModelSelector>,
|
model_selector: Entity<AssistantModelSelector>,
|
||||||
|
model_selector_menu_handle: PopoverMenuHandle<LanguageModelSelector>,
|
||||||
use_tools: bool,
|
use_tools: bool,
|
||||||
_subscriptions: Vec<Subscription>,
|
_subscriptions: Vec<Subscription>,
|
||||||
}
|
}
|
||||||
@@ -53,6 +53,7 @@ impl MessageEditor {
|
|||||||
let context_store = cx.new(|_cx| ContextStore::new(workspace.clone()));
|
let context_store = cx.new(|_cx| ContextStore::new(workspace.clone()));
|
||||||
let context_picker_menu_handle = PopoverMenuHandle::default();
|
let context_picker_menu_handle = PopoverMenuHandle::default();
|
||||||
let inline_context_picker_menu_handle = PopoverMenuHandle::default();
|
let inline_context_picker_menu_handle = PopoverMenuHandle::default();
|
||||||
|
let model_selector_menu_handle = PopoverMenuHandle::default();
|
||||||
|
|
||||||
let editor = cx.new(|cx| {
|
let editor = cx.new(|cx| {
|
||||||
let mut editor = Editor::auto_height(10, window, cx);
|
let mut editor = Editor::auto_height(10, window, cx);
|
||||||
@@ -105,13 +106,30 @@ impl MessageEditor {
|
|||||||
context_picker_menu_handle,
|
context_picker_menu_handle,
|
||||||
inline_context_picker,
|
inline_context_picker,
|
||||||
inline_context_picker_menu_handle,
|
inline_context_picker_menu_handle,
|
||||||
model_selector: cx
|
model_selector: cx.new(|cx| {
|
||||||
.new(|cx| AssistantModelSelector::new(fs, editor.focus_handle(cx), window, cx)),
|
AssistantModelSelector::new(
|
||||||
|
fs,
|
||||||
|
model_selector_menu_handle.clone(),
|
||||||
|
editor.focus_handle(cx),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
model_selector_menu_handle,
|
||||||
use_tools: false,
|
use_tools: false,
|
||||||
_subscriptions: subscriptions,
|
_subscriptions: subscriptions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn toggle_model_selector(
|
||||||
|
&mut self,
|
||||||
|
_: &ToggleModelSelector,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) {
|
||||||
|
self.model_selector_menu_handle.toggle(window, cx)
|
||||||
|
}
|
||||||
|
|
||||||
fn toggle_chat_mode(&mut self, _: &ChatMode, _window: &mut Window, cx: &mut Context<Self>) {
|
fn toggle_chat_mode(&mut self, _: &ChatMode, _window: &mut Window, cx: &mut Context<Self>) {
|
||||||
self.use_tools = !self.use_tools;
|
self.use_tools = !self.use_tools;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
@@ -187,7 +205,22 @@ impl MessageEditor {
|
|||||||
.update(&mut cx, |thread, cx| {
|
.update(&mut cx, |thread, cx| {
|
||||||
let context = context_store.read(cx).snapshot(cx).collect::<Vec<_>>();
|
let context = context_store.read(cx).snapshot(cx).collect::<Vec<_>>();
|
||||||
thread.insert_user_message(user_message, context, cx);
|
thread.insert_user_message(user_message, context, cx);
|
||||||
thread.send_to_model(model, request_kind, use_tools, cx);
|
let mut request = thread.to_completion_request(request_kind, cx);
|
||||||
|
|
||||||
|
if use_tools {
|
||||||
|
request.tools = thread
|
||||||
|
.tools()
|
||||||
|
.tools(cx)
|
||||||
|
.into_iter()
|
||||||
|
.map(|tool| LanguageModelRequestTool {
|
||||||
|
name: tool.name(),
|
||||||
|
description: tool.description(),
|
||||||
|
input_schema: tool.input_schema(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
thread.stream_completion(request, model, cx)
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
})
|
})
|
||||||
@@ -276,6 +309,7 @@ impl Render for MessageEditor {
|
|||||||
let inline_context_picker = self.inline_context_picker.clone();
|
let inline_context_picker = self.inline_context_picker.clone();
|
||||||
let bg_color = cx.theme().colors().editor_background;
|
let bg_color = cx.theme().colors().editor_background;
|
||||||
let is_streaming_completion = self.thread.read(cx).is_streaming();
|
let is_streaming_completion = self.thread.read(cx).is_streaming();
|
||||||
|
let button_width = px(64.);
|
||||||
let is_model_selected = self.is_model_selected(cx);
|
let is_model_selected = self.is_model_selected(cx);
|
||||||
let is_editor_empty = self.is_editor_empty(cx);
|
let is_editor_empty = self.is_editor_empty(cx);
|
||||||
let submit_label_color = if is_editor_empty {
|
let submit_label_color = if is_editor_empty {
|
||||||
@@ -284,25 +318,10 @@ impl Render for MessageEditor {
|
|||||||
Color::Default
|
Color::Default
|
||||||
};
|
};
|
||||||
|
|
||||||
let vim_mode_enabled = VimModeSetting::get_global(cx).0;
|
|
||||||
let platform = PlatformStyle::platform();
|
|
||||||
let linux = platform == PlatformStyle::Linux;
|
|
||||||
let windows = platform == PlatformStyle::Windows;
|
|
||||||
let button_width = if linux || windows || vim_mode_enabled {
|
|
||||||
px(92.)
|
|
||||||
} else {
|
|
||||||
px(64.)
|
|
||||||
};
|
|
||||||
|
|
||||||
v_flex()
|
v_flex()
|
||||||
.key_context("MessageEditor")
|
.key_context("MessageEditor")
|
||||||
.on_action(cx.listener(Self::chat))
|
.on_action(cx.listener(Self::chat))
|
||||||
.on_action(cx.listener(|this, action, window, cx| {
|
.on_action(cx.listener(Self::toggle_model_selector))
|
||||||
let selector = this.model_selector.read(cx).selector.clone();
|
|
||||||
selector.update(cx, |this, cx| {
|
|
||||||
this.toggle_model_selector(action, window, cx);
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
.on_action(cx.listener(Self::toggle_context_picker))
|
.on_action(cx.listener(Self::toggle_context_picker))
|
||||||
.on_action(cx.listener(Self::remove_all_context))
|
.on_action(cx.listener(Self::remove_all_context))
|
||||||
.on_action(cx.listener(Self::move_up))
|
.on_action(cx.listener(Self::move_up))
|
||||||
@@ -314,7 +333,7 @@ impl Render for MessageEditor {
|
|||||||
.child(self.context_strip.clone())
|
.child(self.context_strip.clone())
|
||||||
.child(
|
.child(
|
||||||
v_flex()
|
v_flex()
|
||||||
.gap_5()
|
.gap_4()
|
||||||
.child({
|
.child({
|
||||||
let settings = ThemeSettings::get_global(cx);
|
let settings = ThemeSettings::get_global(cx);
|
||||||
let text_style = TextStyle {
|
let text_style = TextStyle {
|
||||||
@@ -350,7 +369,7 @@ impl Render for MessageEditor {
|
|||||||
.anchor(gpui::Corner::BottomLeft)
|
.anchor(gpui::Corner::BottomLeft)
|
||||||
.offset(gpui::Point {
|
.offset(gpui::Point {
|
||||||
x: px(0.0),
|
x: px(0.0),
|
||||||
y: (-ThemeSettings::get_global(cx).ui_font_size(cx) * 2) - px(4.0),
|
y: (-get_ui_font_size(cx) * 2) - px(4.0),
|
||||||
})
|
})
|
||||||
.with_handle(self.inline_context_picker_menu_handle.clone()),
|
.with_handle(self.inline_context_picker_menu_handle.clone()),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ use crate::inline_prompt_editor::CodegenStatus;
|
|||||||
use client::telemetry::Telemetry;
|
use client::telemetry::Telemetry;
|
||||||
use futures::{channel::mpsc, SinkExt, StreamExt};
|
use futures::{channel::mpsc, SinkExt, StreamExt};
|
||||||
use gpui::{App, AppContext as _, Context, Entity, EventEmitter, Task};
|
use gpui::{App, AppContext as _, Context, Entity, EventEmitter, Task};
|
||||||
use language_model::{report_assistant_event, LanguageModelRegistry, LanguageModelRequest};
|
use language_model::{LanguageModelRegistry, LanguageModelRequest};
|
||||||
|
use language_models::report_assistant_event;
|
||||||
use std::{sync::Arc, time::Instant};
|
use std::{sync::Arc, time::Instant};
|
||||||
use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase};
|
use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase};
|
||||||
use terminal::Terminal;
|
use terminal::Terminal;
|
||||||
@@ -155,10 +156,7 @@ pub enum CodegenEvent {
|
|||||||
Finished,
|
Finished,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
pub const CLEAR_INPUT: &str = "\x15";
|
pub const CLEAR_INPUT: &str = "\x15";
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
pub const CLEAR_INPUT: &str = "\x03";
|
|
||||||
const CARRIAGE_RETURN: &str = "\x0d";
|
const CARRIAGE_RETURN: &str = "\x0d";
|
||||||
|
|
||||||
struct TerminalTransaction {
|
struct TerminalTransaction {
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ use fs::Fs;
|
|||||||
use gpui::{App, Entity, Focusable, Global, Subscription, UpdateGlobal, WeakEntity};
|
use gpui::{App, Entity, Focusable, Global, Subscription, UpdateGlobal, WeakEntity};
|
||||||
use language::Buffer;
|
use language::Buffer;
|
||||||
use language_model::{
|
use language_model::{
|
||||||
report_assistant_event, LanguageModelRegistry, LanguageModelRequest,
|
LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage, Role,
|
||||||
LanguageModelRequestMessage, Role,
|
|
||||||
};
|
};
|
||||||
|
use language_models::report_assistant_event;
|
||||||
use prompt_library::PromptBuilder;
|
use prompt_library::PromptBuilder;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase};
|
use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase};
|
||||||
|
|||||||
@@ -4,26 +4,25 @@ use anyhow::Result;
|
|||||||
use assistant_tool::ToolWorkingSet;
|
use assistant_tool::ToolWorkingSet;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use collections::{BTreeMap, HashMap, HashSet};
|
use collections::{BTreeMap, HashMap, HashSet};
|
||||||
use futures::StreamExt as _;
|
use futures::future::Shared;
|
||||||
|
use futures::{FutureExt as _, StreamExt as _};
|
||||||
use gpui::{App, Context, EventEmitter, SharedString, Task};
|
use gpui::{App, Context, EventEmitter, SharedString, Task};
|
||||||
use language_model::{
|
use language_model::{
|
||||||
LanguageModel, LanguageModelCompletionEvent, LanguageModelRegistry, LanguageModelRequest,
|
LanguageModel, LanguageModelCompletionEvent, LanguageModelRegistry, LanguageModelRequest,
|
||||||
LanguageModelRequestMessage, LanguageModelRequestTool, LanguageModelToolUseId,
|
LanguageModelRequestMessage, LanguageModelToolResult, LanguageModelToolUse,
|
||||||
MaxMonthlySpendReachedError, MessageContent, PaymentRequiredError, Role, StopReason,
|
LanguageModelToolUseId, MessageContent, Role, StopReason,
|
||||||
};
|
};
|
||||||
|
use language_models::provider::cloud::{MaxMonthlySpendReachedError, PaymentRequiredError};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use util::{post_inc, TryFutureExt as _};
|
use util::{post_inc, TryFutureExt as _};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::context::{attach_context_to_message, ContextId, ContextSnapshot};
|
use crate::context::{attach_context_to_message, ContextId, ContextSnapshot};
|
||||||
use crate::thread_store::SavedThread;
|
use crate::thread_store::SavedThread;
|
||||||
use crate::tool_use::{PendingToolUse, ToolUse, ToolUseState};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum RequestKind {
|
pub enum RequestKind {
|
||||||
Chat,
|
Chat,
|
||||||
/// Used when summarizing a thread.
|
|
||||||
Summarize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Serialize, Deserialize)]
|
||||||
@@ -42,7 +41,7 @@ impl std::fmt::Display for ThreadId {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Serialize, Deserialize)]
|
||||||
pub struct MessageId(pub(crate) usize);
|
pub struct MessageId(usize);
|
||||||
|
|
||||||
impl MessageId {
|
impl MessageId {
|
||||||
fn post_inc(&mut self) -> Self {
|
fn post_inc(&mut self) -> Self {
|
||||||
@@ -71,7 +70,9 @@ pub struct Thread {
|
|||||||
completion_count: usize,
|
completion_count: usize,
|
||||||
pending_completions: Vec<PendingCompletion>,
|
pending_completions: Vec<PendingCompletion>,
|
||||||
tools: Arc<ToolWorkingSet>,
|
tools: Arc<ToolWorkingSet>,
|
||||||
tool_use: ToolUseState,
|
tool_uses_by_message: HashMap<MessageId, Vec<LanguageModelToolUse>>,
|
||||||
|
tool_results_by_message: HashMap<MessageId, Vec<LanguageModelToolResult>>,
|
||||||
|
pending_tool_uses_by_id: HashMap<LanguageModelToolUseId, PendingToolUse>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Thread {
|
impl Thread {
|
||||||
@@ -88,7 +89,9 @@ impl Thread {
|
|||||||
completion_count: 0,
|
completion_count: 0,
|
||||||
pending_completions: Vec::new(),
|
pending_completions: Vec::new(),
|
||||||
tools,
|
tools,
|
||||||
tool_use: ToolUseState::default(),
|
tool_uses_by_message: HashMap::default(),
|
||||||
|
tool_results_by_message: HashMap::default(),
|
||||||
|
pending_tool_uses_by_id: HashMap::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +123,9 @@ impl Thread {
|
|||||||
completion_count: 0,
|
completion_count: 0,
|
||||||
pending_completions: Vec::new(),
|
pending_completions: Vec::new(),
|
||||||
tools,
|
tools,
|
||||||
tool_use: ToolUseState::default(),
|
tool_uses_by_message: HashMap::default(),
|
||||||
|
tool_results_by_message: HashMap::default(),
|
||||||
|
pending_tool_uses_by_id: HashMap::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,15 +187,7 @@ impl Thread {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn pending_tool_uses(&self) -> Vec<&PendingToolUse> {
|
pub fn pending_tool_uses(&self) -> Vec<&PendingToolUse> {
|
||||||
self.tool_use.pending_tool_uses()
|
self.pending_tool_uses_by_id.values().collect()
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tool_uses_for_message(&self, id: MessageId) -> Vec<ToolUse> {
|
|
||||||
self.tool_use.tool_uses_for_message(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn message_has_tool_results(&self, message_id: MessageId) -> bool {
|
|
||||||
self.tool_use.message_has_tool_results(message_id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_user_message(
|
pub fn insert_user_message(
|
||||||
@@ -244,34 +241,9 @@ impl Thread {
|
|||||||
text
|
text
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_to_model(
|
|
||||||
&mut self,
|
|
||||||
model: Arc<dyn LanguageModel>,
|
|
||||||
request_kind: RequestKind,
|
|
||||||
use_tools: bool,
|
|
||||||
cx: &mut Context<Self>,
|
|
||||||
) {
|
|
||||||
let mut request = self.to_completion_request(request_kind, cx);
|
|
||||||
|
|
||||||
if use_tools {
|
|
||||||
request.tools = self
|
|
||||||
.tools()
|
|
||||||
.tools(cx)
|
|
||||||
.into_iter()
|
|
||||||
.map(|tool| LanguageModelRequestTool {
|
|
||||||
name: tool.name(),
|
|
||||||
description: tool.description(),
|
|
||||||
input_schema: tool.input_schema(),
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
}
|
|
||||||
|
|
||||||
self.stream_completion(request, model, cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_completion_request(
|
pub fn to_completion_request(
|
||||||
&self,
|
&self,
|
||||||
request_kind: RequestKind,
|
_request_kind: RequestKind,
|
||||||
_cx: &App,
|
_cx: &App,
|
||||||
) -> LanguageModelRequest {
|
) -> LanguageModelRequest {
|
||||||
let mut request = LanguageModelRequest {
|
let mut request = LanguageModelRequest {
|
||||||
@@ -293,13 +265,12 @@ impl Thread {
|
|||||||
content: Vec::new(),
|
content: Vec::new(),
|
||||||
cache: false,
|
cache: false,
|
||||||
};
|
};
|
||||||
match request_kind {
|
|
||||||
RequestKind::Chat => {
|
if let Some(tool_results) = self.tool_results_by_message.get(&message.id) {
|
||||||
self.tool_use
|
for tool_result in tool_results {
|
||||||
.attach_tool_results(message.id, &mut request_message);
|
request_message
|
||||||
}
|
.content
|
||||||
RequestKind::Summarize => {
|
.push(MessageContent::ToolResult(tool_result.clone()));
|
||||||
// We don't care about tool use during summarization.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,13 +280,11 @@ impl Thread {
|
|||||||
.push(MessageContent::Text(message.text.clone()));
|
.push(MessageContent::Text(message.text.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
match request_kind {
|
if let Some(tool_uses) = self.tool_uses_by_message.get(&message.id) {
|
||||||
RequestKind::Chat => {
|
for tool_use in tool_uses {
|
||||||
self.tool_use
|
request_message
|
||||||
.attach_tool_uses(message.id, &mut request_message);
|
.content
|
||||||
}
|
.push(MessageContent::ToolUse(tool_use.clone()));
|
||||||
RequestKind::Summarize => {
|
|
||||||
// We don't care about tool use during summarization.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,8 +360,21 @@ impl Thread {
|
|||||||
.rfind(|message| message.role == Role::Assistant)
|
.rfind(|message| message.role == Role::Assistant)
|
||||||
{
|
{
|
||||||
thread
|
thread
|
||||||
.tool_use
|
.tool_uses_by_message
|
||||||
.request_tool_use(last_assistant_message.id, tool_use);
|
.entry(last_assistant_message.id)
|
||||||
|
.or_default()
|
||||||
|
.push(tool_use.clone());
|
||||||
|
|
||||||
|
thread.pending_tool_uses_by_id.insert(
|
||||||
|
tool_use.id.clone(),
|
||||||
|
PendingToolUse {
|
||||||
|
assistant_message_id: last_assistant_message.id,
|
||||||
|
id: tool_use.id,
|
||||||
|
name: tool_use.name,
|
||||||
|
input: tool_use.input,
|
||||||
|
status: PendingToolUseStatus::Idle,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -469,7 +451,7 @@ impl Thread {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut request = self.to_completion_request(RequestKind::Summarize, cx);
|
let mut request = self.to_completion_request(RequestKind::Chat, cx);
|
||||||
request.messages.push(LanguageModelRequestMessage {
|
request.messages.push(LanguageModelRequestMessage {
|
||||||
role: Role::User,
|
role: Role::User,
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -512,6 +494,7 @@ impl Thread {
|
|||||||
|
|
||||||
pub fn insert_tool_output(
|
pub fn insert_tool_output(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
assistant_message_id: MessageId,
|
||||||
tool_use_id: LanguageModelToolUseId,
|
tool_use_id: LanguageModelToolUseId,
|
||||||
output: Task<Result<String>>,
|
output: Task<Result<String>>,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
@@ -522,18 +505,50 @@ impl Thread {
|
|||||||
let output = output.await;
|
let output = output.await;
|
||||||
thread
|
thread
|
||||||
.update(&mut cx, |thread, cx| {
|
.update(&mut cx, |thread, cx| {
|
||||||
thread
|
// The tool use was requested by an Assistant message,
|
||||||
.tool_use
|
// so we want to attach the tool results to the next
|
||||||
.insert_tool_output(tool_use_id.clone(), output);
|
// user message.
|
||||||
|
let next_user_message = MessageId(assistant_message_id.0 + 1);
|
||||||
|
|
||||||
|
let tool_results = thread
|
||||||
|
.tool_results_by_message
|
||||||
|
.entry(next_user_message)
|
||||||
|
.or_default();
|
||||||
|
|
||||||
|
match output {
|
||||||
|
Ok(output) => {
|
||||||
|
tool_results.push(LanguageModelToolResult {
|
||||||
|
tool_use_id: tool_use_id.to_string(),
|
||||||
|
content: output,
|
||||||
|
is_error: false,
|
||||||
|
});
|
||||||
|
|
||||||
cx.emit(ThreadEvent::ToolFinished { tool_use_id });
|
cx.emit(ThreadEvent::ToolFinished { tool_use_id });
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
tool_results.push(LanguageModelToolResult {
|
||||||
|
tool_use_id: tool_use_id.to_string(),
|
||||||
|
content: err.to_string(),
|
||||||
|
is_error: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(tool_use) =
|
||||||
|
thread.pending_tool_uses_by_id.get_mut(&tool_use_id)
|
||||||
|
{
|
||||||
|
tool_use.status = PendingToolUseStatus::Error(err.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
self.tool_use
|
if let Some(tool_use) = self.pending_tool_uses_by_id.get_mut(&tool_use_id) {
|
||||||
.run_pending_tool(tool_use_id, insert_output_task);
|
tool_use.status = PendingToolUseStatus::Running {
|
||||||
|
_task: insert_output_task.shared(),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cancels the last pending completion, if there are any pending.
|
/// Cancels the last pending completion, if there are any pending.
|
||||||
@@ -575,3 +590,26 @@ struct PendingCompletion {
|
|||||||
id: usize,
|
id: usize,
|
||||||
_task: Task<()>,
|
_task: Task<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct PendingToolUse {
|
||||||
|
pub id: LanguageModelToolUseId,
|
||||||
|
/// The ID of the Assistant message in which the tool use was requested.
|
||||||
|
pub assistant_message_id: MessageId,
|
||||||
|
pub name: String,
|
||||||
|
pub input: serde_json::Value,
|
||||||
|
pub status: PendingToolUseStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum PendingToolUseStatus {
|
||||||
|
Idle,
|
||||||
|
Running { _task: Shared<Task<()>> },
|
||||||
|
Error(#[allow(unused)] String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PendingToolUseStatus {
|
||||||
|
pub fn is_idle(&self) -> bool {
|
||||||
|
matches!(self, PendingToolUseStatus::Idle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use assistant_context_editor::SavedContextMetadata;
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
uniform_list, App, Entity, FocusHandle, Focusable, ScrollStrategy, UniformListScrollHandle,
|
uniform_list, App, Entity, FocusHandle, Focusable, ScrollStrategy, UniformListScrollHandle,
|
||||||
WeakEntity,
|
WeakEntity,
|
||||||
@@ -6,14 +5,13 @@ use gpui::{
|
|||||||
use time::{OffsetDateTime, UtcOffset};
|
use time::{OffsetDateTime, UtcOffset};
|
||||||
use ui::{prelude::*, IconButtonShape, ListItem, ListItemSpacing, Tooltip};
|
use ui::{prelude::*, IconButtonShape, ListItem, ListItemSpacing, Tooltip};
|
||||||
|
|
||||||
use crate::history_store::{HistoryEntry, HistoryStore};
|
use crate::thread_store::{SavedThreadMetadata, ThreadStore};
|
||||||
use crate::thread_store::SavedThreadMetadata;
|
|
||||||
use crate::{AssistantPanel, RemoveSelectedThread};
|
use crate::{AssistantPanel, RemoveSelectedThread};
|
||||||
|
|
||||||
pub struct ThreadHistory {
|
pub struct ThreadHistory {
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
assistant_panel: WeakEntity<AssistantPanel>,
|
assistant_panel: WeakEntity<AssistantPanel>,
|
||||||
history_store: Entity<HistoryStore>,
|
thread_store: Entity<ThreadStore>,
|
||||||
scroll_handle: UniformListScrollHandle,
|
scroll_handle: UniformListScrollHandle,
|
||||||
selected_index: usize,
|
selected_index: usize,
|
||||||
}
|
}
|
||||||
@@ -21,13 +19,14 @@ pub struct ThreadHistory {
|
|||||||
impl ThreadHistory {
|
impl ThreadHistory {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
assistant_panel: WeakEntity<AssistantPanel>,
|
assistant_panel: WeakEntity<AssistantPanel>,
|
||||||
history_store: Entity<HistoryStore>,
|
thread_store: Entity<ThreadStore>,
|
||||||
|
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
focus_handle: cx.focus_handle(),
|
focus_handle: cx.focus_handle(),
|
||||||
assistant_panel,
|
assistant_panel,
|
||||||
history_store,
|
thread_store,
|
||||||
scroll_handle: UniformListScrollHandle::default(),
|
scroll_handle: UniformListScrollHandle::default(),
|
||||||
selected_index: 0,
|
selected_index: 0,
|
||||||
}
|
}
|
||||||
@@ -39,9 +38,7 @@ impl ThreadHistory {
|
|||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
let count = self
|
let count = self.thread_store.read(cx).thread_count();
|
||||||
.history_store
|
|
||||||
.update(cx, |this, cx| this.entry_count(cx));
|
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
if self.selected_index == 0 {
|
if self.selected_index == 0 {
|
||||||
self.set_selected_index(count - 1, window, cx);
|
self.set_selected_index(count - 1, window, cx);
|
||||||
@@ -57,9 +54,7 @@ impl ThreadHistory {
|
|||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
let count = self
|
let count = self.thread_store.read(cx).thread_count();
|
||||||
.history_store
|
|
||||||
.update(cx, |this, cx| this.entry_count(cx));
|
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
if self.selected_index == count - 1 {
|
if self.selected_index == count - 1 {
|
||||||
self.set_selected_index(0, window, cx);
|
self.set_selected_index(0, window, cx);
|
||||||
@@ -70,18 +65,14 @@ impl ThreadHistory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn select_first(&mut self, _: &menu::SelectFirst, window: &mut Window, cx: &mut Context<Self>) {
|
fn select_first(&mut self, _: &menu::SelectFirst, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
let count = self
|
let count = self.thread_store.read(cx).thread_count();
|
||||||
.history_store
|
|
||||||
.update(cx, |this, cx| this.entry_count(cx));
|
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
self.set_selected_index(0, window, cx);
|
self.set_selected_index(0, window, cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_last(&mut self, _: &menu::SelectLast, window: &mut Window, cx: &mut Context<Self>) {
|
fn select_last(&mut self, _: &menu::SelectLast, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
let count = self
|
let count = self.thread_store.read(cx).thread_count();
|
||||||
.history_store
|
|
||||||
.update(cx, |this, cx| this.entry_count(cx));
|
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
self.set_selected_index(count - 1, window, cx);
|
self.set_selected_index(count - 1, window, cx);
|
||||||
}
|
}
|
||||||
@@ -95,23 +86,12 @@ impl ThreadHistory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
|
fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
let entries = self.history_store.update(cx, |this, cx| this.entries(cx));
|
let threads = self.thread_store.update(cx, |this, _cx| this.threads());
|
||||||
|
|
||||||
if let Some(entry) = entries.get(self.selected_index) {
|
if let Some(thread) = threads.get(self.selected_index) {
|
||||||
match entry {
|
|
||||||
HistoryEntry::Thread(thread) => {
|
|
||||||
self.assistant_panel
|
self.assistant_panel
|
||||||
.update(cx, move |this, cx| this.open_thread(&thread.id, window, cx))
|
.update(cx, move |this, cx| this.open_thread(&thread.id, window, cx))
|
||||||
.ok();
|
.ok();
|
||||||
}
|
|
||||||
HistoryEntry::Context(context) => {
|
|
||||||
self.assistant_panel
|
|
||||||
.update(cx, move |this, cx| {
|
|
||||||
this.open_saved_prompt_editor(context.path.clone(), window, cx)
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
@@ -123,25 +103,14 @@ impl ThreadHistory {
|
|||||||
_window: &mut Window,
|
_window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
let entries = self.history_store.update(cx, |this, cx| this.entries(cx));
|
let threads = self.thread_store.update(cx, |this, _cx| this.threads());
|
||||||
|
|
||||||
if let Some(entry) = entries.get(self.selected_index) {
|
if let Some(thread) = threads.get(self.selected_index) {
|
||||||
match entry {
|
|
||||||
HistoryEntry::Thread(thread) => {
|
|
||||||
self.assistant_panel
|
self.assistant_panel
|
||||||
.update(cx, |this, cx| {
|
.update(cx, |this, cx| {
|
||||||
this.delete_thread(&thread.id, cx);
|
this.delete_thread(&thread.id, cx);
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
}
|
|
||||||
HistoryEntry::Context(context) => {
|
|
||||||
self.assistant_panel
|
|
||||||
.update(cx, |this, cx| {
|
|
||||||
this.delete_context(context.path.clone(), cx);
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
@@ -156,7 +125,7 @@ impl Focusable for ThreadHistory {
|
|||||||
|
|
||||||
impl Render for ThreadHistory {
|
impl Render for ThreadHistory {
|
||||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
let history_entries = self.history_store.update(cx, |this, cx| this.entries(cx));
|
let threads = self.thread_store.update(cx, |this, _cx| this.threads());
|
||||||
let selected_index = self.selected_index;
|
let selected_index = self.selected_index;
|
||||||
|
|
||||||
v_flex()
|
v_flex()
|
||||||
@@ -173,7 +142,7 @@ impl Render for ThreadHistory {
|
|||||||
.on_action(cx.listener(Self::confirm))
|
.on_action(cx.listener(Self::confirm))
|
||||||
.on_action(cx.listener(Self::remove_selected_thread))
|
.on_action(cx.listener(Self::remove_selected_thread))
|
||||||
.map(|history| {
|
.map(|history| {
|
||||||
if history_entries.is_empty() {
|
if threads.is_empty() {
|
||||||
history
|
history
|
||||||
.justify_center()
|
.justify_center()
|
||||||
.child(
|
.child(
|
||||||
@@ -187,26 +156,17 @@ impl Render for ThreadHistory {
|
|||||||
uniform_list(
|
uniform_list(
|
||||||
cx.entity().clone(),
|
cx.entity().clone(),
|
||||||
"thread-history",
|
"thread-history",
|
||||||
history_entries.len(),
|
threads.len(),
|
||||||
move |history, range, _window, _cx| {
|
move |history, range, _window, _cx| {
|
||||||
history_entries[range]
|
threads[range]
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(index, entry)| {
|
.map(|(index, thread)| {
|
||||||
h_flex().w_full().pb_1().child(match entry {
|
h_flex().w_full().pb_1().child(PastThread::new(
|
||||||
HistoryEntry::Thread(thread) => PastThread::new(
|
|
||||||
thread.clone(),
|
thread.clone(),
|
||||||
history.assistant_panel.clone(),
|
history.assistant_panel.clone(),
|
||||||
selected_index == index,
|
selected_index == index,
|
||||||
)
|
))
|
||||||
.into_any_element(),
|
|
||||||
HistoryEntry::Context(context) => PastContext::new(
|
|
||||||
context.clone(),
|
|
||||||
history.assistant_panel.clone(),
|
|
||||||
selected_index == index,
|
|
||||||
)
|
|
||||||
.into_any_element(),
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
},
|
},
|
||||||
@@ -254,28 +214,18 @@ impl RenderOnce for PastThread {
|
|||||||
);
|
);
|
||||||
|
|
||||||
ListItem::new(SharedString::from(self.thread.id.to_string()))
|
ListItem::new(SharedString::from(self.thread.id.to_string()))
|
||||||
.rounded()
|
.outlined()
|
||||||
.toggle_state(self.selected)
|
.toggle_state(self.selected)
|
||||||
.spacing(ListItemSpacing::Sparse)
|
|
||||||
.start_slot(
|
.start_slot(
|
||||||
div()
|
Icon::new(IconName::MessageCircle)
|
||||||
.max_w_4_5()
|
.size(IconSize::Small)
|
||||||
.child(Label::new(summary).size(LabelSize::Small).text_ellipsis()),
|
.color(Color::Muted),
|
||||||
)
|
)
|
||||||
|
.spacing(ListItemSpacing::Sparse)
|
||||||
|
.child(Label::new(summary).size(LabelSize::Small).text_ellipsis())
|
||||||
.end_slot(
|
.end_slot(
|
||||||
h_flex()
|
h_flex()
|
||||||
.gap_1p5()
|
.gap_1p5()
|
||||||
.child(
|
|
||||||
Label::new("Thread")
|
|
||||||
.color(Color::Muted)
|
|
||||||
.size(LabelSize::XSmall),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.size(px(3.))
|
|
||||||
.rounded_full()
|
|
||||||
.bg(cx.theme().colors().text_disabled),
|
|
||||||
)
|
|
||||||
.child(
|
.child(
|
||||||
Label::new(thread_timestamp)
|
Label::new(thread_timestamp)
|
||||||
.color(Color::Muted)
|
.color(Color::Muted)
|
||||||
@@ -312,100 +262,3 @@ impl RenderOnce for PastThread {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(IntoElement)]
|
|
||||||
pub struct PastContext {
|
|
||||||
context: SavedContextMetadata,
|
|
||||||
assistant_panel: WeakEntity<AssistantPanel>,
|
|
||||||
selected: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PastContext {
|
|
||||||
pub fn new(
|
|
||||||
context: SavedContextMetadata,
|
|
||||||
assistant_panel: WeakEntity<AssistantPanel>,
|
|
||||||
selected: bool,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
context,
|
|
||||||
assistant_panel,
|
|
||||||
selected,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderOnce for PastContext {
|
|
||||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
|
||||||
let summary = self.context.title;
|
|
||||||
|
|
||||||
let context_timestamp = time_format::format_localized_timestamp(
|
|
||||||
OffsetDateTime::from_unix_timestamp(self.context.mtime.timestamp()).unwrap(),
|
|
||||||
OffsetDateTime::now_utc(),
|
|
||||||
self.assistant_panel
|
|
||||||
.update(cx, |this, _cx| this.local_timezone())
|
|
||||||
.unwrap_or(UtcOffset::UTC),
|
|
||||||
time_format::TimestampFormat::EnhancedAbsolute,
|
|
||||||
);
|
|
||||||
|
|
||||||
ListItem::new(SharedString::from(
|
|
||||||
self.context.path.to_string_lossy().to_string(),
|
|
||||||
))
|
|
||||||
.rounded()
|
|
||||||
.toggle_state(self.selected)
|
|
||||||
.spacing(ListItemSpacing::Sparse)
|
|
||||||
.start_slot(
|
|
||||||
div()
|
|
||||||
.max_w_4_5()
|
|
||||||
.child(Label::new(summary).size(LabelSize::Small).text_ellipsis()),
|
|
||||||
)
|
|
||||||
.end_slot(
|
|
||||||
h_flex()
|
|
||||||
.gap_1p5()
|
|
||||||
.child(
|
|
||||||
Label::new("Prompt Editor")
|
|
||||||
.color(Color::Muted)
|
|
||||||
.size(LabelSize::XSmall),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.size(px(3.))
|
|
||||||
.rounded_full()
|
|
||||||
.bg(cx.theme().colors().text_disabled),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Label::new(context_timestamp)
|
|
||||||
.color(Color::Muted)
|
|
||||||
.size(LabelSize::XSmall),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
IconButton::new("delete", IconName::TrashAlt)
|
|
||||||
.shape(IconButtonShape::Square)
|
|
||||||
.icon_size(IconSize::XSmall)
|
|
||||||
.tooltip(Tooltip::text("Delete Prompt Editor"))
|
|
||||||
.on_click({
|
|
||||||
let assistant_panel = self.assistant_panel.clone();
|
|
||||||
let path = self.context.path.clone();
|
|
||||||
move |_event, _window, cx| {
|
|
||||||
assistant_panel
|
|
||||||
.update(cx, |this, cx| {
|
|
||||||
this.delete_context(path.clone(), cx);
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.on_click({
|
|
||||||
let assistant_panel = self.assistant_panel.clone();
|
|
||||||
let path = self.context.path.clone();
|
|
||||||
move |_event, window, cx| {
|
|
||||||
assistant_panel
|
|
||||||
.update(cx, |this, cx| {
|
|
||||||
this.open_saved_prompt_editor(path.clone(), window, cx)
|
|
||||||
.detach_and_log_err(cx);
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,221 +0,0 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use collections::HashMap;
|
|
||||||
use futures::future::Shared;
|
|
||||||
use futures::FutureExt as _;
|
|
||||||
use gpui::{SharedString, Task};
|
|
||||||
use language_model::{
|
|
||||||
LanguageModelRequestMessage, LanguageModelToolResult, LanguageModelToolUse,
|
|
||||||
LanguageModelToolUseId, MessageContent,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::thread::MessageId;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ToolUse {
|
|
||||||
pub id: LanguageModelToolUseId,
|
|
||||||
pub name: SharedString,
|
|
||||||
pub status: ToolUseStatus,
|
|
||||||
pub input: serde_json::Value,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum ToolUseStatus {
|
|
||||||
Pending,
|
|
||||||
Running,
|
|
||||||
Finished(SharedString),
|
|
||||||
Error(SharedString),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct ToolUseState {
|
|
||||||
tool_uses_by_assistant_message: HashMap<MessageId, Vec<LanguageModelToolUse>>,
|
|
||||||
tool_uses_by_user_message: HashMap<MessageId, Vec<LanguageModelToolUseId>>,
|
|
||||||
tool_results: HashMap<LanguageModelToolUseId, LanguageModelToolResult>,
|
|
||||||
pending_tool_uses_by_id: HashMap<LanguageModelToolUseId, PendingToolUse>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToolUseState {
|
|
||||||
pub fn pending_tool_uses(&self) -> Vec<&PendingToolUse> {
|
|
||||||
self.pending_tool_uses_by_id.values().collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tool_uses_for_message(&self, id: MessageId) -> Vec<ToolUse> {
|
|
||||||
let Some(tool_uses_for_message) = &self.tool_uses_by_assistant_message.get(&id) else {
|
|
||||||
return Vec::new();
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut tool_uses = Vec::new();
|
|
||||||
|
|
||||||
for tool_use in tool_uses_for_message.iter() {
|
|
||||||
let tool_result = self.tool_results.get(&tool_use.id);
|
|
||||||
|
|
||||||
let status = (|| {
|
|
||||||
if let Some(tool_result) = tool_result {
|
|
||||||
return if tool_result.is_error {
|
|
||||||
ToolUseStatus::Error(tool_result.content.clone().into())
|
|
||||||
} else {
|
|
||||||
ToolUseStatus::Finished(tool_result.content.clone().into())
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(pending_tool_use) = self.pending_tool_uses_by_id.get(&tool_use.id) {
|
|
||||||
return match pending_tool_use.status {
|
|
||||||
PendingToolUseStatus::Idle => ToolUseStatus::Pending,
|
|
||||||
PendingToolUseStatus::Running { .. } => ToolUseStatus::Running,
|
|
||||||
PendingToolUseStatus::Error(ref err) => {
|
|
||||||
ToolUseStatus::Error(err.clone().into())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
ToolUseStatus::Pending
|
|
||||||
})();
|
|
||||||
|
|
||||||
tool_uses.push(ToolUse {
|
|
||||||
id: tool_use.id.clone(),
|
|
||||||
name: tool_use.name.clone().into(),
|
|
||||||
input: tool_use.input.clone(),
|
|
||||||
status,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
tool_uses
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn message_has_tool_results(&self, message_id: MessageId) -> bool {
|
|
||||||
self.tool_uses_by_user_message
|
|
||||||
.get(&message_id)
|
|
||||||
.map_or(false, |results| !results.is_empty())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn request_tool_use(
|
|
||||||
&mut self,
|
|
||||||
assistant_message_id: MessageId,
|
|
||||||
tool_use: LanguageModelToolUse,
|
|
||||||
) {
|
|
||||||
self.tool_uses_by_assistant_message
|
|
||||||
.entry(assistant_message_id)
|
|
||||||
.or_default()
|
|
||||||
.push(tool_use.clone());
|
|
||||||
|
|
||||||
// The tool use is being requested by the Assistant, so we want to
|
|
||||||
// attach the tool results to the next user message.
|
|
||||||
let next_user_message_id = MessageId(assistant_message_id.0 + 1);
|
|
||||||
self.tool_uses_by_user_message
|
|
||||||
.entry(next_user_message_id)
|
|
||||||
.or_default()
|
|
||||||
.push(tool_use.id.clone());
|
|
||||||
|
|
||||||
self.pending_tool_uses_by_id.insert(
|
|
||||||
tool_use.id.clone(),
|
|
||||||
PendingToolUse {
|
|
||||||
assistant_message_id,
|
|
||||||
id: tool_use.id,
|
|
||||||
name: tool_use.name,
|
|
||||||
input: tool_use.input,
|
|
||||||
status: PendingToolUseStatus::Idle,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run_pending_tool(&mut self, tool_use_id: LanguageModelToolUseId, task: Task<()>) {
|
|
||||||
if let Some(tool_use) = self.pending_tool_uses_by_id.get_mut(&tool_use_id) {
|
|
||||||
tool_use.status = PendingToolUseStatus::Running {
|
|
||||||
_task: task.shared(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_tool_output(
|
|
||||||
&mut self,
|
|
||||||
tool_use_id: LanguageModelToolUseId,
|
|
||||||
output: Result<String>,
|
|
||||||
) {
|
|
||||||
match output {
|
|
||||||
Ok(output) => {
|
|
||||||
self.tool_results.insert(
|
|
||||||
tool_use_id.clone(),
|
|
||||||
LanguageModelToolResult {
|
|
||||||
tool_use_id: tool_use_id.clone(),
|
|
||||||
content: output.into(),
|
|
||||||
is_error: false,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
self.pending_tool_uses_by_id.remove(&tool_use_id);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
self.tool_results.insert(
|
|
||||||
tool_use_id.clone(),
|
|
||||||
LanguageModelToolResult {
|
|
||||||
tool_use_id: tool_use_id.clone(),
|
|
||||||
content: err.to_string().into(),
|
|
||||||
is_error: true,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(tool_use) = self.pending_tool_uses_by_id.get_mut(&tool_use_id) {
|
|
||||||
tool_use.status = PendingToolUseStatus::Error(err.to_string().into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn attach_tool_uses(
|
|
||||||
&self,
|
|
||||||
message_id: MessageId,
|
|
||||||
request_message: &mut LanguageModelRequestMessage,
|
|
||||||
) {
|
|
||||||
if let Some(tool_uses) = self.tool_uses_by_assistant_message.get(&message_id) {
|
|
||||||
for tool_use in tool_uses {
|
|
||||||
request_message
|
|
||||||
.content
|
|
||||||
.push(MessageContent::ToolUse(tool_use.clone()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn attach_tool_results(
|
|
||||||
&self,
|
|
||||||
message_id: MessageId,
|
|
||||||
request_message: &mut LanguageModelRequestMessage,
|
|
||||||
) {
|
|
||||||
if let Some(tool_uses) = self.tool_uses_by_user_message.get(&message_id) {
|
|
||||||
for tool_use_id in tool_uses {
|
|
||||||
if let Some(tool_result) = self.tool_results.get(tool_use_id) {
|
|
||||||
request_message
|
|
||||||
.content
|
|
||||||
.push(MessageContent::ToolResult(tool_result.clone()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct PendingToolUse {
|
|
||||||
pub id: LanguageModelToolUseId,
|
|
||||||
/// The ID of the Assistant message in which the tool use was requested.
|
|
||||||
pub assistant_message_id: MessageId,
|
|
||||||
pub name: Arc<str>,
|
|
||||||
pub input: serde_json::Value,
|
|
||||||
pub status: PendingToolUseStatus,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum PendingToolUseStatus {
|
|
||||||
Idle,
|
|
||||||
Running { _task: Shared<Task<()>> },
|
|
||||||
Error(#[allow(unused)] Arc<str>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PendingToolUseStatus {
|
|
||||||
pub fn is_idle(&self) -> bool {
|
|
||||||
matches!(self, PendingToolUseStatus::Idle)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_error(&self) -> bool {
|
|
||||||
matches!(self, PendingToolUseStatus::Error(_))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -30,6 +30,7 @@ indexed_docs.workspace = true
|
|||||||
language.workspace = true
|
language.workspace = true
|
||||||
language_model.workspace = true
|
language_model.workspace = true
|
||||||
language_model_selector.workspace = true
|
language_model_selector.workspace = true
|
||||||
|
language_models.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
multi_buffer.workspace = true
|
multi_buffer.workspace = true
|
||||||
open_ai.workspace = true
|
open_ai.workspace = true
|
||||||
|
|||||||
@@ -19,10 +19,13 @@ use gpui::{
|
|||||||
};
|
};
|
||||||
use language::{AnchorRangeExt, Bias, Buffer, LanguageRegistry, OffsetRangeExt, Point, ToOffset};
|
use language::{AnchorRangeExt, Bias, Buffer, LanguageRegistry, OffsetRangeExt, Point, ToOffset};
|
||||||
use language_model::{
|
use language_model::{
|
||||||
report_assistant_event, LanguageModel, LanguageModelCacheConfiguration,
|
LanguageModel, LanguageModelCacheConfiguration, LanguageModelCompletionEvent,
|
||||||
LanguageModelCompletionEvent, LanguageModelImage, LanguageModelRegistry, LanguageModelRequest,
|
LanguageModelImage, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage,
|
||||||
LanguageModelRequestMessage, LanguageModelToolUseId, MaxMonthlySpendReachedError,
|
LanguageModelToolUseId, MessageContent, Role, StopReason,
|
||||||
MessageContent, PaymentRequiredError, Role, StopReason,
|
};
|
||||||
|
use language_models::{
|
||||||
|
provider::cloud::{MaxMonthlySpendReachedError, PaymentRequiredError},
|
||||||
|
report_assistant_event,
|
||||||
};
|
};
|
||||||
use open_ai::Model as OpenAiModel;
|
use open_ai::Model as OpenAiModel;
|
||||||
use paths::contexts_dir;
|
use paths::contexts_dir;
|
||||||
@@ -3363,7 +3366,7 @@ impl SavedContextV0_1_0 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SavedContextMetadata {
|
pub struct SavedContextMetadata {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
|
|||||||
@@ -29,22 +29,19 @@ use gpui::{
|
|||||||
WeakEntity,
|
WeakEntity,
|
||||||
};
|
};
|
||||||
use indexed_docs::IndexedDocsStore;
|
use indexed_docs::IndexedDocsStore;
|
||||||
use language::{
|
use language::{language_settings::SoftWrap, BufferSnapshot, LspAdapterDelegate, ToOffset};
|
||||||
language_settings::{all_language_settings, SoftWrap},
|
|
||||||
BufferSnapshot, LspAdapterDelegate, ToOffset,
|
|
||||||
};
|
|
||||||
use language_model::{
|
use language_model::{
|
||||||
LanguageModelImage, LanguageModelProvider, LanguageModelProviderTosView, LanguageModelRegistry,
|
LanguageModelImage, LanguageModelProvider, LanguageModelProviderTosView, LanguageModelRegistry,
|
||||||
Role,
|
Role,
|
||||||
};
|
};
|
||||||
use language_model_selector::{AssistantLanguageModelSelector, LanguageModelSelector};
|
use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};
|
||||||
use multi_buffer::MultiBufferRow;
|
use multi_buffer::MultiBufferRow;
|
||||||
use picker::Picker;
|
use picker::Picker;
|
||||||
use project::lsp_store::LocalLspAdapterDelegate;
|
use project::lsp_store::LocalLspAdapterDelegate;
|
||||||
use project::{Project, Worktree};
|
use project::{Project, Worktree};
|
||||||
use rope::Point;
|
use rope::Point;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use settings::{update_settings_file, Settings, SettingsStore};
|
use settings::{update_settings_file, Settings};
|
||||||
use std::{any::TypeId, borrow::Cow, cmp, ops::Range, path::PathBuf, sync::Arc, time::Duration};
|
use std::{any::TypeId, borrow::Cow, cmp, ops::Range, path::PathBuf, sync::Arc, time::Duration};
|
||||||
use text::SelectionGoal;
|
use text::SelectionGoal;
|
||||||
use ui::{
|
use ui::{
|
||||||
@@ -80,6 +77,7 @@ actions!(
|
|||||||
InsertIntoEditor,
|
InsertIntoEditor,
|
||||||
QuoteSelection,
|
QuoteSelection,
|
||||||
Split,
|
Split,
|
||||||
|
ToggleModelSelector,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -196,6 +194,7 @@ pub struct ContextEditor {
|
|||||||
// context editor, we keep a reference here.
|
// context editor, we keep a reference here.
|
||||||
dragged_file_worktrees: Vec<Entity<Worktree>>,
|
dragged_file_worktrees: Vec<Entity<Worktree>>,
|
||||||
language_model_selector: Entity<LanguageModelSelector>,
|
language_model_selector: Entity<LanguageModelSelector>,
|
||||||
|
language_model_selector_menu_handle: PopoverMenuHandle<LanguageModelSelector>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const DEFAULT_TAB_TITLE: &str = "New Chat";
|
pub const DEFAULT_TAB_TITLE: &str = "New Chat";
|
||||||
@@ -231,13 +230,6 @@ impl ContextEditor {
|
|||||||
editor.set_completion_provider(Some(Box::new(completion_provider)));
|
editor.set_completion_provider(Some(Box::new(completion_provider)));
|
||||||
editor.set_menu_inline_completions_policy(MenuInlineCompletionsPolicy::Never);
|
editor.set_menu_inline_completions_policy(MenuInlineCompletionsPolicy::Never);
|
||||||
editor.set_collaboration_hub(Box::new(project.clone()));
|
editor.set_collaboration_hub(Box::new(project.clone()));
|
||||||
|
|
||||||
let show_edit_predictions = all_language_settings(None, cx)
|
|
||||||
.edit_predictions
|
|
||||||
.enabled_in_assistant;
|
|
||||||
|
|
||||||
editor.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
|
|
||||||
|
|
||||||
editor
|
editor
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -246,7 +238,6 @@ impl ContextEditor {
|
|||||||
cx.subscribe_in(&context, window, Self::handle_context_event),
|
cx.subscribe_in(&context, window, Self::handle_context_event),
|
||||||
cx.subscribe_in(&editor, window, Self::handle_editor_event),
|
cx.subscribe_in(&editor, window, Self::handle_editor_event),
|
||||||
cx.subscribe_in(&editor, window, Self::handle_editor_search_event),
|
cx.subscribe_in(&editor, window, Self::handle_editor_search_event),
|
||||||
cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
let fs_clone = fs.clone();
|
let fs_clone = fs.clone();
|
||||||
@@ -264,6 +255,7 @@ impl ContextEditor {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let language_model_selector_menu_handle = PopoverMenuHandle::default();
|
||||||
let sections = context.read(cx).slash_command_output_sections().to_vec();
|
let sections = context.read(cx).slash_command_output_sections().to_vec();
|
||||||
let patch_ranges = context.read(cx).patch_ranges().collect::<Vec<_>>();
|
let patch_ranges = context.read(cx).patch_ranges().collect::<Vec<_>>();
|
||||||
let slash_commands = context.read(cx).slash_commands().clone();
|
let slash_commands = context.read(cx).slash_commands().clone();
|
||||||
@@ -289,6 +281,7 @@ impl ContextEditor {
|
|||||||
slash_menu_handle: Default::default(),
|
slash_menu_handle: Default::default(),
|
||||||
dragged_file_worktrees: Vec::new(),
|
dragged_file_worktrees: Vec::new(),
|
||||||
language_model_selector,
|
language_model_selector,
|
||||||
|
language_model_selector_menu_handle,
|
||||||
};
|
};
|
||||||
this.update_message_headers(cx);
|
this.update_message_headers(cx);
|
||||||
this.update_image_blocks(cx);
|
this.update_image_blocks(cx);
|
||||||
@@ -297,16 +290,6 @@ impl ContextEditor {
|
|||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
|
||||||
self.editor.update(cx, |editor, cx| {
|
|
||||||
let show_edit_predictions = all_language_settings(None, cx)
|
|
||||||
.edit_predictions
|
|
||||||
.enabled_in_assistant;
|
|
||||||
|
|
||||||
editor.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn context(&self) -> &Entity<AssistantContext> {
|
pub fn context(&self) -> &Entity<AssistantContext> {
|
||||||
&self.context
|
&self.context
|
||||||
}
|
}
|
||||||
@@ -651,7 +634,7 @@ impl ContextEditor {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
let placeholder = FoldPlaceholder {
|
let placeholder = FoldPlaceholder {
|
||||||
render: Arc::new(move |_, _, _| Empty.into_any()),
|
render: Arc::new(move |_, _, _, _| Empty.into_any()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let render_toggle = {
|
let render_toggle = {
|
||||||
@@ -1104,7 +1087,7 @@ impl ContextEditor {
|
|||||||
patch: AssistantPatch,
|
patch: AssistantPatch,
|
||||||
mut cx: AsyncWindowContext,
|
mut cx: AsyncWindowContext,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let project = this.read_with(&cx, |this, _| this.project.clone())?;
|
let project = this.update(&mut cx, |this, _| this.project.clone())?;
|
||||||
let resolved_patch = patch.resolve(project.clone(), &mut cx).await;
|
let resolved_patch = patch.resolve(project.clone(), &mut cx).await;
|
||||||
|
|
||||||
let editor = cx.new_window_entity(|window, cx| {
|
let editor = cx.new_window_entity(|window, cx| {
|
||||||
@@ -1129,7 +1112,7 @@ impl ContextEditor {
|
|||||||
editor
|
editor
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
this.update(&mut cx, |this, _| {
|
this.update_in(&mut cx, |this, window, cx| {
|
||||||
if let Some(patch_state) = this.patches.get_mut(&patch.range) {
|
if let Some(patch_state) = this.patches.get_mut(&patch.range) {
|
||||||
patch_state.editor = Some(PatchEditorState {
|
patch_state.editor = Some(PatchEditorState {
|
||||||
editor: editor.downgrade(),
|
editor: editor.downgrade(),
|
||||||
@@ -1137,12 +1120,19 @@ impl ContextEditor {
|
|||||||
});
|
});
|
||||||
patch_state.update_task.take();
|
patch_state.update_task.take();
|
||||||
}
|
}
|
||||||
})?;
|
|
||||||
this.read_with(&cx, |this, _| this.workspace.clone())?
|
this.workspace
|
||||||
.update_in(&mut cx, |workspace, window, cx| {
|
.update(cx, |workspace, cx| {
|
||||||
workspace.add_item_to_active_pane(Box::new(editor.clone()), None, false, window, cx)
|
workspace.add_item_to_active_pane(
|
||||||
|
Box::new(editor.clone()),
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.log_err();
|
.log_err();
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -1244,8 +1234,8 @@ impl ContextEditor {
|
|||||||
.px_1()
|
.px_1()
|
||||||
.mr_0p5()
|
.mr_0p5()
|
||||||
.border_1()
|
.border_1()
|
||||||
.border_color(colors.border_variant.alpha(0.6))
|
.border_color(theme::color_alpha(colors.border_variant, 0.6))
|
||||||
.bg(colors.element_background.alpha(0.6))
|
.bg(theme::color_alpha(colors.element_background, 0.6))
|
||||||
.child("esc"),
|
.child("esc"),
|
||||||
)
|
)
|
||||||
.child("to cancel")
|
.child("to cancel")
|
||||||
@@ -1524,11 +1514,15 @@ impl ContextEditor {
|
|||||||
|
|
||||||
(!text.is_empty()).then_some((text, true))
|
(!text.is_empty()).then_some((text, true))
|
||||||
} else {
|
} else {
|
||||||
let selection = context_editor.selections.newest_adjusted(cx);
|
let anchor = context_editor.selections.newest_anchor();
|
||||||
let buffer = context_editor.buffer().read(cx).snapshot(cx);
|
let text = context_editor
|
||||||
let selected_text = buffer.text_for_range(selection.range()).collect::<String>();
|
.buffer()
|
||||||
|
.read(cx)
|
||||||
|
.read(cx)
|
||||||
|
.text_for_range(anchor.range())
|
||||||
|
.collect::<String>();
|
||||||
|
|
||||||
(!selected_text.is_empty()).then_some((selected_text, false))
|
(!text.is_empty()).then_some((text, false))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1783,16 +1777,23 @@ impl ContextEditor {
|
|||||||
&mut self,
|
&mut self,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> (String, CopyMetadata, Vec<text::Selection<usize>>) {
|
) -> (String, CopyMetadata, Vec<text::Selection<usize>>) {
|
||||||
let (selection, creases) = self.editor.update(cx, |editor, cx| {
|
let (snapshot, selection, creases) = self.editor.update(cx, |editor, cx| {
|
||||||
let mut selection = editor.selections.newest_adjusted(cx);
|
let mut selection = editor.selections.newest::<Point>(cx);
|
||||||
let snapshot = editor.buffer().read(cx).snapshot(cx);
|
let snapshot = editor.buffer().read(cx).snapshot(cx);
|
||||||
|
|
||||||
|
let is_entire_line = selection.is_empty() || editor.selections.line_mode;
|
||||||
|
if is_entire_line {
|
||||||
|
selection.start = Point::new(selection.start.row, 0);
|
||||||
|
selection.end =
|
||||||
|
cmp::min(snapshot.max_point(), Point::new(selection.start.row + 1, 0));
|
||||||
selection.goal = SelectionGoal::None;
|
selection.goal = SelectionGoal::None;
|
||||||
|
}
|
||||||
|
|
||||||
let selection_start = snapshot.point_to_offset(selection.start);
|
let selection_start = snapshot.point_to_offset(selection.start);
|
||||||
|
|
||||||
(
|
(
|
||||||
selection.map(|point| snapshot.point_to_offset(point)),
|
snapshot.clone(),
|
||||||
|
selection.clone(),
|
||||||
editor.display_map.update(cx, |display_map, cx| {
|
editor.display_map.update(cx, |display_map, cx| {
|
||||||
display_map
|
display_map
|
||||||
.snapshot(cx)
|
.snapshot(cx)
|
||||||
@@ -1832,6 +1833,7 @@ impl ContextEditor {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let selection = selection.map(|point| snapshot.point_to_offset(point));
|
||||||
let context = self.context.read(cx);
|
let context = self.context.read(cx);
|
||||||
|
|
||||||
let mut text = String::new();
|
let mut text = String::new();
|
||||||
@@ -2388,6 +2390,46 @@ impl ContextEditor {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_language_model_selector(&self, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
|
let active_model = LanguageModelRegistry::read_global(cx).active_model();
|
||||||
|
let focus_handle = self.editor().focus_handle(cx).clone();
|
||||||
|
let model_name = match active_model {
|
||||||
|
Some(model) => model.name().0,
|
||||||
|
None => SharedString::from("No model selected"),
|
||||||
|
};
|
||||||
|
|
||||||
|
LanguageModelSelectorPopoverMenu::new(
|
||||||
|
self.language_model_selector.clone(),
|
||||||
|
ButtonLike::new("active-model")
|
||||||
|
.style(ButtonStyle::Subtle)
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.gap_0p5()
|
||||||
|
.child(
|
||||||
|
Label::new(model_name)
|
||||||
|
.size(LabelSize::Small)
|
||||||
|
.color(Color::Muted),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Icon::new(IconName::ChevronDown)
|
||||||
|
.color(Color::Muted)
|
||||||
|
.size(IconSize::XSmall),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
move |window, cx| {
|
||||||
|
Tooltip::for_action_in(
|
||||||
|
"Change Model",
|
||||||
|
&ToggleModelSelector,
|
||||||
|
&focus_handle,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
gpui::Corner::BottomLeft,
|
||||||
|
)
|
||||||
|
.with_handle(self.language_model_selector_menu_handle.clone())
|
||||||
|
}
|
||||||
|
|
||||||
fn render_last_error(&self, cx: &mut Context<Self>) -> Option<AnyElement> {
|
fn render_last_error(&self, cx: &mut Context<Self>) -> Option<AnyElement> {
|
||||||
let last_error = self.last_error.as_ref()?;
|
let last_error = self.last_error.as_ref()?;
|
||||||
|
|
||||||
@@ -2617,8 +2659,8 @@ fn render_fold_icon_button(
|
|||||||
editor: WeakEntity<Editor>,
|
editor: WeakEntity<Editor>,
|
||||||
icon: IconName,
|
icon: IconName,
|
||||||
label: SharedString,
|
label: SharedString,
|
||||||
) -> Arc<dyn Send + Sync + Fn(FoldId, Range<Anchor>, &mut App) -> AnyElement> {
|
) -> Arc<dyn Send + Sync + Fn(FoldId, Range<Anchor>, &mut Window, &mut App) -> AnyElement> {
|
||||||
Arc::new(move |fold_id, fold_range, _cx| {
|
Arc::new(move |fold_id, fold_range, _window, _cx| {
|
||||||
let editor = editor.clone();
|
let editor = editor.clone();
|
||||||
ButtonLike::new(fold_id)
|
ButtonLike::new(fold_id)
|
||||||
.style(ButtonStyle::Filled)
|
.style(ButtonStyle::Filled)
|
||||||
@@ -2678,7 +2720,7 @@ pub fn fold_toggle(
|
|||||||
fn quote_selection_fold_placeholder(title: String, editor: WeakEntity<Editor>) -> FoldPlaceholder {
|
fn quote_selection_fold_placeholder(title: String, editor: WeakEntity<Editor>) -> FoldPlaceholder {
|
||||||
FoldPlaceholder {
|
FoldPlaceholder {
|
||||||
render: Arc::new({
|
render: Arc::new({
|
||||||
move |fold_id, fold_range, _cx| {
|
move |fold_id, fold_range, _window, _cx| {
|
||||||
let editor = editor.clone();
|
let editor = editor.clone();
|
||||||
ButtonLike::new(fold_id)
|
ButtonLike::new(fold_id)
|
||||||
.style(ButtonStyle::Filled)
|
.style(ButtonStyle::Filled)
|
||||||
@@ -2832,7 +2874,6 @@ impl Render for ContextEditor {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let language_model_selector = self.language_model_selector.clone();
|
|
||||||
v_flex()
|
v_flex()
|
||||||
.key_context("ContextEditor")
|
.key_context("ContextEditor")
|
||||||
.capture_action(cx.listener(ContextEditor::cancel))
|
.capture_action(cx.listener(ContextEditor::cancel))
|
||||||
@@ -2845,11 +2886,6 @@ impl Render for ContextEditor {
|
|||||||
.on_action(cx.listener(ContextEditor::edit))
|
.on_action(cx.listener(ContextEditor::edit))
|
||||||
.on_action(cx.listener(ContextEditor::assist))
|
.on_action(cx.listener(ContextEditor::assist))
|
||||||
.on_action(cx.listener(ContextEditor::split))
|
.on_action(cx.listener(ContextEditor::split))
|
||||||
.on_action(move |action, window, cx| {
|
|
||||||
language_model_selector.update(cx, |this, cx| {
|
|
||||||
this.toggle_model_selector(action, window, cx);
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.size_full()
|
.size_full()
|
||||||
.children(self.render_notice(cx))
|
.children(self.render_notice(cx))
|
||||||
.child(
|
.child(
|
||||||
@@ -2887,14 +2923,11 @@ impl Render for ContextEditor {
|
|||||||
.gap_1()
|
.gap_1()
|
||||||
.child(self.render_inject_context_menu(cx))
|
.child(self.render_inject_context_menu(cx))
|
||||||
.child(ui::Divider::vertical())
|
.child(ui::Divider::vertical())
|
||||||
.child(div().pl_0p5().child({
|
.child(
|
||||||
let focus_handle = self.editor().focus_handle(cx).clone();
|
div()
|
||||||
AssistantLanguageModelSelector::new(
|
.pl_0p5()
|
||||||
focus_handle,
|
.child(self.render_language_model_selector(cx)),
|
||||||
self.language_model_selector.clone(),
|
),
|
||||||
)
|
|
||||||
.render(window, cx)
|
|
||||||
})),
|
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
@@ -3370,7 +3403,7 @@ fn invoked_slash_command_fold_placeholder(
|
|||||||
FoldPlaceholder {
|
FoldPlaceholder {
|
||||||
constrain_width: false,
|
constrain_width: false,
|
||||||
merge_adjacent: false,
|
merge_adjacent: false,
|
||||||
render: Arc::new(move |fold_id, _, cx| {
|
render: Arc::new(move |fold_id, _, _window, cx| {
|
||||||
let Some(context) = context.upgrade() else {
|
let Some(context) = context.upgrade() else {
|
||||||
return Empty.into_any();
|
return Empty.into_any();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use clock::ReplicaId;
|
|||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use context_server::manager::ContextServerManager;
|
use context_server::manager::ContextServerManager;
|
||||||
use context_server::ContextServerFactoryRegistry;
|
use context_server::ContextServerFactoryRegistry;
|
||||||
use fs::{Fs, RemoveOptions};
|
use fs::Fs;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use fuzzy::StringMatchCandidate;
|
use fuzzy::StringMatchCandidate;
|
||||||
use gpui::{App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Task, WeakEntity};
|
use gpui::{App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Task, WeakEntity};
|
||||||
@@ -350,12 +350,6 @@ impl ContextStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn contexts(&self) -> Vec<SavedContextMetadata> {
|
|
||||||
let mut contexts = self.contexts_metadata.iter().cloned().collect::<Vec<_>>();
|
|
||||||
contexts.sort_unstable_by_key(|thread| std::cmp::Reverse(thread.mtime));
|
|
||||||
contexts
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create(&mut self, cx: &mut Context<Self>) -> Entity<AssistantContext> {
|
pub fn create(&mut self, cx: &mut Context<Self>) -> Entity<AssistantContext> {
|
||||||
let context = cx.new(|cx| {
|
let context = cx.new(|cx| {
|
||||||
AssistantContext::local(
|
AssistantContext::local(
|
||||||
@@ -475,38 +469,6 @@ impl ContextStore {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_local_context(
|
|
||||||
&mut self,
|
|
||||||
path: PathBuf,
|
|
||||||
cx: &mut Context<Self>,
|
|
||||||
) -> Task<Result<()>> {
|
|
||||||
let fs = self.fs.clone();
|
|
||||||
|
|
||||||
cx.spawn(|this, mut cx| async move {
|
|
||||||
fs.remove_file(
|
|
||||||
&path,
|
|
||||||
RemoveOptions {
|
|
||||||
recursive: false,
|
|
||||||
ignore_if_not_exists: true,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
this.update(&mut cx, |this, cx| {
|
|
||||||
this.contexts.retain(|context| {
|
|
||||||
context
|
|
||||||
.upgrade()
|
|
||||||
.and_then(|context| context.read(cx).path())
|
|
||||||
!= Some(&path)
|
|
||||||
});
|
|
||||||
this.contexts_metadata
|
|
||||||
.retain(|context| context.path != path);
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn loaded_context_for_path(&self, path: &Path, cx: &App) -> Option<Entity<AssistantContext>> {
|
fn loaded_context_for_path(&self, path: &Path, cx: &App) -> Option<Entity<AssistantContext>> {
|
||||||
self.contexts.iter().find_map(|context| {
|
self.contexts.iter().find_map(|context| {
|
||||||
let context = context.upgrade()?;
|
let context = context.upgrade()?;
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ impl ResolvedPatch {
|
|||||||
buffer.edit(
|
buffer.edit(
|
||||||
edits,
|
edits,
|
||||||
Some(AutoindentMode::Block {
|
Some(AutoindentMode::Block {
|
||||||
original_start_columns: Vec::new(),
|
original_indent_columns: Vec::new(),
|
||||||
}),
|
}),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -359,7 +359,6 @@ fn providers_schema(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema:
|
|||||||
schemars::schema::SchemaObject {
|
schemars::schema::SchemaObject {
|
||||||
enum_values: Some(vec![
|
enum_values: Some(vec![
|
||||||
"anthropic".into(),
|
"anthropic".into(),
|
||||||
"bedrock".into(),
|
|
||||||
"google".into(),
|
"google".into(),
|
||||||
"lmstudio".into(),
|
"lmstudio".into(),
|
||||||
"ollama".into(),
|
"ollama".into(),
|
||||||
@@ -513,7 +512,7 @@ mod tests {
|
|||||||
AssistantSettings::get_global(cx).default_model,
|
AssistantSettings::get_global(cx).default_model,
|
||||||
LanguageModelSelection {
|
LanguageModelSelection {
|
||||||
provider: "zed.dev".into(),
|
provider: "zed.dev".into(),
|
||||||
model: "claude-3-5-sonnet-latest".into(),
|
model: "claude-3-5-sonnet".into(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ impl SlashCommand for DefaultSlashCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn description(&self) -> String {
|
fn description(&self) -> String {
|
||||||
"Insert default prompt".into()
|
"insert default prompt".into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn menu_text(&self) -> String {
|
fn menu_text(&self) -> String {
|
||||||
self.description()
|
"Insert Default Prompt".into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn requires_argument(&self) -> bool {
|
fn requires_argument(&self) -> bool {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ pub enum Timezone {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct NowToolInput {
|
pub struct FileToolInput {
|
||||||
/// The timezone to use for the datetime.
|
/// The timezone to use for the datetime.
|
||||||
timezone: Timezone,
|
timezone: Timezone,
|
||||||
}
|
}
|
||||||
@@ -34,7 +34,7 @@ impl Tool for NowTool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn input_schema(&self) -> serde_json::Value {
|
fn input_schema(&self) -> serde_json::Value {
|
||||||
let schema = schemars::schema_for!(NowToolInput);
|
let schema = schemars::schema_for!(FileToolInput);
|
||||||
serde_json::to_value(&schema).unwrap()
|
serde_json::to_value(&schema).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ impl Tool for NowTool {
|
|||||||
_window: &mut Window,
|
_window: &mut Window,
|
||||||
_cx: &mut App,
|
_cx: &mut App,
|
||||||
) -> Task<Result<String>> {
|
) -> Task<Result<String>> {
|
||||||
let input: NowToolInput = match serde_json::from_value(input) {
|
let input: FileToolInput = match serde_json::from_value(input) {
|
||||||
Ok(input) => input,
|
Ok(input) => input,
|
||||||
Err(err) => return Task::ready(Err(anyhow!(err))),
|
Err(err) => return Task::ready(Err(anyhow!(err))),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "aws_http_client"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition.workspace = true
|
|
||||||
publish.workspace = true
|
|
||||||
license = "GPL-3.0-or-later"
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
path = "src/aws_http_client.rs"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = []
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
aws-smithy-runtime-api.workspace = true
|
|
||||||
aws-smithy-types.workspace = true
|
|
||||||
futures.workspace = true
|
|
||||||
http_client.workspace = true
|
|
||||||
tokio = { workspace = true, features = ["rt", "rt-multi-thread"] }
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../LICENSE-GPL
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
use std::fmt;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use aws_smithy_runtime_api::client::http::{
|
|
||||||
HttpClient as AwsClient, HttpConnector as AwsConnector,
|
|
||||||
HttpConnectorFuture as AwsConnectorFuture, HttpConnectorFuture, HttpConnectorSettings,
|
|
||||||
SharedHttpConnector,
|
|
||||||
};
|
|
||||||
use aws_smithy_runtime_api::client::orchestrator::{HttpRequest as AwsHttpRequest, HttpResponse};
|
|
||||||
use aws_smithy_runtime_api::client::result::ConnectorError;
|
|
||||||
use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents;
|
|
||||||
use aws_smithy_runtime_api::http::StatusCode;
|
|
||||||
use aws_smithy_types::body::SdkBody;
|
|
||||||
use futures::AsyncReadExt;
|
|
||||||
use http_client::{AsyncBody, Inner};
|
|
||||||
use http_client::{HttpClient, Request};
|
|
||||||
use tokio::runtime::Handle;
|
|
||||||
|
|
||||||
struct AwsHttpConnector {
|
|
||||||
client: Arc<dyn HttpClient>,
|
|
||||||
handle: Handle,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Debug for AwsHttpConnector {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("AwsHttpConnector").finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AwsConnector for AwsHttpConnector {
|
|
||||||
fn call(&self, request: AwsHttpRequest) -> AwsConnectorFuture {
|
|
||||||
let req = match request.try_into_http1x() {
|
|
||||||
Ok(req) => req,
|
|
||||||
Err(err) => {
|
|
||||||
return HttpConnectorFuture::ready(Err(ConnectorError::other(err.into(), None)))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let (parts, body) = req.into_parts();
|
|
||||||
|
|
||||||
let response = self
|
|
||||||
.client
|
|
||||||
.send(Request::from_parts(parts, convert_to_async_body(body)));
|
|
||||||
|
|
||||||
let handle = self.handle.clone();
|
|
||||||
|
|
||||||
HttpConnectorFuture::new(async move {
|
|
||||||
let response = match response.await {
|
|
||||||
Ok(response) => response,
|
|
||||||
Err(err) => return Err(ConnectorError::other(err.into(), None)),
|
|
||||||
};
|
|
||||||
let (parts, body) = response.into_parts();
|
|
||||||
let body = convert_to_sdk_body(body, handle).await;
|
|
||||||
|
|
||||||
Ok(HttpResponse::new(
|
|
||||||
StatusCode::try_from(parts.status.as_u16()).unwrap(),
|
|
||||||
body,
|
|
||||||
))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct AwsHttpClient {
|
|
||||||
client: Arc<dyn HttpClient>,
|
|
||||||
handler: Handle,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Debug for AwsHttpClient {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("AwsHttpClient").finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AwsHttpClient {
|
|
||||||
pub fn new(client: Arc<dyn HttpClient>, handle: Handle) -> Self {
|
|
||||||
Self {
|
|
||||||
client,
|
|
||||||
handler: handle,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AwsClient for AwsHttpClient {
|
|
||||||
fn http_connector(
|
|
||||||
&self,
|
|
||||||
_settings: &HttpConnectorSettings,
|
|
||||||
_components: &RuntimeComponents,
|
|
||||||
) -> SharedHttpConnector {
|
|
||||||
SharedHttpConnector::new(AwsHttpConnector {
|
|
||||||
client: self.client.clone(),
|
|
||||||
handle: self.handler.clone(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn convert_to_sdk_body(body: AsyncBody, handle: Handle) -> SdkBody {
|
|
||||||
match body.0 {
|
|
||||||
Inner::Empty => SdkBody::empty(),
|
|
||||||
Inner::Bytes(bytes) => SdkBody::from(bytes.into_inner()),
|
|
||||||
Inner::AsyncReader(mut reader) => {
|
|
||||||
let buffer = handle.spawn(async move {
|
|
||||||
let mut buffer = Vec::new();
|
|
||||||
let _ = reader.read_to_end(&mut buffer).await;
|
|
||||||
buffer
|
|
||||||
});
|
|
||||||
|
|
||||||
SdkBody::from(buffer.await.unwrap_or_default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn convert_to_async_body(body: SdkBody) -> AsyncBody {
|
|
||||||
match body.bytes() {
|
|
||||||
Some(bytes) => AsyncBody::from((*bytes).to_vec()),
|
|
||||||
None => AsyncBody::empty(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "bedrock"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition.workspace = true
|
|
||||||
publish.workspace = true
|
|
||||||
license = "GPL-3.0-or-later"
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
path = "src/bedrock.rs"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = []
|
|
||||||
schemars = ["dep:schemars"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
anyhow.workspace = true
|
|
||||||
aws-sdk-bedrockruntime = { workspace = true, features = ["behavior-version-latest"] }
|
|
||||||
aws-smithy-types = {workspace = true}
|
|
||||||
futures.workspace = true
|
|
||||||
schemars = { workspace = true, optional = true }
|
|
||||||
serde.workspace = true
|
|
||||||
serde_json.workspace = true
|
|
||||||
strum.workspace = true
|
|
||||||
thiserror.workspace = true
|
|
||||||
tokio = { workspace = true, features = ["rt", "rt-multi-thread"] }
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../LICENSE-GPL
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
mod models;
|
|
||||||
|
|
||||||
use std::pin::Pin;
|
|
||||||
|
|
||||||
use anyhow::{anyhow, Context, Error, Result};
|
|
||||||
use aws_sdk_bedrockruntime as bedrock;
|
|
||||||
pub use aws_sdk_bedrockruntime as bedrock_client;
|
|
||||||
pub use aws_sdk_bedrockruntime::types::{
|
|
||||||
ContentBlock as BedrockInnerContent, SpecificToolChoice as BedrockSpecificTool,
|
|
||||||
ToolChoice as BedrockToolChoice, ToolInputSchema as BedrockToolInputSchema,
|
|
||||||
ToolSpecification as BedrockTool,
|
|
||||||
};
|
|
||||||
use aws_smithy_types::{Document, Number as AwsNumber};
|
|
||||||
pub use bedrock::operation::converse_stream::ConverseStreamInput as BedrockStreamingRequest;
|
|
||||||
pub use bedrock::types::{
|
|
||||||
ContentBlock as BedrockRequestContent, ConversationRole as BedrockRole,
|
|
||||||
ConverseOutput as BedrockResponse, ConverseStreamOutput as BedrockStreamingResponse,
|
|
||||||
Message as BedrockMessage, ResponseStream as BedrockResponseStream,
|
|
||||||
};
|
|
||||||
use futures::stream::{self, BoxStream, Stream};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use serde_json::{Number, Value};
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
pub use crate::models::*;
|
|
||||||
|
|
||||||
pub async fn complete(
|
|
||||||
client: &bedrock::Client,
|
|
||||||
request: Request,
|
|
||||||
) -> Result<BedrockResponse, BedrockError> {
|
|
||||||
let response = bedrock::Client::converse(client)
|
|
||||||
.model_id(request.model.clone())
|
|
||||||
.set_messages(request.messages.into())
|
|
||||||
.send()
|
|
||||||
.await
|
|
||||||
.context("failed to send request to Bedrock");
|
|
||||||
|
|
||||||
match response {
|
|
||||||
Ok(output) => output
|
|
||||||
.output
|
|
||||||
.ok_or_else(|| BedrockError::Other(anyhow!("no output"))),
|
|
||||||
Err(err) => Err(BedrockError::Other(err)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn stream_completion(
|
|
||||||
client: bedrock::Client,
|
|
||||||
request: Request,
|
|
||||||
handle: tokio::runtime::Handle,
|
|
||||||
) -> Result<BoxStream<'static, Result<BedrockStreamingResponse, BedrockError>>, Error> {
|
|
||||||
handle
|
|
||||||
.spawn(async move {
|
|
||||||
let response = bedrock::Client::converse_stream(&client)
|
|
||||||
.model_id(request.model.clone())
|
|
||||||
.set_messages(request.messages.into())
|
|
||||||
.send()
|
|
||||||
.await;
|
|
||||||
|
|
||||||
match response {
|
|
||||||
Ok(output) => {
|
|
||||||
let stream: Pin<
|
|
||||||
Box<
|
|
||||||
dyn Stream<Item = Result<BedrockStreamingResponse, BedrockError>>
|
|
||||||
+ Send,
|
|
||||||
>,
|
|
||||||
> = Box::pin(stream::unfold(output.stream, |mut stream| async move {
|
|
||||||
match stream.recv().await {
|
|
||||||
Ok(Some(output)) => Some((Ok(output), stream)),
|
|
||||||
Ok(None) => None,
|
|
||||||
Err(err) => {
|
|
||||||
Some((
|
|
||||||
// TODO: Figure out how we can capture Throttling Exceptions
|
|
||||||
Err(BedrockError::ClientError(anyhow!(
|
|
||||||
"{:?}",
|
|
||||||
aws_sdk_bedrockruntime::error::DisplayErrorContext(err)
|
|
||||||
))),
|
|
||||||
stream,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
Ok(stream)
|
|
||||||
}
|
|
||||||
Err(err) => Err(anyhow!(
|
|
||||||
"{:?}",
|
|
||||||
aws_sdk_bedrockruntime::error::DisplayErrorContext(err)
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.map_err(|err| anyhow!("failed to spawn task: {err:?}"))?
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn aws_document_to_value(document: &Document) -> Value {
|
|
||||||
match document {
|
|
||||||
Document::Null => Value::Null,
|
|
||||||
Document::Bool(value) => Value::Bool(*value),
|
|
||||||
Document::Number(value) => match *value {
|
|
||||||
AwsNumber::PosInt(value) => Value::Number(Number::from(value)),
|
|
||||||
AwsNumber::NegInt(value) => Value::Number(Number::from(value)),
|
|
||||||
AwsNumber::Float(value) => Value::Number(Number::from_f64(value).unwrap()),
|
|
||||||
},
|
|
||||||
Document::String(value) => Value::String(value.clone()),
|
|
||||||
Document::Array(array) => Value::Array(array.iter().map(aws_document_to_value).collect()),
|
|
||||||
Document::Object(map) => Value::Object(
|
|
||||||
map.iter()
|
|
||||||
.map(|(key, value)| (key.clone(), aws_document_to_value(value)))
|
|
||||||
.collect(),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn value_to_aws_document(value: &Value) -> Document {
|
|
||||||
match value {
|
|
||||||
Value::Null => Document::Null,
|
|
||||||
Value::Bool(value) => Document::Bool(*value),
|
|
||||||
Value::Number(value) => {
|
|
||||||
if let Some(value) = value.as_u64() {
|
|
||||||
Document::Number(AwsNumber::PosInt(value))
|
|
||||||
} else if let Some(value) = value.as_i64() {
|
|
||||||
Document::Number(AwsNumber::NegInt(value))
|
|
||||||
} else if let Some(value) = value.as_f64() {
|
|
||||||
Document::Number(AwsNumber::Float(value))
|
|
||||||
} else {
|
|
||||||
Document::Null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Value::String(value) => Document::String(value.clone()),
|
|
||||||
Value::Array(array) => Document::Array(array.iter().map(value_to_aws_document).collect()),
|
|
||||||
Value::Object(map) => Document::Object(
|
|
||||||
map.iter()
|
|
||||||
.map(|(key, value)| (key.clone(), value_to_aws_document(value)))
|
|
||||||
.collect(),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Request {
|
|
||||||
pub model: String,
|
|
||||||
pub max_tokens: u32,
|
|
||||||
pub messages: Vec<BedrockMessage>,
|
|
||||||
pub tools: Vec<BedrockTool>,
|
|
||||||
pub tool_choice: Option<BedrockToolChoice>,
|
|
||||||
pub system: Option<String>,
|
|
||||||
pub metadata: Option<Metadata>,
|
|
||||||
pub stop_sequences: Vec<String>,
|
|
||||||
pub temperature: Option<f32>,
|
|
||||||
pub top_k: Option<u32>,
|
|
||||||
pub top_p: Option<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Metadata {
|
|
||||||
pub user_id: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub enum BedrockError {
|
|
||||||
#[error("client error: {0}")]
|
|
||||||
ClientError(anyhow::Error),
|
|
||||||
#[error("extension error: {0}")]
|
|
||||||
ExtensionError(anyhow::Error),
|
|
||||||
#[error(transparent)]
|
|
||||||
Other(#[from] anyhow::Error),
|
|
||||||
}
|
|
||||||
@@ -1,207 +0,0 @@
|
|||||||
use anyhow::anyhow;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use strum::EnumIter;
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
|
||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, EnumIter)]
|
|
||||||
pub enum Model {
|
|
||||||
// Anthropic models (already included)
|
|
||||||
#[default]
|
|
||||||
#[serde(rename = "claude-3-5-sonnet-v2", alias = "claude-3-5-sonnet-latest")]
|
|
||||||
Claude3_5Sonnet,
|
|
||||||
#[serde(rename = "claude-3-7-sonnet", alias = "claude-3-7-sonnet-latest")]
|
|
||||||
Claude3_7Sonnet,
|
|
||||||
#[serde(rename = "claude-3-opus", alias = "claude-3-opus-latest")]
|
|
||||||
Claude3Opus,
|
|
||||||
#[serde(rename = "claude-3-sonnet", alias = "claude-3-sonnet-latest")]
|
|
||||||
Claude3Sonnet,
|
|
||||||
#[serde(rename = "claude-3-5-haiku", alias = "claude-3-5-haiku-latest")]
|
|
||||||
Claude3_5Haiku,
|
|
||||||
// Amazon Nova Models
|
|
||||||
AmazonNovaLite,
|
|
||||||
AmazonNovaMicro,
|
|
||||||
AmazonNovaPro,
|
|
||||||
// AI21 models
|
|
||||||
AI21J2GrandeInstruct,
|
|
||||||
AI21J2JumboInstruct,
|
|
||||||
AI21J2Mid,
|
|
||||||
AI21J2MidV1,
|
|
||||||
AI21J2Ultra,
|
|
||||||
AI21J2UltraV1_8k,
|
|
||||||
AI21J2UltraV1,
|
|
||||||
AI21JambaInstructV1,
|
|
||||||
AI21Jamba15LargeV1,
|
|
||||||
AI21Jamba15MiniV1,
|
|
||||||
// Cohere models
|
|
||||||
CohereCommandTextV14_4k,
|
|
||||||
CohereCommandRV1,
|
|
||||||
CohereCommandRPlusV1,
|
|
||||||
CohereCommandLightTextV14_4k,
|
|
||||||
// Meta models
|
|
||||||
MetaLlama38BInstructV1,
|
|
||||||
MetaLlama370BInstructV1,
|
|
||||||
MetaLlama318BInstructV1_128k,
|
|
||||||
MetaLlama318BInstructV1,
|
|
||||||
MetaLlama3170BInstructV1_128k,
|
|
||||||
MetaLlama3170BInstructV1,
|
|
||||||
MetaLlama3211BInstructV1,
|
|
||||||
MetaLlama3290BInstructV1,
|
|
||||||
MetaLlama321BInstructV1,
|
|
||||||
MetaLlama323BInstructV1,
|
|
||||||
// Mistral models
|
|
||||||
MistralMistral7BInstructV0,
|
|
||||||
MistralMixtral8x7BInstructV0,
|
|
||||||
MistralMistralLarge2402V1,
|
|
||||||
MistralMistralSmall2402V1,
|
|
||||||
#[serde(rename = "custom")]
|
|
||||||
Custom {
|
|
||||||
name: String,
|
|
||||||
max_tokens: usize,
|
|
||||||
/// The name displayed in the UI, such as in the assistant panel model dropdown menu.
|
|
||||||
display_name: Option<String>,
|
|
||||||
max_output_tokens: Option<u32>,
|
|
||||||
default_temperature: Option<f32>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Model {
|
|
||||||
pub fn from_id(id: &str) -> anyhow::Result<Self> {
|
|
||||||
if id.starts_with("claude-3-5-sonnet-v2") {
|
|
||||||
Ok(Self::Claude3_5Sonnet)
|
|
||||||
} else if id.starts_with("claude-3-opus") {
|
|
||||||
Ok(Self::Claude3Opus)
|
|
||||||
} else if id.starts_with("claude-3-sonnet") {
|
|
||||||
Ok(Self::Claude3Sonnet)
|
|
||||||
} else if id.starts_with("claude-3-5-haiku") {
|
|
||||||
Ok(Self::Claude3_5Haiku)
|
|
||||||
} else if id.starts_with("claude-3-7-sonnet") {
|
|
||||||
Ok(Self::Claude3_7Sonnet)
|
|
||||||
} else {
|
|
||||||
Err(anyhow!("invalid model id"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn id(&self) -> &str {
|
|
||||||
match self {
|
|
||||||
Model::Claude3_5Sonnet => "us.anthropic.claude-3-5-sonnet-20241022-v2:0",
|
|
||||||
Model::Claude3Opus => "us.anthropic.claude-3-opus-20240229-v1:0",
|
|
||||||
Model::Claude3Sonnet => "us.anthropic.claude-3-sonnet-20240229-v1:0",
|
|
||||||
Model::Claude3_5Haiku => "us.anthropic.claude-3-5-haiku-20241022-v1:0",
|
|
||||||
Model::Claude3_7Sonnet => "us.anthropic.claude-3-7-sonnet-20250219-v1:0",
|
|
||||||
Model::AmazonNovaLite => "us.amazon.nova-lite-v1:0",
|
|
||||||
Model::AmazonNovaMicro => "us.amazon.nova-micro-v1:0",
|
|
||||||
Model::AmazonNovaPro => "us.amazon.nova-pro-v1:0",
|
|
||||||
Model::AI21J2GrandeInstruct => "ai21.j2-grande-instruct",
|
|
||||||
Model::AI21J2JumboInstruct => "ai21.j2-jumbo-instruct",
|
|
||||||
Model::AI21J2Mid => "ai21.j2-mid",
|
|
||||||
Model::AI21J2MidV1 => "ai21.j2-mid-v1",
|
|
||||||
Model::AI21J2Ultra => "ai21.j2-ultra",
|
|
||||||
Model::AI21J2UltraV1_8k => "ai21.j2-ultra-v1:0:8k",
|
|
||||||
Model::AI21J2UltraV1 => "ai21.j2-ultra-v1",
|
|
||||||
Model::AI21JambaInstructV1 => "ai21.jamba-instruct-v1:0",
|
|
||||||
Model::AI21Jamba15LargeV1 => "ai21.jamba-1-5-large-v1:0",
|
|
||||||
Model::AI21Jamba15MiniV1 => "ai21.jamba-1-5-mini-v1:0",
|
|
||||||
Model::CohereCommandTextV14_4k => "cohere.command-text-v14:7:4k",
|
|
||||||
Model::CohereCommandRV1 => "cohere.command-r-v1:0",
|
|
||||||
Model::CohereCommandRPlusV1 => "cohere.command-r-plus-v1:0",
|
|
||||||
Model::CohereCommandLightTextV14_4k => "cohere.command-light-text-v14:7:4k",
|
|
||||||
Model::MetaLlama38BInstructV1 => "meta.llama3-8b-instruct-v1:0",
|
|
||||||
Model::MetaLlama370BInstructV1 => "meta.llama3-70b-instruct-v1:0",
|
|
||||||
Model::MetaLlama318BInstructV1_128k => "meta.llama3-1-8b-instruct-v1:0:128k",
|
|
||||||
Model::MetaLlama318BInstructV1 => "meta.llama3-1-8b-instruct-v1:0",
|
|
||||||
Model::MetaLlama3170BInstructV1_128k => "meta.llama3-1-70b-instruct-v1:0:128k",
|
|
||||||
Model::MetaLlama3170BInstructV1 => "meta.llama3-1-70b-instruct-v1:0",
|
|
||||||
Model::MetaLlama3211BInstructV1 => "meta.llama3-2-11b-instruct-v1:0",
|
|
||||||
Model::MetaLlama3290BInstructV1 => "meta.llama3-2-90b-instruct-v1:0",
|
|
||||||
Model::MetaLlama321BInstructV1 => "meta.llama3-2-1b-instruct-v1:0",
|
|
||||||
Model::MetaLlama323BInstructV1 => "meta.llama3-2-3b-instruct-v1:0",
|
|
||||||
Model::MistralMistral7BInstructV0 => "mistral.mistral-7b-instruct-v0:2",
|
|
||||||
Model::MistralMixtral8x7BInstructV0 => "mistral.mixtral-8x7b-instruct-v0:1",
|
|
||||||
Model::MistralMistralLarge2402V1 => "mistral.mistral-large-2402-v1:0",
|
|
||||||
Model::MistralMistralSmall2402V1 => "mistral.mistral-small-2402-v1:0",
|
|
||||||
Self::Custom { name, .. } => name,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn display_name(&self) -> &str {
|
|
||||||
match self {
|
|
||||||
Self::Claude3_5Sonnet => "Claude 3.5 Sonnet v2",
|
|
||||||
Self::Claude3Opus => "Claude 3 Opus",
|
|
||||||
Self::Claude3Sonnet => "Claude 3 Sonnet",
|
|
||||||
Self::Claude3_5Haiku => "Claude 3.5 Haiku",
|
|
||||||
Self::Claude3_7Sonnet => "Claude 3.7 Sonnet",
|
|
||||||
Self::AmazonNovaLite => "Amazon Nova Lite",
|
|
||||||
Self::AmazonNovaMicro => "Amazon Nova Micro",
|
|
||||||
Self::AmazonNovaPro => "Amazon Nova Pro",
|
|
||||||
Self::AI21J2GrandeInstruct => "AI21 Jurassic2 Grande Instruct",
|
|
||||||
Self::AI21J2JumboInstruct => "AI21 Jurassic2 Jumbo Instruct",
|
|
||||||
Self::AI21J2Mid => "AI21 Jurassic2 Mid",
|
|
||||||
Self::AI21J2MidV1 => "AI21 Jurassic2 Mid V1",
|
|
||||||
Self::AI21J2Ultra => "AI21 Jurassic2 Ultra",
|
|
||||||
Self::AI21J2UltraV1_8k => "AI21 Jurassic2 Ultra V1 8K",
|
|
||||||
Self::AI21J2UltraV1 => "AI21 Jurassic2 Ultra V1",
|
|
||||||
Self::AI21JambaInstructV1 => "AI21 Jamba Instruct",
|
|
||||||
Self::AI21Jamba15LargeV1 => "AI21 Jamba 1.5 Large",
|
|
||||||
Self::AI21Jamba15MiniV1 => "AI21 Jamba 1.5 Mini",
|
|
||||||
Self::CohereCommandTextV14_4k => "Cohere Command Text V14 4K",
|
|
||||||
Self::CohereCommandRV1 => "Cohere Command R V1",
|
|
||||||
Self::CohereCommandRPlusV1 => "Cohere Command R Plus V1",
|
|
||||||
Self::CohereCommandLightTextV14_4k => "Cohere Command Light Text V14 4K",
|
|
||||||
Self::MetaLlama38BInstructV1 => "Meta Llama 3 8B Instruct V1",
|
|
||||||
Self::MetaLlama370BInstructV1 => "Meta Llama 3 70B Instruct V1",
|
|
||||||
Self::MetaLlama318BInstructV1_128k => "Meta Llama 3 1.8B Instruct V1 128K",
|
|
||||||
Self::MetaLlama318BInstructV1 => "Meta Llama 3 1.8B Instruct V1",
|
|
||||||
Self::MetaLlama3170BInstructV1_128k => "Meta Llama 3 1 70B Instruct V1 128K",
|
|
||||||
Self::MetaLlama3170BInstructV1 => "Meta Llama 3 1 70B Instruct V1",
|
|
||||||
Self::MetaLlama3211BInstructV1 => "Meta Llama 3 2 11B Instruct V1",
|
|
||||||
Self::MetaLlama3290BInstructV1 => "Meta Llama 3 2 90B Instruct V1",
|
|
||||||
Self::MetaLlama321BInstructV1 => "Meta Llama 3 2 1B Instruct V1",
|
|
||||||
Self::MetaLlama323BInstructV1 => "Meta Llama 3 2 3B Instruct V1",
|
|
||||||
Self::MistralMistral7BInstructV0 => "Mistral 7B Instruct V0",
|
|
||||||
Self::MistralMixtral8x7BInstructV0 => "Mistral Mixtral 8x7B Instruct V0",
|
|
||||||
Self::MistralMistralLarge2402V1 => "Mistral Large 2402 V1",
|
|
||||||
Self::MistralMistralSmall2402V1 => "Mistral Small 2402 V1",
|
|
||||||
Self::Custom {
|
|
||||||
display_name, name, ..
|
|
||||||
} => display_name.as_deref().unwrap_or(name),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn max_token_count(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
Self::Claude3_5Sonnet
|
|
||||||
| Self::Claude3Opus
|
|
||||||
| Self::Claude3Sonnet
|
|
||||||
| Self::Claude3_5Haiku
|
|
||||||
| Self::Claude3_7Sonnet => 200_000,
|
|
||||||
Self::Custom { max_tokens, .. } => *max_tokens,
|
|
||||||
_ => 200_000,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn max_output_tokens(&self) -> u32 {
|
|
||||||
match self {
|
|
||||||
Self::Claude3Opus | Self::Claude3Sonnet | Self::Claude3_5Haiku => 4_096,
|
|
||||||
Self::Claude3_5Sonnet => 8_192,
|
|
||||||
Self::Custom {
|
|
||||||
max_output_tokens, ..
|
|
||||||
} => max_output_tokens.unwrap_or(4_096),
|
|
||||||
_ => 4_096,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn default_temperature(&self) -> f32 {
|
|
||||||
match self {
|
|
||||||
Self::Claude3_5Sonnet
|
|
||||||
| Self::Claude3Opus
|
|
||||||
| Self::Claude3Sonnet
|
|
||||||
| Self::Claude3_5Haiku
|
|
||||||
| Self::Claude3_7Sonnet => 1.0,
|
|
||||||
Self::Custom {
|
|
||||||
default_temperature,
|
|
||||||
..
|
|
||||||
} => default_temperature.unwrap_or(1.0),
|
|
||||||
_ => 1.0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -81,7 +81,7 @@ impl Render for Breadcrumbs {
|
|||||||
}
|
}
|
||||||
text_style.color = Color::Muted.color(cx);
|
text_style.color = Color::Muted.color(cx);
|
||||||
|
|
||||||
StyledText::new(segment.text.replace('\n', "⏎"))
|
StyledText::new(segment.text.replace('\n', ""))
|
||||||
.with_highlights(&text_style, segment.highlights.unwrap_or_default())
|
.with_highlights(&text_style, segment.highlights.unwrap_or_default())
|
||||||
.into_any()
|
.into_any()
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ use git2::{DiffLineType as GitDiffLineType, DiffOptions as GitOptions, Patch as
|
|||||||
use gpui::{App, AppContext as _, AsyncApp, Context, Entity, EventEmitter};
|
use gpui::{App, AppContext as _, AsyncApp, Context, Entity, EventEmitter};
|
||||||
use language::{Language, LanguageRegistry};
|
use language::{Language, LanguageRegistry};
|
||||||
use rope::Rope;
|
use rope::Rope;
|
||||||
use std::cmp::Ordering;
|
use std::{cmp, future::Future, iter, ops::Range, sync::Arc};
|
||||||
use std::{future::Future, iter, ops::Range, sync::Arc};
|
|
||||||
use sum_tree::SumTree;
|
use sum_tree::SumTree;
|
||||||
use text::ToOffset as _;
|
use text::ToOffset as _;
|
||||||
use text::{Anchor, Bias, BufferId, OffsetRangeExt, Point};
|
use text::{Anchor, Bias, BufferId, OffsetRangeExt, Point};
|
||||||
@@ -69,6 +68,7 @@ pub struct DiffHunk {
|
|||||||
/// The range in the buffer's diff base text to which this hunk corresponds.
|
/// The range in the buffer's diff base text to which this hunk corresponds.
|
||||||
pub diff_base_byte_range: Range<usize>,
|
pub diff_base_byte_range: Range<usize>,
|
||||||
pub secondary_status: DiffHunkSecondaryStatus,
|
pub secondary_status: DiffHunkSecondaryStatus,
|
||||||
|
pub secondary_diff_base_byte_range: Option<Range<usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// We store [`InternalDiffHunk`]s internally so we don't need to store the additional row range.
|
/// We store [`InternalDiffHunk`]s internally so we don't need to store the additional row range.
|
||||||
@@ -109,18 +109,13 @@ impl sum_tree::Summary for DiffHunkSummary {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl sum_tree::SeekTarget<'_, DiffHunkSummary, DiffHunkSummary> for Anchor {
|
impl<'a> sum_tree::SeekTarget<'a, DiffHunkSummary, DiffHunkSummary> for Anchor {
|
||||||
fn cmp(&self, cursor_location: &DiffHunkSummary, buffer: &text::BufferSnapshot) -> Ordering {
|
fn cmp(
|
||||||
if self
|
&self,
|
||||||
.cmp(&cursor_location.buffer_range.start, buffer)
|
cursor_location: &DiffHunkSummary,
|
||||||
.is_lt()
|
buffer: &text::BufferSnapshot,
|
||||||
{
|
) -> cmp::Ordering {
|
||||||
Ordering::Less
|
self.cmp(&cursor_location.buffer_range.end, buffer)
|
||||||
} else if self.cmp(&cursor_location.buffer_range.end, buffer).is_gt() {
|
|
||||||
Ordering::Greater
|
|
||||||
} else {
|
|
||||||
Ordering::Equal
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,110 +171,97 @@ impl BufferDiffSnapshot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_secondary_text_for_stage_or_unstage(
|
fn buffer_range_to_unchanged_diff_base_range(
|
||||||
&self,
|
&self,
|
||||||
stage: bool,
|
buffer_range: Range<Anchor>,
|
||||||
hunks: impl Iterator<Item = (Range<Anchor>, Range<usize>)>,
|
|
||||||
buffer: &text::BufferSnapshot,
|
buffer: &text::BufferSnapshot,
|
||||||
cx: &mut App,
|
) -> Option<Range<usize>> {
|
||||||
) -> Option<Rope> {
|
let mut hunks = self.inner.hunks.iter();
|
||||||
let secondary_diff = self.secondary_diff()?;
|
let mut start = 0;
|
||||||
let head_text = self.base_text().map(|text| text.as_rope().clone());
|
let mut pos = buffer.anchor_before(0);
|
||||||
let index_text = secondary_diff
|
while let Some(hunk) = hunks.next() {
|
||||||
.base_text()
|
assert!(buffer_range.start.cmp(&pos, buffer).is_ge());
|
||||||
.map(|text| text.as_rope().clone());
|
assert!(hunk.buffer_range.start.cmp(&pos, buffer).is_ge());
|
||||||
let (index_text, head_text) = match (index_text, head_text) {
|
if hunk
|
||||||
(Some(index_text), Some(head_text)) => (index_text, head_text),
|
.buffer_range
|
||||||
// file is deleted in both index and head
|
|
||||||
(None, None) => return None,
|
|
||||||
// file is deleted in index
|
|
||||||
(None, Some(head_text)) => {
|
|
||||||
return if stage {
|
|
||||||
Some(buffer.as_rope().clone())
|
|
||||||
} else {
|
|
||||||
Some(head_text)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// file exists in the index, but is deleted in head
|
|
||||||
(Some(_), None) => {
|
|
||||||
return if stage {
|
|
||||||
Some(buffer.as_rope().clone())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut secondary_cursor = secondary_diff.inner.hunks.cursor::<DiffHunkSummary>(buffer);
|
|
||||||
secondary_cursor.next(buffer);
|
|
||||||
let mut edits = Vec::new();
|
|
||||||
let mut prev_secondary_hunk_buffer_offset = 0;
|
|
||||||
let mut prev_secondary_hunk_base_text_offset = 0;
|
|
||||||
for (buffer_range, diff_base_byte_range) in hunks {
|
|
||||||
let skipped_hunks = secondary_cursor.slice(&buffer_range.start, Bias::Left, buffer);
|
|
||||||
|
|
||||||
if let Some(secondary_hunk) = skipped_hunks.last() {
|
|
||||||
prev_secondary_hunk_base_text_offset = secondary_hunk.diff_base_byte_range.end;
|
|
||||||
prev_secondary_hunk_buffer_offset =
|
|
||||||
secondary_hunk.buffer_range.end.to_offset(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut buffer_offset_range = buffer_range.to_offset(buffer);
|
|
||||||
let start_overshoot = buffer_offset_range.start - prev_secondary_hunk_buffer_offset;
|
|
||||||
let mut secondary_base_text_start =
|
|
||||||
prev_secondary_hunk_base_text_offset + start_overshoot;
|
|
||||||
|
|
||||||
while let Some(secondary_hunk) = secondary_cursor.item().filter(|item| {
|
|
||||||
item.buffer_range
|
|
||||||
.start
|
.start
|
||||||
.cmp(&buffer_range.end, buffer)
|
.cmp(&buffer_range.end, buffer)
|
||||||
.is_le()
|
.is_ge()
|
||||||
}) {
|
{
|
||||||
let secondary_hunk_offset_range = secondary_hunk.buffer_range.to_offset(buffer);
|
// target buffer range is contained in the unchanged stretch leading up to this next hunk,
|
||||||
prev_secondary_hunk_base_text_offset = secondary_hunk.diff_base_byte_range.end;
|
// so do a final adjustment based on that
|
||||||
prev_secondary_hunk_buffer_offset = secondary_hunk_offset_range.end;
|
break;
|
||||||
|
|
||||||
secondary_base_text_start =
|
|
||||||
secondary_base_text_start.min(secondary_hunk.diff_base_byte_range.start);
|
|
||||||
buffer_offset_range.start = buffer_offset_range
|
|
||||||
.start
|
|
||||||
.min(secondary_hunk_offset_range.start);
|
|
||||||
|
|
||||||
secondary_cursor.next(buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let end_overshoot = buffer_offset_range
|
// if the target buffer range intersects this hunk at all, no dice
|
||||||
.end
|
if buffer_range
|
||||||
.saturating_sub(prev_secondary_hunk_buffer_offset);
|
.start
|
||||||
let secondary_base_text_end = prev_secondary_hunk_base_text_offset + end_overshoot;
|
.cmp(&hunk.buffer_range.end, buffer)
|
||||||
|
.is_lt()
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
let secondary_base_text_range = secondary_base_text_start..secondary_base_text_end;
|
start += hunk.buffer_range.start.to_offset(buffer) - pos.to_offset(buffer);
|
||||||
buffer_offset_range.end = buffer_offset_range
|
start += hunk.diff_base_byte_range.end - hunk.diff_base_byte_range.start;
|
||||||
.end
|
pos = hunk.buffer_range.end;
|
||||||
.max(prev_secondary_hunk_buffer_offset);
|
}
|
||||||
|
start += buffer_range.start.to_offset(buffer) - pos.to_offset(buffer);
|
||||||
|
let end = start + buffer_range.end.to_offset(buffer) - buffer_range.start.to_offset(buffer);
|
||||||
|
Some(start..end)
|
||||||
|
}
|
||||||
|
|
||||||
let replacement_text = if stage {
|
pub fn secondary_edits_for_stage_or_unstage(
|
||||||
|
&self,
|
||||||
|
stage: bool,
|
||||||
|
hunks: impl Iterator<Item = (Range<usize>, Option<Range<usize>>, Range<Anchor>)>,
|
||||||
|
buffer: &text::BufferSnapshot,
|
||||||
|
) -> Vec<(Range<usize>, String)> {
|
||||||
|
let Some(secondary_diff) = self.secondary_diff() else {
|
||||||
|
log::debug!("no secondary diff");
|
||||||
|
return Vec::new();
|
||||||
|
};
|
||||||
|
let index_base = secondary_diff.base_text().map_or_else(
|
||||||
|
|| Rope::from(""),
|
||||||
|
|snapshot| snapshot.text.as_rope().clone(),
|
||||||
|
);
|
||||||
|
let head_base = self.base_text().map_or_else(
|
||||||
|
|| Rope::from(""),
|
||||||
|
|snapshot| snapshot.text.as_rope().clone(),
|
||||||
|
);
|
||||||
|
log::debug!("original: {:?}", index_base.to_string());
|
||||||
|
let mut edits = Vec::new();
|
||||||
|
for (diff_base_byte_range, secondary_diff_base_byte_range, buffer_range) in hunks {
|
||||||
|
let (index_byte_range, replacement_text) = if stage {
|
||||||
log::debug!("staging");
|
log::debug!("staging");
|
||||||
buffer
|
let mut replacement_text = String::new();
|
||||||
.text_for_range(buffer_offset_range)
|
let Some(index_byte_range) = secondary_diff_base_byte_range.clone() else {
|
||||||
.collect::<String>()
|
log::debug!("not a stageable hunk");
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
log::debug!("using {:?}", index_byte_range);
|
||||||
|
for chunk in buffer.text_for_range(buffer_range.clone()) {
|
||||||
|
replacement_text.push_str(chunk);
|
||||||
|
}
|
||||||
|
(index_byte_range, replacement_text)
|
||||||
} else {
|
} else {
|
||||||
log::debug!("unstaging");
|
log::debug!("unstaging");
|
||||||
head_text
|
let mut replacement_text = String::new();
|
||||||
.chunks_in_range(diff_base_byte_range.clone())
|
let Some(index_byte_range) = secondary_diff
|
||||||
.collect::<String>()
|
.buffer_range_to_unchanged_diff_base_range(buffer_range.clone(), &buffer)
|
||||||
|
else {
|
||||||
|
log::debug!("not an unstageable hunk");
|
||||||
|
continue;
|
||||||
};
|
};
|
||||||
edits.push((secondary_base_text_range, replacement_text));
|
for chunk in head_base.chunks_in_range(diff_base_byte_range.clone()) {
|
||||||
|
replacement_text.push_str(chunk);
|
||||||
}
|
}
|
||||||
|
(index_byte_range, replacement_text)
|
||||||
let buffer = cx.new(|cx| {
|
};
|
||||||
language::Buffer::local_normalized(index_text, text::LineEnding::default(), cx)
|
edits.push((index_byte_range, replacement_text));
|
||||||
});
|
}
|
||||||
let new_text = buffer.update(cx, |buffer, cx| {
|
log::debug!("edits: {edits:?}");
|
||||||
buffer.edit(edits, None, cx);
|
edits
|
||||||
buffer.as_rope().clone()
|
|
||||||
});
|
|
||||||
Some(new_text)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,12 +322,13 @@ impl BufferDiffInner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut secondary_status = DiffHunkSecondaryStatus::None;
|
let mut secondary_status = DiffHunkSecondaryStatus::None;
|
||||||
|
let mut secondary_diff_base_byte_range = None;
|
||||||
if let Some(secondary_cursor) = secondary_cursor.as_mut() {
|
if let Some(secondary_cursor) = secondary_cursor.as_mut() {
|
||||||
if start_anchor
|
if start_anchor
|
||||||
.cmp(&secondary_cursor.start().buffer_range.start, buffer)
|
.cmp(&secondary_cursor.start().buffer_range.start, buffer)
|
||||||
.is_gt()
|
.is_gt()
|
||||||
{
|
{
|
||||||
secondary_cursor.seek_forward(&start_anchor, Bias::Left, buffer);
|
secondary_cursor.seek_forward(&end_anchor, Bias::Left, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(secondary_hunk) = secondary_cursor.item() {
|
if let Some(secondary_hunk) = secondary_cursor.item() {
|
||||||
@@ -356,12 +339,12 @@ impl BufferDiffInner {
|
|||||||
}
|
}
|
||||||
if secondary_range == (start_point..end_point) {
|
if secondary_range == (start_point..end_point) {
|
||||||
secondary_status = DiffHunkSecondaryStatus::HasSecondaryHunk;
|
secondary_status = DiffHunkSecondaryStatus::HasSecondaryHunk;
|
||||||
|
secondary_diff_base_byte_range =
|
||||||
|
Some(secondary_hunk.diff_base_byte_range.clone());
|
||||||
} else if secondary_range.start <= end_point {
|
} else if secondary_range.start <= end_point {
|
||||||
secondary_status = DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk;
|
secondary_status = DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
log::debug!("no secondary cursor!!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Some(DiffHunk {
|
return Some(DiffHunk {
|
||||||
@@ -369,6 +352,7 @@ impl BufferDiffInner {
|
|||||||
diff_base_byte_range: start_base..end_base,
|
diff_base_byte_range: start_base..end_base,
|
||||||
buffer_range: start_anchor..end_anchor,
|
buffer_range: start_anchor..end_anchor,
|
||||||
secondary_status,
|
secondary_status,
|
||||||
|
secondary_diff_base_byte_range,
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -403,6 +387,7 @@ impl BufferDiffInner {
|
|||||||
buffer_range: hunk.buffer_range.clone(),
|
buffer_range: hunk.buffer_range.clone(),
|
||||||
// The secondary status is not used by callers of this method.
|
// The secondary status is not used by callers of this method.
|
||||||
secondary_status: DiffHunkSecondaryStatus::None,
|
secondary_status: DiffHunkSecondaryStatus::None,
|
||||||
|
secondary_diff_base_byte_range: None,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -423,12 +408,12 @@ impl BufferDiffInner {
|
|||||||
.start
|
.start
|
||||||
.cmp(&old_hunk.buffer_range.start, new_snapshot)
|
.cmp(&old_hunk.buffer_range.start, new_snapshot)
|
||||||
{
|
{
|
||||||
Ordering::Less => {
|
cmp::Ordering::Less => {
|
||||||
start.get_or_insert(new_hunk.buffer_range.start);
|
start.get_or_insert(new_hunk.buffer_range.start);
|
||||||
end.replace(new_hunk.buffer_range.end);
|
end.replace(new_hunk.buffer_range.end);
|
||||||
new_cursor.next(new_snapshot);
|
new_cursor.next(new_snapshot);
|
||||||
}
|
}
|
||||||
Ordering::Equal => {
|
cmp::Ordering::Equal => {
|
||||||
if new_hunk != old_hunk {
|
if new_hunk != old_hunk {
|
||||||
start.get_or_insert(new_hunk.buffer_range.start);
|
start.get_or_insert(new_hunk.buffer_range.start);
|
||||||
if old_hunk
|
if old_hunk
|
||||||
@@ -446,7 +431,7 @@ impl BufferDiffInner {
|
|||||||
new_cursor.next(new_snapshot);
|
new_cursor.next(new_snapshot);
|
||||||
old_cursor.next(new_snapshot);
|
old_cursor.next(new_snapshot);
|
||||||
}
|
}
|
||||||
Ordering::Greater => {
|
cmp::Ordering::Greater => {
|
||||||
start.get_or_insert(old_hunk.buffer_range.start);
|
start.get_or_insert(old_hunk.buffer_range.start);
|
||||||
end.replace(old_hunk.buffer_range.end);
|
end.replace(old_hunk.buffer_range.end);
|
||||||
old_cursor.next(new_snapshot);
|
old_cursor.next(new_snapshot);
|
||||||
@@ -1074,7 +1059,6 @@ mod tests {
|
|||||||
use rand::{rngs::StdRng, Rng as _};
|
use rand::{rngs::StdRng, Rng as _};
|
||||||
use text::{Buffer, BufferId, Rope};
|
use text::{Buffer, BufferId, Rope};
|
||||||
use unindent::Unindent as _;
|
use unindent::Unindent as _;
|
||||||
use util::test::marked_text_ranges;
|
|
||||||
|
|
||||||
#[ctor::ctor]
|
#[ctor::ctor]
|
||||||
fn init_logger() {
|
fn init_logger() {
|
||||||
@@ -1273,208 +1257,6 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
|
||||||
async fn test_stage_hunk(cx: &mut TestAppContext) {
|
|
||||||
struct Example {
|
|
||||||
name: &'static str,
|
|
||||||
head_text: String,
|
|
||||||
index_text: String,
|
|
||||||
buffer_marked_text: String,
|
|
||||||
final_index_text: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
let table = [
|
|
||||||
Example {
|
|
||||||
name: "uncommitted hunk straddles end of unstaged hunk",
|
|
||||||
head_text: "
|
|
||||||
one
|
|
||||||
two
|
|
||||||
three
|
|
||||||
four
|
|
||||||
five
|
|
||||||
"
|
|
||||||
.unindent(),
|
|
||||||
index_text: "
|
|
||||||
one
|
|
||||||
TWO_HUNDRED
|
|
||||||
three
|
|
||||||
FOUR_HUNDRED
|
|
||||||
five
|
|
||||||
"
|
|
||||||
.unindent(),
|
|
||||||
buffer_marked_text: "
|
|
||||||
ZERO
|
|
||||||
one
|
|
||||||
two
|
|
||||||
«THREE_HUNDRED
|
|
||||||
FOUR_HUNDRED»
|
|
||||||
five
|
|
||||||
SIX
|
|
||||||
"
|
|
||||||
.unindent(),
|
|
||||||
final_index_text: "
|
|
||||||
one
|
|
||||||
two
|
|
||||||
THREE_HUNDRED
|
|
||||||
FOUR_HUNDRED
|
|
||||||
five
|
|
||||||
"
|
|
||||||
.unindent(),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
name: "uncommitted hunk straddles start of unstaged hunk",
|
|
||||||
head_text: "
|
|
||||||
one
|
|
||||||
two
|
|
||||||
three
|
|
||||||
four
|
|
||||||
five
|
|
||||||
"
|
|
||||||
.unindent(),
|
|
||||||
index_text: "
|
|
||||||
one
|
|
||||||
TWO_HUNDRED
|
|
||||||
three
|
|
||||||
FOUR_HUNDRED
|
|
||||||
five
|
|
||||||
"
|
|
||||||
.unindent(),
|
|
||||||
buffer_marked_text: "
|
|
||||||
ZERO
|
|
||||||
one
|
|
||||||
«TWO_HUNDRED
|
|
||||||
THREE_HUNDRED»
|
|
||||||
four
|
|
||||||
five
|
|
||||||
SIX
|
|
||||||
"
|
|
||||||
.unindent(),
|
|
||||||
final_index_text: "
|
|
||||||
one
|
|
||||||
TWO_HUNDRED
|
|
||||||
THREE_HUNDRED
|
|
||||||
four
|
|
||||||
five
|
|
||||||
"
|
|
||||||
.unindent(),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
name: "uncommitted hunk strictly contains unstaged hunks",
|
|
||||||
head_text: "
|
|
||||||
one
|
|
||||||
two
|
|
||||||
three
|
|
||||||
four
|
|
||||||
five
|
|
||||||
six
|
|
||||||
seven
|
|
||||||
"
|
|
||||||
.unindent(),
|
|
||||||
index_text: "
|
|
||||||
one
|
|
||||||
TWO
|
|
||||||
THREE
|
|
||||||
FOUR
|
|
||||||
FIVE
|
|
||||||
SIX
|
|
||||||
seven
|
|
||||||
"
|
|
||||||
.unindent(),
|
|
||||||
buffer_marked_text: "
|
|
||||||
one
|
|
||||||
TWO
|
|
||||||
«THREE_HUNDRED
|
|
||||||
FOUR
|
|
||||||
FIVE_HUNDRED»
|
|
||||||
SIX
|
|
||||||
seven
|
|
||||||
"
|
|
||||||
.unindent(),
|
|
||||||
final_index_text: "
|
|
||||||
one
|
|
||||||
TWO
|
|
||||||
THREE_HUNDRED
|
|
||||||
FOUR
|
|
||||||
FIVE_HUNDRED
|
|
||||||
SIX
|
|
||||||
seven
|
|
||||||
"
|
|
||||||
.unindent(),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
name: "uncommitted deletion hunk",
|
|
||||||
head_text: "
|
|
||||||
one
|
|
||||||
two
|
|
||||||
three
|
|
||||||
four
|
|
||||||
five
|
|
||||||
"
|
|
||||||
.unindent(),
|
|
||||||
index_text: "
|
|
||||||
one
|
|
||||||
two
|
|
||||||
three
|
|
||||||
four
|
|
||||||
five
|
|
||||||
"
|
|
||||||
.unindent(),
|
|
||||||
buffer_marked_text: "
|
|
||||||
one
|
|
||||||
ˇfive
|
|
||||||
"
|
|
||||||
.unindent(),
|
|
||||||
final_index_text: "
|
|
||||||
one
|
|
||||||
five
|
|
||||||
"
|
|
||||||
.unindent(),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
for example in table {
|
|
||||||
let (buffer_text, ranges) = marked_text_ranges(&example.buffer_marked_text, false);
|
|
||||||
let buffer = Buffer::new(0, BufferId::new(1).unwrap(), buffer_text);
|
|
||||||
let uncommitted_diff =
|
|
||||||
BufferDiff::build_sync(buffer.clone(), example.head_text.clone(), cx);
|
|
||||||
let unstaged_diff =
|
|
||||||
BufferDiff::build_sync(buffer.clone(), example.index_text.clone(), cx);
|
|
||||||
let uncommitted_diff = BufferDiffSnapshot {
|
|
||||||
inner: uncommitted_diff,
|
|
||||||
secondary_diff: Some(Box::new(BufferDiffSnapshot {
|
|
||||||
inner: unstaged_diff,
|
|
||||||
is_single_insertion: false,
|
|
||||||
secondary_diff: None,
|
|
||||||
})),
|
|
||||||
is_single_insertion: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
let range = buffer.anchor_before(ranges[0].start)..buffer.anchor_before(ranges[0].end);
|
|
||||||
|
|
||||||
let new_index_text = cx
|
|
||||||
.update(|cx| {
|
|
||||||
uncommitted_diff.new_secondary_text_for_stage_or_unstage(
|
|
||||||
true,
|
|
||||||
uncommitted_diff
|
|
||||||
.hunks_intersecting_range(range, &buffer)
|
|
||||||
.map(|hunk| {
|
|
||||||
(hunk.buffer_range.clone(), hunk.diff_base_byte_range.clone())
|
|
||||||
}),
|
|
||||||
&buffer,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.unwrap()
|
|
||||||
.to_string();
|
|
||||||
pretty_assertions::assert_eq!(
|
|
||||||
new_index_text,
|
|
||||||
example.final_index_text,
|
|
||||||
"example: {}",
|
|
||||||
example.name
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_buffer_diff_compare(cx: &mut TestAppContext) {
|
async fn test_buffer_diff_compare(cx: &mut TestAppContext) {
|
||||||
let base_text = "
|
let base_text = "
|
||||||
@@ -1600,7 +1382,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test(iterations = 100)]
|
#[gpui::test(iterations = 100)]
|
||||||
async fn test_staging_and_unstaging_hunks(cx: &mut TestAppContext, mut rng: StdRng) {
|
async fn test_secondary_edits_for_stage_unstage(cx: &mut TestAppContext, mut rng: StdRng) {
|
||||||
fn gen_line(rng: &mut StdRng) -> String {
|
fn gen_line(rng: &mut StdRng) -> String {
|
||||||
if rng.gen_bool(0.2) {
|
if rng.gen_bool(0.2) {
|
||||||
"\n".to_owned()
|
"\n".to_owned()
|
||||||
@@ -1665,7 +1447,7 @@ mod tests {
|
|||||||
|
|
||||||
fn uncommitted_diff(
|
fn uncommitted_diff(
|
||||||
working_copy: &language::BufferSnapshot,
|
working_copy: &language::BufferSnapshot,
|
||||||
index_text: &Rope,
|
index_text: &Entity<language::Buffer>,
|
||||||
head_text: String,
|
head_text: String,
|
||||||
cx: &mut TestAppContext,
|
cx: &mut TestAppContext,
|
||||||
) -> BufferDiff {
|
) -> BufferDiff {
|
||||||
@@ -1674,7 +1456,7 @@ mod tests {
|
|||||||
buffer_id: working_copy.remote_id(),
|
buffer_id: working_copy.remote_id(),
|
||||||
inner: BufferDiff::build_sync(
|
inner: BufferDiff::build_sync(
|
||||||
working_copy.text.clone(),
|
working_copy.text.clone(),
|
||||||
index_text.to_string(),
|
index_text.read_with(cx, |index_text, _| index_text.text()),
|
||||||
cx,
|
cx,
|
||||||
),
|
),
|
||||||
secondary_diff: None,
|
secondary_diff: None,
|
||||||
@@ -1705,11 +1487,17 @@ mod tests {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
let working_copy = working_copy.read_with(cx, |working_copy, _| working_copy.snapshot());
|
let working_copy = working_copy.read_with(cx, |working_copy, _| working_copy.snapshot());
|
||||||
let mut index_text = if rng.gen() {
|
let index_text = cx.new(|cx| {
|
||||||
|
language::Buffer::local_normalized(
|
||||||
|
if rng.gen() {
|
||||||
Rope::from(head_text.as_str())
|
Rope::from(head_text.as_str())
|
||||||
} else {
|
} else {
|
||||||
working_copy.as_rope().clone()
|
working_copy.as_rope().clone()
|
||||||
};
|
},
|
||||||
|
text::LineEnding::default(),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
let mut diff = uncommitted_diff(&working_copy, &index_text, head_text.clone(), cx);
|
let mut diff = uncommitted_diff(&working_copy, &index_text, head_text.clone(), cx);
|
||||||
let mut hunks = cx.update(|cx| {
|
let mut hunks = cx.update(|cx| {
|
||||||
@@ -1723,29 +1511,37 @@ mod tests {
|
|||||||
for _ in 0..operations {
|
for _ in 0..operations {
|
||||||
let i = rng.gen_range(0..hunks.len());
|
let i = rng.gen_range(0..hunks.len());
|
||||||
let hunk = &mut hunks[i];
|
let hunk = &mut hunks[i];
|
||||||
let stage = match hunk.secondary_status {
|
let hunk_fields = (
|
||||||
DiffHunkSecondaryStatus::HasSecondaryHunk => {
|
hunk.diff_base_byte_range.clone(),
|
||||||
|
hunk.secondary_diff_base_byte_range.clone(),
|
||||||
|
hunk.buffer_range.clone(),
|
||||||
|
);
|
||||||
|
let stage = match (
|
||||||
|
hunk.secondary_status,
|
||||||
|
hunk.secondary_diff_base_byte_range.clone(),
|
||||||
|
) {
|
||||||
|
(DiffHunkSecondaryStatus::HasSecondaryHunk, Some(_)) => {
|
||||||
hunk.secondary_status = DiffHunkSecondaryStatus::None;
|
hunk.secondary_status = DiffHunkSecondaryStatus::None;
|
||||||
|
hunk.secondary_diff_base_byte_range = None;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
DiffHunkSecondaryStatus::None => {
|
(DiffHunkSecondaryStatus::None, None) => {
|
||||||
hunk.secondary_status = DiffHunkSecondaryStatus::HasSecondaryHunk;
|
hunk.secondary_status = DiffHunkSecondaryStatus::HasSecondaryHunk;
|
||||||
|
// We don't look at this, just notice whether it's Some or not.
|
||||||
|
hunk.secondary_diff_base_byte_range = Some(17..17);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let snapshot = cx.update(|cx| diff.snapshot(cx));
|
let snapshot = cx.update(|cx| diff.snapshot(cx));
|
||||||
index_text = cx.update(|cx| {
|
let edits = snapshot.secondary_edits_for_stage_or_unstage(
|
||||||
snapshot
|
|
||||||
.new_secondary_text_for_stage_or_unstage(
|
|
||||||
stage,
|
stage,
|
||||||
[(hunk.buffer_range.clone(), hunk.diff_base_byte_range.clone())]
|
[hunk_fields].into_iter(),
|
||||||
.into_iter(),
|
|
||||||
&working_copy,
|
&working_copy,
|
||||||
cx,
|
);
|
||||||
)
|
index_text.update(cx, |index_text, cx| {
|
||||||
.unwrap()
|
index_text.edit(edits, None, cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
diff = uncommitted_diff(&working_copy, &index_text, head_text.clone(), cx);
|
diff = uncommitted_diff(&working_copy, &index_text, head_text.clone(), cx);
|
||||||
@@ -1754,7 +1550,6 @@ mod tests {
|
|||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
});
|
});
|
||||||
assert_eq!(hunks.len(), found_hunks.len());
|
assert_eq!(hunks.len(), found_hunks.len());
|
||||||
|
|
||||||
for (expected_hunk, found_hunk) in hunks.iter().zip(&found_hunks) {
|
for (expected_hunk, found_hunk) in hunks.iter().zip(&found_hunks) {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expected_hunk.buffer_range.to_point(&working_copy),
|
expected_hunk.buffer_range.to_point(&working_copy),
|
||||||
@@ -1765,6 +1560,10 @@ mod tests {
|
|||||||
found_hunk.diff_base_byte_range
|
found_hunk.diff_base_byte_range
|
||||||
);
|
);
|
||||||
assert_eq!(expected_hunk.secondary_status, found_hunk.secondary_status);
|
assert_eq!(expected_hunk.secondary_status, found_hunk.secondary_status);
|
||||||
|
assert_eq!(
|
||||||
|
expected_hunk.secondary_diff_base_byte_range.is_some(),
|
||||||
|
found_hunk.secondary_diff_base_byte_range.is_some()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
hunks = found_hunks;
|
hunks = found_hunks;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ pub struct ChannelPathsInsertGuard<'a> {
|
|||||||
channels_by_id: &'a mut BTreeMap<ChannelId, Arc<Channel>>,
|
channels_by_id: &'a mut BTreeMap<ChannelId, Arc<Channel>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChannelPathsInsertGuard<'_> {
|
impl<'a> ChannelPathsInsertGuard<'a> {
|
||||||
pub fn insert(&mut self, channel_proto: proto::Channel) -> bool {
|
pub fn insert(&mut self, channel_proto: proto::Channel) -> bool {
|
||||||
let mut ret = false;
|
let mut ret = false;
|
||||||
let parent_path = channel_proto
|
let parent_path = channel_proto
|
||||||
@@ -86,7 +86,7 @@ impl ChannelPathsInsertGuard<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for ChannelPathsInsertGuard<'_> {
|
impl<'a> Drop for ChannelPathsInsertGuard<'a> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.channels_ordered.sort_by(|a, b| {
|
self.channels_ordered.sort_by(|a, b| {
|
||||||
let a = channel_path_sorting_key(*a, self.channels_by_id);
|
let a = channel_path_sorting_key(*a, self.channels_by_id);
|
||||||
|
|||||||
@@ -40,6 +40,3 @@ fork.workspace = true
|
|||||||
core-foundation.workspace = true
|
core-foundation.workspace = true
|
||||||
core-services = "0.2"
|
core-services = "0.2"
|
||||||
plist = "1.3"
|
plist = "1.3"
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.dependencies]
|
|
||||||
windows.workspace = true
|
|
||||||
|
|||||||
@@ -521,108 +521,30 @@ mod flatpak {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo("windows")
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
mod windows {
|
mod windows {
|
||||||
use anyhow::Context;
|
|
||||||
use release_channel::app_identifier;
|
|
||||||
use windows::{
|
|
||||||
core::HSTRING,
|
|
||||||
Win32::{
|
|
||||||
Foundation::{CloseHandle, GetLastError, ERROR_ALREADY_EXISTS, GENERIC_WRITE},
|
|
||||||
Storage::FileSystem::{
|
|
||||||
CreateFileW, WriteFile, FILE_FLAGS_AND_ATTRIBUTES, FILE_SHARE_MODE, OPEN_EXISTING,
|
|
||||||
},
|
|
||||||
System::Threading::CreateMutexW,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{Detect, InstalledApp};
|
use crate::{Detect, InstalledApp};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::Path;
|
||||||
use std::process::ExitStatus;
|
use std::process::ExitStatus;
|
||||||
|
|
||||||
fn check_single_instance() -> bool {
|
struct App;
|
||||||
let mutex = unsafe {
|
|
||||||
CreateMutexW(
|
|
||||||
None,
|
|
||||||
false,
|
|
||||||
&HSTRING::from(format!("{}-Instance-Mutex", app_identifier())),
|
|
||||||
)
|
|
||||||
.expect("Unable to create instance sync event")
|
|
||||||
};
|
|
||||||
let last_err = unsafe { GetLastError() };
|
|
||||||
let _ = unsafe { CloseHandle(mutex) };
|
|
||||||
last_err != ERROR_ALREADY_EXISTS
|
|
||||||
}
|
|
||||||
|
|
||||||
struct App(PathBuf);
|
|
||||||
|
|
||||||
impl InstalledApp for App {
|
impl InstalledApp for App {
|
||||||
fn zed_version_string(&self) -> String {
|
fn zed_version_string(&self) -> String {
|
||||||
format!(
|
unimplemented!()
|
||||||
"Zed {}{}{} – {}",
|
|
||||||
if *release_channel::RELEASE_CHANNEL_NAME == "stable" {
|
|
||||||
"".to_string()
|
|
||||||
} else {
|
|
||||||
format!("{} ", *release_channel::RELEASE_CHANNEL_NAME)
|
|
||||||
},
|
|
||||||
option_env!("RELEASE_VERSION").unwrap_or_default(),
|
|
||||||
match option_env!("ZED_COMMIT_SHA") {
|
|
||||||
Some(commit_sha) => format!(" {commit_sha} "),
|
|
||||||
None => "".to_string(),
|
|
||||||
},
|
|
||||||
self.0.display(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
fn launch(&self, _ipc_url: String) -> anyhow::Result<()> {
|
||||||
fn launch(&self, ipc_url: String) -> anyhow::Result<()> {
|
unimplemented!()
|
||||||
if check_single_instance() {
|
|
||||||
std::process::Command::new(self.0.clone())
|
|
||||||
.arg(ipc_url)
|
|
||||||
.spawn()?;
|
|
||||||
} else {
|
|
||||||
unsafe {
|
|
||||||
let pipe = CreateFileW(
|
|
||||||
&HSTRING::from(format!("\\\\.\\pipe\\{}-Named-Pipe", app_identifier())),
|
|
||||||
GENERIC_WRITE.0,
|
|
||||||
FILE_SHARE_MODE::default(),
|
|
||||||
None,
|
|
||||||
OPEN_EXISTING,
|
|
||||||
FILE_FLAGS_AND_ATTRIBUTES::default(),
|
|
||||||
None,
|
|
||||||
)?;
|
|
||||||
let message = ipc_url.as_bytes();
|
|
||||||
let mut bytes_written = 0;
|
|
||||||
WriteFile(pipe, Some(message), Some(&mut bytes_written), None)?;
|
|
||||||
CloseHandle(pipe)?;
|
|
||||||
}
|
}
|
||||||
}
|
fn run_foreground(&self, _ipc_url: String) -> io::Result<ExitStatus> {
|
||||||
Ok(())
|
unimplemented!()
|
||||||
}
|
|
||||||
|
|
||||||
fn run_foreground(&self, ipc_url: String) -> io::Result<ExitStatus> {
|
|
||||||
std::process::Command::new(self.0.clone())
|
|
||||||
.arg(ipc_url)
|
|
||||||
.arg("--foreground")
|
|
||||||
.spawn()?
|
|
||||||
.wait()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Detect {
|
impl Detect {
|
||||||
pub fn detect(path: Option<&Path>) -> anyhow::Result<impl InstalledApp> {
|
pub fn detect(_path: Option<&Path>) -> anyhow::Result<impl InstalledApp> {
|
||||||
let path = if let Some(path) = path {
|
Ok(App)
|
||||||
path.to_path_buf().canonicalize()?
|
|
||||||
} else {
|
|
||||||
std::env::current_exe()?
|
|
||||||
.parent()
|
|
||||||
.context("no parent path for cli")?
|
|
||||||
.parent()
|
|
||||||
.context("no parent path for cli folder")?
|
|
||||||
.join("Zed.exe")
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(App(path))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ async-tungstenite = { workspace = true, features = ["async-std", "async-tls"] }
|
|||||||
chrono = { workspace = true, features = ["serde"] }
|
chrono = { workspace = true, features = ["serde"] }
|
||||||
clock.workspace = true
|
clock.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
credentials_provider.workspace = true
|
|
||||||
feature_flags.workspace = true
|
feature_flags.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ use async_tungstenite::tungstenite::{
|
|||||||
};
|
};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use clock::SystemClock;
|
use clock::SystemClock;
|
||||||
use credentials_provider::CredentialsProvider;
|
|
||||||
use futures::{
|
use futures::{
|
||||||
channel::oneshot, future::BoxFuture, AsyncReadExt, FutureExt, SinkExt, Stream, StreamExt,
|
channel::oneshot, future::BoxFuture, AsyncReadExt, FutureExt, SinkExt, Stream, StreamExt,
|
||||||
TryFutureExt as _, TryStreamExt,
|
TryFutureExt as _, TryStreamExt,
|
||||||
@@ -58,6 +57,14 @@ static ZED_SERVER_URL: LazyLock<Option<String>> =
|
|||||||
LazyLock::new(|| std::env::var("ZED_SERVER_URL").ok());
|
LazyLock::new(|| std::env::var("ZED_SERVER_URL").ok());
|
||||||
static ZED_RPC_URL: LazyLock<Option<String>> = LazyLock::new(|| std::env::var("ZED_RPC_URL").ok());
|
static ZED_RPC_URL: LazyLock<Option<String>> = LazyLock::new(|| std::env::var("ZED_RPC_URL").ok());
|
||||||
|
|
||||||
|
/// An environment variable whose presence indicates that the development auth
|
||||||
|
/// provider should be used.
|
||||||
|
///
|
||||||
|
/// Only works in development. Setting this environment variable in other release
|
||||||
|
/// channels is a no-op.
|
||||||
|
pub static ZED_DEVELOPMENT_AUTH: LazyLock<bool> = LazyLock::new(|| {
|
||||||
|
std::env::var("ZED_DEVELOPMENT_AUTH").map_or(false, |value| !value.is_empty())
|
||||||
|
});
|
||||||
pub static IMPERSONATE_LOGIN: LazyLock<Option<String>> = LazyLock::new(|| {
|
pub static IMPERSONATE_LOGIN: LazyLock<Option<String>> = LazyLock::new(|| {
|
||||||
std::env::var("ZED_IMPERSONATE")
|
std::env::var("ZED_IMPERSONATE")
|
||||||
.ok()
|
.ok()
|
||||||
@@ -186,7 +193,7 @@ pub struct Client {
|
|||||||
peer: Arc<Peer>,
|
peer: Arc<Peer>,
|
||||||
http: Arc<HttpClientWithUrl>,
|
http: Arc<HttpClientWithUrl>,
|
||||||
telemetry: Arc<Telemetry>,
|
telemetry: Arc<Telemetry>,
|
||||||
credentials_provider: ClientCredentialsProvider,
|
credentials_provider: Arc<dyn CredentialsProvider + Send + Sync + 'static>,
|
||||||
state: RwLock<ClientState>,
|
state: RwLock<ClientState>,
|
||||||
handler_set: parking_lot::Mutex<ProtoMessageHandlerSet>,
|
handler_set: parking_lot::Mutex<ProtoMessageHandlerSet>,
|
||||||
|
|
||||||
@@ -297,46 +304,16 @@ impl Credentials {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ClientCredentialsProvider {
|
/// A provider for [`Credentials`].
|
||||||
provider: Arc<dyn CredentialsProvider>,
|
///
|
||||||
}
|
/// Used to abstract over reading and writing credentials to some form of
|
||||||
|
/// persistence (like the system keychain).
|
||||||
impl ClientCredentialsProvider {
|
trait CredentialsProvider {
|
||||||
pub fn new(cx: &App) -> Self {
|
|
||||||
Self {
|
|
||||||
provider: <dyn CredentialsProvider>::global(cx),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn server_url(&self, cx: &AsyncApp) -> Result<String> {
|
|
||||||
cx.update(|cx| ClientSettings::get_global(cx).server_url.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads the credentials from the provider.
|
/// Reads the credentials from the provider.
|
||||||
fn read_credentials<'a>(
|
fn read_credentials<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
cx: &'a AsyncApp,
|
cx: &'a AsyncApp,
|
||||||
) -> Pin<Box<dyn Future<Output = Option<Credentials>> + 'a>> {
|
) -> Pin<Box<dyn Future<Output = Option<Credentials>> + 'a>>;
|
||||||
async move {
|
|
||||||
if IMPERSONATE_LOGIN.is_some() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let server_url = self.server_url(cx).ok()?;
|
|
||||||
let (user_id, access_token) = self
|
|
||||||
.provider
|
|
||||||
.read_credentials(&server_url, cx)
|
|
||||||
.await
|
|
||||||
.log_err()
|
|
||||||
.flatten()?;
|
|
||||||
|
|
||||||
Some(Credentials {
|
|
||||||
user_id: user_id.parse().ok()?,
|
|
||||||
access_token: String::from_utf8(access_token).ok()?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
.boxed_local()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Writes the credentials to the provider.
|
/// Writes the credentials to the provider.
|
||||||
fn write_credentials<'a>(
|
fn write_credentials<'a>(
|
||||||
@@ -344,32 +321,13 @@ impl ClientCredentialsProvider {
|
|||||||
user_id: u64,
|
user_id: u64,
|
||||||
access_token: String,
|
access_token: String,
|
||||||
cx: &'a AsyncApp,
|
cx: &'a AsyncApp,
|
||||||
) -> Pin<Box<dyn Future<Output = Result<()>> + 'a>> {
|
) -> Pin<Box<dyn Future<Output = Result<()>> + 'a>>;
|
||||||
async move {
|
|
||||||
let server_url = self.server_url(cx)?;
|
|
||||||
self.provider
|
|
||||||
.write_credentials(
|
|
||||||
&server_url,
|
|
||||||
&user_id.to_string(),
|
|
||||||
access_token.as_bytes(),
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
.boxed_local()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deletes the credentials from the provider.
|
/// Deletes the credentials from the provider.
|
||||||
fn delete_credentials<'a>(
|
fn delete_credentials<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
cx: &'a AsyncApp,
|
cx: &'a AsyncApp,
|
||||||
) -> Pin<Box<dyn Future<Output = Result<()>> + 'a>> {
|
) -> Pin<Box<dyn Future<Output = Result<()>> + 'a>>;
|
||||||
async move {
|
|
||||||
let server_url = self.server_url(cx)?;
|
|
||||||
self.provider.delete_credentials(&server_url, cx).await
|
|
||||||
}
|
|
||||||
.boxed_local()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ClientState {
|
impl Default for ClientState {
|
||||||
@@ -526,12 +484,27 @@ impl Client {
|
|||||||
http: Arc<HttpClientWithUrl>,
|
http: Arc<HttpClientWithUrl>,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Arc<Self> {
|
) -> Arc<Self> {
|
||||||
|
let use_zed_development_auth = match ReleaseChannel::try_global(cx) {
|
||||||
|
Some(ReleaseChannel::Dev) => *ZED_DEVELOPMENT_AUTH,
|
||||||
|
Some(ReleaseChannel::Nightly | ReleaseChannel::Preview | ReleaseChannel::Stable)
|
||||||
|
| None => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let credentials_provider: Arc<dyn CredentialsProvider + Send + Sync + 'static> =
|
||||||
|
if use_zed_development_auth {
|
||||||
|
Arc::new(DevelopmentCredentialsProvider {
|
||||||
|
path: paths::config_dir().join("development_auth"),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Arc::new(KeychainCredentialsProvider)
|
||||||
|
};
|
||||||
|
|
||||||
Arc::new(Self {
|
Arc::new(Self {
|
||||||
id: AtomicU64::new(0),
|
id: AtomicU64::new(0),
|
||||||
peer: Peer::new(0),
|
peer: Peer::new(0),
|
||||||
telemetry: Telemetry::new(clock, http.clone(), cx),
|
telemetry: Telemetry::new(clock, http.clone(), cx),
|
||||||
http,
|
http,
|
||||||
credentials_provider: ClientCredentialsProvider::new(cx),
|
credentials_provider,
|
||||||
state: Default::default(),
|
state: Default::default(),
|
||||||
handler_set: Default::default(),
|
handler_set: Default::default(),
|
||||||
|
|
||||||
@@ -870,6 +843,7 @@ impl Client {
|
|||||||
self.state.write().credentials = Some(credentials.clone());
|
self.state.write().credentials = Some(credentials.clone());
|
||||||
if !read_from_provider && IMPERSONATE_LOGIN.is_none() {
|
if !read_from_provider && IMPERSONATE_LOGIN.is_none() {
|
||||||
self.credentials_provider.write_credentials(credentials.user_id, credentials.access_token, cx).await.log_err();
|
self.credentials_provider.write_credentials(credentials.user_id, credentials.access_token, cx).await.log_err();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
futures::select_biased! {
|
futures::select_biased! {
|
||||||
@@ -1614,6 +1588,130 @@ impl ProtoClient for Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct DevelopmentCredentials {
|
||||||
|
user_id: u64,
|
||||||
|
access_token: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A credentials provider that stores credentials in a local file.
|
||||||
|
///
|
||||||
|
/// This MUST only be used in development, as this is not a secure way of storing
|
||||||
|
/// credentials on user machines.
|
||||||
|
///
|
||||||
|
/// Its existence is purely to work around the annoyance of having to constantly
|
||||||
|
/// re-allow access to the system keychain when developing Zed.
|
||||||
|
struct DevelopmentCredentialsProvider {
|
||||||
|
path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CredentialsProvider for DevelopmentCredentialsProvider {
|
||||||
|
fn read_credentials<'a>(
|
||||||
|
&'a self,
|
||||||
|
_cx: &'a AsyncApp,
|
||||||
|
) -> Pin<Box<dyn Future<Output = Option<Credentials>> + 'a>> {
|
||||||
|
async move {
|
||||||
|
if IMPERSONATE_LOGIN.is_some() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let json = std::fs::read(&self.path).log_err()?;
|
||||||
|
|
||||||
|
let credentials: DevelopmentCredentials = serde_json::from_slice(&json).log_err()?;
|
||||||
|
|
||||||
|
Some(Credentials {
|
||||||
|
user_id: credentials.user_id,
|
||||||
|
access_token: credentials.access_token,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
.boxed_local()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_credentials<'a>(
|
||||||
|
&'a self,
|
||||||
|
user_id: u64,
|
||||||
|
access_token: String,
|
||||||
|
_cx: &'a AsyncApp,
|
||||||
|
) -> Pin<Box<dyn Future<Output = Result<()>> + 'a>> {
|
||||||
|
async move {
|
||||||
|
let json = serde_json::to_string(&DevelopmentCredentials {
|
||||||
|
user_id,
|
||||||
|
access_token,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
std::fs::write(&self.path, json)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
.boxed_local()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delete_credentials<'a>(
|
||||||
|
&'a self,
|
||||||
|
_cx: &'a AsyncApp,
|
||||||
|
) -> Pin<Box<dyn Future<Output = Result<()>> + 'a>> {
|
||||||
|
async move { Ok(std::fs::remove_file(&self.path)?) }.boxed_local()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A credentials provider that stores credentials in the system keychain.
|
||||||
|
struct KeychainCredentialsProvider;
|
||||||
|
|
||||||
|
impl CredentialsProvider for KeychainCredentialsProvider {
|
||||||
|
fn read_credentials<'a>(
|
||||||
|
&'a self,
|
||||||
|
cx: &'a AsyncApp,
|
||||||
|
) -> Pin<Box<dyn Future<Output = Option<Credentials>> + 'a>> {
|
||||||
|
async move {
|
||||||
|
if IMPERSONATE_LOGIN.is_some() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (user_id, access_token) = cx
|
||||||
|
.update(|cx| cx.read_credentials(&ClientSettings::get_global(cx).server_url))
|
||||||
|
.log_err()?
|
||||||
|
.await
|
||||||
|
.log_err()??;
|
||||||
|
|
||||||
|
Some(Credentials {
|
||||||
|
user_id: user_id.parse().ok()?,
|
||||||
|
access_token: String::from_utf8(access_token).ok()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
.boxed_local()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_credentials<'a>(
|
||||||
|
&'a self,
|
||||||
|
user_id: u64,
|
||||||
|
access_token: String,
|
||||||
|
cx: &'a AsyncApp,
|
||||||
|
) -> Pin<Box<dyn Future<Output = Result<()>> + 'a>> {
|
||||||
|
async move {
|
||||||
|
cx.update(move |cx| {
|
||||||
|
cx.write_credentials(
|
||||||
|
&ClientSettings::get_global(cx).server_url,
|
||||||
|
&user_id.to_string(),
|
||||||
|
access_token.as_bytes(),
|
||||||
|
)
|
||||||
|
})?
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
.boxed_local()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delete_credentials<'a>(
|
||||||
|
&'a self,
|
||||||
|
cx: &'a AsyncApp,
|
||||||
|
) -> Pin<Box<dyn Future<Output = Result<()>> + 'a>> {
|
||||||
|
async move {
|
||||||
|
cx.update(move |cx| cx.delete_credentials(&ClientSettings::get_global(cx).server_url))?
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
.boxed_local()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// prefix for the zed:// url scheme
|
/// prefix for the zed:// url scheme
|
||||||
pub const ZED_URL_SCHEME: &str = "zed";
|
pub const ZED_URL_SCHEME: &str = "zed";
|
||||||
|
|
||||||
|
|||||||
@@ -97,7 +97,6 @@ extension.workspace = true
|
|||||||
file_finder.workspace = true
|
file_finder.workspace = true
|
||||||
fs = { workspace = true, features = ["test-support"] }
|
fs = { workspace = true, features = ["test-support"] }
|
||||||
git = { workspace = true, features = ["test-support"] }
|
git = { workspace = true, features = ["test-support"] }
|
||||||
git_ui = { workspace = true, features = ["test-support"] }
|
|
||||||
git_hosting_providers.workspace = true
|
git_hosting_providers.workspace = true
|
||||||
gpui = { workspace = true, features = ["test-support"] }
|
gpui = { workspace = true, features = ["test-support"] }
|
||||||
hyper.workspace = true
|
hyper.workspace = true
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ impl Database {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_known_extension_versions(&self) -> Result<HashMap<String, Vec<String>>> {
|
pub async fn get_known_extension_versions<'a>(&self) -> Result<HashMap<String, Vec<String>>> {
|
||||||
self.transaction(|tx| async move {
|
self.transaction(|tx| async move {
|
||||||
let mut extension_external_ids_by_id = HashMap::default();
|
let mut extension_external_ids_by_id = HashMap::default();
|
||||||
|
|
||||||
|
|||||||
@@ -256,7 +256,6 @@ async fn perform_completion(
|
|||||||
// so that users can use the new version, without having to update Zed.
|
// so that users can use the new version, without having to update Zed.
|
||||||
request.model = match model.as_str() {
|
request.model = match model.as_str() {
|
||||||
"claude-3-5-sonnet" => anthropic::Model::Claude3_5Sonnet.id().to_string(),
|
"claude-3-5-sonnet" => anthropic::Model::Claude3_5Sonnet.id().to_string(),
|
||||||
"claude-3-7-sonnet" => anthropic::Model::Claude3_7Sonnet.id().to_string(),
|
|
||||||
"claude-3-opus" => anthropic::Model::Claude3Opus.id().to_string(),
|
"claude-3-opus" => anthropic::Model::Claude3Opus.id().to_string(),
|
||||||
"claude-3-haiku" => anthropic::Model::Claude3Haiku.id().to_string(),
|
"claude-3-haiku" => anthropic::Model::Claude3Haiku.id().to_string(),
|
||||||
"claude-3-sonnet" => anthropic::Model::Claude3Sonnet.id().to_string(),
|
"claude-3-sonnet" => anthropic::Model::Claude3Sonnet.id().to_string(),
|
||||||
@@ -451,15 +450,19 @@ async fn check_usage_limit(
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let user_id = UserId::from_proto(claims.user_id);
|
|
||||||
let model = state.db.model(provider, model_name)?;
|
let model = state.db.model(provider, model_name)?;
|
||||||
|
let usage = state
|
||||||
|
.db
|
||||||
|
.get_usage(
|
||||||
|
UserId::from_proto(claims.user_id),
|
||||||
|
provider,
|
||||||
|
model_name,
|
||||||
|
Utc::now(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
let free_tier = claims.free_tier_monthly_spending_limit();
|
let free_tier = claims.free_tier_monthly_spending_limit();
|
||||||
|
|
||||||
let spending_this_month = state
|
if usage.spending_this_month >= free_tier {
|
||||||
.db
|
|
||||||
.get_user_spending_for_month(user_id, Utc::now())
|
|
||||||
.await?;
|
|
||||||
if spending_this_month >= free_tier {
|
|
||||||
if !claims.has_llm_subscription {
|
if !claims.has_llm_subscription {
|
||||||
return Err(Error::http(
|
return Err(Error::http(
|
||||||
StatusCode::PAYMENT_REQUIRED,
|
StatusCode::PAYMENT_REQUIRED,
|
||||||
@@ -467,8 +470,7 @@ async fn check_usage_limit(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let monthly_spend = spending_this_month.saturating_sub(free_tier);
|
if (usage.spending_this_month - free_tier) >= Cents(claims.max_monthly_spend_in_cents) {
|
||||||
if monthly_spend >= Cents(claims.max_monthly_spend_in_cents) {
|
|
||||||
return Err(Error::Http(
|
return Err(Error::Http(
|
||||||
StatusCode::FORBIDDEN,
|
StatusCode::FORBIDDEN,
|
||||||
"Maximum spending limit reached for this month.".to_string(),
|
"Maximum spending limit reached for this month.".to_string(),
|
||||||
@@ -493,11 +495,6 @@ async fn check_usage_limit(
|
|||||||
model.max_tokens_per_minute as usize / users_in_recent_minutes;
|
model.max_tokens_per_minute as usize / users_in_recent_minutes;
|
||||||
let per_user_max_tokens_per_day = model.max_tokens_per_day as usize / users_in_recent_days;
|
let per_user_max_tokens_per_day = model.max_tokens_per_day as usize / users_in_recent_days;
|
||||||
|
|
||||||
let usage = state
|
|
||||||
.db
|
|
||||||
.get_usage(user_id, provider, model_name, Utc::now())
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let checks = [
|
let checks = [
|
||||||
(
|
(
|
||||||
usage.requests_this_minute,
|
usage.requests_this_minute,
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ fn authorize_access_to_model(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if provider == LanguageModelProvider::Anthropic {
|
if provider == LanguageModelProvider::Anthropic {
|
||||||
if model == "claude-3-5-sonnet" || model == "claude-3-7-sonnet" {
|
if model == "claude-3-5-sonnet" {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -393,12 +393,10 @@ impl Server {
|
|||||||
.add_request_handler(forward_mutating_project_request::<proto::CreateContext>)
|
.add_request_handler(forward_mutating_project_request::<proto::CreateContext>)
|
||||||
.add_request_handler(forward_mutating_project_request::<proto::SynchronizeContexts>)
|
.add_request_handler(forward_mutating_project_request::<proto::SynchronizeContexts>)
|
||||||
.add_request_handler(forward_mutating_project_request::<proto::Push>)
|
.add_request_handler(forward_mutating_project_request::<proto::Push>)
|
||||||
.add_request_handler(forward_mutating_project_request::<proto::Pull>)
|
.add_request_handler(forward_mutating_project_request::<proto::ForcePush>)
|
||||||
.add_request_handler(forward_mutating_project_request::<proto::Fetch>)
|
|
||||||
.add_request_handler(forward_mutating_project_request::<proto::Stage>)
|
.add_request_handler(forward_mutating_project_request::<proto::Stage>)
|
||||||
.add_request_handler(forward_mutating_project_request::<proto::Unstage>)
|
.add_request_handler(forward_mutating_project_request::<proto::Unstage>)
|
||||||
.add_request_handler(forward_mutating_project_request::<proto::Commit>)
|
.add_request_handler(forward_mutating_project_request::<proto::Commit>)
|
||||||
.add_request_handler(forward_read_only_project_request::<proto::GetRemotes>)
|
|
||||||
.add_request_handler(forward_read_only_project_request::<proto::GitShow>)
|
.add_request_handler(forward_read_only_project_request::<proto::GitShow>)
|
||||||
.add_request_handler(forward_read_only_project_request::<proto::GitReset>)
|
.add_request_handler(forward_read_only_project_request::<proto::GitReset>)
|
||||||
.add_request_handler(forward_read_only_project_request::<proto::GitCheckoutFiles>)
|
.add_request_handler(forward_read_only_project_request::<proto::GitCheckoutFiles>)
|
||||||
@@ -975,7 +973,7 @@ impl Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for ConnectionPoolGuard<'_> {
|
impl<'a> Deref for ConnectionPoolGuard<'a> {
|
||||||
type Target = ConnectionPool;
|
type Target = ConnectionPool;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
@@ -983,13 +981,13 @@ impl Deref for ConnectionPoolGuard<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DerefMut for ConnectionPoolGuard<'_> {
|
impl<'a> DerefMut for ConnectionPoolGuard<'a> {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
&mut self.guard
|
&mut self.guard
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for ConnectionPoolGuard<'_> {
|
impl<'a> Drop for ConnectionPoolGuard<'a> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
self.check_invariants();
|
self.check_invariants();
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ mod channel_message_tests;
|
|||||||
mod channel_tests;
|
mod channel_tests;
|
||||||
mod editor_tests;
|
mod editor_tests;
|
||||||
mod following_tests;
|
mod following_tests;
|
||||||
mod git_tests;
|
|
||||||
mod integration_tests;
|
mod integration_tests;
|
||||||
mod notification_tests;
|
mod notification_tests;
|
||||||
mod random_channel_buffer_tests;
|
mod random_channel_buffer_tests;
|
||||||
|
|||||||
@@ -1537,7 +1537,6 @@ async fn test_mutual_editor_inlay_hint_cache_update(
|
|||||||
show_parameter_hints: false,
|
show_parameter_hints: false,
|
||||||
show_other_hints: true,
|
show_other_hints: true,
|
||||||
show_background: false,
|
show_background: false,
|
||||||
toggle_on_modifiers_press: None,
|
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1553,7 +1552,6 @@ async fn test_mutual_editor_inlay_hint_cache_update(
|
|||||||
show_parameter_hints: false,
|
show_parameter_hints: false,
|
||||||
show_other_hints: true,
|
show_other_hints: true,
|
||||||
show_background: false,
|
show_background: false,
|
||||||
toggle_on_modifiers_press: None,
|
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1772,7 +1770,6 @@ async fn test_inlay_hint_refresh_is_forwarded(
|
|||||||
show_parameter_hints: false,
|
show_parameter_hints: false,
|
||||||
show_other_hints: false,
|
show_other_hints: false,
|
||||||
show_background: false,
|
show_background: false,
|
||||||
toggle_on_modifiers_press: None,
|
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1788,7 +1785,6 @@ async fn test_inlay_hint_refresh_is_forwarded(
|
|||||||
show_parameter_hints: true,
|
show_parameter_hints: true,
|
||||||
show_other_hints: true,
|
show_other_hints: true,
|
||||||
show_background: false,
|
show_background: false,
|
||||||
toggle_on_modifiers_press: None,
|
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,130 +0,0 @@
|
|||||||
use std::{
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use call::ActiveCall;
|
|
||||||
use git::status::{FileStatus, StatusCode, TrackedStatus};
|
|
||||||
use git_ui::project_diff::ProjectDiff;
|
|
||||||
use gpui::{TestAppContext, VisualTestContext};
|
|
||||||
use project::ProjectPath;
|
|
||||||
use serde_json::json;
|
|
||||||
use workspace::Workspace;
|
|
||||||
|
|
||||||
//
|
|
||||||
use crate::tests::TestServer;
|
|
||||||
|
|
||||||
#[gpui::test]
|
|
||||||
async fn test_project_diff(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
|
|
||||||
let mut server = TestServer::start(cx_a.background_executor.clone()).await;
|
|
||||||
let client_a = server.create_client(cx_a, "user_a").await;
|
|
||||||
let client_b = server.create_client(cx_b, "user_b").await;
|
|
||||||
cx_a.set_name("cx_a");
|
|
||||||
cx_b.set_name("cx_b");
|
|
||||||
|
|
||||||
server
|
|
||||||
.create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
|
|
||||||
.await;
|
|
||||||
|
|
||||||
client_a
|
|
||||||
.fs()
|
|
||||||
.insert_tree(
|
|
||||||
"/a",
|
|
||||||
json!({
|
|
||||||
".git": {},
|
|
||||||
"changed.txt": "after\n",
|
|
||||||
"unchanged.txt": "unchanged\n",
|
|
||||||
"created.txt": "created\n",
|
|
||||||
"secret.pem": "secret-changed\n",
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
client_a.fs().set_git_content_for_repo(
|
|
||||||
Path::new("/a/.git"),
|
|
||||||
&[
|
|
||||||
("changed.txt".into(), "before\n".to_string(), None),
|
|
||||||
("unchanged.txt".into(), "unchanged\n".to_string(), None),
|
|
||||||
("deleted.txt".into(), "deleted\n".to_string(), None),
|
|
||||||
("secret.pem".into(), "shh\n".to_string(), None),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
|
|
||||||
let active_call_a = cx_a.read(ActiveCall::global);
|
|
||||||
let project_id = active_call_a
|
|
||||||
.update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
cx_b.update(editor::init);
|
|
||||||
cx_b.update(git_ui::init);
|
|
||||||
let project_b = client_b.join_remote_project(project_id, cx_b).await;
|
|
||||||
let workspace_b = cx_b.add_window(|window, cx| {
|
|
||||||
Workspace::new(
|
|
||||||
None,
|
|
||||||
project_b.clone(),
|
|
||||||
client_b.app_state.clone(),
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
let cx_b = &mut VisualTestContext::from_window(*workspace_b, cx_b);
|
|
||||||
let workspace_b = workspace_b.root(cx_b).unwrap();
|
|
||||||
|
|
||||||
cx_b.update(|window, cx| {
|
|
||||||
window
|
|
||||||
.focused(cx)
|
|
||||||
.unwrap()
|
|
||||||
.dispatch_action(&git_ui::project_diff::Diff, window, cx)
|
|
||||||
});
|
|
||||||
let diff = workspace_b.update(cx_b, |workspace, cx| {
|
|
||||||
workspace.active_item(cx).unwrap().act_as::<ProjectDiff>(cx)
|
|
||||||
});
|
|
||||||
let diff = diff.unwrap();
|
|
||||||
cx_b.run_until_parked();
|
|
||||||
|
|
||||||
diff.update(cx_b, |diff, cx| {
|
|
||||||
assert_eq!(
|
|
||||||
diff.excerpt_paths(cx),
|
|
||||||
vec!["changed.txt", "deleted.txt", "created.txt"]
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
client_a
|
|
||||||
.fs()
|
|
||||||
.insert_tree(
|
|
||||||
"/a",
|
|
||||||
json!({
|
|
||||||
".git": {},
|
|
||||||
"changed.txt": "before\n",
|
|
||||||
"unchanged.txt": "changed\n",
|
|
||||||
"created.txt": "created\n",
|
|
||||||
"secret.pem": "secret-changed\n",
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
client_a.fs().recalculate_git_status(Path::new("/a/.git"));
|
|
||||||
cx_b.run_until_parked();
|
|
||||||
|
|
||||||
project_b.update(cx_b, |project, cx| {
|
|
||||||
let project_path = ProjectPath {
|
|
||||||
worktree_id,
|
|
||||||
path: Arc::from(PathBuf::from("unchanged.txt")),
|
|
||||||
};
|
|
||||||
let status = project.project_path_git_status(&project_path, cx);
|
|
||||||
assert_eq!(
|
|
||||||
status.unwrap(),
|
|
||||||
FileStatus::Tracked(TrackedStatus {
|
|
||||||
worktree_status: StatusCode::Modified,
|
|
||||||
index_status: StatusCode::Unmodified,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
diff.update(cx_b, |diff, cx| {
|
|
||||||
assert_eq!(
|
|
||||||
diff.excerpt_paths(cx),
|
|
||||||
vec!["deleted.txt", "unchanged.txt", "created.txt"]
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -6354,7 +6354,7 @@ async fn test_preview_tabs(cx: &mut TestAppContext) {
|
|||||||
// Open item 1 as preview
|
// Open item 1 as preview
|
||||||
workspace
|
workspace
|
||||||
.update_in(cx, |workspace, window, cx| {
|
.update_in(cx, |workspace, window, cx| {
|
||||||
workspace.open_path_preview(path_1.clone(), None, true, true, true, window, cx)
|
workspace.open_path_preview(path_1.clone(), None, true, true, window, cx)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -6375,7 +6375,7 @@ async fn test_preview_tabs(cx: &mut TestAppContext) {
|
|||||||
// Open item 2 as preview
|
// Open item 2 as preview
|
||||||
workspace
|
workspace
|
||||||
.update_in(cx, |workspace, window, cx| {
|
.update_in(cx, |workspace, window, cx| {
|
||||||
workspace.open_path_preview(path_2.clone(), None, true, true, true, window, cx)
|
workspace.open_path_preview(path_2.clone(), None, true, true, window, cx)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -6507,7 +6507,7 @@ async fn test_preview_tabs(cx: &mut TestAppContext) {
|
|||||||
// Open item 2 as preview in right pane
|
// Open item 2 as preview in right pane
|
||||||
workspace
|
workspace
|
||||||
.update_in(cx, |workspace, window, cx| {
|
.update_in(cx, |workspace, window, cx| {
|
||||||
workspace.open_path_preview(path_2.clone(), None, true, true, true, window, cx)
|
workspace.open_path_preview(path_2.clone(), None, true, true, window, cx)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -6545,7 +6545,7 @@ async fn test_preview_tabs(cx: &mut TestAppContext) {
|
|||||||
// Open item 2 as preview in left pane
|
// Open item 2 as preview in left pane
|
||||||
workspace
|
workspace
|
||||||
.update_in(cx, |workspace, window, cx| {
|
.update_in(cx, |workspace, window, cx| {
|
||||||
workspace.open_path_preview(path_2.clone(), None, true, true, true, window, cx)
|
workspace.open_path_preview(path_2.clone(), None, true, true, window, cx)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|||||||
@@ -2458,8 +2458,8 @@ impl CollabPanel {
|
|||||||
Avatar::new(contact.user.avatar_uri.clone())
|
Avatar::new(contact.user.avatar_uri.clone())
|
||||||
.indicator::<AvatarAvailabilityIndicator>(if online {
|
.indicator::<AvatarAvailabilityIndicator>(if online {
|
||||||
Some(AvatarAvailabilityIndicator::new(match busy {
|
Some(AvatarAvailabilityIndicator::new(match busy {
|
||||||
true => ui::CollaboratorAvailability::Busy,
|
true => ui::Availability::Busy,
|
||||||
false => ui::CollaboratorAvailability::Free,
|
false => ui::Availability::Free,
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ impl<'de> Deserialize<'de> for ChatPanelButton {
|
|||||||
{
|
{
|
||||||
struct Visitor;
|
struct Visitor;
|
||||||
|
|
||||||
impl serde::de::Visitor<'_> for Visitor {
|
impl<'de> serde::de::Visitor<'de> for Visitor {
|
||||||
type Value = ChatPanelButton;
|
type Value = ChatPanelButton;
|
||||||
|
|
||||||
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ path = "src/component.rs"
|
|||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
linkme.workspace = true
|
linkme.workspace = true
|
||||||
|
once_cell.workspace = true
|
||||||
parking_lot.workspace = true
|
parking_lot.workspace = true
|
||||||
theme.workspace = true
|
theme.workspace = true
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::sync::LazyLock;
|
|
||||||
|
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use gpui::{div, prelude::*, px, AnyElement, App, IntoElement, RenderOnce, SharedString, Window};
|
use gpui::{div, prelude::*, px, AnyElement, App, IntoElement, RenderOnce, SharedString, Window};
|
||||||
use linkme::distributed_slice;
|
use linkme::distributed_slice;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use theme::ActiveTheme;
|
use theme::ActiveTheme;
|
||||||
|
|
||||||
@@ -27,8 +27,8 @@ pub static __ALL_COMPONENTS: [fn()] = [..];
|
|||||||
#[distributed_slice]
|
#[distributed_slice]
|
||||||
pub static __ALL_PREVIEWS: [fn()] = [..];
|
pub static __ALL_PREVIEWS: [fn()] = [..];
|
||||||
|
|
||||||
pub static COMPONENT_DATA: LazyLock<RwLock<ComponentRegistry>> =
|
pub static COMPONENT_DATA: Lazy<RwLock<ComponentRegistry>> =
|
||||||
LazyLock::new(|| RwLock::new(ComponentRegistry::new()));
|
Lazy::new(|| RwLock::new(ComponentRegistry::new()));
|
||||||
|
|
||||||
pub struct ComponentRegistry {
|
pub struct ComponentRegistry {
|
||||||
components: Vec<(Option<&'static str>, &'static str, Option<&'static str>)>,
|
components: Vec<(Option<&'static str>, &'static str, Option<&'static str>)>,
|
||||||
@@ -173,9 +173,9 @@ pub enum ExampleLabelSide {
|
|||||||
Left,
|
Left,
|
||||||
/// Right side
|
/// Right side
|
||||||
Right,
|
Right,
|
||||||
|
#[default]
|
||||||
/// Top side
|
/// Top side
|
||||||
Top,
|
Top,
|
||||||
#[default]
|
|
||||||
/// Bottom side
|
/// Bottom side
|
||||||
Bottom,
|
Bottom,
|
||||||
}
|
}
|
||||||
@@ -200,10 +200,10 @@ impl RenderOnce for ComponentExample {
|
|||||||
ExampleLabelSide::Top => base.flex_col_reverse(),
|
ExampleLabelSide::Top => base.flex_col_reverse(),
|
||||||
};
|
};
|
||||||
|
|
||||||
base.gap_2()
|
base.gap_1()
|
||||||
.p_2()
|
.p_2()
|
||||||
.text_size(px(10.))
|
.text_sm()
|
||||||
.text_color(cx.theme().colors().text_muted)
|
.text_color(cx.theme().colors().text)
|
||||||
.when(self.grow, |this| this.flex_1())
|
.when(self.grow, |this| this.flex_1())
|
||||||
.child(self.element)
|
.child(self.element)
|
||||||
.child(self.variant_name)
|
.child(self.variant_name)
|
||||||
@@ -245,13 +245,12 @@ impl RenderOnce for ComponentExampleGroup {
|
|||||||
.text_color(cx.theme().colors().text_muted)
|
.text_color(cx.theme().colors().text_muted)
|
||||||
.when(self.grow, |this| this.w_full().flex_1())
|
.when(self.grow, |this| this.w_full().flex_1())
|
||||||
.when_some(self.title, |this, title| {
|
.when_some(self.title, |this, title| {
|
||||||
this.gap_4().child(
|
this.gap_4().pb_5().child(
|
||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
.items_center()
|
.items_center()
|
||||||
.gap_3()
|
.gap_3()
|
||||||
.pb_1()
|
.child(div().h_px().w_4().bg(cx.theme().colors().border_variant))
|
||||||
.child(div().h_px().w_4().bg(cx.theme().colors().border))
|
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.flex_none()
|
.flex_none()
|
||||||
@@ -272,7 +271,7 @@ impl RenderOnce for ComponentExampleGroup {
|
|||||||
.flex()
|
.flex()
|
||||||
.items_start()
|
.items_start()
|
||||||
.w_full()
|
.w_full()
|
||||||
.gap_6()
|
.gap_8()
|
||||||
.children(self.examples)
|
.children(self.examples)
|
||||||
.into_any_element(),
|
.into_any_element(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,9 +3,8 @@
|
|||||||
//! A view for exploring Zed components.
|
//! A view for exploring Zed components.
|
||||||
|
|
||||||
use component::{components, ComponentMetadata};
|
use component::{components, ComponentMetadata};
|
||||||
use gpui::{list, prelude::*, uniform_list, App, EventEmitter, FocusHandle, Focusable, Window};
|
use gpui::{prelude::*, App, EventEmitter, FocusHandle, Focusable, Window};
|
||||||
use gpui::{ListState, ScrollHandle, UniformListScrollHandle};
|
use ui::prelude::*;
|
||||||
use ui::{prelude::*, ListItem};
|
|
||||||
|
|
||||||
use workspace::{item::ItemEvent, Item, Workspace, WorkspaceId};
|
use workspace::{item::ItemEvent, Item, Workspace, WorkspaceId};
|
||||||
|
|
||||||
@@ -13,7 +12,7 @@ pub fn init(cx: &mut App) {
|
|||||||
cx.observe_new(|workspace: &mut Workspace, _, _cx| {
|
cx.observe_new(|workspace: &mut Workspace, _, _cx| {
|
||||||
workspace.register_action(
|
workspace.register_action(
|
||||||
|workspace, _: &workspace::OpenComponentPreview, window, cx| {
|
|workspace, _: &workspace::OpenComponentPreview, window, cx| {
|
||||||
let component_preview = cx.new(|cx| ComponentPreview::new(window, cx));
|
let component_preview = cx.new(ComponentPreview::new);
|
||||||
workspace.add_item_to_active_pane(
|
workspace.add_item_to_active_pane(
|
||||||
Box::new(component_preview),
|
Box::new(component_preview),
|
||||||
None,
|
None,
|
||||||
@@ -29,98 +28,73 @@ pub fn init(cx: &mut App) {
|
|||||||
|
|
||||||
struct ComponentPreview {
|
struct ComponentPreview {
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
_view_scroll_handle: ScrollHandle,
|
|
||||||
nav_scroll_handle: UniformListScrollHandle,
|
|
||||||
components: Vec<ComponentMetadata>,
|
|
||||||
component_list: ListState,
|
|
||||||
selected_index: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ComponentPreview {
|
impl ComponentPreview {
|
||||||
pub fn new(_window: &mut Window, cx: &mut Context<Self>) -> Self {
|
pub fn new(cx: &mut Context<Self>) -> Self {
|
||||||
let components = components().all_sorted();
|
|
||||||
let initial_length = components.len();
|
|
||||||
|
|
||||||
let component_list = ListState::new(initial_length, gpui::ListAlignment::Top, px(500.0), {
|
|
||||||
let this = cx.entity().downgrade();
|
|
||||||
move |ix, window: &mut Window, cx: &mut App| {
|
|
||||||
this.update(cx, |this, cx| {
|
|
||||||
this.render_preview(ix, window, cx).into_any_element()
|
|
||||||
})
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
focus_handle: cx.focus_handle(),
|
focus_handle: cx.focus_handle(),
|
||||||
_view_scroll_handle: ScrollHandle::new(),
|
|
||||||
nav_scroll_handle: UniformListScrollHandle::new(),
|
|
||||||
components,
|
|
||||||
component_list,
|
|
||||||
selected_index: 0,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scroll_to_preview(&mut self, ix: usize, cx: &mut Context<Self>) {
|
fn render_sidebar(&self, _window: &Window, _cx: &Context<Self>) -> impl IntoElement {
|
||||||
self.component_list.scroll_to_reveal_item(ix);
|
let components = components().all_sorted();
|
||||||
self.selected_index = ix;
|
let sorted_components = components.clone();
|
||||||
cx.notify();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_component(&self, ix: usize) -> ComponentMetadata {
|
v_flex()
|
||||||
self.components[ix].clone()
|
.max_w_48()
|
||||||
|
.gap_px()
|
||||||
|
.p_1()
|
||||||
|
.children(
|
||||||
|
sorted_components
|
||||||
|
.into_iter()
|
||||||
|
.map(|component| self.render_sidebar_entry(&component, _cx)),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Label::new("These will be clickable once the layout is moved to a gpui::List.")
|
||||||
|
.color(Color::Muted)
|
||||||
|
.size(LabelSize::XSmall)
|
||||||
|
.italic(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_sidebar_entry(
|
fn render_sidebar_entry(
|
||||||
&self,
|
&self,
|
||||||
ix: usize,
|
component: &ComponentMetadata,
|
||||||
selected: bool,
|
_cx: &Context<Self>,
|
||||||
cx: &Context<Self>,
|
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement {
|
||||||
let component = self.get_component(ix);
|
h_flex()
|
||||||
|
.w_40()
|
||||||
ListItem::new(ix)
|
.px_1p5()
|
||||||
.child(Label::new(component.name().clone()).color(Color::Default))
|
.py_0p5()
|
||||||
.selectable(true)
|
.text_sm()
|
||||||
.toggle_state(selected)
|
.child(component.name().clone())
|
||||||
.inset(true)
|
|
||||||
.on_click(cx.listener(move |this, _, _, cx| {
|
|
||||||
this.scroll_to_preview(ix, cx);
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_preview(
|
fn render_preview(
|
||||||
&self,
|
&self,
|
||||||
ix: usize,
|
component: &ComponentMetadata,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &Context<Self>,
|
cx: &Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement {
|
||||||
let component = self.get_component(ix);
|
|
||||||
|
|
||||||
let name = component.name();
|
let name = component.name();
|
||||||
let scope = component.scope();
|
let scope = component.scope();
|
||||||
|
|
||||||
let description = component.description();
|
let description = component.description();
|
||||||
|
|
||||||
v_flex()
|
v_flex()
|
||||||
.py_2()
|
.border_b_1()
|
||||||
.child(
|
|
||||||
v_flex()
|
|
||||||
.border_1()
|
|
||||||
.border_color(cx.theme().colors().border)
|
.border_color(cx.theme().colors().border)
|
||||||
.rounded_md()
|
|
||||||
.w_full()
|
.w_full()
|
||||||
.gap_4()
|
.gap_3()
|
||||||
.py_4()
|
.py_6()
|
||||||
.px_6()
|
|
||||||
.flex_none()
|
|
||||||
.child(
|
.child(
|
||||||
v_flex()
|
v_flex()
|
||||||
.gap_1()
|
.gap_1()
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
.gap_1()
|
.gap_1()
|
||||||
.text_xl()
|
.text_2xl()
|
||||||
.child(div().child(name))
|
.child(div().child(name))
|
||||||
.when_some(scope, |this, scope| {
|
.when_some(scope, |this, scope| {
|
||||||
this.child(div().opacity(0.5).child(format!("({})", scope)))
|
this.child(div().opacity(0.5).child(format!("({})", scope)))
|
||||||
@@ -138,52 +112,40 @@ impl ComponentPreview {
|
|||||||
)
|
)
|
||||||
.when_some(component.preview(), |this, preview| {
|
.when_some(component.preview(), |this, preview| {
|
||||||
this.child(preview(window, cx))
|
this.child(preview(window, cx))
|
||||||
}),
|
})
|
||||||
)
|
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_previews(&self, window: &mut Window, cx: &Context<Self>) -> impl IntoElement {
|
||||||
|
v_flex()
|
||||||
|
.id("component-previews")
|
||||||
|
.size_full()
|
||||||
|
.overflow_y_scroll()
|
||||||
|
.p_4()
|
||||||
|
.gap_4()
|
||||||
|
.children(
|
||||||
|
components()
|
||||||
|
.all_previews_sorted()
|
||||||
|
.iter()
|
||||||
|
.map(|component| self.render_preview(component, window, cx)),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for ComponentPreview {
|
impl Render for ComponentPreview {
|
||||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
|
fn render(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
|
||||||
h_flex()
|
h_flex()
|
||||||
.id("component-preview")
|
.id("component-preview")
|
||||||
.key_context("ComponentPreview")
|
.key_context("ComponentPreview")
|
||||||
.items_start()
|
.items_start()
|
||||||
.overflow_hidden()
|
.overflow_hidden()
|
||||||
.size_full()
|
.size_full()
|
||||||
|
.max_h_full()
|
||||||
.track_focus(&self.focus_handle)
|
.track_focus(&self.focus_handle)
|
||||||
.px_2()
|
.px_2()
|
||||||
.bg(cx.theme().colors().editor_background)
|
.bg(cx.theme().colors().editor_background)
|
||||||
.child(
|
.child(self.render_sidebar(window, cx))
|
||||||
uniform_list(
|
.child(self.render_previews(window, cx))
|
||||||
cx.entity().clone(),
|
|
||||||
"component-nav",
|
|
||||||
self.components.len(),
|
|
||||||
move |this, range, _window, cx| {
|
|
||||||
range
|
|
||||||
.map(|ix| this.render_sidebar_entry(ix, ix == this.selected_index, cx))
|
|
||||||
.collect()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.track_scroll(self.nav_scroll_handle.clone())
|
|
||||||
.pt_4()
|
|
||||||
.w(px(240.))
|
|
||||||
.h_full()
|
|
||||||
.flex_grow(),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
v_flex()
|
|
||||||
.id("component-list")
|
|
||||||
.px_8()
|
|
||||||
.pt_4()
|
|
||||||
.size_full()
|
|
||||||
.child(
|
|
||||||
list(self.component_list.clone())
|
|
||||||
.flex_grow()
|
|
||||||
.with_sizing_behavior(gpui::ListSizingBehavior::Auto),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,13 +175,13 @@ impl Item for ComponentPreview {
|
|||||||
fn clone_on_split(
|
fn clone_on_split(
|
||||||
&self,
|
&self,
|
||||||
_workspace_id: Option<WorkspaceId>,
|
_workspace_id: Option<WorkspaceId>,
|
||||||
window: &mut Window,
|
_window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Option<gpui::Entity<Self>>
|
) -> Option<gpui::Entity<Self>>
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
Some(cx.new(|cx| Self::new(window, cx)))
|
Some(cx.new(Self::new))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_item_events(event: &Self::Event, mut f: impl FnMut(workspace::item::ItemEvent)) {
|
fn to_item_events(event: &Self::Event, mut f: impl FnMut(workspace::item::ItemEvent)) {
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ path = "src/context_server.rs"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
assistant_tool.workspace = true
|
assistant_tool.workspace = true
|
||||||
async-trait.workspace = true
|
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
command_palette_hooks.workspace = true
|
command_palette_hooks.workspace = true
|
||||||
context_server_settings.workspace = true
|
context_server_settings.workspace = true
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context as _, Result};
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use futures::{channel::oneshot, select, FutureExt, StreamExt};
|
use futures::{channel::oneshot, io::BufWriter, select, AsyncRead, AsyncWrite, FutureExt};
|
||||||
use gpui::{AppContext as _, AsyncApp, BackgroundExecutor, Task};
|
use gpui::{AppContext as _, AsyncApp, BackgroundExecutor, Task};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use postage::barrier;
|
use postage::barrier;
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
use serde_json::{value::RawValue, Value};
|
use serde_json::{value::RawValue, Value};
|
||||||
use smol::channel;
|
use smol::{
|
||||||
|
channel,
|
||||||
|
io::{AsyncBufReadExt, AsyncWriteExt, BufReader},
|
||||||
|
process::Child,
|
||||||
|
};
|
||||||
use std::{
|
use std::{
|
||||||
fmt,
|
fmt,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
@@ -18,8 +22,6 @@ use std::{
|
|||||||
};
|
};
|
||||||
use util::TryFutureExt;
|
use util::TryFutureExt;
|
||||||
|
|
||||||
use crate::transport::{StdioTransport, Transport};
|
|
||||||
|
|
||||||
const JSON_RPC_VERSION: &str = "2.0";
|
const JSON_RPC_VERSION: &str = "2.0";
|
||||||
const REQUEST_TIMEOUT: Duration = Duration::from_secs(60);
|
const REQUEST_TIMEOUT: Duration = Duration::from_secs(60);
|
||||||
|
|
||||||
@@ -53,8 +55,7 @@ pub struct Client {
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
output_done_rx: Mutex<Option<barrier::Receiver>>,
|
output_done_rx: Mutex<Option<barrier::Receiver>>,
|
||||||
executor: BackgroundExecutor,
|
executor: BackgroundExecutor,
|
||||||
#[allow(dead_code)]
|
server: Arc<Mutex<Option<Child>>>,
|
||||||
transport: Arc<dyn Transport>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
@@ -151,13 +152,25 @@ impl Client {
|
|||||||
&binary.args
|
&binary.args
|
||||||
);
|
);
|
||||||
|
|
||||||
let server_name = binary
|
let mut command = util::command::new_smol_command(&binary.executable);
|
||||||
.executable
|
command
|
||||||
.file_name()
|
.args(&binary.args)
|
||||||
.map(|name| name.to_string_lossy().to_string())
|
.envs(binary.env.unwrap_or_default())
|
||||||
.unwrap_or_else(String::new);
|
.stdin(std::process::Stdio::piped())
|
||||||
|
.stdout(std::process::Stdio::piped())
|
||||||
|
.stderr(std::process::Stdio::piped())
|
||||||
|
.kill_on_drop(true);
|
||||||
|
|
||||||
let transport = Arc::new(StdioTransport::new(binary, &cx)?);
|
let mut server = command.spawn().with_context(|| {
|
||||||
|
format!(
|
||||||
|
"failed to spawn command. (path={:?}, args={:?})",
|
||||||
|
binary.executable, &binary.args
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let stdin = server.stdin.take().unwrap();
|
||||||
|
let stdout = server.stdout.take().unwrap();
|
||||||
|
let stderr = server.stderr.take().unwrap();
|
||||||
|
|
||||||
let (outbound_tx, outbound_rx) = channel::unbounded::<String>();
|
let (outbound_tx, outbound_rx) = channel::unbounded::<String>();
|
||||||
let (output_done_tx, output_done_rx) = barrier::channel();
|
let (output_done_tx, output_done_rx) = barrier::channel();
|
||||||
@@ -170,22 +183,18 @@ impl Client {
|
|||||||
let stdout_input_task = cx.spawn({
|
let stdout_input_task = cx.spawn({
|
||||||
let notification_handlers = notification_handlers.clone();
|
let notification_handlers = notification_handlers.clone();
|
||||||
let response_handlers = response_handlers.clone();
|
let response_handlers = response_handlers.clone();
|
||||||
let transport = transport.clone();
|
|
||||||
move |cx| {
|
move |cx| {
|
||||||
Self::handle_input(transport, notification_handlers, response_handlers, cx)
|
Self::handle_input(stdout, notification_handlers, response_handlers, cx).log_err()
|
||||||
.log_err()
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let stderr_input_task = cx.spawn(|_| Self::handle_stderr(transport.clone()).log_err());
|
let stderr_input_task = cx.spawn(|_| Self::handle_stderr(stderr).log_err());
|
||||||
let input_task = cx.spawn(|_| async move {
|
let input_task = cx.spawn(|_| async move {
|
||||||
let (stdout, stderr) = futures::join!(stdout_input_task, stderr_input_task);
|
let (stdout, stderr) = futures::join!(stdout_input_task, stderr_input_task);
|
||||||
stdout.or(stderr)
|
stdout.or(stderr)
|
||||||
});
|
});
|
||||||
|
|
||||||
let output_task = cx.background_spawn({
|
let output_task = cx.background_spawn({
|
||||||
let transport = transport.clone();
|
|
||||||
Self::handle_output(
|
Self::handle_output(
|
||||||
transport,
|
stdin,
|
||||||
outbound_rx,
|
outbound_rx,
|
||||||
output_done_tx,
|
output_done_tx,
|
||||||
response_handlers.clone(),
|
response_handlers.clone(),
|
||||||
@@ -193,18 +202,24 @@ impl Client {
|
|||||||
.log_err()
|
.log_err()
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(Self {
|
let mut context_server = Self {
|
||||||
server_id,
|
server_id,
|
||||||
notification_handlers,
|
notification_handlers,
|
||||||
response_handlers,
|
response_handlers,
|
||||||
name: server_name.into(),
|
name: "".into(),
|
||||||
next_id: Default::default(),
|
next_id: Default::default(),
|
||||||
outbound_tx,
|
outbound_tx,
|
||||||
executor: cx.background_executor().clone(),
|
executor: cx.background_executor().clone(),
|
||||||
io_tasks: Mutex::new(Some((input_task, output_task))),
|
io_tasks: Mutex::new(Some((input_task, output_task))),
|
||||||
output_done_rx: Mutex::new(Some(output_done_rx)),
|
output_done_rx: Mutex::new(Some(output_done_rx)),
|
||||||
transport,
|
server: Arc::new(Mutex::new(Some(server))),
|
||||||
})
|
};
|
||||||
|
|
||||||
|
if let Some(name) = binary.executable.file_name() {
|
||||||
|
context_server.name = name.to_string_lossy().into();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(context_server)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles input from the server's stdout.
|
/// Handles input from the server's stdout.
|
||||||
@@ -213,53 +228,79 @@ impl Client {
|
|||||||
/// parses them as JSON-RPC responses or notifications, and dispatches them
|
/// parses them as JSON-RPC responses or notifications, and dispatches them
|
||||||
/// to the appropriate handlers. It processes both responses (which are matched
|
/// to the appropriate handlers. It processes both responses (which are matched
|
||||||
/// to pending requests) and notifications (which trigger registered handlers).
|
/// to pending requests) and notifications (which trigger registered handlers).
|
||||||
async fn handle_input(
|
async fn handle_input<Stdout>(
|
||||||
transport: Arc<dyn Transport>,
|
stdout: Stdout,
|
||||||
notification_handlers: Arc<Mutex<HashMap<&'static str, NotificationHandler>>>,
|
notification_handlers: Arc<Mutex<HashMap<&'static str, NotificationHandler>>>,
|
||||||
response_handlers: Arc<Mutex<Option<HashMap<RequestId, ResponseHandler>>>>,
|
response_handlers: Arc<Mutex<Option<HashMap<RequestId, ResponseHandler>>>>,
|
||||||
cx: AsyncApp,
|
cx: AsyncApp,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()>
|
||||||
let mut receiver = transport.receive();
|
where
|
||||||
|
Stdout: AsyncRead + Unpin + Send + 'static,
|
||||||
|
{
|
||||||
|
let mut stdout = BufReader::new(stdout);
|
||||||
|
let mut buffer = String::new();
|
||||||
|
|
||||||
while let Some(message) = receiver.next().await {
|
loop {
|
||||||
if let Ok(response) = serde_json::from_str::<AnyResponse>(&message) {
|
buffer.clear();
|
||||||
|
if stdout.read_line(&mut buffer).await? == 0 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = buffer.trim();
|
||||||
|
|
||||||
|
if !content.is_empty() {
|
||||||
|
if let Ok(response) = serde_json::from_str::<AnyResponse>(content) {
|
||||||
if let Some(handlers) = response_handlers.lock().as_mut() {
|
if let Some(handlers) = response_handlers.lock().as_mut() {
|
||||||
if let Some(handler) = handlers.remove(&response.id) {
|
if let Some(handler) = handlers.remove(&response.id) {
|
||||||
handler(Ok(message.to_string()));
|
handler(Ok(content.to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let Ok(notification) = serde_json::from_str::<AnyNotification>(&message) {
|
} else if let Ok(notification) = serde_json::from_str::<AnyNotification>(content) {
|
||||||
let mut notification_handlers = notification_handlers.lock();
|
let mut notification_handlers = notification_handlers.lock();
|
||||||
if let Some(handler) = notification_handlers.get_mut(notification.method.as_str()) {
|
if let Some(handler) =
|
||||||
|
notification_handlers.get_mut(notification.method.as_str())
|
||||||
|
{
|
||||||
handler(notification.params.unwrap_or(Value::Null), cx.clone());
|
handler(notification.params.unwrap_or(Value::Null), cx.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
smol::future::yield_now().await;
|
smol::future::yield_now().await;
|
||||||
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles the stderr output from the context server.
|
/// Handles the stderr output from the context server.
|
||||||
/// Continuously reads and logs any error messages from the server.
|
/// Continuously reads and logs any error messages from the server.
|
||||||
async fn handle_stderr(transport: Arc<dyn Transport>) -> anyhow::Result<()> {
|
async fn handle_stderr<Stderr>(stderr: Stderr) -> anyhow::Result<()>
|
||||||
while let Some(err) = transport.receive_err().next().await {
|
where
|
||||||
log::warn!("context server stderr: {}", err.trim());
|
Stderr: AsyncRead + Unpin + Send + 'static,
|
||||||
}
|
{
|
||||||
|
let mut stderr = BufReader::new(stderr);
|
||||||
|
let mut buffer = String::new();
|
||||||
|
|
||||||
Ok(())
|
loop {
|
||||||
|
buffer.clear();
|
||||||
|
if stderr.read_line(&mut buffer).await? == 0 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
log::warn!("context server stderr: {}", buffer.trim());
|
||||||
|
smol::future::yield_now().await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles the output to the context server's stdin.
|
/// Handles the output to the context server's stdin.
|
||||||
/// This function continuously receives messages from the outbound channel,
|
/// This function continuously receives messages from the outbound channel,
|
||||||
/// writes them to the server's stdin, and manages the lifecycle of response handlers.
|
/// writes them to the server's stdin, and manages the lifecycle of response handlers.
|
||||||
async fn handle_output(
|
async fn handle_output<Stdin>(
|
||||||
transport: Arc<dyn Transport>,
|
stdin: Stdin,
|
||||||
outbound_rx: channel::Receiver<String>,
|
outbound_rx: channel::Receiver<String>,
|
||||||
output_done_tx: barrier::Sender,
|
output_done_tx: barrier::Sender,
|
||||||
response_handlers: Arc<Mutex<Option<HashMap<RequestId, ResponseHandler>>>>,
|
response_handlers: Arc<Mutex<Option<HashMap<RequestId, ResponseHandler>>>>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()>
|
||||||
|
where
|
||||||
|
Stdin: AsyncWrite + Unpin + Send + 'static,
|
||||||
|
{
|
||||||
|
let mut stdin = BufWriter::new(stdin);
|
||||||
let _clear_response_handlers = util::defer({
|
let _clear_response_handlers = util::defer({
|
||||||
let response_handlers = response_handlers.clone();
|
let response_handlers = response_handlers.clone();
|
||||||
move || {
|
move || {
|
||||||
@@ -268,7 +309,10 @@ impl Client {
|
|||||||
});
|
});
|
||||||
while let Ok(message) = outbound_rx.recv().await {
|
while let Ok(message) = outbound_rx.recv().await {
|
||||||
log::trace!("outgoing message: {}", message);
|
log::trace!("outgoing message: {}", message);
|
||||||
transport.send(message).await?;
|
|
||||||
|
stdin.write_all(message.as_bytes()).await?;
|
||||||
|
stdin.write_all(b"\n").await?;
|
||||||
|
stdin.flush().await?;
|
||||||
}
|
}
|
||||||
drop(output_done_tx);
|
drop(output_done_tx);
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -372,6 +416,14 @@ impl Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Drop for Client {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(mut server) = self.server.lock().take() {
|
||||||
|
let _ = server.kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for ContextServerId {
|
impl fmt::Display for ContextServerId {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
self.0.fmt(f)
|
self.0.fmt(f)
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ mod extension_context_server;
|
|||||||
pub mod manager;
|
pub mod manager;
|
||||||
pub mod protocol;
|
pub mod protocol;
|
||||||
mod registry;
|
mod registry;
|
||||||
mod transport;
|
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
use command_palette_hooks::CommandPaletteFilter;
|
use command_palette_hooks::CommandPaletteFilter;
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
mod stdio_transport;
|
|
||||||
|
|
||||||
use std::pin::Pin;
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use futures::Stream;
|
|
||||||
|
|
||||||
pub use stdio_transport::*;
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait Transport: Send + Sync {
|
|
||||||
async fn send(&self, message: String) -> Result<()>;
|
|
||||||
fn receive(&self) -> Pin<Box<dyn Stream<Item = String> + Send>>;
|
|
||||||
fn receive_err(&self) -> Pin<Box<dyn Stream<Item = String> + Send>>;
|
|
||||||
}
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
use std::pin::Pin;
|
|
||||||
|
|
||||||
use anyhow::{Context as _, Result};
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use futures::io::{BufReader, BufWriter};
|
|
||||||
use futures::{
|
|
||||||
AsyncBufReadExt as _, AsyncRead, AsyncWrite, AsyncWriteExt as _, Stream, StreamExt as _,
|
|
||||||
};
|
|
||||||
use gpui::AsyncApp;
|
|
||||||
use smol::channel;
|
|
||||||
use smol::process::Child;
|
|
||||||
use util::TryFutureExt as _;
|
|
||||||
|
|
||||||
use crate::client::ModelContextServerBinary;
|
|
||||||
use crate::transport::Transport;
|
|
||||||
|
|
||||||
pub struct StdioTransport {
|
|
||||||
stdout_sender: channel::Sender<String>,
|
|
||||||
stdin_receiver: channel::Receiver<String>,
|
|
||||||
stderr_receiver: channel::Receiver<String>,
|
|
||||||
server: Child,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StdioTransport {
|
|
||||||
pub fn new(binary: ModelContextServerBinary, cx: &AsyncApp) -> Result<Self> {
|
|
||||||
let mut command = util::command::new_smol_command(&binary.executable);
|
|
||||||
command
|
|
||||||
.args(&binary.args)
|
|
||||||
.envs(binary.env.unwrap_or_default())
|
|
||||||
.stdin(std::process::Stdio::piped())
|
|
||||||
.stdout(std::process::Stdio::piped())
|
|
||||||
.stderr(std::process::Stdio::piped())
|
|
||||||
.kill_on_drop(true);
|
|
||||||
|
|
||||||
let mut server = command.spawn().with_context(|| {
|
|
||||||
format!(
|
|
||||||
"failed to spawn command. (path={:?}, args={:?})",
|
|
||||||
binary.executable, &binary.args
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let stdin = server.stdin.take().unwrap();
|
|
||||||
let stdout = server.stdout.take().unwrap();
|
|
||||||
let stderr = server.stderr.take().unwrap();
|
|
||||||
|
|
||||||
let (stdin_sender, stdin_receiver) = channel::unbounded::<String>();
|
|
||||||
let (stdout_sender, stdout_receiver) = channel::unbounded::<String>();
|
|
||||||
let (stderr_sender, stderr_receiver) = channel::unbounded::<String>();
|
|
||||||
|
|
||||||
cx.spawn(|_| Self::handle_output(stdin, stdout_receiver).log_err())
|
|
||||||
.detach();
|
|
||||||
|
|
||||||
cx.spawn(|_| async move { Self::handle_input(stdout, stdin_sender).await })
|
|
||||||
.detach();
|
|
||||||
|
|
||||||
cx.spawn(|_| async move { Self::handle_err(stderr, stderr_sender).await })
|
|
||||||
.detach();
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
stdout_sender,
|
|
||||||
stdin_receiver,
|
|
||||||
stderr_receiver,
|
|
||||||
server,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_input<Stdout>(stdin: Stdout, inbound_rx: channel::Sender<String>)
|
|
||||||
where
|
|
||||||
Stdout: AsyncRead + Unpin + Send + 'static,
|
|
||||||
{
|
|
||||||
let mut stdin = BufReader::new(stdin);
|
|
||||||
let mut line = String::new();
|
|
||||||
while let Ok(n) = stdin.read_line(&mut line).await {
|
|
||||||
if n == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if inbound_rx.send(line.clone()).await.is_err() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
line.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_output<Stdin>(
|
|
||||||
stdin: Stdin,
|
|
||||||
outbound_rx: channel::Receiver<String>,
|
|
||||||
) -> Result<()>
|
|
||||||
where
|
|
||||||
Stdin: AsyncWrite + Unpin + Send + 'static,
|
|
||||||
{
|
|
||||||
let mut stdin = BufWriter::new(stdin);
|
|
||||||
let mut pinned_rx = Box::pin(outbound_rx);
|
|
||||||
while let Some(message) = pinned_rx.next().await {
|
|
||||||
log::trace!("outgoing message: {}", message);
|
|
||||||
|
|
||||||
stdin.write_all(message.as_bytes()).await?;
|
|
||||||
stdin.write_all(b"\n").await?;
|
|
||||||
stdin.flush().await?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_err<Stderr>(stderr: Stderr, stderr_tx: channel::Sender<String>)
|
|
||||||
where
|
|
||||||
Stderr: AsyncRead + Unpin + Send + 'static,
|
|
||||||
{
|
|
||||||
let mut stderr = BufReader::new(stderr);
|
|
||||||
let mut line = String::new();
|
|
||||||
while let Ok(n) = stderr.read_line(&mut line).await {
|
|
||||||
if n == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if stderr_tx.send(line.clone()).await.is_err() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
line.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl Transport for StdioTransport {
|
|
||||||
async fn send(&self, message: String) -> Result<()> {
|
|
||||||
Ok(self.stdout_sender.send(message).await?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn receive(&self) -> Pin<Box<dyn Stream<Item = String> + Send>> {
|
|
||||||
Box::pin(self.stdin_receiver.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn receive_err(&self) -> Pin<Box<dyn Stream<Item = String> + Send>> {
|
|
||||||
Box::pin(self.stderr_receiver.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for StdioTransport {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
let _ = self.server.kill();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -38,7 +38,6 @@ gpui.workspace = true
|
|||||||
http_client.workspace = true
|
http_client.workspace = true
|
||||||
inline_completion.workspace = true
|
inline_completion.workspace = true
|
||||||
language.workspace = true
|
language.workspace = true
|
||||||
log.workspace = true
|
|
||||||
lsp.workspace = true
|
lsp.workspace = true
|
||||||
menu.workspace = true
|
menu.workspace = true
|
||||||
node_runtime.workspace = true
|
node_runtime.workspace = true
|
||||||
@@ -63,9 +62,7 @@ async-std = { version = "1.12.0", features = ["unstable"] }
|
|||||||
client = { workspace = true, features = ["test-support"] }
|
client = { workspace = true, features = ["test-support"] }
|
||||||
clock = { workspace = true, features = ["test-support"] }
|
clock = { workspace = true, features = ["test-support"] }
|
||||||
collections = { workspace = true, features = ["test-support"] }
|
collections = { workspace = true, features = ["test-support"] }
|
||||||
ctor.workspace = true
|
|
||||||
editor = { workspace = true, features = ["test-support"] }
|
editor = { workspace = true, features = ["test-support"] }
|
||||||
env_logger.workspace = true
|
|
||||||
fs = { workspace = true, features = ["test-support"] }
|
fs = { workspace = true, features = ["test-support"] }
|
||||||
gpui = { workspace = true, features = ["test-support"] }
|
gpui = { workspace = true, features = ["test-support"] }
|
||||||
http_client = { workspace = true, features = ["test-support"] }
|
http_client = { workspace = true, features = ["test-support"] }
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ use gpui::{
|
|||||||
};
|
};
|
||||||
use http_client::github::get_release_by_tag_name;
|
use http_client::github::get_release_by_tag_name;
|
||||||
use http_client::HttpClient;
|
use http_client::HttpClient;
|
||||||
use language::language_settings::CopilotSettings;
|
|
||||||
use language::{
|
use language::{
|
||||||
language_settings::{all_language_settings, language_settings, EditPredictionProvider},
|
language_settings::{all_language_settings, language_settings, EditPredictionProvider},
|
||||||
point_from_lsp, point_to_lsp, Anchor, Bias, Buffer, BufferSnapshot, Language, PointUtf16,
|
point_from_lsp, point_to_lsp, Anchor, Bias, Buffer, BufferSnapshot, Language, PointUtf16,
|
||||||
@@ -368,13 +367,13 @@ impl Copilot {
|
|||||||
let server_id = self.server_id;
|
let server_id = self.server_id;
|
||||||
let http = self.http.clone();
|
let http = self.http.clone();
|
||||||
let node_runtime = self.node_runtime.clone();
|
let node_runtime = self.node_runtime.clone();
|
||||||
let language_settings = all_language_settings(None, cx);
|
if all_language_settings(None, cx).edit_predictions.provider
|
||||||
if language_settings.edit_predictions.provider == EditPredictionProvider::Copilot {
|
== EditPredictionProvider::Copilot
|
||||||
|
{
|
||||||
if matches!(self.server, CopilotServer::Disabled) {
|
if matches!(self.server, CopilotServer::Disabled) {
|
||||||
let env = self.build_env(&language_settings.edit_predictions.copilot);
|
|
||||||
let start_task = cx
|
let start_task = cx
|
||||||
.spawn(move |this, cx| {
|
.spawn(move |this, cx| {
|
||||||
Self::start_language_server(server_id, http, node_runtime, env, this, cx)
|
Self::start_language_server(server_id, http, node_runtime, this, cx)
|
||||||
})
|
})
|
||||||
.shared();
|
.shared();
|
||||||
self.server = CopilotServer::Starting { task: start_task };
|
self.server = CopilotServer::Starting { task: start_task };
|
||||||
@@ -386,30 +385,6 @@ impl Copilot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_env(&self, copilot_settings: &CopilotSettings) -> Option<HashMap<String, String>> {
|
|
||||||
let proxy_url = copilot_settings.proxy.clone()?;
|
|
||||||
let no_verify = copilot_settings.proxy_no_verify;
|
|
||||||
let http_or_https_proxy = if proxy_url.starts_with("http:") {
|
|
||||||
"HTTP_PROXY"
|
|
||||||
} else if proxy_url.starts_with("https:") {
|
|
||||||
"HTTPS_PROXY"
|
|
||||||
} else {
|
|
||||||
log::error!(
|
|
||||||
"Unsupported protocol scheme for language server proxy (must be http or https)"
|
|
||||||
);
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut env = HashMap::default();
|
|
||||||
env.insert(http_or_https_proxy.to_string(), proxy_url);
|
|
||||||
|
|
||||||
if let Some(true) = no_verify {
|
|
||||||
env.insert("NODE_TLS_REJECT_UNAUTHORIZED".to_string(), "0".to_string());
|
|
||||||
};
|
|
||||||
|
|
||||||
Some(env)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
pub fn fake(cx: &mut gpui::TestAppContext) -> (Entity<Self>, lsp::FakeLanguageServer) {
|
pub fn fake(cx: &mut gpui::TestAppContext) -> (Entity<Self>, lsp::FakeLanguageServer) {
|
||||||
use lsp::FakeLanguageServer;
|
use lsp::FakeLanguageServer;
|
||||||
@@ -447,7 +422,6 @@ impl Copilot {
|
|||||||
new_server_id: LanguageServerId,
|
new_server_id: LanguageServerId,
|
||||||
http: Arc<dyn HttpClient>,
|
http: Arc<dyn HttpClient>,
|
||||||
node_runtime: NodeRuntime,
|
node_runtime: NodeRuntime,
|
||||||
env: Option<HashMap<String, String>>,
|
|
||||||
this: WeakEntity<Self>,
|
this: WeakEntity<Self>,
|
||||||
mut cx: AsyncApp,
|
mut cx: AsyncApp,
|
||||||
) {
|
) {
|
||||||
@@ -458,7 +432,8 @@ impl Copilot {
|
|||||||
let binary = LanguageServerBinary {
|
let binary = LanguageServerBinary {
|
||||||
path: node_path,
|
path: node_path,
|
||||||
arguments,
|
arguments,
|
||||||
env,
|
// TODO: We could set HTTP_PROXY etc here and fix the copilot issue.
|
||||||
|
env: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let root_path = if cfg!(target_os = "windows") {
|
let root_path = if cfg!(target_os = "windows") {
|
||||||
@@ -475,7 +450,6 @@ impl Copilot {
|
|||||||
binary,
|
binary,
|
||||||
root_path,
|
root_path,
|
||||||
None,
|
None,
|
||||||
Default::default(),
|
|
||||||
cx.clone(),
|
cx.clone(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -637,8 +611,6 @@ impl Copilot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn reinstall(&mut self, cx: &mut Context<Self>) -> Task<()> {
|
pub fn reinstall(&mut self, cx: &mut Context<Self>) -> Task<()> {
|
||||||
let language_settings = all_language_settings(None, cx);
|
|
||||||
let env = self.build_env(&language_settings.edit_predictions.copilot);
|
|
||||||
let start_task = cx
|
let start_task = cx
|
||||||
.spawn({
|
.spawn({
|
||||||
let http = self.http.clone();
|
let http = self.http.clone();
|
||||||
@@ -646,7 +618,7 @@ impl Copilot {
|
|||||||
let server_id = self.server_id;
|
let server_id = self.server_id;
|
||||||
move |this, cx| async move {
|
move |this, cx| async move {
|
||||||
clear_copilot_dir().await;
|
clear_copilot_dir().await;
|
||||||
Self::start_language_server(server_id, http, node_runtime, env, this, cx).await
|
Self::start_language_server(server_id, http, node_runtime, this, cx).await
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.shared();
|
.shared();
|
||||||
@@ -1307,11 +1279,3 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
#[ctor::ctor]
|
|
||||||
fn init_logger() {
|
|
||||||
if std::env::var("RUST_LOG").is_ok() {
|
|
||||||
env_logger::init();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -40,8 +40,6 @@ pub enum Model {
|
|||||||
O3Mini,
|
O3Mini,
|
||||||
#[serde(alias = "claude-3-5-sonnet", rename = "claude-3.5-sonnet")]
|
#[serde(alias = "claude-3-5-sonnet", rename = "claude-3.5-sonnet")]
|
||||||
Claude3_5Sonnet,
|
Claude3_5Sonnet,
|
||||||
#[serde(alias = "claude-3-7-sonnet", rename = "claude-3.7-sonnet")]
|
|
||||||
Claude3_7Sonnet,
|
|
||||||
#[serde(alias = "gemini-2.0-flash", rename = "gemini-2.0-flash-001")]
|
#[serde(alias = "gemini-2.0-flash", rename = "gemini-2.0-flash-001")]
|
||||||
Gemini20Flash,
|
Gemini20Flash,
|
||||||
}
|
}
|
||||||
@@ -49,11 +47,7 @@ pub enum Model {
|
|||||||
impl Model {
|
impl Model {
|
||||||
pub fn uses_streaming(&self) -> bool {
|
pub fn uses_streaming(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Gpt4o
|
Self::Gpt4o | Self::Gpt4 | Self::Gpt3_5Turbo | Self::Claude3_5Sonnet => true,
|
||||||
| Self::Gpt4
|
|
||||||
| Self::Gpt3_5Turbo
|
|
||||||
| Self::Claude3_5Sonnet
|
|
||||||
| Self::Claude3_7Sonnet => true,
|
|
||||||
Self::O3Mini | Self::O1 | Self::Gemini20Flash => false,
|
Self::O3Mini | Self::O1 | Self::Gemini20Flash => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,7 +60,6 @@ impl Model {
|
|||||||
"o1" => Ok(Self::O1),
|
"o1" => Ok(Self::O1),
|
||||||
"o3-mini" => Ok(Self::O3Mini),
|
"o3-mini" => Ok(Self::O3Mini),
|
||||||
"claude-3-5-sonnet" => Ok(Self::Claude3_5Sonnet),
|
"claude-3-5-sonnet" => Ok(Self::Claude3_5Sonnet),
|
||||||
"claude-3-7-sonnet" => Ok(Self::Claude3_7Sonnet),
|
|
||||||
"gemini-2.0-flash-001" => Ok(Self::Gemini20Flash),
|
"gemini-2.0-flash-001" => Ok(Self::Gemini20Flash),
|
||||||
_ => Err(anyhow!("Invalid model id: {}", id)),
|
_ => Err(anyhow!("Invalid model id: {}", id)),
|
||||||
}
|
}
|
||||||
@@ -80,7 +73,6 @@ impl Model {
|
|||||||
Self::O3Mini => "o3-mini",
|
Self::O3Mini => "o3-mini",
|
||||||
Self::O1 => "o1",
|
Self::O1 => "o1",
|
||||||
Self::Claude3_5Sonnet => "claude-3-5-sonnet",
|
Self::Claude3_5Sonnet => "claude-3-5-sonnet",
|
||||||
Self::Claude3_7Sonnet => "claude-3-7-sonnet",
|
|
||||||
Self::Gemini20Flash => "gemini-2.0-flash-001",
|
Self::Gemini20Flash => "gemini-2.0-flash-001",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,7 +85,6 @@ impl Model {
|
|||||||
Self::O3Mini => "o3-mini",
|
Self::O3Mini => "o3-mini",
|
||||||
Self::O1 => "o1",
|
Self::O1 => "o1",
|
||||||
Self::Claude3_5Sonnet => "Claude 3.5 Sonnet",
|
Self::Claude3_5Sonnet => "Claude 3.5 Sonnet",
|
||||||
Self::Claude3_7Sonnet => "Claude 3.7 Sonnet",
|
|
||||||
Self::Gemini20Flash => "Gemini 2.0 Flash",
|
Self::Gemini20Flash => "Gemini 2.0 Flash",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,8 +96,7 @@ impl Model {
|
|||||||
Self::Gpt3_5Turbo => 12_288,
|
Self::Gpt3_5Turbo => 12_288,
|
||||||
Self::O3Mini => 64_000,
|
Self::O3Mini => 64_000,
|
||||||
Self::O1 => 20_000,
|
Self::O1 => 20_000,
|
||||||
Self::Claude3_5Sonnet => 200_000,
|
Self::Claude3_5Sonnet => 128_000,
|
||||||
Self::Claude3_7Sonnet => 90_000,
|
|
||||||
Model::Gemini20Flash => 128_000,
|
Model::Gemini20Flash => 128_000,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -411,7 +401,7 @@ async fn stream_completion(
|
|||||||
|
|
||||||
match serde_json::from_str::<ResponseEvent>(line) {
|
match serde_json::from_str::<ResponseEvent>(line) {
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
if response.choices.is_empty()
|
if response.choices.first().is_none()
|
||||||
|| response.choices.first().unwrap().finish_reason.is_some()
|
|| response.choices.first().unwrap().finish_reason.is_some()
|
||||||
{
|
{
|
||||||
None
|
None
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ impl EditPredictionProvider for CopilotCompletionProvider {
|
|||||||
fn discard(&mut self, cx: &mut Context<Self>) {
|
fn discard(&mut self, cx: &mut Context<Self>) {
|
||||||
let settings = AllLanguageSettings::get_global(cx);
|
let settings = AllLanguageSettings::get_global(cx);
|
||||||
|
|
||||||
let copilot_enabled = settings.show_edit_predictions(None, cx);
|
let copilot_enabled = settings.show_inline_completions(None, cx);
|
||||||
|
|
||||||
if !copilot_enabled {
|
if !copilot_enabled {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "credentials_provider"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition.workspace = true
|
|
||||||
publish.workspace = true
|
|
||||||
license = "GPL-3.0-or-later"
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
path = "src/credentials_provider.rs"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
anyhow.workspace = true
|
|
||||||
futures.workspace = true
|
|
||||||
gpui.workspace = true
|
|
||||||
paths.workspace = true
|
|
||||||
release_channel.workspace = true
|
|
||||||
serde.workspace = true
|
|
||||||
serde_json.workspace = true
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../LICENSE-GPL
|
|
||||||
@@ -1,199 +0,0 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
use std::future::Future;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::pin::Pin;
|
|
||||||
use std::sync::{Arc, LazyLock};
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use futures::FutureExt as _;
|
|
||||||
use gpui::{App, AsyncApp};
|
|
||||||
use release_channel::ReleaseChannel;
|
|
||||||
|
|
||||||
/// An environment variable whose presence indicates that the system keychain
|
|
||||||
/// should be used in development.
|
|
||||||
///
|
|
||||||
/// By default, running Zed in development uses the development credentials
|
|
||||||
/// provider. Setting this environment variable allows you to interact with the
|
|
||||||
/// system keychain (for instance, if you need to test something).
|
|
||||||
///
|
|
||||||
/// Only works in development. Setting this environment variable in other
|
|
||||||
/// release channels is a no-op.
|
|
||||||
static ZED_DEVELOPMENT_USE_KEYCHAIN: LazyLock<bool> = LazyLock::new(|| {
|
|
||||||
std::env::var("ZED_DEVELOPMENT_USE_KEYCHAIN").map_or(false, |value| !value.is_empty())
|
|
||||||
});
|
|
||||||
|
|
||||||
/// A provider for credentials.
|
|
||||||
///
|
|
||||||
/// Used to abstract over reading and writing credentials to some form of
|
|
||||||
/// persistence (like the system keychain).
|
|
||||||
pub trait CredentialsProvider: Send + Sync {
|
|
||||||
/// Reads the credentials from the provider.
|
|
||||||
fn read_credentials<'a>(
|
|
||||||
&'a self,
|
|
||||||
url: &'a str,
|
|
||||||
cx: &'a AsyncApp,
|
|
||||||
) -> Pin<Box<dyn Future<Output = Result<Option<(String, Vec<u8>)>>> + 'a>>;
|
|
||||||
|
|
||||||
/// Writes the credentials to the provider.
|
|
||||||
fn write_credentials<'a>(
|
|
||||||
&'a self,
|
|
||||||
url: &'a str,
|
|
||||||
username: &'a str,
|
|
||||||
password: &'a [u8],
|
|
||||||
cx: &'a AsyncApp,
|
|
||||||
) -> Pin<Box<dyn Future<Output = Result<()>> + 'a>>;
|
|
||||||
|
|
||||||
/// Deletes the credentials from the provider.
|
|
||||||
fn delete_credentials<'a>(
|
|
||||||
&'a self,
|
|
||||||
url: &'a str,
|
|
||||||
cx: &'a AsyncApp,
|
|
||||||
) -> Pin<Box<dyn Future<Output = Result<()>> + 'a>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl dyn CredentialsProvider {
|
|
||||||
/// Returns the global [`CredentialsProvider`].
|
|
||||||
pub fn global(cx: &App) -> Arc<Self> {
|
|
||||||
// The `CredentialsProvider` trait has `Send + Sync` bounds on it, so it
|
|
||||||
// seems like this is a false positive from Clippy.
|
|
||||||
#[allow(clippy::arc_with_non_send_sync)]
|
|
||||||
Self::new(cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new(cx: &App) -> Arc<Self> {
|
|
||||||
let use_development_provider = match ReleaseChannel::try_global(cx) {
|
|
||||||
Some(ReleaseChannel::Dev) => {
|
|
||||||
// In development we default to using the development
|
|
||||||
// credentials provider to avoid getting spammed by relentless
|
|
||||||
// keychain access prompts.
|
|
||||||
//
|
|
||||||
// However, if the `ZED_DEVELOPMENT_USE_KEYCHAIN` environment
|
|
||||||
// variable is set, we will use the actual keychain.
|
|
||||||
!*ZED_DEVELOPMENT_USE_KEYCHAIN
|
|
||||||
}
|
|
||||||
Some(ReleaseChannel::Nightly | ReleaseChannel::Preview | ReleaseChannel::Stable)
|
|
||||||
| None => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if use_development_provider {
|
|
||||||
Arc::new(DevelopmentCredentialsProvider::new())
|
|
||||||
} else {
|
|
||||||
Arc::new(KeychainCredentialsProvider)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A credentials provider that stores credentials in the system keychain.
|
|
||||||
struct KeychainCredentialsProvider;
|
|
||||||
|
|
||||||
impl CredentialsProvider for KeychainCredentialsProvider {
|
|
||||||
fn read_credentials<'a>(
|
|
||||||
&'a self,
|
|
||||||
url: &'a str,
|
|
||||||
cx: &'a AsyncApp,
|
|
||||||
) -> Pin<Box<dyn Future<Output = Result<Option<(String, Vec<u8>)>>> + 'a>> {
|
|
||||||
async move { cx.update(|cx| cx.read_credentials(url))?.await }.boxed_local()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_credentials<'a>(
|
|
||||||
&'a self,
|
|
||||||
url: &'a str,
|
|
||||||
username: &'a str,
|
|
||||||
password: &'a [u8],
|
|
||||||
cx: &'a AsyncApp,
|
|
||||||
) -> Pin<Box<dyn Future<Output = Result<()>> + 'a>> {
|
|
||||||
async move {
|
|
||||||
cx.update(move |cx| cx.write_credentials(url, username, password))?
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
.boxed_local()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn delete_credentials<'a>(
|
|
||||||
&'a self,
|
|
||||||
url: &'a str,
|
|
||||||
cx: &'a AsyncApp,
|
|
||||||
) -> Pin<Box<dyn Future<Output = Result<()>> + 'a>> {
|
|
||||||
async move { cx.update(move |cx| cx.delete_credentials(url))?.await }.boxed_local()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A credentials provider that stores credentials in a local file.
|
|
||||||
///
|
|
||||||
/// This MUST only be used in development, as this is not a secure way of storing
|
|
||||||
/// credentials on user machines.
|
|
||||||
///
|
|
||||||
/// Its existence is purely to work around the annoyance of having to constantly
|
|
||||||
/// re-allow access to the system keychain when developing Zed.
|
|
||||||
struct DevelopmentCredentialsProvider {
|
|
||||||
path: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DevelopmentCredentialsProvider {
|
|
||||||
fn new() -> Self {
|
|
||||||
let path = paths::config_dir().join("development_credentials");
|
|
||||||
|
|
||||||
Self { path }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_credentials(&self) -> Result<HashMap<String, (String, Vec<u8>)>> {
|
|
||||||
let json = std::fs::read(&self.path)?;
|
|
||||||
let credentials: HashMap<String, (String, Vec<u8>)> = serde_json::from_slice(&json)?;
|
|
||||||
|
|
||||||
Ok(credentials)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn save_credentials(&self, credentials: &HashMap<String, (String, Vec<u8>)>) -> Result<()> {
|
|
||||||
let json = serde_json::to_string(credentials)?;
|
|
||||||
std::fs::write(&self.path, json)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CredentialsProvider for DevelopmentCredentialsProvider {
|
|
||||||
fn read_credentials<'a>(
|
|
||||||
&'a self,
|
|
||||||
url: &'a str,
|
|
||||||
_cx: &'a AsyncApp,
|
|
||||||
) -> Pin<Box<dyn Future<Output = Result<Option<(String, Vec<u8>)>>> + 'a>> {
|
|
||||||
async move {
|
|
||||||
Ok(self
|
|
||||||
.load_credentials()
|
|
||||||
.unwrap_or_default()
|
|
||||||
.get(url)
|
|
||||||
.cloned())
|
|
||||||
}
|
|
||||||
.boxed_local()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_credentials<'a>(
|
|
||||||
&'a self,
|
|
||||||
url: &'a str,
|
|
||||||
username: &'a str,
|
|
||||||
password: &'a [u8],
|
|
||||||
_cx: &'a AsyncApp,
|
|
||||||
) -> Pin<Box<dyn Future<Output = Result<()>> + 'a>> {
|
|
||||||
async move {
|
|
||||||
let mut credentials = self.load_credentials().unwrap_or_default();
|
|
||||||
credentials.insert(url.to_string(), (username.to_string(), password.to_vec()));
|
|
||||||
|
|
||||||
self.save_credentials(&credentials)
|
|
||||||
}
|
|
||||||
.boxed_local()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn delete_credentials<'a>(
|
|
||||||
&'a self,
|
|
||||||
url: &'a str,
|
|
||||||
_cx: &'a AsyncApp,
|
|
||||||
) -> Pin<Box<dyn Future<Output = Result<()>> + 'a>> {
|
|
||||||
async move {
|
|
||||||
let mut credentials = self.load_credentials()?;
|
|
||||||
credentials.remove(url);
|
|
||||||
|
|
||||||
self.save_credentials(&credentials)
|
|
||||||
}
|
|
||||||
.boxed_local()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -24,6 +24,7 @@ log.workspace = true
|
|||||||
lsp.workspace = true
|
lsp.workspace = true
|
||||||
project.workspace = true
|
project.workspace = true
|
||||||
rand.workspace = true
|
rand.workspace = true
|
||||||
|
schemars.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
settings.workspace = true
|
settings.workspace = true
|
||||||
theme.workspace = true
|
theme.workspace = true
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
pub mod items;
|
pub mod items;
|
||||||
|
mod project_diagnostics_settings;
|
||||||
mod toolbar_controls;
|
mod toolbar_controls;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -14,16 +15,17 @@ use editor::{
|
|||||||
Editor, EditorEvent, ExcerptId, ExcerptRange, MultiBuffer, ToOffset,
|
Editor, EditorEvent, ExcerptId, ExcerptRange, MultiBuffer, ToOffset,
|
||||||
};
|
};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, div, svg, AnyElement, AnyView, App, AsyncApp, Context, Entity, EventEmitter,
|
actions, div, svg, AnyElement, AnyView, App, Context, Entity, EventEmitter, FocusHandle,
|
||||||
FocusHandle, Focusable, Global, HighlightStyle, InteractiveElement, IntoElement, ParentElement,
|
Focusable, Global, HighlightStyle, InteractiveElement, IntoElement, ParentElement, Render,
|
||||||
Render, SharedString, Styled, StyledText, Subscription, Task, WeakEntity, Window,
|
SharedString, Styled, StyledText, Subscription, Task, WeakEntity, Window,
|
||||||
};
|
};
|
||||||
use language::{
|
use language::{
|
||||||
Bias, Buffer, BufferRow, BufferSnapshot, Diagnostic, DiagnosticEntry, DiagnosticSeverity,
|
Bias, Buffer, BufferRow, BufferSnapshot, Diagnostic, DiagnosticEntry, DiagnosticSeverity,
|
||||||
Point, Selection, SelectionGoal, ToTreeSitterPoint,
|
Point, Selection, SelectionGoal, ToTreeSitterPoint,
|
||||||
};
|
};
|
||||||
use lsp::LanguageServerId;
|
use lsp::LanguageServerId;
|
||||||
use project::{project_settings::ProjectSettings, DiagnosticSummary, Project, ProjectPath};
|
use project::{DiagnosticSummary, Project, ProjectPath};
|
||||||
|
use project_diagnostics_settings::ProjectDiagnosticsSettings;
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use std::{
|
use std::{
|
||||||
any::{Any, TypeId},
|
any::{Any, TypeId},
|
||||||
@@ -50,6 +52,7 @@ struct IncludeWarnings(bool);
|
|||||||
impl Global for IncludeWarnings {}
|
impl Global for IncludeWarnings {}
|
||||||
|
|
||||||
pub fn init(cx: &mut App) {
|
pub fn init(cx: &mut App) {
|
||||||
|
ProjectDiagnosticsSettings::register(cx);
|
||||||
cx.observe_new(ProjectDiagnosticsEditor::register).detach();
|
cx.observe_new(ProjectDiagnosticsEditor::register).detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,46 +91,15 @@ const DIAGNOSTICS_UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
|
|||||||
|
|
||||||
impl Render for ProjectDiagnosticsEditor {
|
impl Render for ProjectDiagnosticsEditor {
|
||||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
let warning_count = if self.include_warnings {
|
let child = if self.path_states.is_empty() {
|
||||||
self.summary.warning_count
|
div()
|
||||||
} else {
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
let child = if warning_count + self.summary.error_count == 0 {
|
|
||||||
let label = if self.summary.warning_count == 0 {
|
|
||||||
SharedString::new_static("No problems in workspace")
|
|
||||||
} else {
|
|
||||||
SharedString::new_static("No errors in workspace")
|
|
||||||
};
|
|
||||||
v_flex()
|
|
||||||
.key_context("EmptyPane")
|
.key_context("EmptyPane")
|
||||||
.size_full()
|
|
||||||
.gap_1()
|
|
||||||
.justify_center()
|
|
||||||
.items_center()
|
|
||||||
.text_center()
|
|
||||||
.bg(cx.theme().colors().editor_background)
|
.bg(cx.theme().colors().editor_background)
|
||||||
.child(Label::new(label).color(Color::Muted))
|
.flex()
|
||||||
.when(self.summary.warning_count > 0, |this| {
|
.items_center()
|
||||||
let plural_suffix = if self.summary.warning_count > 1 {
|
.justify_center()
|
||||||
"s"
|
.size_full()
|
||||||
} else {
|
.child(Label::new("No problems in workspace"))
|
||||||
""
|
|
||||||
};
|
|
||||||
let label = format!(
|
|
||||||
"Show {} warning{}",
|
|
||||||
self.summary.warning_count, plural_suffix
|
|
||||||
);
|
|
||||||
this.child(
|
|
||||||
Button::new("diagnostics-show-warning-label", label).on_click(cx.listener(
|
|
||||||
|this, _, window, cx| {
|
|
||||||
this.toggle_warnings(&Default::default(), window, cx);
|
|
||||||
cx.notify();
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
div().size_full().child(self.editor.clone())
|
div().size_full().child(self.editor.clone())
|
||||||
};
|
};
|
||||||
@@ -206,7 +178,6 @@ impl ProjectDiagnosticsEditor {
|
|||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
editor.set_vertical_scroll_margin(5, cx);
|
editor.set_vertical_scroll_margin(5, cx);
|
||||||
editor.disable_inline_diagnostics();
|
|
||||||
editor
|
editor
|
||||||
});
|
});
|
||||||
cx.subscribe_in(
|
cx.subscribe_in(
|
||||||
@@ -278,9 +249,8 @@ impl ProjectDiagnosticsEditor {
|
|||||||
.log_err()
|
.log_err()
|
||||||
{
|
{
|
||||||
this.update_in(&mut cx, |this, window, cx| {
|
this.update_in(&mut cx, |this, window, cx| {
|
||||||
this.update_excerpts(path, language_server_id, buffer, window, cx)
|
this.update_excerpts(path, language_server_id, buffer, window, cx);
|
||||||
})?
|
})?;
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -317,7 +287,7 @@ impl ProjectDiagnosticsEditor {
|
|||||||
|
|
||||||
let include_warnings = match cx.try_global::<IncludeWarnings>() {
|
let include_warnings = match cx.try_global::<IncludeWarnings>() {
|
||||||
Some(include_warnings) => include_warnings.0,
|
Some(include_warnings) => include_warnings.0,
|
||||||
None => ProjectSettings::get_global(cx).diagnostics.include_warnings,
|
None => ProjectDiagnosticsSettings::get_global(cx).include_warnings,
|
||||||
};
|
};
|
||||||
|
|
||||||
let diagnostics = cx.new(|cx| {
|
let diagnostics = cx.new(|cx| {
|
||||||
@@ -380,7 +350,7 @@ impl ProjectDiagnosticsEditor {
|
|||||||
buffer: Entity<Buffer>,
|
buffer: Entity<Buffer>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Task<Result<()>> {
|
) {
|
||||||
let was_empty = self.path_states.is_empty();
|
let was_empty = self.path_states.is_empty();
|
||||||
let snapshot = buffer.read(cx).snapshot();
|
let snapshot = buffer.read(cx).snapshot();
|
||||||
let path_ix = match self
|
let path_ix = match self
|
||||||
@@ -399,6 +369,7 @@ impl ProjectDiagnosticsEditor {
|
|||||||
ix
|
ix
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut prev_excerpt_id = if path_ix > 0 {
|
let mut prev_excerpt_id = if path_ix > 0 {
|
||||||
let prev_path_last_group = &self.path_states[path_ix - 1]
|
let prev_path_last_group = &self.path_states[path_ix - 1]
|
||||||
.diagnostic_groups
|
.diagnostic_groups
|
||||||
@@ -409,6 +380,7 @@ impl ProjectDiagnosticsEditor {
|
|||||||
ExcerptId::min()
|
ExcerptId::min()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let path_state = &mut self.path_states[path_ix];
|
||||||
let mut new_group_ixs = Vec::new();
|
let mut new_group_ixs = Vec::new();
|
||||||
let mut blocks_to_add = Vec::new();
|
let mut blocks_to_add = Vec::new();
|
||||||
let mut blocks_to_remove = HashSet::default();
|
let mut blocks_to_remove = HashSet::default();
|
||||||
@@ -418,14 +390,8 @@ impl ProjectDiagnosticsEditor {
|
|||||||
} else {
|
} else {
|
||||||
DiagnosticSeverity::ERROR
|
DiagnosticSeverity::ERROR
|
||||||
};
|
};
|
||||||
let excerpts = self.excerpts.clone().downgrade();
|
let excerpts_snapshot = self.excerpts.update(cx, |excerpts, cx| {
|
||||||
let context = self.context;
|
let mut old_groups = mem::take(&mut path_state.diagnostic_groups)
|
||||||
let editor = self.editor.clone().downgrade();
|
|
||||||
cx.spawn_in(window, move |this, mut cx| async move {
|
|
||||||
let mut old_groups = this
|
|
||||||
.update(&mut cx, |this, _| {
|
|
||||||
mem::take(&mut this.path_states[path_ix].diagnostic_groups)
|
|
||||||
})?
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.peekable();
|
.peekable();
|
||||||
@@ -487,19 +453,9 @@ impl ProjectDiagnosticsEditor {
|
|||||||
let mut is_first_excerpt_for_group = true;
|
let mut is_first_excerpt_for_group = true;
|
||||||
for (ix, entry) in group.entries.iter().map(Some).chain([None]).enumerate() {
|
for (ix, entry) in group.entries.iter().map(Some).chain([None]).enumerate() {
|
||||||
let resolved_entry = entry.map(|e| e.resolve::<Point>(&snapshot));
|
let resolved_entry = entry.map(|e| e.resolve::<Point>(&snapshot));
|
||||||
let expanded_range = if let Some(entry) = &resolved_entry {
|
let expanded_range = resolved_entry.as_ref().map(|entry| {
|
||||||
Some(
|
context_range_for_entry(entry, self.context, &snapshot, cx)
|
||||||
context_range_for_entry(
|
});
|
||||||
entry.range.clone(),
|
|
||||||
context,
|
|
||||||
snapshot.clone(),
|
|
||||||
(*cx).clone(),
|
|
||||||
)
|
|
||||||
.await,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
if let Some((range, context_range, start_ix)) = &mut pending_range {
|
if let Some((range, context_range, start_ix)) = &mut pending_range {
|
||||||
if let Some(expanded_range) = expanded_range.clone() {
|
if let Some(expanded_range) = expanded_range.clone() {
|
||||||
// If the entries are overlapping or next to each-other, merge them into one excerpt.
|
// If the entries are overlapping or next to each-other, merge them into one excerpt.
|
||||||
@@ -509,8 +465,7 @@ impl ProjectDiagnosticsEditor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let excerpt_id = excerpts.update(&mut cx, |excerpts, cx| {
|
let excerpt_id = excerpts
|
||||||
excerpts
|
|
||||||
.insert_excerpts_after(
|
.insert_excerpts_after(
|
||||||
prev_excerpt_id,
|
prev_excerpt_id,
|
||||||
buffer.clone(),
|
buffer.clone(),
|
||||||
@@ -521,8 +476,7 @@ impl ProjectDiagnosticsEditor {
|
|||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
.pop()
|
.pop()
|
||||||
.unwrap()
|
.unwrap();
|
||||||
})?;
|
|
||||||
|
|
||||||
prev_excerpt_id = excerpt_id;
|
prev_excerpt_id = excerpt_id;
|
||||||
first_excerpt_id.get_or_insert(prev_excerpt_id);
|
first_excerpt_id.get_or_insert(prev_excerpt_id);
|
||||||
@@ -562,7 +516,9 @@ impl ProjectDiagnosticsEditor {
|
|||||||
)),
|
)),
|
||||||
height: diagnostic.message.matches('\n').count() as u32 + 1,
|
height: diagnostic.message.matches('\n').count() as u32 + 1,
|
||||||
style: BlockStyle::Fixed,
|
style: BlockStyle::Fixed,
|
||||||
render: diagnostic_block_renderer(diagnostic, None, true),
|
render: diagnostic_block_renderer(
|
||||||
|
diagnostic, None, true, true,
|
||||||
|
),
|
||||||
priority: 0,
|
priority: 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -577,46 +533,32 @@ impl ProjectDiagnosticsEditor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.update(&mut cx, |this, _| {
|
new_group_ixs.push(path_state.diagnostic_groups.len());
|
||||||
new_group_ixs.push(this.path_states[path_ix].diagnostic_groups.len());
|
path_state.diagnostic_groups.push(group_state);
|
||||||
this.path_states[path_ix]
|
|
||||||
.diagnostic_groups
|
|
||||||
.push(group_state);
|
|
||||||
})?;
|
|
||||||
} else if let Some((_, group_state)) = to_remove {
|
} else if let Some((_, group_state)) = to_remove {
|
||||||
excerpts.update(&mut cx, |excerpts, cx| {
|
excerpts.remove_excerpts(group_state.excerpts.iter().copied(), cx);
|
||||||
excerpts.remove_excerpts(group_state.excerpts.iter().copied(), cx)
|
|
||||||
})?;
|
|
||||||
blocks_to_remove.extend(group_state.blocks.iter().copied());
|
blocks_to_remove.extend(group_state.blocks.iter().copied());
|
||||||
} else if let Some((_, group_state)) = to_keep {
|
} else if let Some((_, group_state)) = to_keep {
|
||||||
prev_excerpt_id = *group_state.excerpts.last().unwrap();
|
prev_excerpt_id = *group_state.excerpts.last().unwrap();
|
||||||
first_excerpt_id.get_or_insert(prev_excerpt_id);
|
first_excerpt_id.get_or_insert(prev_excerpt_id);
|
||||||
|
path_state.diagnostic_groups.push(group_state);
|
||||||
this.update(&mut cx, |this, _| {
|
|
||||||
this.path_states[path_ix]
|
|
||||||
.diagnostic_groups
|
|
||||||
.push(group_state)
|
|
||||||
})?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let excerpts_snapshot =
|
excerpts.snapshot(cx)
|
||||||
excerpts.update(&mut cx, |excerpts, cx| excerpts.snapshot(cx))?;
|
});
|
||||||
editor.update(&mut cx, |editor, cx| {
|
|
||||||
|
self.editor.update(cx, |editor, cx| {
|
||||||
editor.remove_blocks(blocks_to_remove, None, cx);
|
editor.remove_blocks(blocks_to_remove, None, cx);
|
||||||
let block_ids = editor.insert_blocks(
|
let block_ids = editor.insert_blocks(
|
||||||
blocks_to_add.into_iter().flat_map(|block| {
|
blocks_to_add.into_iter().flat_map(|block| {
|
||||||
let placement = match block.placement {
|
let placement = match block.placement {
|
||||||
BlockPlacement::Above((excerpt_id, text_anchor)) => {
|
BlockPlacement::Above((excerpt_id, text_anchor)) => BlockPlacement::Above(
|
||||||
BlockPlacement::Above(
|
|
||||||
excerpts_snapshot.anchor_in_excerpt(excerpt_id, text_anchor)?,
|
excerpts_snapshot.anchor_in_excerpt(excerpt_id, text_anchor)?,
|
||||||
)
|
),
|
||||||
}
|
BlockPlacement::Below((excerpt_id, text_anchor)) => BlockPlacement::Below(
|
||||||
BlockPlacement::Below((excerpt_id, text_anchor)) => {
|
|
||||||
BlockPlacement::Below(
|
|
||||||
excerpts_snapshot.anchor_in_excerpt(excerpt_id, text_anchor)?,
|
excerpts_snapshot.anchor_in_excerpt(excerpt_id, text_anchor)?,
|
||||||
)
|
),
|
||||||
}
|
|
||||||
BlockPlacement::Replace(_) => {
|
BlockPlacement::Replace(_) => {
|
||||||
unreachable!(
|
unreachable!(
|
||||||
"no Replace block should have been pushed to blocks_to_add"
|
"no Replace block should have been pushed to blocks_to_add"
|
||||||
@@ -636,29 +578,23 @@ impl ProjectDiagnosticsEditor {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let mut block_ids = block_ids.into_iter();
|
let mut block_ids = block_ids.into_iter();
|
||||||
this.update(cx, |this, _| {
|
|
||||||
for ix in new_group_ixs {
|
for ix in new_group_ixs {
|
||||||
let group_state = &mut this.path_states[path_ix].diagnostic_groups[ix];
|
let group_state = &mut path_state.diagnostic_groups[ix];
|
||||||
group_state.blocks =
|
group_state.blocks = block_ids.by_ref().take(group_state.block_count).collect();
|
||||||
block_ids.by_ref().take(group_state.block_count).collect();
|
|
||||||
}
|
}
|
||||||
})?;
|
});
|
||||||
Result::<(), anyhow::Error>::Ok(())
|
|
||||||
})??;
|
|
||||||
|
|
||||||
this.update_in(&mut cx, |this, window, cx| {
|
if path_state.diagnostic_groups.is_empty() {
|
||||||
if this.path_states[path_ix].diagnostic_groups.is_empty() {
|
self.path_states.remove(path_ix);
|
||||||
this.path_states.remove(path_ix);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.editor.update(cx, |editor, cx| {
|
self.editor.update(cx, |editor, cx| {
|
||||||
let groups;
|
let groups;
|
||||||
let mut selections;
|
let mut selections;
|
||||||
let new_excerpt_ids_by_selection_id;
|
let new_excerpt_ids_by_selection_id;
|
||||||
if was_empty {
|
if was_empty {
|
||||||
groups = this.path_states.first()?.diagnostic_groups.as_slice();
|
groups = self.path_states.first()?.diagnostic_groups.as_slice();
|
||||||
new_excerpt_ids_by_selection_id =
|
new_excerpt_ids_by_selection_id = [(0, ExcerptId::min())].into_iter().collect();
|
||||||
[(0, ExcerptId::min())].into_iter().collect();
|
|
||||||
selections = vec![Selection {
|
selections = vec![Selection {
|
||||||
id: 0,
|
id: 0,
|
||||||
start: 0,
|
start: 0,
|
||||||
@@ -667,20 +603,16 @@ impl ProjectDiagnosticsEditor {
|
|||||||
goal: SelectionGoal::None,
|
goal: SelectionGoal::None,
|
||||||
}];
|
}];
|
||||||
} else {
|
} else {
|
||||||
groups = this.path_states.get(path_ix)?.diagnostic_groups.as_slice();
|
groups = self.path_states.get(path_ix)?.diagnostic_groups.as_slice();
|
||||||
new_excerpt_ids_by_selection_id =
|
new_excerpt_ids_by_selection_id =
|
||||||
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
|
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| s.refresh());
|
||||||
s.refresh()
|
|
||||||
});
|
|
||||||
selections = editor.selections.all::<usize>(cx);
|
selections = editor.selections.all::<usize>(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If any selection has lost its position, move it to start of the next primary diagnostic.
|
// If any selection has lost its position, move it to start of the next primary diagnostic.
|
||||||
let snapshot = editor.snapshot(window, cx);
|
let snapshot = editor.snapshot(window, cx);
|
||||||
for selection in &mut selections {
|
for selection in &mut selections {
|
||||||
if let Some(new_excerpt_id) =
|
if let Some(new_excerpt_id) = new_excerpt_ids_by_selection_id.get(&selection.id) {
|
||||||
new_excerpt_ids_by_selection_id.get(&selection.id)
|
|
||||||
{
|
|
||||||
let group_ix = match groups.binary_search_by(|probe| {
|
let group_ix = match groups.binary_search_by(|probe| {
|
||||||
probe
|
probe
|
||||||
.excerpts
|
.excerpts
|
||||||
@@ -709,24 +641,20 @@ impl ProjectDiagnosticsEditor {
|
|||||||
});
|
});
|
||||||
Some(())
|
Some(())
|
||||||
});
|
});
|
||||||
})?;
|
|
||||||
|
|
||||||
this.update_in(&mut cx, |this, window, cx| {
|
if self.path_states.is_empty() {
|
||||||
if this.path_states.is_empty() {
|
if self.editor.focus_handle(cx).is_focused(window) {
|
||||||
if this.editor.focus_handle(cx).is_focused(window) {
|
window.focus(&self.focus_handle);
|
||||||
window.focus(&this.focus_handle);
|
|
||||||
}
|
}
|
||||||
} else if this.focus_handle.is_focused(window) {
|
} else if self.focus_handle.is_focused(window) {
|
||||||
let focus_handle = this.editor.focus_handle(cx);
|
let focus_handle = self.editor.focus_handle(cx);
|
||||||
window.focus(&focus_handle);
|
window.focus(&focus_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
this.check_invariants(cx);
|
self.check_invariants(cx);
|
||||||
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -1050,30 +978,29 @@ fn compare_diagnostics(
|
|||||||
const DIAGNOSTIC_EXPANSION_ROW_LIMIT: u32 = 32;
|
const DIAGNOSTIC_EXPANSION_ROW_LIMIT: u32 = 32;
|
||||||
|
|
||||||
fn context_range_for_entry(
|
fn context_range_for_entry(
|
||||||
range: Range<Point>,
|
entry: &DiagnosticEntry<Point>,
|
||||||
context: u32,
|
context: u32,
|
||||||
snapshot: BufferSnapshot,
|
snapshot: &BufferSnapshot,
|
||||||
cx: AsyncApp,
|
cx: &App,
|
||||||
) -> Task<Range<Point>> {
|
) -> Range<Point> {
|
||||||
cx.spawn(move |cx| async move {
|
|
||||||
if let Some(rows) = heuristic_syntactic_expand(
|
if let Some(rows) = heuristic_syntactic_expand(
|
||||||
range.clone(),
|
entry.range.clone(),
|
||||||
DIAGNOSTIC_EXPANSION_ROW_LIMIT,
|
DIAGNOSTIC_EXPANSION_ROW_LIMIT,
|
||||||
snapshot.clone(),
|
snapshot,
|
||||||
cx,
|
cx,
|
||||||
)
|
) {
|
||||||
.await
|
|
||||||
{
|
|
||||||
return Range {
|
return Range {
|
||||||
start: Point::new(*rows.start(), 0),
|
start: Point::new(*rows.start(), 0),
|
||||||
end: snapshot.clip_point(Point::new(*rows.end(), u32::MAX), Bias::Left),
|
end: snapshot.clip_point(Point::new(*rows.end(), u32::MAX), Bias::Left),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Range {
|
Range {
|
||||||
start: Point::new(range.start.row.saturating_sub(context), 0),
|
start: Point::new(entry.range.start.row.saturating_sub(context), 0),
|
||||||
end: snapshot.clip_point(Point::new(range.end.row + context, u32::MAX), Bias::Left),
|
end: snapshot.clip_point(
|
||||||
|
Point::new(entry.range.end.row + context, u32::MAX),
|
||||||
|
Bias::Left,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Expands the input range using syntax information from TreeSitter. This expansion will be limited
|
/// Expands the input range using syntax information from TreeSitter. This expansion will be limited
|
||||||
@@ -1081,11 +1008,11 @@ fn context_range_for_entry(
|
|||||||
///
|
///
|
||||||
/// If there is a containing outline item that is less than `max_row_count`, it will be returned.
|
/// If there is a containing outline item that is less than `max_row_count`, it will be returned.
|
||||||
/// Otherwise fairly arbitrary heuristics are applied to attempt to return a logical block of code.
|
/// Otherwise fairly arbitrary heuristics are applied to attempt to return a logical block of code.
|
||||||
async fn heuristic_syntactic_expand(
|
fn heuristic_syntactic_expand<'a>(
|
||||||
input_range: Range<Point>,
|
input_range: Range<Point>,
|
||||||
max_row_count: u32,
|
max_row_count: u32,
|
||||||
snapshot: BufferSnapshot,
|
snapshot: &'a BufferSnapshot,
|
||||||
cx: AsyncApp,
|
cx: &'a App,
|
||||||
) -> Option<RangeInclusive<BufferRow>> {
|
) -> Option<RangeInclusive<BufferRow>> {
|
||||||
let input_row_count = input_range.end.row - input_range.start.row;
|
let input_row_count = input_range.end.row - input_range.start.row;
|
||||||
if input_row_count > max_row_count {
|
if input_row_count > max_row_count {
|
||||||
@@ -1115,18 +1042,12 @@ async fn heuristic_syntactic_expand(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut node = snapshot.syntax_ancestor(input_range.clone())?;
|
let mut node = snapshot.syntax_ancestor(input_range.clone())?;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let node_start = Point::from_ts_point(node.start_position());
|
let node_start = Point::from_ts_point(node.start_position());
|
||||||
let node_end = Point::from_ts_point(node.end_position());
|
let node_end = Point::from_ts_point(node.end_position());
|
||||||
let node_range = node_start..node_end;
|
let node_range = node_start..node_end;
|
||||||
let row_count = node_end.row - node_start.row + 1;
|
let row_count = node_end.row - node_start.row + 1;
|
||||||
let mut ancestor_range = None;
|
|
||||||
let reached_outline_node = cx.background_executor().scoped({
|
|
||||||
let node_range = node_range.clone();
|
|
||||||
let outline_range = outline_range.clone();
|
|
||||||
let ancestor_range = &mut ancestor_range;
|
|
||||||
|scope| {scope.spawn(async move {
|
|
||||||
// Stop if we've exceeded the row count or reached an outline node. Then, find the interval
|
// Stop if we've exceeded the row count or reached an outline node. Then, find the interval
|
||||||
// of node children which contains the query range. For example, this allows just returning
|
// of node children which contains the query range. For example, this allows just returning
|
||||||
// the header of a declaration rather than the entire declaration.
|
// the header of a declaration rather than the entire declaration.
|
||||||
@@ -1155,8 +1076,7 @@ async fn heuristic_syntactic_expand(
|
|||||||
if let Some(start) = included_child_start {
|
if let Some(start) = included_child_start {
|
||||||
let row_count = end.row - start.row;
|
let row_count = end.row - start.row;
|
||||||
if row_count < max_row_count {
|
if row_count < max_row_count {
|
||||||
*ancestor_range = Some(Some(RangeInclusive::new(start.row, end.row)));
|
return Some(RangeInclusive::new(start.row, end.row));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1164,13 +1084,7 @@ async fn heuristic_syntactic_expand(
|
|||||||
"Expanding to ancestor started on {} node exceeding row limit of {max_row_count}.",
|
"Expanding to ancestor started on {} node exceeding row limit of {max_row_count}.",
|
||||||
node.grammar_name()
|
node.grammar_name()
|
||||||
);
|
);
|
||||||
*ancestor_range = Some(None);
|
return None;
|
||||||
}
|
|
||||||
})
|
|
||||||
}});
|
|
||||||
reached_outline_node.await;
|
|
||||||
if let Some(node) = ancestor_range {
|
|
||||||
return node;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let node_name = node.grammar_name();
|
let node_name = node.grammar_name();
|
||||||
@@ -1179,9 +1093,7 @@ async fn heuristic_syntactic_expand(
|
|||||||
return Some(node_row_range);
|
return Some(node_row_range);
|
||||||
} else if node_name.ends_with("statement") || node_name.ends_with("declaration") {
|
} else if node_name.ends_with("statement") || node_name.ends_with("declaration") {
|
||||||
// Expand to the nearest dedent or blank line for statements and declarations.
|
// Expand to the nearest dedent or blank line for statements and declarations.
|
||||||
let tab_size = cx
|
let tab_size = snapshot.settings_at(node_range.start, cx).tab_size.get();
|
||||||
.update(|cx| snapshot.settings_at(node_range.start, cx).tab_size.get())
|
|
||||||
.ok()?;
|
|
||||||
let indent_level = snapshot
|
let indent_level = snapshot
|
||||||
.line_indent_for_row(node_range.start.row)
|
.line_indent_for_row(node_range.start.row)
|
||||||
.len(tab_size);
|
.len(tab_size);
|
||||||
@@ -1189,9 +1101,7 @@ async fn heuristic_syntactic_expand(
|
|||||||
let Some(start_row) = (node_range.start.row.saturating_sub(rows_remaining)
|
let Some(start_row) = (node_range.start.row.saturating_sub(rows_remaining)
|
||||||
..node_range.start.row)
|
..node_range.start.row)
|
||||||
.rev()
|
.rev()
|
||||||
.find(|row| {
|
.find(|row| is_line_blank_or_indented_less(indent_level, *row, tab_size, snapshot))
|
||||||
is_line_blank_or_indented_less(indent_level, *row, tab_size, &snapshot.clone())
|
|
||||||
})
|
|
||||||
else {
|
else {
|
||||||
return Some(node_row_range);
|
return Some(node_row_range);
|
||||||
};
|
};
|
||||||
@@ -1201,9 +1111,7 @@ async fn heuristic_syntactic_expand(
|
|||||||
node_range.end.row + rows_remaining + 1,
|
node_range.end.row + rows_remaining + 1,
|
||||||
snapshot.row_count(),
|
snapshot.row_count(),
|
||||||
))
|
))
|
||||||
.find(|row| {
|
.find(|row| is_line_blank_or_indented_less(indent_level, *row, tab_size, snapshot))
|
||||||
is_line_blank_or_indented_less(indent_level, *row, tab_size, &snapshot.clone())
|
|
||||||
})
|
|
||||||
else {
|
else {
|
||||||
return Some(node_row_range);
|
return Some(node_row_range);
|
||||||
};
|
};
|
||||||
|
|||||||
28
crates/diagnostics/src/project_diagnostics_settings.rs
Normal file
28
crates/diagnostics/src/project_diagnostics_settings.rs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use gpui::App;
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use settings::{Settings, SettingsSources};
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct ProjectDiagnosticsSettings {
|
||||||
|
pub include_warnings: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Diagnostics configuration.
|
||||||
|
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
|
||||||
|
pub struct ProjectDiagnosticsSettingsContent {
|
||||||
|
/// Whether to show warnings or not by default.
|
||||||
|
///
|
||||||
|
/// Default: true
|
||||||
|
include_warnings: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Settings for ProjectDiagnosticsSettings {
|
||||||
|
const KEY: Option<&'static str> = Some("diagnostics");
|
||||||
|
type FileContent = ProjectDiagnosticsSettingsContent;
|
||||||
|
|
||||||
|
fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
|
||||||
|
sources.json_merge()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,8 +22,6 @@ pub struct SelectPrevious {
|
|||||||
pub struct MoveToBeginningOfLine {
|
pub struct MoveToBeginningOfLine {
|
||||||
#[serde(default = "default_true")]
|
#[serde(default = "default_true")]
|
||||||
pub stop_at_soft_wraps: bool,
|
pub stop_at_soft_wraps: bool,
|
||||||
#[serde(default)]
|
|
||||||
pub stop_at_indent: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
|
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
|
||||||
@@ -31,8 +29,6 @@ pub struct MoveToBeginningOfLine {
|
|||||||
pub struct SelectToBeginningOfLine {
|
pub struct SelectToBeginningOfLine {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub(super) stop_at_soft_wraps: bool,
|
pub(super) stop_at_soft_wraps: bool,
|
||||||
#[serde(default)]
|
|
||||||
pub stop_at_indent: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
|
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
|
||||||
@@ -284,6 +280,7 @@ gpui::actions!(
|
|||||||
DuplicateLineDown,
|
DuplicateLineDown,
|
||||||
DuplicateLineUp,
|
DuplicateLineUp,
|
||||||
DuplicateSelection,
|
DuplicateSelection,
|
||||||
|
ExpandAllHunkDiffs,
|
||||||
ExpandMacroRecursively,
|
ExpandMacroRecursively,
|
||||||
FindAllReferences,
|
FindAllReferences,
|
||||||
Fold,
|
Fold,
|
||||||
@@ -332,8 +329,6 @@ gpui::actions!(
|
|||||||
MoveToPreviousSubwordStart,
|
MoveToPreviousSubwordStart,
|
||||||
MoveToPreviousWordStart,
|
MoveToPreviousWordStart,
|
||||||
MoveToStartOfParagraph,
|
MoveToStartOfParagraph,
|
||||||
MoveToStartOfExcerpt,
|
|
||||||
MoveToEndOfExcerpt,
|
|
||||||
MoveUp,
|
MoveUp,
|
||||||
Newline,
|
Newline,
|
||||||
NewlineAbove,
|
NewlineAbove,
|
||||||
@@ -369,8 +364,6 @@ gpui::actions!(
|
|||||||
ScrollCursorTop,
|
ScrollCursorTop,
|
||||||
SelectAll,
|
SelectAll,
|
||||||
SelectAllMatches,
|
SelectAllMatches,
|
||||||
SelectToStartOfExcerpt,
|
|
||||||
SelectToEndOfExcerpt,
|
|
||||||
SelectDown,
|
SelectDown,
|
||||||
SelectEnclosingSymbol,
|
SelectEnclosingSymbol,
|
||||||
SelectLargerSyntaxNode,
|
SelectLargerSyntaxNode,
|
||||||
@@ -404,7 +397,6 @@ gpui::actions!(
|
|||||||
ToggleGitBlameInline,
|
ToggleGitBlameInline,
|
||||||
ToggleIndentGuides,
|
ToggleIndentGuides,
|
||||||
ToggleInlayHints,
|
ToggleInlayHints,
|
||||||
ToggleInlineDiagnostics,
|
|
||||||
ToggleEditPrediction,
|
ToggleEditPrediction,
|
||||||
ToggleLineNumbers,
|
ToggleLineNumbers,
|
||||||
SwapSelectionEnds,
|
SwapSelectionEnds,
|
||||||
@@ -428,4 +420,3 @@ action_as!(go_to_line, ToggleGoToLine as Toggle);
|
|||||||
|
|
||||||
action_with_deprecated_aliases!(editor, OpenSelectedFilename, ["editor::OpenFile"]);
|
action_with_deprecated_aliases!(editor, OpenSelectedFilename, ["editor::OpenFile"]);
|
||||||
action_with_deprecated_aliases!(editor, ToggleSelectedDiffHunks, ["editor::ToggleHunkDiff"]);
|
action_with_deprecated_aliases!(editor, ToggleSelectedDiffHunks, ["editor::ToggleHunkDiff"]);
|
||||||
action_with_deprecated_aliases!(editor, ExpandAllDiffHunks, ["editor::ExpandAllHunkDiffs"]);
|
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ impl Render for CommitTooltip {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|details| details.pull_request.clone());
|
.and_then(|details| details.pull_request.clone());
|
||||||
|
|
||||||
let ui_font_size = ThemeSettings::get_global(cx).ui_font_size(cx);
|
let ui_font_size = ThemeSettings::get_global(cx).ui_font_size;
|
||||||
let message_max_height = window.line_height() * 12 + (ui_font_size / 0.4);
|
let message_max_height = window.line_height() * 12 + (ui_font_size / 0.4);
|
||||||
|
|
||||||
tooltip_container(window, cx, move |this, _, cx| {
|
tooltip_container(window, cx, move |this, _, cx| {
|
||||||
|
|||||||
@@ -330,11 +330,7 @@ impl DisplayMap {
|
|||||||
block_map.remove_intersecting_replace_blocks(offset_ranges, inclusive);
|
block_map.remove_intersecting_replace_blocks(offset_ranges, inclusive);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fold_buffers(
|
pub fn fold_buffer(&mut self, buffer_id: language::BufferId, cx: &mut Context<Self>) {
|
||||||
&mut self,
|
|
||||||
buffer_ids: impl IntoIterator<Item = language::BufferId>,
|
|
||||||
cx: &mut Context<Self>,
|
|
||||||
) {
|
|
||||||
let snapshot = self.buffer.read(cx).snapshot(cx);
|
let snapshot = self.buffer.read(cx).snapshot(cx);
|
||||||
let edits = self.buffer_subscription.consume().into_inner();
|
let edits = self.buffer_subscription.consume().into_inner();
|
||||||
let tab_size = Self::tab_size(&self.buffer, cx);
|
let tab_size = Self::tab_size(&self.buffer, cx);
|
||||||
@@ -345,14 +341,10 @@ impl DisplayMap {
|
|||||||
.wrap_map
|
.wrap_map
|
||||||
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
|
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
|
||||||
let mut block_map = self.block_map.write(snapshot, edits);
|
let mut block_map = self.block_map.write(snapshot, edits);
|
||||||
block_map.fold_buffers(buffer_ids, self.buffer.read(cx), cx)
|
block_map.fold_buffer(buffer_id, self.buffer.read(cx), cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unfold_buffers(
|
pub fn unfold_buffer(&mut self, buffer_id: language::BufferId, cx: &mut Context<Self>) {
|
||||||
&mut self,
|
|
||||||
buffer_ids: impl IntoIterator<Item = language::BufferId>,
|
|
||||||
cx: &mut Context<Self>,
|
|
||||||
) {
|
|
||||||
let snapshot = self.buffer.read(cx).snapshot(cx);
|
let snapshot = self.buffer.read(cx).snapshot(cx);
|
||||||
let edits = self.buffer_subscription.consume().into_inner();
|
let edits = self.buffer_subscription.consume().into_inner();
|
||||||
let tab_size = Self::tab_size(&self.buffer, cx);
|
let tab_size = Self::tab_size(&self.buffer, cx);
|
||||||
@@ -363,7 +355,7 @@ impl DisplayMap {
|
|||||||
.wrap_map
|
.wrap_map
|
||||||
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
|
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
|
||||||
let mut block_map = self.block_map.write(snapshot, edits);
|
let mut block_map = self.block_map.write(snapshot, edits);
|
||||||
block_map.unfold_buffers(buffer_ids, self.buffer.read(cx), cx)
|
block_map.unfold_buffer(buffer_id, self.buffer.read(cx), cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_buffer_folded(&self, buffer_id: language::BufferId) -> bool {
|
pub(crate) fn is_buffer_folded(&self, buffer_id: language::BufferId) -> bool {
|
||||||
@@ -1919,67 +1911,6 @@ pub mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
|
||||||
fn test_inlays_with_newlines_after_blocks(cx: &mut gpui::TestAppContext) {
|
|
||||||
cx.update(|cx| init_test(cx, |_| {}));
|
|
||||||
|
|
||||||
let buffer = cx.new(|cx| Buffer::local("a", cx));
|
|
||||||
let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
|
|
||||||
let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
|
|
||||||
|
|
||||||
let font_size = px(14.0);
|
|
||||||
let map = cx.new(|cx| {
|
|
||||||
DisplayMap::new(
|
|
||||||
buffer.clone(),
|
|
||||||
font("Helvetica"),
|
|
||||||
font_size,
|
|
||||||
None,
|
|
||||||
true,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
FoldPlaceholder::test(),
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
map.update(cx, |map, cx| {
|
|
||||||
map.insert_blocks(
|
|
||||||
[BlockProperties {
|
|
||||||
placement: BlockPlacement::Above(
|
|
||||||
buffer_snapshot.anchor_before(Point::new(0, 0)),
|
|
||||||
),
|
|
||||||
height: 2,
|
|
||||||
style: BlockStyle::Sticky,
|
|
||||||
render: Arc::new(|_| div().into_any()),
|
|
||||||
priority: 0,
|
|
||||||
}],
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
map.update(cx, |m, cx| assert_eq!(m.snapshot(cx).text(), "\n\na"));
|
|
||||||
|
|
||||||
map.update(cx, |map, cx| {
|
|
||||||
map.splice_inlays(
|
|
||||||
&[],
|
|
||||||
vec![Inlay {
|
|
||||||
id: InlayId::InlineCompletion(0),
|
|
||||||
position: buffer_snapshot.anchor_after(0),
|
|
||||||
text: "\n".into(),
|
|
||||||
}],
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
map.update(cx, |m, cx| assert_eq!(m.snapshot(cx).text(), "\n\n\na"));
|
|
||||||
|
|
||||||
// Regression test: updating the display map does not crash when a
|
|
||||||
// block is immediately followed by a multi-line inlay.
|
|
||||||
buffer.update(cx, |buffer, cx| {
|
|
||||||
buffer.edit([(1..1, "b")], None, cx);
|
|
||||||
});
|
|
||||||
map.update(cx, |m, cx| assert_eq!(m.snapshot(cx).text(), "\n\n\nab"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_chunks(cx: &mut gpui::TestAppContext) {
|
async fn test_chunks(cx: &mut gpui::TestAppContext) {
|
||||||
let text = r#"
|
let text = r#"
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user