Compare commits
202 Commits
git-push-2
...
v0.176.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
efe4418b9a | ||
|
|
64b2ddca38 | ||
|
|
4468d1925d | ||
|
|
8d16ddbad8 | ||
|
|
21dd8c21b8 | ||
|
|
121bd97cb1 | ||
|
|
2433e99d05 | ||
|
|
c75c2276b0 | ||
|
|
3659466f5b | ||
|
|
a82d7aa2a9 | ||
|
|
977e051983 | ||
|
|
d82a132477 | ||
|
|
f11357db7c | ||
|
|
6d17546b1a | ||
|
|
60a96ab799 | ||
|
|
1f80f58104 | ||
|
|
bab65011b4 | ||
|
|
c0b6d86c41 | ||
|
|
39728cfc59 | ||
|
|
ebccef1aa4 | ||
|
|
33754f8eac | ||
|
|
dd1ff9b998 | ||
|
|
7f214ed25a | ||
|
|
08539b32d0 | ||
|
|
88baf171c3 | ||
|
|
2f34af7811 | ||
|
|
2978be95d7 | ||
|
|
30568e6dd1 | ||
|
|
a5698a430d | ||
|
|
57659b5552 | ||
|
|
3db18ff053 | ||
|
|
198f56c763 | ||
|
|
d68d858a10 | ||
|
|
7f166298db | ||
|
|
0066071a89 | ||
|
|
e5b6194914 | ||
|
|
23f61d5954 | ||
|
|
0559e1f348 | ||
|
|
014d9dfce1 | ||
|
|
a0aea6ef62 | ||
|
|
278620df33 | ||
|
|
75dbe189bd | ||
|
|
3d7ba7c1c0 | ||
|
|
eebee4ab18 | ||
|
|
3a3621f2d8 | ||
|
|
524e813d20 | ||
|
|
b12b8340de | ||
|
|
7075bd700f | ||
|
|
21fc3c07b6 | ||
|
|
c90f87898a | ||
|
|
796e87ecbc | ||
|
|
3a041cac72 | ||
|
|
86283f4e3d | ||
|
|
8e1003ef59 | ||
|
|
8e891c16f8 | ||
|
|
cea06bc0ce | ||
|
|
45146b6f30 | ||
|
|
bcbb19e06e | ||
|
|
3f168e85c2 | ||
|
|
20440f83e9 | ||
|
|
2d63f7628f | ||
|
|
52f73e0c2d | ||
|
|
980e1b533f | ||
|
|
2ea332421c | ||
|
|
4edecfed3e | ||
|
|
113c471bb0 | ||
|
|
10fef92eea | ||
|
|
3ee4edc404 | ||
|
|
eda233344c | ||
|
|
17a483cb03 | ||
|
|
7b277d2efd | ||
|
|
63cfcc26fb | ||
|
|
53a5145dc8 | ||
|
|
def342e35c | ||
|
|
bbb8d63de0 | ||
|
|
e5b97a5e48 | ||
|
|
e06666759a | ||
|
|
a78f3cfea2 | ||
|
|
0acd556106 | ||
|
|
2f7a62780a | ||
|
|
f1e6b144e8 | ||
|
|
10a4760f90 | ||
|
|
cdd07fdf29 | ||
|
|
8a3fb890b0 | ||
|
|
30af8d0a81 | ||
|
|
ceb7fc2cb2 | ||
|
|
d8694510b5 | ||
|
|
ec7ce41324 | ||
|
|
b2921bd3fd | ||
|
|
64756fa96f | ||
|
|
ff6844300e | ||
|
|
b1b6401ce7 | ||
|
|
07ba7c8c44 | ||
|
|
6516249302 | ||
|
|
fc8218d728 | ||
|
|
a8d56877ee | ||
|
|
dd0de3cfa9 | ||
|
|
198c36811e | ||
|
|
133704a419 | ||
|
|
d1302a7a08 | ||
|
|
2b28b5969f | ||
|
|
bc941bfc97 | ||
|
|
72a9429ef6 | ||
|
|
f020291039 | ||
|
|
4b3a2a33a8 | ||
|
|
1f257f4704 | ||
|
|
f517050548 | ||
|
|
535ba75bc7 | ||
|
|
ee280b0d05 | ||
|
|
a43793493a | ||
|
|
3b3c379852 | ||
|
|
147f407b7a | ||
|
|
822f42e8fe | ||
|
|
7e097d529a | ||
|
|
65f76e6f4d | ||
|
|
918cba4cce | ||
|
|
4067ae4b37 | ||
|
|
084a0233b6 | ||
|
|
04732b23fb | ||
|
|
abc027558a | ||
|
|
4d106a4b0b | ||
|
|
ec4d9ec111 | ||
|
|
10053e2566 | ||
|
|
7a55da58d9 | ||
|
|
5043eaedc4 | ||
|
|
328e4d6278 | ||
|
|
83513bab59 | ||
|
|
b9ca240242 | ||
|
|
5d751cd407 | ||
|
|
ef53f7af22 | ||
|
|
4118f42d61 | ||
|
|
ec56755f9e | ||
|
|
aba89ba12a | ||
|
|
7ff40091d8 | ||
|
|
dabc35b257 | ||
|
|
8f40bcc86c | ||
|
|
3e75a661dd | ||
|
|
a9e753fc07 | ||
|
|
2d6592dcaa | ||
|
|
59a153b2e1 | ||
|
|
10a6cd00e7 | ||
|
|
f6f6980463 | ||
|
|
2e302b1a7c | ||
|
|
17323ed7b2 | ||
|
|
3759e0bf55 | ||
|
|
144d8a1db6 | ||
|
|
7deceb62dc | ||
|
|
a97a2ebf35 | ||
|
|
5e4bdbbcde | ||
|
|
d0a0303428 | ||
|
|
a4ade37a56 | ||
|
|
1fb4620a90 | ||
|
|
3c4903c6bf | ||
|
|
ad4163b9be | ||
|
|
2f416aebbe | ||
|
|
5397ca23a1 | ||
|
|
c9235ff916 | ||
|
|
dff47a8436 | ||
|
|
d45aaa1745 | ||
|
|
5e1dd91ee5 | ||
|
|
4871d3c9e7 | ||
|
|
6b9397c380 | ||
|
|
c31c638006 | ||
|
|
ee1a559827 | ||
|
|
30850fe3bd | ||
|
|
ec00fb97fd | ||
|
|
5dd351551d | ||
|
|
5ae93ce68d | ||
|
|
74c581b9f4 | ||
|
|
3a222f0666 | ||
|
|
300ed6bb4a | ||
|
|
3c8dbaf06b | ||
|
|
4151b5b12b | ||
|
|
274e70e81d | ||
|
|
0eea8c3e60 | ||
|
|
191a1d7b10 | ||
|
|
2581f8be92 | ||
|
|
a1223e0646 | ||
|
|
a8610fbd13 | ||
|
|
3116850688 | ||
|
|
21bb7242ea | ||
|
|
31aad858f8 | ||
|
|
c18be3ecd0 | ||
|
|
f609abb48c | ||
|
|
78a8002415 | ||
|
|
f2b7d8a9c9 | ||
|
|
1d513dd32b | ||
|
|
b84aec0fae | ||
|
|
d4392aaf2d | ||
|
|
7f3e2e4aba | ||
|
|
43f2e4c476 | ||
|
|
48541fb05a | ||
|
|
f973b260c5 | ||
|
|
5c40326174 | ||
|
|
c043adf764 | ||
|
|
c309ceb52b | ||
|
|
b8df91aa25 | ||
|
|
1429363218 | ||
|
|
528da6eb26 | ||
|
|
6b06f39fa8 | ||
|
|
cc46a1fe30 | ||
|
|
0c0201c79f |
25
.github/ISSUE_TEMPLATE/1_bug_report.yml
vendored
25
.github/ISSUE_TEMPLATE/1_bug_report.yml
vendored
@@ -10,16 +10,39 @@ body:
|
||||
value: |
|
||||
<!-- Please insert a one line summary of the issue below -->
|
||||
|
||||
<!-- Include all steps necessary to reproduce from a clean Zed installation. Be verbose -->
|
||||
SUMMARY_SENTENCE_HERE
|
||||
|
||||
<!-- 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:
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
|
||||
Actual 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:
|
||||
required: true
|
||||
|
||||
|
||||
15
.github/workflows/ci.yml
vendored
15
.github/workflows/ci.yml
vendored
@@ -109,8 +109,16 @@ jobs:
|
||||
- name: cargo clippy
|
||||
run: ./script/clippy
|
||||
|
||||
- name: Install cargo-machete
|
||||
uses: clechasseur/rs-cargo@v2
|
||||
with:
|
||||
command: install
|
||||
args: cargo-machete@0.7.0
|
||||
|
||||
- name: Check unused dependencies
|
||||
uses: bnjbvr/cargo-machete@main
|
||||
uses: clechasseur/rs-cargo@v2
|
||||
with:
|
||||
command: machete
|
||||
|
||||
- name: Check licenses
|
||||
run: |
|
||||
@@ -298,8 +306,9 @@ jobs:
|
||||
env:
|
||||
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
|
||||
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
|
||||
APPLE_NOTARIZATION_USERNAME: ${{ secrets.APPLE_NOTARIZATION_USERNAME }}
|
||||
APPLE_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_NOTARIZATION_PASSWORD }}
|
||||
APPLE_NOTARIZATION_KEY: ${{ secrets.APPLE_NOTARIZATION_KEY }}
|
||||
APPLE_NOTARIZATION_KEY_ID: ${{ secrets.APPLE_NOTARIZATION_KEY_ID }}
|
||||
APPLE_NOTARIZATION_ISSUER_ID: ${{ secrets.APPLE_NOTARIZATION_ISSUER_ID }}
|
||||
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
|
||||
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
|
||||
DIGITALOCEAN_SPACES_ACCESS_KEY: ${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }}
|
||||
|
||||
25
.github/workflows/community_update_all_top_ranking_issues.yml
vendored
Normal file
25
.github/workflows/community_update_all_top_ranking_issues.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
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
|
||||
25
.github/workflows/community_update_weekly_top_ranking_issues.yml
vendored
Normal file
25
.github/workflows/community_update_weekly_top_ranking_issues.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
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
|
||||
5
.github/workflows/release_nightly.yml
vendored
5
.github/workflows/release_nightly.yml
vendored
@@ -62,8 +62,9 @@ jobs:
|
||||
env:
|
||||
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
|
||||
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
|
||||
APPLE_NOTARIZATION_USERNAME: ${{ secrets.APPLE_NOTARIZATION_USERNAME }}
|
||||
APPLE_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_NOTARIZATION_PASSWORD }}
|
||||
APPLE_NOTARIZATION_KEY: ${{ secrets.APPLE_NOTARIZATION_KEY }}
|
||||
APPLE_NOTARIZATION_KEY_ID: ${{ secrets.APPLE_NOTARIZATION_KEY_ID }}
|
||||
APPLE_NOTARIZATION_ISSUER_ID: ${{ secrets.APPLE_NOTARIZATION_ISSUER_ID }}
|
||||
DIGITALOCEAN_SPACES_ACCESS_KEY: ${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }}
|
||||
DIGITALOCEAN_SPACES_SECRET_KEY: ${{ secrets.DIGITALOCEAN_SPACES_SECRET_KEY }}
|
||||
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
|
||||
|
||||
4
.mailmap
4
.mailmap
@@ -21,6 +21,8 @@ Andrei Zvonimir Crnković <andrei@0x7f.dev>
|
||||
Andrei Zvonimir Crnković <andrei@0x7f.dev> <andreicek@0x7f.dev>
|
||||
Antonio Scandurra <me@as-cii.com>
|
||||
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> <53836821+bennetbo@users.noreply.github.com>
|
||||
Bennet Bo Fenner <bennet@zed.dev> <bennetbo@gmx.de>
|
||||
@@ -112,6 +114,8 @@ Sebastijan Kelnerič <sebastijan.kelneric@sebba.dev> <sebastijan.kelneric@vichav
|
||||
Sergey Onufrienko <sergey@onufrienko.com>
|
||||
Shish <webmaster@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.kroeger@hexagon.com>
|
||||
Thorsten Ball <thorsten@zed.dev>
|
||||
|
||||
@@ -14,12 +14,12 @@
|
||||
},
|
||||
"JSON": {
|
||||
"tab_size": 2,
|
||||
"preferred_line_length": 100,
|
||||
"preferred_line_length": 120,
|
||||
"formatter": "prettier"
|
||||
},
|
||||
"JSONC": {
|
||||
"tab_size": 2,
|
||||
"preferred_line_length": 100,
|
||||
"preferred_line_length": 120,
|
||||
"formatter": "prettier"
|
||||
},
|
||||
"JavaScript": {
|
||||
|
||||
190
Cargo.lock
generated
190
Cargo.lock
generated
@@ -83,9 +83,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "alacritty_terminal"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bccc2e60c2112dc8e8a722d6d30f2bb1a6a7b5d0e65fa695e09e57415dca7f7"
|
||||
version = "0.25.1-dev"
|
||||
source = "git+https://github.com/zed-industries/alacritty.git?rev=03c2907b44b4189aac5fdeaea331f5aab5c7072e#03c2907b44b4189aac5fdeaea331f5aab5c7072e"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bitflags 2.8.0",
|
||||
@@ -258,9 +257,9 @@ checksum = "34cd60c5e3152cef0a592f1b296f1cc93715d89d2551d85315828c3a09575ff4"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.95"
|
||||
version = "1.0.96"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
|
||||
checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4"
|
||||
|
||||
[[package]]
|
||||
name = "approx"
|
||||
@@ -396,7 +395,6 @@ dependencies = [
|
||||
"language",
|
||||
"language_model",
|
||||
"language_model_selector",
|
||||
"language_models",
|
||||
"languages",
|
||||
"log",
|
||||
"lsp",
|
||||
@@ -464,7 +462,6 @@ dependencies = [
|
||||
"language",
|
||||
"language_model",
|
||||
"language_model_selector",
|
||||
"language_models",
|
||||
"log",
|
||||
"lsp",
|
||||
"markdown",
|
||||
@@ -519,7 +516,6 @@ dependencies = [
|
||||
"language",
|
||||
"language_model",
|
||||
"language_model_selector",
|
||||
"language_models",
|
||||
"languages",
|
||||
"log",
|
||||
"multi_buffer",
|
||||
@@ -1270,6 +1266,30 @@ dependencies = [
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-bedrockruntime"
|
||||
version = "1.74.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6938541d1948a543bca23303fec4cff9c36bf0e63b8fa3ae1b337bcb9d5b81af"
|
||||
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]]
|
||||
name = "aws-sdk-kinesis"
|
||||
version = "1.61.0"
|
||||
@@ -1599,6 +1619,17 @@ dependencies = [
|
||||
"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]]
|
||||
name = "axum"
|
||||
version = "0.6.20"
|
||||
@@ -1728,6 +1759,22 @@ version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "bigdecimal"
|
||||
version = "0.4.7"
|
||||
@@ -1918,15 +1965,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "blake3"
|
||||
version = "1.5.5"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8ee0c1824c4dea5b5f81736aff91bae041d2c07ee1192bec91054e10e3e601e"
|
||||
checksum = "1230237285e3e10cde447185e8975408ae24deaa67205ce684805c25bc0c7937"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"arrayvec",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"constant_time_eq 0.3.1",
|
||||
"memmap2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2538,9 +2586,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.29"
|
||||
version = "4.5.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8acebd8ad879283633b343856142139f2da2317c96b05b4dd6181c61e2480184"
|
||||
checksum = "92b7b18d71fad5313a1e320fa9897994228ce274b60faa4d694fe0ea89cd9e6d"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -2548,9 +2596,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.29"
|
||||
version = "4.5.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6ba32cbda51c7e1dfd49acc1457ba1a7dec5b64fe360e828acb13ca8dc9c2f9"
|
||||
checksum = "a35db2071778a7344791a4fb4f95308b5673d219dee3ae348b86642574ecc90c"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -2619,6 +2667,7 @@ dependencies = [
|
||||
"clock",
|
||||
"cocoa 0.26.0",
|
||||
"collections",
|
||||
"credentials_provider",
|
||||
"feature_flags",
|
||||
"futures 0.3.31",
|
||||
"gpui",
|
||||
@@ -2784,6 +2833,7 @@ dependencies = [
|
||||
"futures 0.3.31",
|
||||
"git",
|
||||
"git_hosting_providers",
|
||||
"git_ui",
|
||||
"google_ai",
|
||||
"gpui",
|
||||
"hex",
|
||||
@@ -3051,6 +3101,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assistant_tool",
|
||||
"async-trait",
|
||||
"collections",
|
||||
"command_palette_hooks",
|
||||
"context_server_settings",
|
||||
@@ -3111,7 +3162,9 @@ dependencies = [
|
||||
"clock",
|
||||
"collections",
|
||||
"command_palette_hooks",
|
||||
"ctor",
|
||||
"editor",
|
||||
"env_logger 0.11.6",
|
||||
"fs",
|
||||
"futures 0.3.31",
|
||||
"gpui",
|
||||
@@ -3119,6 +3172,7 @@ dependencies = [
|
||||
"indoc",
|
||||
"inline_completion",
|
||||
"language",
|
||||
"log",
|
||||
"lsp",
|
||||
"menu",
|
||||
"node_runtime",
|
||||
@@ -3477,6 +3531,19 @@ dependencies = [
|
||||
"crc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "credentials_provider"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures 0.3.31",
|
||||
"gpui",
|
||||
"paths",
|
||||
"release_channel",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "criterion"
|
||||
version = "0.5.1"
|
||||
@@ -3597,9 +3664,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ctor"
|
||||
version = "0.3.1"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bba74424191d99c617a172ef126d429f62fe54aba1002c4d36e49403ce4b00a2"
|
||||
checksum = "21d960ecacd0a1bf55e73144b72de745e7bf275c7952c50e36e8af0a0cb7ab1f"
|
||||
dependencies = [
|
||||
"ctor-proc-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctor-proc-macro"
|
||||
version = "0.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c426d2ba3e525b39c1f0a9ba41b9fe61878dee11fa4e4a76b6ab440f46c5db5d"
|
||||
|
||||
[[package]]
|
||||
name = "ctrlc"
|
||||
@@ -3837,7 +3913,6 @@ dependencies = [
|
||||
"pretty_assertions",
|
||||
"project",
|
||||
"rand 0.8.5",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"settings",
|
||||
@@ -5273,6 +5348,7 @@ dependencies = [
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"rope",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smol",
|
||||
@@ -6937,17 +7013,14 @@ dependencies = [
|
||||
"anthropic",
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
"client",
|
||||
"collections",
|
||||
"deepseek",
|
||||
"futures 0.3.31",
|
||||
"google_ai",
|
||||
"gpui",
|
||||
"http_client",
|
||||
"image",
|
||||
"lmstudio",
|
||||
"log",
|
||||
"mistral",
|
||||
"ollama",
|
||||
"open_ai",
|
||||
"parking_lot",
|
||||
"proto",
|
||||
@@ -6956,6 +7029,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"smol",
|
||||
"strum",
|
||||
"telemetry_events",
|
||||
"thiserror 1.0.69",
|
||||
"ui",
|
||||
"util",
|
||||
@@ -6982,9 +7056,14 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anthropic",
|
||||
"anyhow",
|
||||
"aws-config",
|
||||
"aws-credential-types",
|
||||
"aws_http_client",
|
||||
"bedrock",
|
||||
"client",
|
||||
"collections",
|
||||
"copilot",
|
||||
"credentials_provider",
|
||||
"deepseek",
|
||||
"editor",
|
||||
"feature_flags",
|
||||
@@ -6992,6 +7071,7 @@ dependencies = [
|
||||
"futures 0.3.31",
|
||||
"google_ai",
|
||||
"gpui",
|
||||
"gpui_tokio",
|
||||
"http_client",
|
||||
"language_model",
|
||||
"lmstudio",
|
||||
@@ -7007,10 +7087,9 @@ dependencies = [
|
||||
"settings",
|
||||
"smol",
|
||||
"strum",
|
||||
"telemetry_events",
|
||||
"theme",
|
||||
"thiserror 1.0.69",
|
||||
"tiktoken-rs",
|
||||
"tokio",
|
||||
"ui",
|
||||
"util",
|
||||
]
|
||||
@@ -7192,7 +7271,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.48.5",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9248,7 +9327,7 @@ name = "perplexity"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"zed_extension_api 0.2.0",
|
||||
"zed_extension_api 0.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10302,7 +10381,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4"
|
||||
dependencies = [
|
||||
"bytes 1.10.0",
|
||||
"heck 0.4.1",
|
||||
"heck 0.5.0",
|
||||
"itertools 0.12.1",
|
||||
"log",
|
||||
"multimap 0.10.0",
|
||||
@@ -11743,9 +11822,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sea-orm"
|
||||
version = "1.1.4"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a93194430b419da0801f404baf3b986399d6a2a4f43bc79bc96dea83f92ca43"
|
||||
checksum = "00733e5418e8ae3758cdb988c3654174e716230cc53ee2cb884207cf86a23029"
|
||||
dependencies = [
|
||||
"async-stream",
|
||||
"async-trait",
|
||||
@@ -11771,9 +11850,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sea-orm-macros"
|
||||
version = "1.1.4"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d19e8f22fb474a8a622eb516c46885a080535d8d559386188f525977eaad32b3"
|
||||
checksum = "a98408f82fb4875d41ef469a79944a7da29767c7b3e4028e22188a3dd613b10f"
|
||||
dependencies = [
|
||||
"heck 0.4.1",
|
||||
"proc-macro2",
|
||||
@@ -11846,6 +11925,7 @@ dependencies = [
|
||||
"unindent",
|
||||
"util",
|
||||
"workspace",
|
||||
"zed_actions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -12005,9 +12085,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.138"
|
||||
version = "1.0.139"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949"
|
||||
checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itoa",
|
||||
@@ -12356,9 +12436,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.2"
|
||||
version = "1.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -13332,9 +13412,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.16.0"
|
||||
version = "3.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91"
|
||||
checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand 2.3.0",
|
||||
@@ -14861,26 +14941,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vte"
|
||||
version = "0.13.1"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a0b683b20ef64071ff03745b14391751f6beab06a54347885459b77a3f2caa5"
|
||||
checksum = "a5924018406ce0063cd67f8e008104968b74b563ee1b85dde3ed1f7cb87d3dbd"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitflags 2.8.0",
|
||||
"cursor-icon",
|
||||
"log",
|
||||
"memchr",
|
||||
"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]]
|
||||
@@ -15682,7 +15752,7 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -16662,7 +16732,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed"
|
||||
version = "0.176.0"
|
||||
version = "0.176.2"
|
||||
dependencies = [
|
||||
"activity_indicator",
|
||||
"anyhow",
|
||||
@@ -16807,7 +16877,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed_deno"
|
||||
version = "0.0.2"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"zed_extension_api 0.1.0",
|
||||
]
|
||||
@@ -16816,7 +16886,7 @@ dependencies = [
|
||||
name = "zed_elixir"
|
||||
version = "0.1.4"
|
||||
dependencies = [
|
||||
"zed_extension_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"zed_extension_api 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -16847,6 +16917,8 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "zed_extension_api"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fd16b8b30a9dc920fc1678ff852f696b5bdf5b5843bc745a128be0aac29859e"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -16855,9 +16927,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed_extension_api"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fd16b8b30a9dc920fc1678ff852f696b5bdf5b5843bc745a128be0aac29859e"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -16943,12 +17013,12 @@ dependencies = [
|
||||
name = "zed_test_extension"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"zed_extension_api 0.2.0",
|
||||
"zed_extension_api 0.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zed_toml"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
dependencies = [
|
||||
"zed_extension_api 0.1.0",
|
||||
]
|
||||
@@ -17125,7 +17195,7 @@ dependencies = [
|
||||
"indoc",
|
||||
"inline_completion",
|
||||
"language",
|
||||
"language_models",
|
||||
"language_model",
|
||||
"log",
|
||||
"menu",
|
||||
"migrator",
|
||||
|
||||
15
Cargo.toml
15
Cargo.toml
@@ -15,7 +15,10 @@ members = [
|
||||
"crates/audio",
|
||||
"crates/auto_update",
|
||||
"crates/auto_update_ui",
|
||||
"crates/aws_http_client",
|
||||
"crates/bedrock",
|
||||
"crates/breadcrumbs",
|
||||
"crates/buffer_diff",
|
||||
"crates/call",
|
||||
"crates/channel",
|
||||
"crates/cli",
|
||||
@@ -31,10 +34,10 @@ members = [
|
||||
"crates/context_server",
|
||||
"crates/context_server_settings",
|
||||
"crates/copilot",
|
||||
"crates/credentials_provider",
|
||||
"crates/db",
|
||||
"crates/deepseek",
|
||||
"crates/diagnostics",
|
||||
"crates/buffer_diff",
|
||||
"crates/docs_preprocessor",
|
||||
"crates/editor",
|
||||
"crates/evals",
|
||||
@@ -217,6 +220,8 @@ assistant_tools = { path = "crates/assistant_tools" }
|
||||
audio = { path = "crates/audio" }
|
||||
auto_update = { path = "crates/auto_update" }
|
||||
auto_update_ui = { path = "crates/auto_update_ui" }
|
||||
aws_http_client = { path = "crates/aws_http_client" }
|
||||
bedrock = { path = "crates/bedrock" }
|
||||
breadcrumbs = { path = "crates/breadcrumbs" }
|
||||
call = { path = "crates/call" }
|
||||
channel = { path = "crates/channel" }
|
||||
@@ -233,6 +238,7 @@ component_preview = { path = "crates/component_preview" }
|
||||
context_server = { path = "crates/context_server" }
|
||||
context_server_settings = { path = "crates/context_server_settings" }
|
||||
copilot = { path = "crates/copilot" }
|
||||
credentials_provider = { path = "crates/credentials_provider" }
|
||||
db = { path = "crates/db" }
|
||||
deepseek = { path = "crates/deepseek" }
|
||||
diagnostics = { path = "crates/diagnostics" }
|
||||
@@ -364,7 +370,7 @@ zeta = { path = "crates/zeta" }
|
||||
#
|
||||
|
||||
aho-corasick = "1.1"
|
||||
alacritty_terminal = "0.25"
|
||||
alacritty_terminal = { git = "https://github.com/zed-industries/alacritty.git", rev = "03c2907b44b4189aac5fdeaea331f5aab5c7072e"}
|
||||
any_vec = "0.14"
|
||||
anyhow = "1.0.86"
|
||||
arrayvec = { version = "0.7.4", features = ["serde"] }
|
||||
@@ -380,6 +386,11 @@ async-trait = "0.1"
|
||||
async-tungstenite = "0.28"
|
||||
async-watch = "0.3.1"
|
||||
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"
|
||||
bitflags = "2.6.0"
|
||||
blade-graphics = { git = "https://github.com/kvark/blade", rev = "b16f5c7bd873c7126f48c82c39e7ae64602ae74f" }
|
||||
|
||||
4
assets/icons/ai_bedrock.svg
Normal file
4
assets/icons/ai_bedrock.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<?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>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
@@ -10,24 +10,49 @@
|
||||
"pagedown": "menu::SelectLast",
|
||||
"ctrl-n": "menu::SelectNext",
|
||||
"tab": "menu::SelectNext",
|
||||
"ctrl-p": "menu::SelectPrev",
|
||||
"shift-tab": "menu::SelectPrev",
|
||||
"ctrl-p": "menu::SelectPrevious",
|
||||
"shift-tab": "menu::SelectPrevious",
|
||||
"enter": "menu::Confirm",
|
||||
"ctrl-enter": "menu::SecondaryConfirm",
|
||||
"ctrl-escape": "menu::Cancel",
|
||||
"ctrl-c": "menu::Cancel",
|
||||
"escape": "menu::Cancel",
|
||||
"alt-shift-enter": "menu::Restart",
|
||||
"alt-enter": ["picker::ConfirmInput", { "secondary": false }],
|
||||
"ctrl-alt-enter": ["picker::ConfirmInput", { "secondary": true }],
|
||||
"alt-enter": [
|
||||
"picker::ConfirmInput",
|
||||
{
|
||||
"secondary": false
|
||||
}
|
||||
],
|
||||
"ctrl-alt-enter": [
|
||||
"picker::ConfirmInput",
|
||||
{
|
||||
"secondary": true
|
||||
}
|
||||
],
|
||||
"ctrl-shift-w": "workspace::CloseWindow",
|
||||
"shift-escape": "workspace::ToggleZoom",
|
||||
"open": "workspace::Open",
|
||||
"ctrl-o": "workspace::Open",
|
||||
"ctrl-=": ["zed::IncreaseBufferFontSize", { "persist": false }],
|
||||
"ctrl-+": ["zed::IncreaseBufferFontSize", { "persist": false }],
|
||||
"ctrl--": ["zed::DecreaseBufferFontSize", { "persist": false }],
|
||||
"ctrl-0": ["zed::ResetBufferFontSize", { "persist": false }],
|
||||
"ctrl-+": [
|
||||
"zed::IncreaseBufferFontSize",
|
||||
{
|
||||
"persist": false
|
||||
}
|
||||
],
|
||||
"ctrl--": [
|
||||
"zed::DecreaseBufferFontSize",
|
||||
{
|
||||
"persist": false
|
||||
}
|
||||
],
|
||||
"ctrl-0": [
|
||||
"zed::ResetBufferFontSize",
|
||||
{
|
||||
"persist": false
|
||||
}
|
||||
],
|
||||
"ctrl-,": "zed::OpenSettings",
|
||||
"ctrl-q": "zed::Quit",
|
||||
"f11": "zed::ToggleFullScreen",
|
||||
@@ -38,14 +63,14 @@
|
||||
{
|
||||
"context": "Picker || menu",
|
||||
"bindings": {
|
||||
"up": "menu::SelectPrev",
|
||||
"up": "menu::SelectPrevious",
|
||||
"down": "menu::SelectNext"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Prompt",
|
||||
"bindings": {
|
||||
"left": "menu::SelectPrev",
|
||||
"left": "menu::SelectPrevious",
|
||||
"right": "menu::SelectNext"
|
||||
}
|
||||
},
|
||||
@@ -57,7 +82,7 @@
|
||||
"backspace": "editor::Backspace",
|
||||
"delete": "editor::Delete",
|
||||
"tab": "editor::Tab",
|
||||
"shift-tab": "editor::TabPrev",
|
||||
"shift-tab": "editor::Backtab",
|
||||
"ctrl-k": "editor::CutToEndOfLine",
|
||||
// "ctrl-t": "editor::Transpose",
|
||||
"ctrl-k ctrl-q": "editor::Rewrap",
|
||||
@@ -84,12 +109,23 @@
|
||||
"pageup": "editor::MovePageUp",
|
||||
"alt-pageup": "editor::PageUp",
|
||||
"shift-pageup": "editor::SelectPageUp",
|
||||
"home": "editor::MoveToBeginningOfLine",
|
||||
"home": [
|
||||
"editor::MoveToBeginningOfLine",
|
||||
{
|
||||
"stop_at_soft_wraps": true,
|
||||
"stop_at_indent": true
|
||||
}
|
||||
],
|
||||
"down": "editor::MoveDown",
|
||||
"pagedown": "editor::MovePageDown",
|
||||
"alt-pagedown": "editor::PageDown",
|
||||
"shift-pagedown": "editor::SelectPageDown",
|
||||
"end": "editor::MoveToEndOfLine",
|
||||
"end": [
|
||||
"editor::MoveToEndOfLine",
|
||||
{
|
||||
"stop_at_soft_wraps": true
|
||||
}
|
||||
],
|
||||
"left": "editor::MoveLeft",
|
||||
"right": "editor::MoveRight",
|
||||
"ctrl-left": "editor::MoveToPreviousWordStart",
|
||||
@@ -107,18 +143,29 @@
|
||||
"ctrl-a": "editor::SelectAll",
|
||||
"ctrl-l": "editor::SelectLine",
|
||||
"ctrl-shift-i": "editor::Format",
|
||||
// "cmd-shift-left": ["editor::SelectToBeginningOfLine", {"stop_at_soft_wraps": true }],
|
||||
// "ctrl-shift-a": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true }],
|
||||
"shift-home": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true }],
|
||||
// "cmd-shift-left": ["editor::SelectToBeginningOfLine", {"stop_at_soft_wraps": true, "stop_at_indent": true }],
|
||||
// "ctrl-shift-a": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true, "stop_at_indent": true }],
|
||||
"shift-home": [
|
||||
"editor::SelectToBeginningOfLine",
|
||||
{
|
||||
"stop_at_soft_wraps": true,
|
||||
"stop_at_indent": true
|
||||
}
|
||||
],
|
||||
// "cmd-shift-right": ["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
|
||||
}
|
||||
],
|
||||
// "alt-v": ["editor::MovePageUp", { "center_cursor": true }],
|
||||
"ctrl-alt-space": "editor::ShowCharacterPalette",
|
||||
"ctrl-;": "editor::ToggleLineNumbers",
|
||||
"ctrl-k ctrl-r": "git::Restore",
|
||||
"ctrl-'": "editor::ToggleSelectedDiffHunks",
|
||||
"ctrl-\"": "editor::ExpandAllHunkDiffs",
|
||||
"ctrl-\"": "editor::ExpandAllDiffHunks",
|
||||
"ctrl-i": "editor::ShowSignatureHelp",
|
||||
"alt-g b": "editor::ToggleGitBlame",
|
||||
"menu": "editor::OpenContextMenu",
|
||||
@@ -180,12 +227,13 @@
|
||||
"ctrl-k c": "assistant::CopyCode",
|
||||
"ctrl-shift-e": "project_panel::ToggleFocus",
|
||||
"ctrl-g": "search::SelectNextMatch",
|
||||
"ctrl-shift-g": "search::SelectPrevMatch",
|
||||
"ctrl-shift-g": "search::SelectPreviousMatch",
|
||||
"ctrl-alt-/": "assistant::ToggleModelSelector",
|
||||
"ctrl-k h": "assistant::DeployHistory",
|
||||
"ctrl-k l": "assistant::DeployPromptLibrary",
|
||||
"new": "assistant::NewContext",
|
||||
"ctrl-n": "assistant::NewContext"
|
||||
"new": "assistant::NewChat",
|
||||
"ctrl-t": "assistant::NewChat",
|
||||
"ctrl-n": "assistant::NewChat"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -202,7 +250,7 @@
|
||||
"escape": "buffer_search::Dismiss",
|
||||
"tab": "buffer_search::FocusEditor",
|
||||
"enter": "search::SelectNextMatch",
|
||||
"shift-enter": "search::SelectPrevMatch",
|
||||
"shift-enter": "search::SelectPreviousMatch",
|
||||
"alt-enter": "search::SelectAllMatches",
|
||||
"find": "search::FocusSearch",
|
||||
"ctrl-f": "search::FocusSearch",
|
||||
@@ -271,26 +319,61 @@
|
||||
"alt-8": ["pane::ActivateItem", 7],
|
||||
"alt-9": ["pane::ActivateItem", 8],
|
||||
"alt-0": "pane::ActivateLastItem",
|
||||
"ctrl-pageup": "pane::ActivatePrevItem",
|
||||
"ctrl-pageup": "pane::ActivatePreviousItem",
|
||||
"ctrl-pagedown": "pane::ActivateNextItem",
|
||||
"ctrl-shift-pageup": "pane::SwapItemLeft",
|
||||
"ctrl-shift-pagedown": "pane::SwapItemRight",
|
||||
"ctrl-f4": ["pane::CloseActiveItem", { "close_pinned": false }],
|
||||
"ctrl-w": ["pane::CloseActiveItem", { "close_pinned": false }],
|
||||
"alt-ctrl-t": ["pane::CloseInactiveItems", { "close_pinned": false }],
|
||||
"ctrl-f4": [
|
||||
"pane::CloseActiveItem",
|
||||
{
|
||||
"close_pinned": false
|
||||
}
|
||||
],
|
||||
"ctrl-w": [
|
||||
"pane::CloseActiveItem",
|
||||
{
|
||||
"close_pinned": false
|
||||
}
|
||||
],
|
||||
"alt-ctrl-t": [
|
||||
"pane::CloseInactiveItems",
|
||||
{
|
||||
"close_pinned": false
|
||||
}
|
||||
],
|
||||
"alt-ctrl-shift-w": "workspace::CloseInactiveTabsAndPanes",
|
||||
"ctrl-k e": ["pane::CloseItemsToTheLeft", { "close_pinned": false }],
|
||||
"ctrl-k t": ["pane::CloseItemsToTheRight", { "close_pinned": false }],
|
||||
"ctrl-k u": ["pane::CloseCleanItems", { "close_pinned": false }],
|
||||
"ctrl-k w": ["pane::CloseAllItems", { "close_pinned": false }],
|
||||
"ctrl-k e": [
|
||||
"pane::CloseItemsToTheLeft",
|
||||
{
|
||||
"close_pinned": false
|
||||
}
|
||||
],
|
||||
"ctrl-k t": [
|
||||
"pane::CloseItemsToTheRight",
|
||||
{
|
||||
"close_pinned": false
|
||||
}
|
||||
],
|
||||
"ctrl-k u": [
|
||||
"pane::CloseCleanItems",
|
||||
{
|
||||
"close_pinned": false
|
||||
}
|
||||
],
|
||||
"ctrl-k w": [
|
||||
"pane::CloseAllItems",
|
||||
{
|
||||
"close_pinned": false
|
||||
}
|
||||
],
|
||||
"back": "pane::GoBack",
|
||||
"ctrl-alt--": "pane::GoBack",
|
||||
"ctrl-alt-_": "pane::GoForward",
|
||||
"forward": "pane::GoForward",
|
||||
"ctrl-alt-g": "search::SelectNextMatch",
|
||||
"f3": "search::SelectNextMatch",
|
||||
"ctrl-alt-shift-g": "search::SelectPrevMatch",
|
||||
"shift-f3": "search::SelectPrevMatch",
|
||||
"ctrl-alt-shift-g": "search::SelectPreviousMatch",
|
||||
"shift-f3": "search::SelectPreviousMatch",
|
||||
"shift-find": "project_search::ToggleFocus",
|
||||
"ctrl-shift-f": "project_search::ToggleFocus",
|
||||
"ctrl-alt-shift-h": "search::ToggleReplace",
|
||||
@@ -323,17 +406,47 @@
|
||||
"alt-shift-left": "editor::SelectSmallerSyntaxNode", // Shrink Selection
|
||||
"ctrl-shift-l": "editor::SelectAllMatches", // Select all occurrences of current selection
|
||||
"ctrl-f2": "editor::SelectAllMatches", // Select all occurrences of current word
|
||||
"ctrl-d": ["editor::SelectNext", { "replace_newest": false }],
|
||||
"ctrl-shift-down": ["editor::SelectNext", { "replace_newest": false }], // Add selection to Next Find Match
|
||||
"ctrl-shift-up": ["editor::SelectPrevious", { "replace_newest": false }],
|
||||
"ctrl-k ctrl-d": ["editor::SelectNext", { "replace_newest": true }],
|
||||
"ctrl-k ctrl-shift-d": ["editor::SelectPrevious", { "replace_newest": true }],
|
||||
"ctrl-d": [
|
||||
"editor::SelectNext",
|
||||
{
|
||||
"replace_newest": false
|
||||
}
|
||||
],
|
||||
"ctrl-shift-down": [
|
||||
"editor::SelectNext",
|
||||
{
|
||||
"replace_newest": false
|
||||
}
|
||||
], // Add selection to Next Find Match
|
||||
"ctrl-shift-up": [
|
||||
"editor::SelectPrevious",
|
||||
{
|
||||
"replace_newest": false
|
||||
}
|
||||
],
|
||||
"ctrl-k ctrl-d": [
|
||||
"editor::SelectNext",
|
||||
{
|
||||
"replace_newest": true
|
||||
}
|
||||
],
|
||||
"ctrl-k ctrl-shift-d": [
|
||||
"editor::SelectPrevious",
|
||||
{
|
||||
"replace_newest": true
|
||||
}
|
||||
],
|
||||
"ctrl-k ctrl-i": "editor::Hover",
|
||||
"ctrl-/": ["editor::ToggleComments", { "advance_downwards": false }],
|
||||
"ctrl-/": [
|
||||
"editor::ToggleComments",
|
||||
{
|
||||
"advance_downwards": false
|
||||
}
|
||||
],
|
||||
"ctrl-u": "editor::UndoSelection",
|
||||
"ctrl-shift-u": "editor::RedoSelection",
|
||||
"f8": "editor::GoToDiagnostic",
|
||||
"shift-f8": "editor::GoToPrevDiagnostic",
|
||||
"shift-f8": "editor::GoToPreviousDiagnostic",
|
||||
"f2": "editor::Rename",
|
||||
"f12": "editor::GoToDefinition",
|
||||
"alt-f12": "editor::GoToDefinitionSplit",
|
||||
@@ -343,9 +456,9 @@
|
||||
"alt-ctrl-f12": "editor::GoToTypeDefinitionSplit",
|
||||
"alt-shift-f12": "editor::FindAllReferences",
|
||||
"ctrl-m": "editor::MoveToEnclosingBracket",
|
||||
"ctrl-shift-\\": "editor::MoveToEnclosingBracket",
|
||||
"ctrl-shift-[": "editor::Fold",
|
||||
"ctrl-shift-]": "editor::UnfoldLines",
|
||||
"ctrl-|": "editor::MoveToEnclosingBracket",
|
||||
"ctrl-{": "editor::Fold",
|
||||
"ctrl-}": "editor::UnfoldLines",
|
||||
"ctrl-k ctrl-l": "editor::ToggleFold",
|
||||
"ctrl-k ctrl-[": "editor::FoldRecursive",
|
||||
"ctrl-k ctrl-]": "editor::UnfoldRecursive",
|
||||
@@ -367,7 +480,12 @@
|
||||
"ctrl-\\": "pane::SplitRight",
|
||||
"ctrl-k v": "markdown::OpenPreviewToTheSide",
|
||||
"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::GoToPreviousHunk"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -413,14 +531,24 @@
|
||||
"ctrl-alt-y": "workspace::CloseAllDocks",
|
||||
"shift-find": "pane::DeploySearch",
|
||||
"ctrl-shift-f": "pane::DeploySearch",
|
||||
"ctrl-shift-h": ["pane::DeploySearch", { "replace_enabled": true }],
|
||||
"ctrl-shift-h": [
|
||||
"pane::DeploySearch",
|
||||
{
|
||||
"replace_enabled": true
|
||||
}
|
||||
],
|
||||
"ctrl-shift-t": "pane::ReopenClosedItem",
|
||||
"ctrl-k ctrl-s": "zed::OpenKeymap",
|
||||
"ctrl-k ctrl-t": "theme_selector::Toggle",
|
||||
"ctrl-t": "project_symbols::Toggle",
|
||||
"ctrl-p": "file_finder::Toggle",
|
||||
"ctrl-tab": "tab_switcher::Toggle",
|
||||
"ctrl-shift-tab": ["tab_switcher::Toggle", { "select_last": true }],
|
||||
"ctrl-shift-tab": [
|
||||
"tab_switcher::Toggle",
|
||||
{
|
||||
"select_last": true
|
||||
}
|
||||
],
|
||||
"ctrl-e": "file_finder::Toggle",
|
||||
"f1": "command_palette::Toggle",
|
||||
"ctrl-shift-p": "command_palette::Toggle",
|
||||
@@ -446,7 +574,12 @@
|
||||
"ctrl-alt-r": "task::Rerun",
|
||||
"alt-t": "task::Rerun",
|
||||
"alt-shift-t": "task::Spawn",
|
||||
"alt-shift-r": ["task::Spawn", { "reveal_target": "center" }]
|
||||
"alt-shift-r": [
|
||||
"task::Spawn",
|
||||
{
|
||||
"reveal_target": "center"
|
||||
}
|
||||
]
|
||||
// also possible to spawn tasks by name:
|
||||
// "foo-bar": ["task::Spawn", { "task_name": "MyTask", "reveal_target": "dock" }]
|
||||
}
|
||||
@@ -530,8 +663,8 @@
|
||||
{
|
||||
"context": "Editor && (showing_code_actions || showing_completions)",
|
||||
"bindings": {
|
||||
"ctrl-p": "editor::ContextMenuPrev",
|
||||
"up": "editor::ContextMenuPrev",
|
||||
"ctrl-p": "editor::ContextMenuPrevious",
|
||||
"up": "editor::ContextMenuPrevious",
|
||||
"ctrl-n": "editor::ContextMenuNext",
|
||||
"down": "editor::ContextMenuNext",
|
||||
"pageup": "editor::ContextMenuFirst",
|
||||
@@ -559,7 +692,7 @@
|
||||
"ctrl-alt-enter": "editor::OpenExcerptsSplit",
|
||||
"ctrl-shift-e": "pane::RevealInProjectPanel",
|
||||
"ctrl-f8": "editor::GoToHunk",
|
||||
"ctrl-shift-f8": "editor::GoToPrevHunk",
|
||||
"ctrl-shift-f8": "editor::GoToPreviousHunk",
|
||||
"ctrl-enter": "assistant::InlineAssist",
|
||||
"ctrl-:": "editor::ToggleInlayHints"
|
||||
}
|
||||
@@ -656,7 +789,7 @@
|
||||
"alt-ctrl-r": "outline_panel::RevealInFileManager",
|
||||
"space": "outline_panel::Open",
|
||||
"shift-down": "menu::SelectNext",
|
||||
"shift-up": "menu::SelectPrev",
|
||||
"shift-up": "menu::SelectPrevious",
|
||||
"alt-enter": "editor::OpenExcerpts",
|
||||
"ctrl-alt-enter": "editor::OpenExcerptsSplit"
|
||||
}
|
||||
@@ -684,17 +817,42 @@
|
||||
"alt-ctrl-shift-c": "workspace::CopyRelativePath",
|
||||
"enter": "project_panel::Rename",
|
||||
"f2": "project_panel::Rename",
|
||||
"backspace": ["project_panel::Trash", { "skip_prompt": false }],
|
||||
"delete": ["project_panel::Trash", { "skip_prompt": false }],
|
||||
"shift-delete": ["project_panel::Delete", { "skip_prompt": false }],
|
||||
"ctrl-backspace": ["project_panel::Delete", { "skip_prompt": false }],
|
||||
"ctrl-delete": ["project_panel::Delete", { "skip_prompt": false }],
|
||||
"backspace": [
|
||||
"project_panel::Trash",
|
||||
{
|
||||
"skip_prompt": false
|
||||
}
|
||||
],
|
||||
"delete": [
|
||||
"project_panel::Trash",
|
||||
{
|
||||
"skip_prompt": false
|
||||
}
|
||||
],
|
||||
"shift-delete": [
|
||||
"project_panel::Delete",
|
||||
{
|
||||
"skip_prompt": false
|
||||
}
|
||||
],
|
||||
"ctrl-backspace": [
|
||||
"project_panel::Delete",
|
||||
{
|
||||
"skip_prompt": false
|
||||
}
|
||||
],
|
||||
"ctrl-delete": [
|
||||
"project_panel::Delete",
|
||||
{
|
||||
"skip_prompt": false
|
||||
}
|
||||
],
|
||||
"alt-ctrl-r": "project_panel::RevealInFileManager",
|
||||
"ctrl-shift-enter": "project_panel::OpenWithSystem",
|
||||
"shift-find": "project_panel::NewSearchInDirectory",
|
||||
"ctrl-shift-f": "project_panel::NewSearchInDirectory",
|
||||
"shift-down": "menu::SelectNext",
|
||||
"shift-up": "menu::SelectPrev",
|
||||
"shift-up": "menu::SelectPrevious",
|
||||
"escape": "menu::Cancel"
|
||||
}
|
||||
},
|
||||
@@ -704,16 +862,10 @@
|
||||
"space": "project_panel::Open"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "GitPanel && !CommitEditor",
|
||||
"bindings": {
|
||||
"escape": "git_panel::Close"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "GitPanel && ChangesList",
|
||||
"bindings": {
|
||||
"up": "menu::SelectPrev",
|
||||
"up": "menu::SelectPrevious",
|
||||
"down": "menu::SelectNext",
|
||||
"enter": "menu::Confirm",
|
||||
"space": "git::ToggleStaged",
|
||||
@@ -721,19 +873,42 @@
|
||||
"ctrl-shift-space": "git::UnstageAll",
|
||||
"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",
|
||||
"bindings": {
|
||||
"escape": "git_panel::FocusChanges",
|
||||
"ctrl-enter": "git::Commit",
|
||||
"tab": "git_panel::FocusChanges",
|
||||
"shift-tab": "git_panel::FocusChanges",
|
||||
"ctrl-enter": "git::Commit",
|
||||
"alt-up": "git_panel::FocusChanges"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "GitCommit > Editor",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"enter": "editor::Newline",
|
||||
"ctrl-enter": "git::Commit"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "CollabPanel && not_editing",
|
||||
"bindings": {
|
||||
@@ -757,7 +932,12 @@
|
||||
"context": "Picker > Editor",
|
||||
"bindings": {
|
||||
"tab": "picker::ConfirmCompletion",
|
||||
"alt-enter": ["picker::ConfirmInput", { "secondary": false }]
|
||||
"alt-enter": [
|
||||
"picker::ConfirmInput",
|
||||
{
|
||||
"secondary": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -769,7 +949,7 @@
|
||||
{
|
||||
"context": "FileFinder || (FileFinder > Picker > Editor) || (FileFinder > Picker > menu)",
|
||||
"bindings": {
|
||||
"ctrl-shift-p": "file_finder::SelectPrev",
|
||||
"ctrl-shift-p": "file_finder::SelectPrevious",
|
||||
"ctrl-j": "pane::SplitDown",
|
||||
"ctrl-k": "pane::SplitUp",
|
||||
"ctrl-h": "pane::SplitLeft",
|
||||
@@ -779,8 +959,8 @@
|
||||
{
|
||||
"context": "TabSwitcher",
|
||||
"bindings": {
|
||||
"ctrl-shift-tab": "menu::SelectPrev",
|
||||
"ctrl-up": "menu::SelectPrev",
|
||||
"ctrl-shift-tab": "menu::SelectPrevious",
|
||||
"ctrl-up": "menu::SelectPrevious",
|
||||
"ctrl-down": "menu::SelectNext",
|
||||
"ctrl-backspace": "tab_switcher::CloseSelectedItem"
|
||||
}
|
||||
@@ -812,6 +992,7 @@
|
||||
"pagedown": ["terminal::SendKeystroke", "pagedown"],
|
||||
"escape": ["terminal::SendKeystroke", "escape"],
|
||||
"enter": ["terminal::SendKeystroke", "enter"],
|
||||
"ctrl-b": ["terminal::SendKeystroke", "ctrl-b"],
|
||||
"ctrl-c": ["terminal::SendKeystroke", "ctrl-c"],
|
||||
"shift-pageup": "terminal::ScrollPageUp",
|
||||
"shift-pagedown": "terminal::ScrollPageDown",
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
"tab": "menu::SelectNext",
|
||||
"ctrl-n": "menu::SelectNext",
|
||||
"down": "menu::SelectNext",
|
||||
"shift-tab": "menu::SelectPrev",
|
||||
"ctrl-p": "menu::SelectPrev",
|
||||
"up": "menu::SelectPrev",
|
||||
"shift-tab": "menu::SelectPrevious",
|
||||
"ctrl-p": "menu::SelectPrevious",
|
||||
"up": "menu::SelectPrevious",
|
||||
"enter": "menu::Confirm",
|
||||
"ctrl-enter": "menu::SecondaryConfirm",
|
||||
"cmd-enter": "menu::SecondaryConfirm",
|
||||
@@ -28,10 +28,30 @@
|
||||
"cmd-shift-w": "workspace::CloseWindow",
|
||||
"shift-escape": "workspace::ToggleZoom",
|
||||
"cmd-o": "workspace::Open",
|
||||
"cmd-=": ["zed::IncreaseBufferFontSize", { "persist": false }],
|
||||
"cmd-+": ["zed::IncreaseBufferFontSize", { "persist": false }],
|
||||
"cmd--": ["zed::DecreaseBufferFontSize", { "persist": false }],
|
||||
"cmd-0": ["zed::ResetBufferFontSize", { "persist": false }],
|
||||
"cmd-=": [
|
||||
"zed::IncreaseBufferFontSize",
|
||||
{
|
||||
"persist": false
|
||||
}
|
||||
],
|
||||
"cmd-+": [
|
||||
"zed::IncreaseBufferFontSize",
|
||||
{
|
||||
"persist": false
|
||||
}
|
||||
],
|
||||
"cmd--": [
|
||||
"zed::DecreaseBufferFontSize",
|
||||
{
|
||||
"persist": false
|
||||
}
|
||||
],
|
||||
"cmd-0": [
|
||||
"zed::ResetBufferFontSize",
|
||||
{
|
||||
"persist": false
|
||||
}
|
||||
],
|
||||
"cmd-,": "zed::OpenSettings",
|
||||
"cmd-q": "zed::Quit",
|
||||
"cmd-h": "zed::Hide",
|
||||
@@ -54,7 +74,7 @@
|
||||
"ctrl-d": "editor::Delete",
|
||||
"delete": "editor::Delete",
|
||||
"tab": "editor::Tab",
|
||||
"shift-tab": "editor::TabPrev",
|
||||
"shift-tab": "editor::Backtab",
|
||||
"ctrl-t": "editor::Transpose",
|
||||
"ctrl-k": "editor::KillRingCut",
|
||||
"ctrl-y": "editor::KillRingYank",
|
||||
@@ -91,14 +111,49 @@
|
||||
"ctrl-l": "editor::ScrollCursorCenter",
|
||||
"alt-left": "editor::MoveToPreviousWordStart",
|
||||
"alt-right": "editor::MoveToNextWordEnd",
|
||||
"cmd-left": "editor::MoveToBeginningOfLine",
|
||||
"ctrl-a": "editor::MoveToBeginningOfLine",
|
||||
"home": "editor::MoveToBeginningOfLine",
|
||||
"cmd-right": "editor::MoveToEndOfLine",
|
||||
"ctrl-e": "editor::MoveToEndOfLine",
|
||||
"end": "editor::MoveToEndOfLine",
|
||||
"cmd-up": "editor::MoveToBeginning",
|
||||
"cmd-down": "editor::MoveToEnd",
|
||||
"cmd-left": [
|
||||
"editor::MoveToBeginningOfLine",
|
||||
{
|
||||
"stop_at_soft_wraps": true,
|
||||
"stop_at_indent": true
|
||||
}
|
||||
],
|
||||
"ctrl-a": [
|
||||
"editor::MoveToBeginningOfLine",
|
||||
{
|
||||
"stop_at_soft_wraps": false,
|
||||
"stop_at_indent": true
|
||||
}
|
||||
],
|
||||
"home": [
|
||||
"editor::MoveToBeginningOfLine",
|
||||
{
|
||||
"stop_at_soft_wraps": true,
|
||||
"stop_at_indent": true
|
||||
}
|
||||
],
|
||||
"cmd-right": [
|
||||
"editor::MoveToEndOfLine",
|
||||
{
|
||||
"stop_at_soft_wraps": true
|
||||
}
|
||||
],
|
||||
"ctrl-e": [
|
||||
"editor::MoveToEndOfLine",
|
||||
{
|
||||
"stop_at_soft_wraps": false
|
||||
}
|
||||
],
|
||||
"end": [
|
||||
"editor::MoveToEndOfLine",
|
||||
{
|
||||
"stop_at_soft_wraps": true
|
||||
}
|
||||
],
|
||||
"cmd-up": "editor::MoveToStartOfExcerpt",
|
||||
"cmd-down": "editor::MoveToEndOfExcerpt",
|
||||
"cmd-home": "editor::MoveToBeginning", // Typed via `cmd-fn-left`
|
||||
"cmd-end": "editor::MoveToEnd", // Typed via `cmd-fn-right`
|
||||
"shift-up": "editor::SelectUp",
|
||||
"ctrl-shift-p": "editor::SelectUp",
|
||||
"shift-down": "editor::SelectDown",
|
||||
@@ -111,19 +166,62 @@
|
||||
"alt-shift-right": "editor::SelectToNextWordEnd", // cursorWordRightSelect
|
||||
"ctrl-shift-up": "editor::SelectToStartOfParagraph",
|
||||
"ctrl-shift-down": "editor::SelectToEndOfParagraph",
|
||||
"cmd-shift-up": "editor::SelectToBeginning",
|
||||
"cmd-shift-down": "editor::SelectToEnd",
|
||||
"cmd-shift-up": "editor::SelectToStartOfExcerpt",
|
||||
"cmd-shift-down": "editor::SelectToEndOfExcerpt",
|
||||
"cmd-a": "editor::SelectAll",
|
||||
"cmd-l": "editor::SelectLine",
|
||||
"cmd-shift-i": "editor::Format",
|
||||
"cmd-shift-left": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true }],
|
||||
"shift-home": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true }],
|
||||
"ctrl-shift-a": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true }],
|
||||
"cmd-shift-right": ["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-v": ["editor::MovePageDown", { "center_cursor": true }],
|
||||
"ctrl-shift-v": ["editor::MovePageUp", { "center_cursor": true }],
|
||||
"cmd-shift-left": [
|
||||
"editor::SelectToBeginningOfLine",
|
||||
{
|
||||
"stop_at_soft_wraps": true,
|
||||
"stop_at_indent": true
|
||||
}
|
||||
],
|
||||
"shift-home": [
|
||||
"editor::SelectToBeginningOfLine",
|
||||
{
|
||||
"stop_at_soft_wraps": true,
|
||||
"stop_at_indent": true
|
||||
}
|
||||
],
|
||||
"ctrl-shift-a": [
|
||||
"editor::SelectToBeginningOfLine",
|
||||
{
|
||||
"stop_at_soft_wraps": true,
|
||||
"stop_at_indent": true
|
||||
}
|
||||
],
|
||||
"cmd-shift-right": [
|
||||
"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-v": [
|
||||
"editor::MovePageDown",
|
||||
{
|
||||
"center_cursor": true
|
||||
}
|
||||
],
|
||||
"ctrl-shift-v": [
|
||||
"editor::MovePageUp",
|
||||
{
|
||||
"center_cursor": true
|
||||
}
|
||||
],
|
||||
"ctrl-cmd-space": "editor::ShowCharacterPalette",
|
||||
"cmd-;": "editor::ToggleLineNumbers",
|
||||
"cmd-alt-z": "git::Restore",
|
||||
@@ -131,7 +229,7 @@
|
||||
"cmd-y": "git::StageAndNext",
|
||||
"cmd-shift-y": "git::UnstageAndNext",
|
||||
"cmd-'": "editor::ToggleSelectedDiffHunks",
|
||||
"cmd-\"": "editor::ExpandAllHunkDiffs",
|
||||
"cmd-\"": "editor::ExpandAllDiffHunks",
|
||||
"cmd-alt-g b": "editor::ToggleGitBlame",
|
||||
"cmd-i": "editor::ShowSignatureHelp",
|
||||
"ctrl-f12": "editor::GoToDeclaration",
|
||||
@@ -150,8 +248,18 @@
|
||||
"cmd-k z": "editor::ToggleSoftWrap",
|
||||
"cmd-f": "buffer_search::Deploy",
|
||||
"cmd-alt-f": "buffer_search::DeployReplace",
|
||||
"cmd-alt-l": ["buffer_search::Deploy", { "selection_search_enabled": true }],
|
||||
"cmd-e": ["buffer_search::Deploy", { "focus": false }],
|
||||
"cmd-alt-l": [
|
||||
"buffer_search::Deploy",
|
||||
{
|
||||
"selection_search_enabled": true
|
||||
}
|
||||
],
|
||||
"cmd-e": [
|
||||
"buffer_search::Deploy",
|
||||
{
|
||||
"focus": false
|
||||
}
|
||||
],
|
||||
"cmd->": "assistant::QuoteSelection",
|
||||
"cmd-<": "assistant::InsertIntoEditor",
|
||||
"cmd-alt-e": "editor::SelectEnclosingSymbol",
|
||||
@@ -205,11 +313,12 @@
|
||||
"cmd-k c": "assistant::CopyCode",
|
||||
"cmd-shift-e": "project_panel::ToggleFocus",
|
||||
"cmd-g": "search::SelectNextMatch",
|
||||
"cmd-shift-g": "search::SelectPrevMatch",
|
||||
"cmd-shift-g": "search::SelectPreviousMatch",
|
||||
"cmd-alt-/": "assistant::ToggleModelSelector",
|
||||
"cmd-k h": "assistant::DeployHistory",
|
||||
"cmd-k l": "assistant::DeployPromptLibrary",
|
||||
"cmd-n": "assistant::NewContext"
|
||||
"cmd-t": "assistant::NewChat",
|
||||
"cmd-n": "assistant::NewChat"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -282,11 +391,12 @@
|
||||
"escape": "buffer_search::Dismiss",
|
||||
"tab": "buffer_search::FocusEditor",
|
||||
"enter": "search::SelectNextMatch",
|
||||
"shift-enter": "search::SelectPrevMatch",
|
||||
"shift-enter": "search::SelectPreviousMatch",
|
||||
"alt-enter": "search::SelectAllMatches",
|
||||
"cmd-f": "search::FocusSearch",
|
||||
"cmd-alt-f": "search::ToggleReplace",
|
||||
"cmd-alt-l": "search::ToggleSelection"
|
||||
"cmd-alt-l": "search::ToggleSelection",
|
||||
"cmd-shift-o": "outline::Toggle"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -348,22 +458,52 @@
|
||||
"context": "Pane",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"alt-cmd-left": "pane::ActivatePrevItem",
|
||||
"cmd-{": "pane::ActivatePrevItem",
|
||||
"alt-cmd-left": "pane::ActivatePreviousItem",
|
||||
"cmd-{": "pane::ActivatePreviousItem",
|
||||
"alt-cmd-right": "pane::ActivateNextItem",
|
||||
"cmd-}": "pane::ActivateNextItem",
|
||||
"ctrl-shift-pageup": "pane::SwapItemLeft",
|
||||
"ctrl-shift-pagedown": "pane::SwapItemRight",
|
||||
"cmd-w": ["pane::CloseActiveItem", { "close_pinned": false }],
|
||||
"alt-cmd-t": ["pane::CloseInactiveItems", { "close_pinned": false }],
|
||||
"cmd-w": [
|
||||
"pane::CloseActiveItem",
|
||||
{
|
||||
"close_pinned": false
|
||||
}
|
||||
],
|
||||
"alt-cmd-t": [
|
||||
"pane::CloseInactiveItems",
|
||||
{
|
||||
"close_pinned": false
|
||||
}
|
||||
],
|
||||
"ctrl-alt-cmd-w": "workspace::CloseInactiveTabsAndPanes",
|
||||
"cmd-k e": ["pane::CloseItemsToTheLeft", { "close_pinned": false }],
|
||||
"cmd-k t": ["pane::CloseItemsToTheRight", { "close_pinned": false }],
|
||||
"cmd-k u": ["pane::CloseCleanItems", { "close_pinned": false }],
|
||||
"cmd-k cmd-w": ["pane::CloseAllItems", { "close_pinned": false }],
|
||||
"cmd-k e": [
|
||||
"pane::CloseItemsToTheLeft",
|
||||
{
|
||||
"close_pinned": false
|
||||
}
|
||||
],
|
||||
"cmd-k t": [
|
||||
"pane::CloseItemsToTheRight",
|
||||
{
|
||||
"close_pinned": false
|
||||
}
|
||||
],
|
||||
"cmd-k u": [
|
||||
"pane::CloseCleanItems",
|
||||
{
|
||||
"close_pinned": false
|
||||
}
|
||||
],
|
||||
"cmd-k cmd-w": [
|
||||
"pane::CloseAllItems",
|
||||
{
|
||||
"close_pinned": false
|
||||
}
|
||||
],
|
||||
"cmd-f": "project_search::ToggleFocus",
|
||||
"cmd-g": "search::SelectNextMatch",
|
||||
"cmd-shift-g": "search::SelectPrevMatch",
|
||||
"cmd-shift-g": "search::SelectPreviousMatch",
|
||||
"cmd-shift-h": "search::ToggleReplace",
|
||||
"cmd-alt-l": "search::ToggleSelection",
|
||||
"alt-enter": "search::SelectAllMatches",
|
||||
@@ -392,18 +532,43 @@
|
||||
"alt-shift-down": "editor::DuplicateLineDown",
|
||||
"ctrl-shift-right": "editor::SelectLargerSyntaxNode", // Expand Selection
|
||||
"ctrl-shift-left": "editor::SelectSmallerSyntaxNode", // Shrink Selection
|
||||
"cmd-d": ["editor::SelectNext", { "replace_newest": false }], // Add selection to Next Find Match
|
||||
"cmd-d": [
|
||||
"editor::SelectNext",
|
||||
{
|
||||
"replace_newest": false
|
||||
}
|
||||
], // Add selection to Next Find Match
|
||||
"cmd-shift-l": "editor::SelectAllMatches", // Select all occurrences of current selection
|
||||
"cmd-f2": "editor::SelectAllMatches", // Select all occurrences of current word
|
||||
"ctrl-cmd-d": ["editor::SelectPrevious", { "replace_newest": false }],
|
||||
"cmd-k cmd-d": ["editor::SelectNext", { "replace_newest": true }],
|
||||
"cmd-k ctrl-cmd-d": ["editor::SelectPrevious", { "replace_newest": true }],
|
||||
"ctrl-cmd-d": [
|
||||
"editor::SelectPrevious",
|
||||
{
|
||||
"replace_newest": false
|
||||
}
|
||||
],
|
||||
"cmd-k cmd-d": [
|
||||
"editor::SelectNext",
|
||||
{
|
||||
"replace_newest": true
|
||||
}
|
||||
],
|
||||
"cmd-k ctrl-cmd-d": [
|
||||
"editor::SelectPrevious",
|
||||
{
|
||||
"replace_newest": true
|
||||
}
|
||||
],
|
||||
"cmd-k cmd-i": "editor::Hover",
|
||||
"cmd-/": ["editor::ToggleComments", { "advance_downwards": false }],
|
||||
"cmd-/": [
|
||||
"editor::ToggleComments",
|
||||
{
|
||||
"advance_downwards": false
|
||||
}
|
||||
],
|
||||
"cmd-u": "editor::UndoSelection",
|
||||
"cmd-shift-u": "editor::RedoSelection",
|
||||
"f8": "editor::GoToDiagnostic",
|
||||
"shift-f8": "editor::GoToPrevDiagnostic",
|
||||
"shift-f8": "editor::GoToPreviousDiagnostic",
|
||||
"f2": "editor::Rename",
|
||||
"f12": "editor::GoToDefinition",
|
||||
"alt-f12": "editor::GoToDefinitionSplit",
|
||||
@@ -418,15 +583,42 @@
|
||||
"cmd-k cmd-l": "editor::ToggleFold",
|
||||
"cmd-k cmd-[": "editor::FoldRecursive",
|
||||
"cmd-k cmd-]": "editor::UnfoldRecursive",
|
||||
"cmd-k cmd-1": ["editor::FoldAtLevel", 1],
|
||||
"cmd-k cmd-2": ["editor::FoldAtLevel", 2],
|
||||
"cmd-k cmd-3": ["editor::FoldAtLevel", 3],
|
||||
"cmd-k cmd-4": ["editor::FoldAtLevel", 4],
|
||||
"cmd-k cmd-5": ["editor::FoldAtLevel", 5],
|
||||
"cmd-k cmd-6": ["editor::FoldAtLevel", 6],
|
||||
"cmd-k cmd-7": ["editor::FoldAtLevel", 7],
|
||||
"cmd-k cmd-8": ["editor::FoldAtLevel", 8],
|
||||
"cmd-k cmd-9": ["editor::FoldAtLevel", 9],
|
||||
"cmd-k cmd-1": [
|
||||
"editor::FoldAtLevel",
|
||||
1
|
||||
],
|
||||
"cmd-k cmd-2": [
|
||||
"editor::FoldAtLevel",
|
||||
2
|
||||
],
|
||||
"cmd-k cmd-3": [
|
||||
"editor::FoldAtLevel",
|
||||
3
|
||||
],
|
||||
"cmd-k cmd-4": [
|
||||
"editor::FoldAtLevel",
|
||||
4
|
||||
],
|
||||
"cmd-k cmd-5": [
|
||||
"editor::FoldAtLevel",
|
||||
5
|
||||
],
|
||||
"cmd-k cmd-6": [
|
||||
"editor::FoldAtLevel",
|
||||
6
|
||||
],
|
||||
"cmd-k cmd-7": [
|
||||
"editor::FoldAtLevel",
|
||||
7
|
||||
],
|
||||
"cmd-k cmd-8": [
|
||||
"editor::FoldAtLevel",
|
||||
8
|
||||
],
|
||||
"cmd-k cmd-9": [
|
||||
"editor::FoldAtLevel",
|
||||
9
|
||||
],
|
||||
"cmd-k cmd-0": "editor::FoldAll",
|
||||
"cmd-k cmd-j": "editor::UnfoldAll",
|
||||
// Using `ctrl-space` in Zed requires disabling the macOS global shortcut.
|
||||
@@ -453,18 +645,45 @@
|
||||
"context": "Pane",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"ctrl-1": ["pane::ActivateItem", 0],
|
||||
"ctrl-2": ["pane::ActivateItem", 1],
|
||||
"ctrl-3": ["pane::ActivateItem", 2],
|
||||
"ctrl-4": ["pane::ActivateItem", 3],
|
||||
"ctrl-5": ["pane::ActivateItem", 4],
|
||||
"ctrl-6": ["pane::ActivateItem", 5],
|
||||
"ctrl-7": ["pane::ActivateItem", 6],
|
||||
"ctrl-8": ["pane::ActivateItem", 7],
|
||||
"ctrl-9": ["pane::ActivateItem", 8],
|
||||
"ctrl-1": [
|
||||
"pane::ActivateItem",
|
||||
0
|
||||
],
|
||||
"ctrl-2": [
|
||||
"pane::ActivateItem",
|
||||
1
|
||||
],
|
||||
"ctrl-3": [
|
||||
"pane::ActivateItem",
|
||||
2
|
||||
],
|
||||
"ctrl-4": [
|
||||
"pane::ActivateItem",
|
||||
3
|
||||
],
|
||||
"ctrl-5": [
|
||||
"pane::ActivateItem",
|
||||
4
|
||||
],
|
||||
"ctrl-6": [
|
||||
"pane::ActivateItem",
|
||||
5
|
||||
],
|
||||
"ctrl-7": [
|
||||
"pane::ActivateItem",
|
||||
6
|
||||
],
|
||||
"ctrl-8": [
|
||||
"pane::ActivateItem",
|
||||
7
|
||||
],
|
||||
"ctrl-9": [
|
||||
"pane::ActivateItem",
|
||||
8
|
||||
],
|
||||
"ctrl-0": "pane::ActivateLastItem",
|
||||
"ctrl--": "pane::GoBack",
|
||||
"ctrl-shift--": "pane::GoForward",
|
||||
"ctrl-_": "pane::GoForward",
|
||||
"cmd-shift-f": "pane::DeploySearch"
|
||||
}
|
||||
},
|
||||
@@ -483,28 +702,65 @@
|
||||
"cmd-shift-s": "workspace::SaveAs",
|
||||
"cmd-shift-n": "workspace::NewWindow",
|
||||
"ctrl-`": "terminal_panel::ToggleFocus",
|
||||
"cmd-1": ["workspace::ActivatePane", 0],
|
||||
"cmd-2": ["workspace::ActivatePane", 1],
|
||||
"cmd-3": ["workspace::ActivatePane", 2],
|
||||
"cmd-4": ["workspace::ActivatePane", 3],
|
||||
"cmd-5": ["workspace::ActivatePane", 4],
|
||||
"cmd-6": ["workspace::ActivatePane", 5],
|
||||
"cmd-7": ["workspace::ActivatePane", 6],
|
||||
"cmd-8": ["workspace::ActivatePane", 7],
|
||||
"cmd-9": ["workspace::ActivatePane", 8],
|
||||
"cmd-1": [
|
||||
"workspace::ActivatePane",
|
||||
0
|
||||
],
|
||||
"cmd-2": [
|
||||
"workspace::ActivatePane",
|
||||
1
|
||||
],
|
||||
"cmd-3": [
|
||||
"workspace::ActivatePane",
|
||||
2
|
||||
],
|
||||
"cmd-4": [
|
||||
"workspace::ActivatePane",
|
||||
3
|
||||
],
|
||||
"cmd-5": [
|
||||
"workspace::ActivatePane",
|
||||
4
|
||||
],
|
||||
"cmd-6": [
|
||||
"workspace::ActivatePane",
|
||||
5
|
||||
],
|
||||
"cmd-7": [
|
||||
"workspace::ActivatePane",
|
||||
6
|
||||
],
|
||||
"cmd-8": [
|
||||
"workspace::ActivatePane",
|
||||
7
|
||||
],
|
||||
"cmd-9": [
|
||||
"workspace::ActivatePane",
|
||||
8
|
||||
],
|
||||
"cmd-b": "workspace::ToggleLeftDock",
|
||||
"cmd-r": "workspace::ToggleRightDock",
|
||||
"cmd-j": "workspace::ToggleBottomDock",
|
||||
"alt-cmd-y": "workspace::CloseAllDocks",
|
||||
"cmd-shift-f": "pane::DeploySearch",
|
||||
"cmd-shift-h": ["pane::DeploySearch", { "replace_enabled": true }],
|
||||
"cmd-shift-h": [
|
||||
"pane::DeploySearch",
|
||||
{
|
||||
"replace_enabled": true
|
||||
}
|
||||
],
|
||||
"cmd-shift-t": "pane::ReopenClosedItem",
|
||||
"cmd-k cmd-s": "zed::OpenKeymap",
|
||||
"cmd-k cmd-t": "theme_selector::Toggle",
|
||||
"cmd-t": "project_symbols::Toggle",
|
||||
"cmd-p": "file_finder::Toggle",
|
||||
"ctrl-tab": "tab_switcher::Toggle",
|
||||
"ctrl-shift-tab": ["tab_switcher::Toggle", { "select_last": true }],
|
||||
"ctrl-shift-tab": [
|
||||
"tab_switcher::Toggle",
|
||||
{
|
||||
"select_last": true
|
||||
}
|
||||
],
|
||||
"cmd-shift-p": "command_palette::Toggle",
|
||||
"cmd-shift-m": "diagnostics::Deploy",
|
||||
"cmd-shift-e": "project_panel::ToggleFocus",
|
||||
@@ -532,7 +788,12 @@
|
||||
"cmd-n": "workspace::NewFile",
|
||||
"cmd-shift-r": "task::Spawn",
|
||||
"cmd-alt-r": "task::Rerun",
|
||||
"ctrl-alt-shift-r": ["task::Spawn", { "reveal_target": "center" }]
|
||||
"ctrl-alt-shift-r": [
|
||||
"task::Spawn",
|
||||
{
|
||||
"reveal_target": "center"
|
||||
}
|
||||
]
|
||||
// also possible to spawn tasks by name:
|
||||
// "foo-bar": ["task::Spawn", { "task_name": "MyTask", "reveal_target": "dock" }]
|
||||
}
|
||||
@@ -609,8 +870,8 @@
|
||||
"context": "Editor && (showing_code_actions || showing_completions)",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"up": "editor::ContextMenuPrev",
|
||||
"ctrl-p": "editor::ContextMenuPrev",
|
||||
"up": "editor::ContextMenuPrevious",
|
||||
"ctrl-p": "editor::ContextMenuPrevious",
|
||||
"down": "editor::ContextMenuNext",
|
||||
"ctrl-n": "editor::ContextMenuNext",
|
||||
"pageup": "editor::ContextMenuFirst",
|
||||
@@ -636,7 +897,7 @@
|
||||
"cmd-alt-enter": "editor::OpenExcerptsSplit",
|
||||
"cmd-shift-e": "pane::RevealInProjectPanel",
|
||||
"cmd-f8": "editor::GoToHunk",
|
||||
"cmd-shift-f8": "editor::GoToPrevHunk",
|
||||
"cmd-shift-f8": "editor::GoToPreviousHunk",
|
||||
"ctrl-enter": "assistant::InlineAssist",
|
||||
"ctrl-:": "editor::ToggleInlayHints"
|
||||
}
|
||||
@@ -679,7 +940,7 @@
|
||||
"alt-cmd-r": "outline_panel::RevealInFileManager",
|
||||
"space": "outline_panel::Open",
|
||||
"shift-down": "menu::SelectNext",
|
||||
"shift-up": "menu::SelectPrev",
|
||||
"shift-up": "menu::SelectPrevious",
|
||||
"alt-enter": "editor::OpenExcerpts",
|
||||
"cmd-alt-enter": "editor::OpenExcerptsSplit"
|
||||
}
|
||||
@@ -700,16 +961,41 @@
|
||||
"alt-cmd-shift-c": "workspace::CopyRelativePath",
|
||||
"enter": "project_panel::Rename",
|
||||
"f2": "project_panel::Rename",
|
||||
"backspace": ["project_panel::Trash", { "skip_prompt": false }],
|
||||
"delete": ["project_panel::Trash", { "skip_prompt": false }],
|
||||
"cmd-backspace": ["project_panel::Trash", { "skip_prompt": true }],
|
||||
"cmd-delete": ["project_panel::Delete", { "skip_prompt": false }],
|
||||
"backspace": [
|
||||
"project_panel::Trash",
|
||||
{
|
||||
"skip_prompt": false
|
||||
}
|
||||
],
|
||||
"delete": [
|
||||
"project_panel::Trash",
|
||||
{
|
||||
"skip_prompt": false
|
||||
}
|
||||
],
|
||||
"cmd-backspace": [
|
||||
"project_panel::Trash",
|
||||
{
|
||||
"skip_prompt": true
|
||||
}
|
||||
],
|
||||
"cmd-delete": [
|
||||
"project_panel::Delete",
|
||||
{
|
||||
"skip_prompt": false
|
||||
}
|
||||
],
|
||||
"alt-cmd-r": "project_panel::RevealInFileManager",
|
||||
"ctrl-shift-enter": "project_panel::OpenWithSystem",
|
||||
"cmd-alt-backspace": ["project_panel::Delete", { "skip_prompt": false }],
|
||||
"cmd-alt-backspace": [
|
||||
"project_panel::Delete",
|
||||
{
|
||||
"skip_prompt": false
|
||||
}
|
||||
],
|
||||
"cmd-shift-f": "project_panel::NewSearchInDirectory",
|
||||
"shift-down": "menu::SelectNext",
|
||||
"shift-up": "menu::SelectPrev",
|
||||
"shift-up": "menu::SelectPrevious",
|
||||
"escape": "menu::Cancel"
|
||||
}
|
||||
},
|
||||
@@ -724,7 +1010,7 @@
|
||||
"context": "GitPanel && ChangesList",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"up": "menu::SelectPrev",
|
||||
"up": "menu::SelectPrevious",
|
||||
"down": "menu::SelectNext",
|
||||
"cmd-up": "menu::SelectFirst",
|
||||
"cmd-down": "menu::SelectLast",
|
||||
@@ -735,7 +1021,15 @@
|
||||
"alt-down": "git_panel::FocusEditor",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -749,6 +1043,14 @@
|
||||
"alt-up": "git_panel::FocusChanges"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "GitCommit > Editor",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"enter": "editor::Newline",
|
||||
"cmd-enter": "git::Commit"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "CollabPanel && not_editing",
|
||||
"use_key_equivalents": true,
|
||||
@@ -776,8 +1078,18 @@
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"tab": "picker::ConfirmCompletion",
|
||||
"alt-enter": ["picker::ConfirmInput", { "secondary": false }],
|
||||
"cmd-alt-enter": ["picker::ConfirmInput", { "secondary": true }]
|
||||
"alt-enter": [
|
||||
"picker::ConfirmInput",
|
||||
{
|
||||
"secondary": false
|
||||
}
|
||||
],
|
||||
"cmd-alt-enter": [
|
||||
"picker::ConfirmInput",
|
||||
{
|
||||
"secondary": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -791,7 +1103,7 @@
|
||||
"context": "FileFinder || (FileFinder > Picker > Editor) || (FileFinder > Picker > menu)",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"cmd-shift-p": "file_finder::SelectPrev",
|
||||
"cmd-shift-p": "file_finder::SelectPrevious",
|
||||
"cmd-j": "pane::SplitDown",
|
||||
"cmd-k": "pane::SplitUp",
|
||||
"cmd-h": "pane::SplitLeft",
|
||||
@@ -802,8 +1114,8 @@
|
||||
"context": "TabSwitcher",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"ctrl-shift-tab": "menu::SelectPrev",
|
||||
"ctrl-up": "menu::SelectPrev",
|
||||
"ctrl-shift-tab": "menu::SelectPrevious",
|
||||
"ctrl-up": "menu::SelectPrevious",
|
||||
"ctrl-down": "menu::SelectNext",
|
||||
"ctrl-backspace": "tab_switcher::CloseSelectedItem"
|
||||
}
|
||||
@@ -819,24 +1131,67 @@
|
||||
"cmd-k": "terminal::Clear",
|
||||
"cmd-n": "workspace::NewTerminal",
|
||||
"ctrl-enter": "assistant::InlineAssist",
|
||||
"ctrl-_": null, // emacs undo
|
||||
// Some nice conveniences
|
||||
"cmd-backspace": ["terminal::SendText", "\u0015"],
|
||||
"cmd-right": ["terminal::SendText", "\u0005"],
|
||||
"cmd-left": ["terminal::SendText", "\u0001"],
|
||||
"cmd-backspace": [
|
||||
"terminal::SendText",
|
||||
"\u0015"
|
||||
],
|
||||
"cmd-right": [
|
||||
"terminal::SendText",
|
||||
"\u0005"
|
||||
],
|
||||
"cmd-left": [
|
||||
"terminal::SendText",
|
||||
"\u0001"
|
||||
],
|
||||
// Terminal.app compatibility
|
||||
"alt-left": ["terminal::SendText", "\u001bb"],
|
||||
"alt-right": ["terminal::SendText", "\u001bf"],
|
||||
"alt-b": ["terminal::SendText", "\u001bb"],
|
||||
"alt-f": ["terminal::SendText", "\u001bf"],
|
||||
"alt-left": [
|
||||
"terminal::SendText",
|
||||
"\u001bb"
|
||||
],
|
||||
"alt-right": [
|
||||
"terminal::SendText",
|
||||
"\u001bf"
|
||||
],
|
||||
"alt-b": [
|
||||
"terminal::SendText",
|
||||
"\u001bb"
|
||||
],
|
||||
"alt-f": [
|
||||
"terminal::SendText",
|
||||
"\u001bf"
|
||||
],
|
||||
// There are conflicting bindings for these keys in the global context.
|
||||
// these bindings override them, remove at your own risk:
|
||||
"up": ["terminal::SendKeystroke", "up"],
|
||||
"pageup": ["terminal::SendKeystroke", "pageup"],
|
||||
"down": ["terminal::SendKeystroke", "down"],
|
||||
"pagedown": ["terminal::SendKeystroke", "pagedown"],
|
||||
"escape": ["terminal::SendKeystroke", "escape"],
|
||||
"enter": ["terminal::SendKeystroke", "enter"],
|
||||
"ctrl-c": ["terminal::SendKeystroke", "ctrl-c"],
|
||||
"up": [
|
||||
"terminal::SendKeystroke",
|
||||
"up"
|
||||
],
|
||||
"pageup": [
|
||||
"terminal::SendKeystroke",
|
||||
"pageup"
|
||||
],
|
||||
"down": [
|
||||
"terminal::SendKeystroke",
|
||||
"down"
|
||||
],
|
||||
"pagedown": [
|
||||
"terminal::SendKeystroke",
|
||||
"pagedown"
|
||||
],
|
||||
"escape": [
|
||||
"terminal::SendKeystroke",
|
||||
"escape"
|
||||
],
|
||||
"enter": [
|
||||
"terminal::SendKeystroke",
|
||||
"enter"
|
||||
],
|
||||
"ctrl-c": [
|
||||
"terminal::SendKeystroke",
|
||||
"ctrl-c"
|
||||
],
|
||||
"shift-pageup": "terminal::ScrollPageUp",
|
||||
"cmd-up": "terminal::ScrollPageUp",
|
||||
"shift-pagedown": "terminal::ScrollPageDown",
|
||||
@@ -881,4 +1236,4 @@
|
||||
"escape": "menu::Cancel"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
@@ -39,7 +39,7 @@
|
||||
"context": "BufferSearchBar",
|
||||
"bindings": {
|
||||
"ctrl-f3": "search::SelectNextMatch", // find-and-replace:find-next-selected
|
||||
"ctrl-shift-f3": "search::SelectPrevMatch" // find-and-replace:find-previous-selected
|
||||
"ctrl-shift-f3": "search::SelectPreviousMatch" // find-and-replace:find-previous-selected
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
"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-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-b": "editor::MoveToPreviousSubwordStart", // backward-word
|
||||
"alt-u": "editor::ConvertToUpperCase", // upcase-word
|
||||
@@ -48,6 +49,8 @@
|
||||
"ctrl-_": "editor::Undo", // undo
|
||||
"ctrl-/": "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
|
||||
"alt-v": "editor::MovePageUp", // scroll-down
|
||||
"ctrl-x [": "editor::MoveToBeginning", // beginning-of-buffer
|
||||
@@ -119,7 +122,7 @@
|
||||
"context": "BufferSearchBar > Editor",
|
||||
"bindings": {
|
||||
"ctrl-s": "search::SelectNextMatch",
|
||||
"ctrl-r": "search::SelectPrevMatch",
|
||||
"ctrl-r": "search::SelectPreviousMatch",
|
||||
"ctrl-g": "buffer_search::Dismiss"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2,15 +2,25 @@
|
||||
{
|
||||
"bindings": {
|
||||
"ctrl-alt-s": "zed::OpenSettings",
|
||||
"ctrl-shift-[": "pane::ActivatePrevItem",
|
||||
"ctrl-shift-]": "pane::ActivateNextItem"
|
||||
"ctrl-{": "pane::ActivatePreviousItem",
|
||||
"ctrl-}": "pane::ActivateNextItem"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Editor",
|
||||
"bindings": {
|
||||
"ctrl->": ["zed::IncreaseBufferFontSize", { "persist": true }],
|
||||
"ctrl-<": ["zed::DecreaseBufferFontSize", { "persist": true }],
|
||||
"ctrl->": [
|
||||
"zed::IncreaseBufferFontSize",
|
||||
{
|
||||
"persist": true
|
||||
}
|
||||
],
|
||||
"ctrl-<": [
|
||||
"zed::DecreaseBufferFontSize",
|
||||
{
|
||||
"persist": true
|
||||
}
|
||||
],
|
||||
"ctrl-shift-j": "editor::JoinLines",
|
||||
"ctrl-d": "editor::DuplicateSelection",
|
||||
"ctrl-y": "editor::DeleteLine",
|
||||
@@ -23,9 +33,24 @@
|
||||
// "ctrl--": "editor::Fold", // TODO: `ctrl-numpad--` (numpad not implemented)
|
||||
// "ctrl-+": "editor::UnfoldLines", // TODO: `ctrl-numpad+` (numpad not implemented)
|
||||
"alt-shift-g": "editor::SplitSelectionIntoLines",
|
||||
"alt-j": ["editor::SelectNext", { "replace_newest": false }],
|
||||
"alt-shift-j": ["editor::SelectPrevious", { "replace_newest": false }],
|
||||
"ctrl-/": ["editor::ToggleComments", { "advance_downwards": true }],
|
||||
"alt-j": [
|
||||
"editor::SelectNext",
|
||||
{
|
||||
"replace_newest": false
|
||||
}
|
||||
],
|
||||
"alt-shift-j": [
|
||||
"editor::SelectPrevious",
|
||||
{
|
||||
"replace_newest": false
|
||||
}
|
||||
],
|
||||
"ctrl-/": [
|
||||
"editor::ToggleComments",
|
||||
{
|
||||
"advance_downwards": true
|
||||
}
|
||||
],
|
||||
"ctrl-w": "editor::SelectLargerSyntaxNode",
|
||||
"ctrl-shift-w": "editor::SelectSmallerSyntaxNode",
|
||||
"shift-alt-up": "editor::MoveLineUp",
|
||||
@@ -41,9 +66,9 @@
|
||||
"ctrl-shift-b": "editor::GoToTypeDefinition",
|
||||
"ctrl-alt-shift-b": "editor::GoToTypeDefinitionSplit",
|
||||
"f2": "editor::GoToDiagnostic",
|
||||
"shift-f2": "editor::GoToPrevDiagnostic",
|
||||
"shift-f2": "editor::GoToPreviousDiagnostic",
|
||||
"ctrl-alt-shift-down": "editor::GoToHunk",
|
||||
"ctrl-alt-shift-up": "editor::GoToPrevHunk",
|
||||
"ctrl-alt-shift-up": "editor::GoToPreviousHunk",
|
||||
"ctrl-alt-z": "git::Restore",
|
||||
"ctrl-home": "editor::MoveToBeginning",
|
||||
"ctrl-end": "editor::MoveToEnd",
|
||||
@@ -84,10 +109,25 @@
|
||||
"context": "ProjectPanel",
|
||||
"bindings": {
|
||||
"enter": "project_panel::Open",
|
||||
"backspace": ["project_panel::Trash", { "skip_prompt": false }],
|
||||
"delete": ["project_panel::Trash", { "skip_prompt": false }],
|
||||
"shift-delete": ["project_panel::Delete", { "skip_prompt": false }],
|
||||
"backspace": [
|
||||
"project_panel::Trash",
|
||||
{
|
||||
"skip_prompt": false
|
||||
}
|
||||
],
|
||||
"delete": [
|
||||
"project_panel::Trash",
|
||||
{
|
||||
"skip_prompt": false
|
||||
}
|
||||
],
|
||||
"shift-delete": [
|
||||
"project_panel::Delete",
|
||||
{
|
||||
"skip_prompt": false
|
||||
}
|
||||
],
|
||||
"shift-f6": "project_panel::Rename"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
@@ -1,28 +1,101 @@
|
||||
[
|
||||
{
|
||||
"bindings": {
|
||||
"ctrl-shift-[": "pane::ActivatePrevItem",
|
||||
"ctrl-shift-]": "pane::ActivateNextItem",
|
||||
"ctrl-pageup": "pane::ActivatePrevItem",
|
||||
"ctrl-{": "pane::ActivatePreviousItem",
|
||||
"ctrl-}": "pane::ActivateNextItem",
|
||||
"ctrl-pageup": "pane::ActivatePreviousItem",
|
||||
"ctrl-pagedown": "pane::ActivateNextItem",
|
||||
"ctrl-1": ["workspace::ActivatePane", 0],
|
||||
"ctrl-2": ["workspace::ActivatePane", 1],
|
||||
"ctrl-3": ["workspace::ActivatePane", 2],
|
||||
"ctrl-4": ["workspace::ActivatePane", 3],
|
||||
"ctrl-5": ["workspace::ActivatePane", 4],
|
||||
"ctrl-6": ["workspace::ActivatePane", 5],
|
||||
"ctrl-7": ["workspace::ActivatePane", 6],
|
||||
"ctrl-8": ["workspace::ActivatePane", 7],
|
||||
"ctrl-9": ["workspace::ActivatePane", 8],
|
||||
"ctrl-shift-1": ["workspace::MoveItemToPane", { "destination": 0, "focus": true }],
|
||||
"ctrl-shift-2": ["workspace::MoveItemToPane", { "destination": 1 }],
|
||||
"ctrl-shift-3": ["workspace::MoveItemToPane", { "destination": 2 }],
|
||||
"ctrl-shift-4": ["workspace::MoveItemToPane", { "destination": 3 }],
|
||||
"ctrl-shift-5": ["workspace::MoveItemToPane", { "destination": 4 }],
|
||||
"ctrl-shift-6": ["workspace::MoveItemToPane", { "destination": 5 }],
|
||||
"ctrl-shift-7": ["workspace::MoveItemToPane", { "destination": 6 }],
|
||||
"ctrl-shift-8": ["workspace::MoveItemToPane", { "destination": 7 }],
|
||||
"ctrl-shift-9": ["workspace::MoveItemToPane", { "destination": 8 }]
|
||||
"ctrl-1": [
|
||||
"workspace::ActivatePane",
|
||||
0
|
||||
],
|
||||
"ctrl-2": [
|
||||
"workspace::ActivatePane",
|
||||
1
|
||||
],
|
||||
"ctrl-3": [
|
||||
"workspace::ActivatePane",
|
||||
2
|
||||
],
|
||||
"ctrl-4": [
|
||||
"workspace::ActivatePane",
|
||||
3
|
||||
],
|
||||
"ctrl-5": [
|
||||
"workspace::ActivatePane",
|
||||
4
|
||||
],
|
||||
"ctrl-6": [
|
||||
"workspace::ActivatePane",
|
||||
5
|
||||
],
|
||||
"ctrl-7": [
|
||||
"workspace::ActivatePane",
|
||||
6
|
||||
],
|
||||
"ctrl-8": [
|
||||
"workspace::ActivatePane",
|
||||
7
|
||||
],
|
||||
"ctrl-9": [
|
||||
"workspace::ActivatePane",
|
||||
8
|
||||
],
|
||||
"ctrl-!": [
|
||||
"workspace::MoveItemToPane",
|
||||
{
|
||||
"destination": 0,
|
||||
"focus": true
|
||||
}
|
||||
],
|
||||
"ctrl-@": [
|
||||
"workspace::MoveItemToPane",
|
||||
{
|
||||
"destination": 1
|
||||
}
|
||||
],
|
||||
"ctrl-#": [
|
||||
"workspace::MoveItemToPane",
|
||||
{
|
||||
"destination": 2
|
||||
}
|
||||
],
|
||||
"ctrl-$": [
|
||||
"workspace::MoveItemToPane",
|
||||
{
|
||||
"destination": 3
|
||||
}
|
||||
],
|
||||
"ctrl-%": [
|
||||
"workspace::MoveItemToPane",
|
||||
{
|
||||
"destination": 4
|
||||
}
|
||||
],
|
||||
"ctrl-^": [
|
||||
"workspace::MoveItemToPane",
|
||||
{
|
||||
"destination": 5
|
||||
}
|
||||
],
|
||||
"ctrl-&": [
|
||||
"workspace::MoveItemToPane",
|
||||
{
|
||||
"destination": 6
|
||||
}
|
||||
],
|
||||
"ctrl-*": [
|
||||
"workspace::MoveItemToPane",
|
||||
{
|
||||
"destination": 7
|
||||
}
|
||||
],
|
||||
"ctrl-(": [
|
||||
"workspace::MoveItemToPane",
|
||||
{
|
||||
"destination": 8
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -44,7 +117,7 @@
|
||||
"shift-f12": "editor::FindAllReferences",
|
||||
"ctrl-shift-f12": "editor::FindAllReferences",
|
||||
"ctrl-.": "editor::GoToHunk",
|
||||
"ctrl-,": "editor::GoToPrevHunk",
|
||||
"ctrl-,": "editor::GoToPreviousHunk",
|
||||
"ctrl-k ctrl-u": "editor::ConvertToUpperCase",
|
||||
"ctrl-k ctrl-l": "editor::ConvertToLowerCase",
|
||||
"shift-alt-m": "markdown::OpenPreviewToTheSide",
|
||||
@@ -62,15 +135,39 @@
|
||||
"context": "Pane",
|
||||
"bindings": {
|
||||
"f4": "search::SelectNextMatch",
|
||||
"shift-f4": "search::SelectPrevMatch",
|
||||
"alt-1": ["pane::ActivateItem", 0],
|
||||
"alt-2": ["pane::ActivateItem", 1],
|
||||
"alt-3": ["pane::ActivateItem", 2],
|
||||
"alt-4": ["pane::ActivateItem", 3],
|
||||
"alt-5": ["pane::ActivateItem", 4],
|
||||
"alt-6": ["pane::ActivateItem", 5],
|
||||
"alt-7": ["pane::ActivateItem", 6],
|
||||
"alt-8": ["pane::ActivateItem", 7],
|
||||
"shift-f4": "search::SelectPreviousMatch",
|
||||
"alt-1": [
|
||||
"pane::ActivateItem",
|
||||
0
|
||||
],
|
||||
"alt-2": [
|
||||
"pane::ActivateItem",
|
||||
1
|
||||
],
|
||||
"alt-3": [
|
||||
"pane::ActivateItem",
|
||||
2
|
||||
],
|
||||
"alt-4": [
|
||||
"pane::ActivateItem",
|
||||
3
|
||||
],
|
||||
"alt-5": [
|
||||
"pane::ActivateItem",
|
||||
4
|
||||
],
|
||||
"alt-6": [
|
||||
"pane::ActivateItem",
|
||||
5
|
||||
],
|
||||
"alt-7": [
|
||||
"pane::ActivateItem",
|
||||
6
|
||||
],
|
||||
"alt-8": [
|
||||
"pane::ActivateItem",
|
||||
7
|
||||
],
|
||||
"alt-9": "pane::ActivateLastItem"
|
||||
}
|
||||
},
|
||||
@@ -82,4 +179,4 @@
|
||||
"shift-ctrl-r": "project_symbols::Toggle"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
@@ -40,7 +40,7 @@
|
||||
"context": "BufferSearchBar",
|
||||
"bindings": {
|
||||
"cmd-f3": "search::SelectNextMatch",
|
||||
"cmd-shift-f3": "search::SelectPrevMatch"
|
||||
"cmd-shift-f3": "search::SelectPreviousMatch"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
"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-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-b": "editor::MoveToPreviousSubwordStart", // backward-word
|
||||
"alt-u": "editor::ConvertToUpperCase", // upcase-word
|
||||
@@ -48,6 +49,8 @@
|
||||
"ctrl-_": "editor::Undo", // undo
|
||||
"ctrl-/": "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
|
||||
"alt-v": "editor::MovePageUp", // scroll-down
|
||||
"ctrl-x [": "editor::MoveToBeginning", // beginning-of-buffer
|
||||
@@ -119,7 +122,7 @@
|
||||
"context": "BufferSearchBar > Editor",
|
||||
"bindings": {
|
||||
"ctrl-s": "search::SelectNextMatch",
|
||||
"ctrl-r": "search::SelectPrevMatch",
|
||||
"ctrl-r": "search::SelectPreviousMatch",
|
||||
"ctrl-g": "buffer_search::Dismiss"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,15 +1,25 @@
|
||||
[
|
||||
{
|
||||
"bindings": {
|
||||
"cmd-shift-[": "pane::ActivatePrevItem",
|
||||
"cmd-shift-]": "pane::ActivateNextItem"
|
||||
"cmd-{": "pane::ActivatePreviousItem",
|
||||
"cmd-}": "pane::ActivateNextItem"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Editor",
|
||||
"bindings": {
|
||||
"ctrl->": ["zed::IncreaseBufferFontSize", { "persist": true }],
|
||||
"ctrl-<": ["zed::DecreaseBufferFontSize", { "persist": true }],
|
||||
"ctrl->": [
|
||||
"zed::IncreaseBufferFontSize",
|
||||
{
|
||||
"persist": true
|
||||
}
|
||||
],
|
||||
"ctrl-<": [
|
||||
"zed::DecreaseBufferFontSize",
|
||||
{
|
||||
"persist": true
|
||||
}
|
||||
],
|
||||
"ctrl-shift-j": "editor::JoinLines",
|
||||
"cmd-d": "editor::DuplicateSelection",
|
||||
"cmd-backspace": "editor::DeleteLine",
|
||||
@@ -21,9 +31,24 @@
|
||||
"cmd--": "editor::Fold",
|
||||
"cmd-+": "editor::UnfoldLines",
|
||||
"alt-shift-g": "editor::SplitSelectionIntoLines",
|
||||
"ctrl-g": ["editor::SelectNext", { "replace_newest": false }],
|
||||
"ctrl-cmd-g": ["editor::SelectPrevious", { "replace_newest": false }],
|
||||
"cmd-/": ["editor::ToggleComments", { "advance_downwards": true }],
|
||||
"ctrl-g": [
|
||||
"editor::SelectNext",
|
||||
{
|
||||
"replace_newest": false
|
||||
}
|
||||
],
|
||||
"ctrl-cmd-g": [
|
||||
"editor::SelectPrevious",
|
||||
{
|
||||
"replace_newest": false
|
||||
}
|
||||
],
|
||||
"cmd-/": [
|
||||
"editor::ToggleComments",
|
||||
{
|
||||
"advance_downwards": true
|
||||
}
|
||||
],
|
||||
"alt-up": "editor::SelectLargerSyntaxNode",
|
||||
"alt-down": "editor::SelectSmallerSyntaxNode",
|
||||
"shift-alt-up": "editor::MoveLineUp",
|
||||
@@ -39,9 +64,9 @@
|
||||
"cmd-shift-b": "editor::GoToTypeDefinition",
|
||||
"cmd-alt-shift-b": "editor::GoToTypeDefinitionSplit",
|
||||
"f2": "editor::GoToDiagnostic",
|
||||
"shift-f2": "editor::GoToPrevDiagnostic",
|
||||
"shift-f2": "editor::GoToPreviousDiagnostic",
|
||||
"ctrl-alt-shift-down": "editor::GoToHunk",
|
||||
"ctrl-alt-shift-up": "editor::GoToPrevHunk",
|
||||
"ctrl-alt-shift-up": "editor::GoToPreviousHunk",
|
||||
"cmd-home": "editor::MoveToBeginning",
|
||||
"cmd-end": "editor::MoveToEnd",
|
||||
"cmd-shift-home": "editor::SelectToBeginning",
|
||||
@@ -61,7 +86,7 @@
|
||||
{
|
||||
"context": "BufferSearchBar > Editor",
|
||||
"bindings": {
|
||||
"shift-enter": "search::SelectPrevMatch"
|
||||
"shift-enter": "search::SelectPreviousMatch"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -87,10 +112,30 @@
|
||||
"context": "ProjectPanel",
|
||||
"bindings": {
|
||||
"enter": "project_panel::Open",
|
||||
"cmd-backspace": ["project_panel::Trash", { "skip_prompt": false }],
|
||||
"backspace": ["project_panel::Trash", { "skip_prompt": false }],
|
||||
"delete": ["project_panel::Trash", { "skip_prompt": false }],
|
||||
"shift-delete": ["project_panel::Delete", { "skip_prompt": false }],
|
||||
"cmd-backspace": [
|
||||
"project_panel::Trash",
|
||||
{
|
||||
"skip_prompt": false
|
||||
}
|
||||
],
|
||||
"backspace": [
|
||||
"project_panel::Trash",
|
||||
{
|
||||
"skip_prompt": false
|
||||
}
|
||||
],
|
||||
"delete": [
|
||||
"project_panel::Trash",
|
||||
{
|
||||
"skip_prompt": false
|
||||
}
|
||||
],
|
||||
"shift-delete": [
|
||||
"project_panel::Delete",
|
||||
{
|
||||
"skip_prompt": false
|
||||
}
|
||||
],
|
||||
"shift-f6": "project_panel::Rename"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,101 @@
|
||||
[
|
||||
{
|
||||
"bindings": {
|
||||
"cmd-shift-[": "pane::ActivatePrevItem",
|
||||
"cmd-shift-]": "pane::ActivateNextItem",
|
||||
"ctrl-pageup": "pane::ActivatePrevItem",
|
||||
"cmd-{": "pane::ActivatePreviousItem",
|
||||
"cmd-}": "pane::ActivateNextItem",
|
||||
"ctrl-pageup": "pane::ActivatePreviousItem",
|
||||
"ctrl-pagedown": "pane::ActivateNextItem",
|
||||
"ctrl-1": ["workspace::ActivatePane", 0],
|
||||
"ctrl-2": ["workspace::ActivatePane", 1],
|
||||
"ctrl-3": ["workspace::ActivatePane", 2],
|
||||
"ctrl-4": ["workspace::ActivatePane", 3],
|
||||
"ctrl-5": ["workspace::ActivatePane", 4],
|
||||
"ctrl-6": ["workspace::ActivatePane", 5],
|
||||
"ctrl-7": ["workspace::ActivatePane", 6],
|
||||
"ctrl-8": ["workspace::ActivatePane", 7],
|
||||
"ctrl-9": ["workspace::ActivatePane", 8],
|
||||
"ctrl-shift-1": ["workspace::MoveItemToPane", { "destination": 0, "focus": true }],
|
||||
"ctrl-shift-2": ["workspace::MoveItemToPane", { "destination": 1 }],
|
||||
"ctrl-shift-3": ["workspace::MoveItemToPane", { "destination": 2 }],
|
||||
"ctrl-shift-4": ["workspace::MoveItemToPane", { "destination": 3 }],
|
||||
"ctrl-shift-5": ["workspace::MoveItemToPane", { "destination": 4 }],
|
||||
"ctrl-shift-6": ["workspace::MoveItemToPane", { "destination": 5 }],
|
||||
"ctrl-shift-7": ["workspace::MoveItemToPane", { "destination": 6 }],
|
||||
"ctrl-shift-8": ["workspace::MoveItemToPane", { "destination": 7 }],
|
||||
"ctrl-shift-9": ["workspace::MoveItemToPane", { "destination": 8 }]
|
||||
"ctrl-1": [
|
||||
"workspace::ActivatePane",
|
||||
0
|
||||
],
|
||||
"ctrl-2": [
|
||||
"workspace::ActivatePane",
|
||||
1
|
||||
],
|
||||
"ctrl-3": [
|
||||
"workspace::ActivatePane",
|
||||
2
|
||||
],
|
||||
"ctrl-4": [
|
||||
"workspace::ActivatePane",
|
||||
3
|
||||
],
|
||||
"ctrl-5": [
|
||||
"workspace::ActivatePane",
|
||||
4
|
||||
],
|
||||
"ctrl-6": [
|
||||
"workspace::ActivatePane",
|
||||
5
|
||||
],
|
||||
"ctrl-7": [
|
||||
"workspace::ActivatePane",
|
||||
6
|
||||
],
|
||||
"ctrl-8": [
|
||||
"workspace::ActivatePane",
|
||||
7
|
||||
],
|
||||
"ctrl-9": [
|
||||
"workspace::ActivatePane",
|
||||
8
|
||||
],
|
||||
"ctrl-!": [
|
||||
"workspace::MoveItemToPane",
|
||||
{
|
||||
"destination": 0,
|
||||
"focus": true
|
||||
}
|
||||
],
|
||||
"ctrl-@": [
|
||||
"workspace::MoveItemToPane",
|
||||
{
|
||||
"destination": 1
|
||||
}
|
||||
],
|
||||
"ctrl-#": [
|
||||
"workspace::MoveItemToPane",
|
||||
{
|
||||
"destination": 2
|
||||
}
|
||||
],
|
||||
"ctrl-$": [
|
||||
"workspace::MoveItemToPane",
|
||||
{
|
||||
"destination": 3
|
||||
}
|
||||
],
|
||||
"ctrl-%": [
|
||||
"workspace::MoveItemToPane",
|
||||
{
|
||||
"destination": 4
|
||||
}
|
||||
],
|
||||
"ctrl-^": [
|
||||
"workspace::MoveItemToPane",
|
||||
{
|
||||
"destination": 5
|
||||
}
|
||||
],
|
||||
"ctrl-&": [
|
||||
"workspace::MoveItemToPane",
|
||||
{
|
||||
"destination": 6
|
||||
}
|
||||
],
|
||||
"ctrl-*": [
|
||||
"workspace::MoveItemToPane",
|
||||
{
|
||||
"destination": 7
|
||||
}
|
||||
],
|
||||
"ctrl-(": [
|
||||
"workspace::MoveItemToPane",
|
||||
{
|
||||
"destination": 8
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -45,7 +118,7 @@
|
||||
"ctrl-alt-cmd-down": "editor::GoToDefinitionSplit",
|
||||
"alt-shift-cmd-down": "editor::FindAllReferences",
|
||||
"ctrl-.": "editor::GoToHunk",
|
||||
"ctrl-,": "editor::GoToPrevHunk",
|
||||
"ctrl-,": "editor::GoToPreviousHunk",
|
||||
"cmd-k cmd-u": "editor::ConvertToUpperCase",
|
||||
"cmd-k cmd-l": "editor::ConvertToLowerCase",
|
||||
"cmd-shift-j": "editor::JoinLines",
|
||||
@@ -64,15 +137,39 @@
|
||||
"context": "Pane",
|
||||
"bindings": {
|
||||
"f4": "search::SelectNextMatch",
|
||||
"shift-f4": "search::SelectPrevMatch",
|
||||
"cmd-1": ["pane::ActivateItem", 0],
|
||||
"cmd-2": ["pane::ActivateItem", 1],
|
||||
"cmd-3": ["pane::ActivateItem", 2],
|
||||
"cmd-4": ["pane::ActivateItem", 3],
|
||||
"cmd-5": ["pane::ActivateItem", 4],
|
||||
"cmd-6": ["pane::ActivateItem", 5],
|
||||
"cmd-7": ["pane::ActivateItem", 6],
|
||||
"cmd-8": ["pane::ActivateItem", 7],
|
||||
"shift-f4": "search::SelectPreviousMatch",
|
||||
"cmd-1": [
|
||||
"pane::ActivateItem",
|
||||
0
|
||||
],
|
||||
"cmd-2": [
|
||||
"pane::ActivateItem",
|
||||
1
|
||||
],
|
||||
"cmd-3": [
|
||||
"pane::ActivateItem",
|
||||
2
|
||||
],
|
||||
"cmd-4": [
|
||||
"pane::ActivateItem",
|
||||
3
|
||||
],
|
||||
"cmd-5": [
|
||||
"pane::ActivateItem",
|
||||
4
|
||||
],
|
||||
"cmd-6": [
|
||||
"pane::ActivateItem",
|
||||
5
|
||||
],
|
||||
"cmd-7": [
|
||||
"pane::ActivateItem",
|
||||
6
|
||||
],
|
||||
"cmd-8": [
|
||||
"pane::ActivateItem",
|
||||
7
|
||||
],
|
||||
"cmd-9": "pane::ActivateLastItem"
|
||||
}
|
||||
},
|
||||
@@ -86,4 +183,4 @@
|
||||
"ctrl-0": "project_panel::ToggleFocus"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
@@ -47,7 +47,7 @@
|
||||
"context": "BufferSearchBar",
|
||||
"bindings": {
|
||||
"ctrl-s": "search::SelectNextMatch",
|
||||
"ctrl-shift-s": "search::SelectPrevMatch"
|
||||
"ctrl-shift-s": "search::SelectPreviousMatch"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
"tab": "menu::SelectNext",
|
||||
"ctrl-n": "menu::SelectNext",
|
||||
"down": "menu::SelectNext",
|
||||
"shift-tab": "menu::SelectPrev",
|
||||
"ctrl-p": "menu::SelectPrev",
|
||||
"up": "menu::SelectPrev",
|
||||
"shift-tab": "menu::SelectPrevious",
|
||||
"ctrl-p": "menu::SelectPrevious",
|
||||
"up": "menu::SelectPrevious",
|
||||
"enter": "menu::Confirm",
|
||||
"ctrl-enter": "menu::SecondaryConfirm",
|
||||
"cmd-enter": "menu::SecondaryConfirm",
|
||||
|
||||
@@ -2,8 +2,18 @@
|
||||
{
|
||||
"context": "VimControl && !menu",
|
||||
"bindings": {
|
||||
"i": ["vim::PushObject", { "around": false }],
|
||||
"a": ["vim::PushObject", { "around": true }],
|
||||
"i": [
|
||||
"vim::PushObject",
|
||||
{
|
||||
"around": false
|
||||
}
|
||||
],
|
||||
"a": [
|
||||
"vim::PushObject",
|
||||
{
|
||||
"around": true
|
||||
}
|
||||
],
|
||||
"left": "vim::Left",
|
||||
"h": "vim::Left",
|
||||
"backspace": "vim::Backspace",
|
||||
@@ -54,29 +64,104 @@
|
||||
// "b": "vim::PreviousSubwordStart",
|
||||
// "e": "vim::NextSubwordEnd",
|
||||
// "g e": "vim::PreviousSubwordEnd",
|
||||
"shift-w": ["vim::NextWordStart", { "ignore_punctuation": true }],
|
||||
"shift-e": ["vim::NextWordEnd", { "ignore_punctuation": true }],
|
||||
"shift-b": ["vim::PreviousWordStart", { "ignore_punctuation": true }],
|
||||
"g shift-e": ["vim::PreviousWordEnd", { "ignore_punctuation": true }],
|
||||
"shift-w": [
|
||||
"vim::NextWordStart",
|
||||
{
|
||||
"ignore_punctuation": true
|
||||
}
|
||||
],
|
||||
"shift-e": [
|
||||
"vim::NextWordEnd",
|
||||
{
|
||||
"ignore_punctuation": true
|
||||
}
|
||||
],
|
||||
"shift-b": [
|
||||
"vim::PreviousWordStart",
|
||||
{
|
||||
"ignore_punctuation": true
|
||||
}
|
||||
],
|
||||
"g shift-e": [
|
||||
"vim::PreviousWordEnd",
|
||||
{
|
||||
"ignore_punctuation": true
|
||||
}
|
||||
],
|
||||
"/": "vim::Search",
|
||||
"g /": "pane::DeploySearch",
|
||||
"?": ["vim::Search", { "backwards": true }],
|
||||
"?": [
|
||||
"vim::Search",
|
||||
{
|
||||
"backwards": true
|
||||
}
|
||||
],
|
||||
"*": "vim::MoveToNext",
|
||||
"#": "vim::MoveToPrev",
|
||||
"#": "vim::MoveToPrevious",
|
||||
"n": "vim::MoveToNextMatch",
|
||||
"shift-n": "vim::MoveToPrevMatch",
|
||||
"shift-n": "vim::MoveToPreviousMatch",
|
||||
"%": "vim::Matching",
|
||||
"] }": ["vim::UnmatchedForward", { "char": "}" }],
|
||||
"[ {": ["vim::UnmatchedBackward", { "char": "{" }],
|
||||
"] )": ["vim::UnmatchedForward", { "char": ")" }],
|
||||
"[ (": ["vim::UnmatchedBackward", { "char": "(" }],
|
||||
"f": ["vim::PushFindForward", { "before": false }],
|
||||
"t": ["vim::PushFindForward", { "before": true }],
|
||||
"shift-f": ["vim::PushFindBackward", { "after": false }],
|
||||
"shift-t": ["vim::PushFindBackward", { "after": true }],
|
||||
"] }": [
|
||||
"vim::UnmatchedForward",
|
||||
{
|
||||
"char": "}"
|
||||
}
|
||||
],
|
||||
"[ {": [
|
||||
"vim::UnmatchedBackward",
|
||||
{
|
||||
"char": "{"
|
||||
}
|
||||
],
|
||||
"] )": [
|
||||
"vim::UnmatchedForward",
|
||||
{
|
||||
"char": ")"
|
||||
}
|
||||
],
|
||||
"[ (": [
|
||||
"vim::UnmatchedBackward",
|
||||
{
|
||||
"char": "("
|
||||
}
|
||||
],
|
||||
"f": [
|
||||
"vim::PushFindForward",
|
||||
{
|
||||
"before": false
|
||||
}
|
||||
],
|
||||
"t": [
|
||||
"vim::PushFindForward",
|
||||
{
|
||||
"before": true
|
||||
}
|
||||
],
|
||||
"shift-f": [
|
||||
"vim::PushFindBackward",
|
||||
{
|
||||
"after": false
|
||||
}
|
||||
],
|
||||
"shift-t": [
|
||||
"vim::PushFindBackward",
|
||||
{
|
||||
"after": true
|
||||
}
|
||||
],
|
||||
"m": "vim::PushMark",
|
||||
"'": ["vim::PushJump", { "line": true }],
|
||||
"`": ["vim::PushJump", { "line": false }],
|
||||
"'": [
|
||||
"vim::PushJump",
|
||||
{
|
||||
"line": true
|
||||
}
|
||||
],
|
||||
"`": [
|
||||
"vim::PushJump",
|
||||
{
|
||||
"line": false
|
||||
}
|
||||
],
|
||||
";": "vim::RepeatFind",
|
||||
",": "vim::RepeatFindReversed",
|
||||
"ctrl-o": "pane::GoBack",
|
||||
@@ -106,7 +191,7 @@
|
||||
"g g": "vim::StartOfDocument",
|
||||
"g h": "editor::Hover",
|
||||
"g t": "pane::ActivateNextItem",
|
||||
"g shift-t": "pane::ActivatePrevItem",
|
||||
"g shift-t": "pane::ActivatePreviousItem",
|
||||
"g d": "editor::GoToDefinition",
|
||||
"g shift-d": "editor::GoToDeclaration",
|
||||
"g y": "editor::GoToTypeDefinition",
|
||||
@@ -117,28 +202,93 @@
|
||||
"g shift-n": "vim::SelectPreviousMatch",
|
||||
"g l": "vim::SelectNext",
|
||||
"g shift-l": "vim::SelectPrevious",
|
||||
"g >": ["editor::SelectNext", { "replace_newest": true }],
|
||||
"g <": ["editor::SelectPrevious", { "replace_newest": true }],
|
||||
"g >": [
|
||||
"editor::SelectNext",
|
||||
{
|
||||
"replace_newest": true
|
||||
}
|
||||
],
|
||||
"g <": [
|
||||
"editor::SelectPrevious",
|
||||
{
|
||||
"replace_newest": true
|
||||
}
|
||||
],
|
||||
"g a": "editor::SelectAllMatches",
|
||||
"g s": "outline::Toggle",
|
||||
"g shift-s": "project_symbols::Toggle",
|
||||
"g .": "editor::ToggleCodeActions", // zed specific
|
||||
"g shift-a": "editor::FindAllReferences", // zed specific
|
||||
"g space": "editor::OpenExcerpts", // zed specific
|
||||
"g *": ["vim::MoveToNext", { "partial_word": true }],
|
||||
"g #": ["vim::MoveToPrev", { "partial_word": true }],
|
||||
"g j": ["vim::Down", { "display_lines": true }],
|
||||
"g down": ["vim::Down", { "display_lines": true }],
|
||||
"g k": ["vim::Up", { "display_lines": true }],
|
||||
"g up": ["vim::Up", { "display_lines": true }],
|
||||
"g $": ["vim::EndOfLine", { "display_lines": true }],
|
||||
"g end": ["vim::EndOfLine", { "display_lines": true }],
|
||||
"g 0": ["vim::StartOfLine", { "display_lines": true }],
|
||||
"g home": ["vim::StartOfLine", { "display_lines": true }],
|
||||
"g ^": ["vim::FirstNonWhitespace", { "display_lines": true }],
|
||||
"g *": [
|
||||
"vim::MoveToNext",
|
||||
{
|
||||
"partial_word": true
|
||||
}
|
||||
],
|
||||
"g #": [
|
||||
"vim::MoveToPrevious",
|
||||
{
|
||||
"partial_word": true
|
||||
}
|
||||
],
|
||||
"g j": [
|
||||
"vim::Down",
|
||||
{
|
||||
"display_lines": true
|
||||
}
|
||||
],
|
||||
"g down": [
|
||||
"vim::Down",
|
||||
{
|
||||
"display_lines": true
|
||||
}
|
||||
],
|
||||
"g k": [
|
||||
"vim::Up",
|
||||
{
|
||||
"display_lines": true
|
||||
}
|
||||
],
|
||||
"g up": [
|
||||
"vim::Up",
|
||||
{
|
||||
"display_lines": true
|
||||
}
|
||||
],
|
||||
"g $": [
|
||||
"vim::EndOfLine",
|
||||
{
|
||||
"display_lines": true
|
||||
}
|
||||
],
|
||||
"g end": [
|
||||
"vim::EndOfLine",
|
||||
{
|
||||
"display_lines": true
|
||||
}
|
||||
],
|
||||
"g 0": [
|
||||
"vim::StartOfLine",
|
||||
{
|
||||
"display_lines": true
|
||||
}
|
||||
],
|
||||
"g home": [
|
||||
"vim::StartOfLine",
|
||||
{
|
||||
"display_lines": true
|
||||
}
|
||||
],
|
||||
"g ^": [
|
||||
"vim::FirstNonWhitespace",
|
||||
{
|
||||
"display_lines": true
|
||||
}
|
||||
],
|
||||
"g v": "vim::RestoreVisualSelection",
|
||||
"g ]": "editor::GoToDiagnostic",
|
||||
"g [": "editor::GoToPrevDiagnostic",
|
||||
"g [": "editor::GoToPreviousDiagnostic",
|
||||
"g i": "vim::InsertAtPrevious",
|
||||
"g ,": "vim::ChangeListNewer",
|
||||
"g ;": "vim::ChangeListOlder",
|
||||
@@ -149,13 +299,28 @@
|
||||
"shift-q": "vim::ReplayLastRecording",
|
||||
"@": "vim::PushReplayRegister",
|
||||
// z commands
|
||||
"z enter": ["workspace::SendKeystrokes", "z t ^"],
|
||||
"z -": ["workspace::SendKeystrokes", "z b ^"],
|
||||
"z ^": ["workspace::SendKeystrokes", "shift-h k z b ^"],
|
||||
"z +": ["workspace::SendKeystrokes", "shift-l j z t ^"],
|
||||
"z enter": [
|
||||
"workspace::SendKeystrokes",
|
||||
"z t ^"
|
||||
],
|
||||
"z -": [
|
||||
"workspace::SendKeystrokes",
|
||||
"z b ^"
|
||||
],
|
||||
"z ^": [
|
||||
"workspace::SendKeystrokes",
|
||||
"shift-h k z b ^"
|
||||
],
|
||||
"z +": [
|
||||
"workspace::SendKeystrokes",
|
||||
"shift-l j z t ^"
|
||||
],
|
||||
"z t": "editor::ScrollCursorTop",
|
||||
"z z": "editor::ScrollCursorCenter",
|
||||
"z .": ["workspace::SendKeystrokes", "z z ^"],
|
||||
"z .": [
|
||||
"workspace::SendKeystrokes",
|
||||
"z z ^"
|
||||
],
|
||||
"z b": "editor::ScrollCursorBottom",
|
||||
"z a": "editor::ToggleFold",
|
||||
"z shift-a": "editor::ToggleFoldRecursive",
|
||||
@@ -166,18 +331,55 @@
|
||||
"z f": "editor::FoldSelectedRanges",
|
||||
"z shift-m": "editor::FoldAll",
|
||||
"z shift-r": "editor::UnfoldAll",
|
||||
"shift-z shift-q": ["pane::CloseActiveItem", { "save_intent": "skip" }],
|
||||
"shift-z shift-z": ["pane::CloseActiveItem", { "save_intent": "save_all" }],
|
||||
"shift-z shift-q": [
|
||||
"pane::CloseActiveItem",
|
||||
{
|
||||
"save_intent": "skip"
|
||||
}
|
||||
],
|
||||
"shift-z shift-z": [
|
||||
"pane::CloseActiveItem",
|
||||
{
|
||||
"save_intent": "save_all"
|
||||
}
|
||||
],
|
||||
// Count support
|
||||
"1": ["vim::Number", 1],
|
||||
"2": ["vim::Number", 2],
|
||||
"3": ["vim::Number", 3],
|
||||
"4": ["vim::Number", 4],
|
||||
"5": ["vim::Number", 5],
|
||||
"6": ["vim::Number", 6],
|
||||
"7": ["vim::Number", 7],
|
||||
"8": ["vim::Number", 8],
|
||||
"9": ["vim::Number", 9],
|
||||
"1": [
|
||||
"vim::Number",
|
||||
1
|
||||
],
|
||||
"2": [
|
||||
"vim::Number",
|
||||
2
|
||||
],
|
||||
"3": [
|
||||
"vim::Number",
|
||||
3
|
||||
],
|
||||
"4": [
|
||||
"vim::Number",
|
||||
4
|
||||
],
|
||||
"5": [
|
||||
"vim::Number",
|
||||
5
|
||||
],
|
||||
"6": [
|
||||
"vim::Number",
|
||||
6
|
||||
],
|
||||
"7": [
|
||||
"vim::Number",
|
||||
7
|
||||
],
|
||||
"8": [
|
||||
"vim::Number",
|
||||
8
|
||||
],
|
||||
"9": [
|
||||
"vim::Number",
|
||||
9
|
||||
],
|
||||
"ctrl-w d": "editor::GoToDefinitionSplit",
|
||||
"ctrl-w g d": "editor::GoToDefinitionSplit",
|
||||
"ctrl-w shift-d": "editor::GoToTypeDefinitionSplit",
|
||||
@@ -214,7 +416,12 @@
|
||||
"ctrl-a": "vim::Increment",
|
||||
"ctrl-x": "vim::Decrement",
|
||||
"p": "vim::Paste",
|
||||
"shift-p": ["vim::Paste", { "before": true }],
|
||||
"shift-p": [
|
||||
"vim::Paste",
|
||||
{
|
||||
"before": true
|
||||
}
|
||||
],
|
||||
"u": "vim::Undo",
|
||||
"ctrl-r": "vim::Redo",
|
||||
"r": "vim::PushReplace",
|
||||
@@ -231,22 +438,25 @@
|
||||
"g w": "vim::PushRewrap",
|
||||
"g q": "vim::PushRewrap",
|
||||
"ctrl-pagedown": "pane::ActivateNextItem",
|
||||
"ctrl-pageup": "pane::ActivatePrevItem",
|
||||
"ctrl-pageup": "pane::ActivatePreviousItem",
|
||||
"insert": "vim::InsertBefore",
|
||||
// tree-sitter related commands
|
||||
"[ x": "vim::SelectLargerSyntaxNode",
|
||||
"] x": "vim::SelectSmallerSyntaxNode",
|
||||
"] d": "editor::GoToDiagnostic",
|
||||
"[ d": "editor::GoToPrevDiagnostic",
|
||||
"[ d": "editor::GoToPreviousDiagnostic",
|
||||
"] c": "editor::GoToHunk",
|
||||
"[ c": "editor::GoToPrevHunk",
|
||||
"[ c": "editor::GoToPreviousHunk",
|
||||
"g c": "vim::PushToggleComments"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "VimControl && VimCount",
|
||||
"bindings": {
|
||||
"0": ["vim::Number", 0],
|
||||
"0": [
|
||||
"vim::Number",
|
||||
0
|
||||
],
|
||||
":": "vim::CountCommand"
|
||||
}
|
||||
},
|
||||
@@ -265,18 +475,43 @@
|
||||
"y": "vim::VisualYank",
|
||||
"shift-y": "vim::VisualYankLine",
|
||||
"p": "vim::Paste",
|
||||
"shift-p": ["vim::Paste", { "preserve_clipboard": true }],
|
||||
"shift-p": [
|
||||
"vim::Paste",
|
||||
{
|
||||
"preserve_clipboard": true
|
||||
}
|
||||
],
|
||||
"c": "vim::Substitute",
|
||||
"s": "vim::Substitute",
|
||||
"shift-r": "vim::SubstituteLine",
|
||||
"shift-s": "vim::SubstituteLine",
|
||||
"~": "vim::ChangeCase",
|
||||
"*": ["vim::MoveToNext", { "partial_word": true }],
|
||||
"#": ["vim::MoveToPrev", { "partial_word": true }],
|
||||
"*": [
|
||||
"vim::MoveToNext",
|
||||
{
|
||||
"partial_word": true
|
||||
}
|
||||
],
|
||||
"#": [
|
||||
"vim::MoveToPrevious",
|
||||
{
|
||||
"partial_word": true
|
||||
}
|
||||
],
|
||||
"ctrl-a": "vim::Increment",
|
||||
"ctrl-x": "vim::Decrement",
|
||||
"g ctrl-a": ["vim::Increment", { "step": true }],
|
||||
"g ctrl-x": ["vim::Decrement", { "step": true }],
|
||||
"g ctrl-a": [
|
||||
"vim::Increment",
|
||||
{
|
||||
"step": true
|
||||
}
|
||||
],
|
||||
"g ctrl-x": [
|
||||
"vim::Decrement",
|
||||
{
|
||||
"step": true
|
||||
}
|
||||
],
|
||||
"shift-i": "vim::InsertBefore",
|
||||
"shift-a": "vim::InsertAfter",
|
||||
"g shift-i": "vim::VisualInsertFirstNonWhiteSpace",
|
||||
@@ -291,8 +526,24 @@
|
||||
"<": "vim::Outdent",
|
||||
"=": "vim::AutoIndent",
|
||||
"!": "vim::ShellCommand",
|
||||
"i": ["vim::PushObject", { "around": false }],
|
||||
"a": ["vim::PushObject", { "around": true }],
|
||||
"i": [
|
||||
"vim::PushObject",
|
||||
{
|
||||
"around": false
|
||||
}
|
||||
],
|
||||
"a": [
|
||||
"vim::PushObject",
|
||||
{
|
||||
"around": true
|
||||
}
|
||||
],
|
||||
"g r": [
|
||||
"vim::Paste",
|
||||
{
|
||||
"preserve_clipboard": true
|
||||
}
|
||||
],
|
||||
"g c": "vim::ToggleComments",
|
||||
"g q": "vim::Rewrap",
|
||||
"\"": "vim::PushRegister",
|
||||
@@ -317,11 +568,23 @@
|
||||
"ctrl-u": "editor::DeleteToBeginningOfLine",
|
||||
"ctrl-t": "vim::Indent",
|
||||
"ctrl-d": "vim::Outdent",
|
||||
"ctrl-k": ["vim::PushDigraph", {}],
|
||||
"ctrl-v": ["vim::PushLiteral", {}],
|
||||
"ctrl-k": [
|
||||
"vim::PushDigraph",
|
||||
{}
|
||||
],
|
||||
"ctrl-v": [
|
||||
"vim::PushLiteral",
|
||||
{}
|
||||
],
|
||||
"ctrl-shift-v": "editor::Paste", // note: this is *very* similar to ctrl-v in vim, but ctrl-shift-v on linux is the typical shortcut for paste when ctrl-v is already in use.
|
||||
"ctrl-q": ["vim::PushLiteral", {}],
|
||||
"ctrl-shift-q": ["vim::PushLiteral", {}],
|
||||
"ctrl-q": [
|
||||
"vim::PushLiteral",
|
||||
{}
|
||||
],
|
||||
"ctrl-shift-q": [
|
||||
"vim::PushLiteral",
|
||||
{}
|
||||
],
|
||||
"ctrl-r": "vim::PushRegister",
|
||||
"insert": "vim::ToggleReplace",
|
||||
"ctrl-o": "vim::TemporaryNormal"
|
||||
@@ -336,14 +599,12 @@
|
||||
"w": "vim::NextWordStart",
|
||||
"e": "vim::NextWordEnd",
|
||||
"b": "vim::PreviousWordStart",
|
||||
|
||||
"h": "vim::Left",
|
||||
"j": "vim::Down",
|
||||
"k": "vim::Up",
|
||||
"l": "vim::Right"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"context": "vim_mode == insert && !(showing_code_actions || showing_completions)",
|
||||
"bindings": {
|
||||
@@ -357,11 +618,23 @@
|
||||
"ctrl-c": "vim::NormalBefore",
|
||||
"ctrl-[": "vim::NormalBefore",
|
||||
"escape": "vim::NormalBefore",
|
||||
"ctrl-k": ["vim::PushDigraph", {}],
|
||||
"ctrl-v": ["vim::PushLiteral", {}],
|
||||
"ctrl-k": [
|
||||
"vim::PushDigraph",
|
||||
{}
|
||||
],
|
||||
"ctrl-v": [
|
||||
"vim::PushLiteral",
|
||||
{}
|
||||
],
|
||||
"ctrl-shift-v": "editor::Paste", // note: this is *very* similar to ctrl-v in vim, but ctrl-shift-v on linux is the typical shortcut for paste when ctrl-v is already in use.
|
||||
"ctrl-q": ["vim::PushLiteral", {}],
|
||||
"ctrl-shift-q": ["vim::PushLiteral", {}],
|
||||
"ctrl-q": [
|
||||
"vim::PushLiteral",
|
||||
{}
|
||||
],
|
||||
"ctrl-shift-q": [
|
||||
"vim::PushLiteral",
|
||||
{}
|
||||
],
|
||||
"backspace": "vim::UndoReplace",
|
||||
"tab": "vim::Tab",
|
||||
"enter": "vim::Enter",
|
||||
@@ -376,9 +649,18 @@
|
||||
"ctrl-c": "vim::ClearOperators",
|
||||
"ctrl-[": "vim::ClearOperators",
|
||||
"escape": "vim::ClearOperators",
|
||||
"ctrl-k": ["vim::PushDigraph", {}],
|
||||
"ctrl-v": ["vim::PushLiteral", {}],
|
||||
"ctrl-q": ["vim::PushLiteral", {}]
|
||||
"ctrl-k": [
|
||||
"vim::PushDigraph",
|
||||
{}
|
||||
],
|
||||
"ctrl-v": [
|
||||
"vim::PushLiteral",
|
||||
{}
|
||||
],
|
||||
"ctrl-q": [
|
||||
"vim::PushLiteral",
|
||||
{}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -400,7 +682,12 @@
|
||||
"context": "vim_operator == a || vim_operator == i || vim_operator == cs",
|
||||
"bindings": {
|
||||
"w": "vim::Word",
|
||||
"shift-w": ["vim::Word", { "ignore_punctuation": true }],
|
||||
"shift-w": [
|
||||
"vim::Word",
|
||||
{
|
||||
"ignore_punctuation": true
|
||||
}
|
||||
],
|
||||
// Subword TextObject
|
||||
// "w": "vim::Subword",
|
||||
// "shift-w": ["vim::Subword", { "ignore_punctuation": true }],
|
||||
@@ -426,7 +713,12 @@
|
||||
">": "vim::AngleBrackets",
|
||||
"a": "vim::Argument",
|
||||
"i": "vim::IndentObj",
|
||||
"shift-i": ["vim::IndentObj", { "include_below": true }],
|
||||
"shift-i": [
|
||||
"vim::IndentObj",
|
||||
{
|
||||
"include_below": true
|
||||
}
|
||||
],
|
||||
"f": "vim::Method",
|
||||
"c": "vim::Class",
|
||||
"e": "vim::EntireFile"
|
||||
@@ -436,8 +728,12 @@
|
||||
"context": "vim_operator == c",
|
||||
"bindings": {
|
||||
"c": "vim::CurrentLine",
|
||||
"x": "vim::Exchange",
|
||||
"d": "editor::Rename", // zed specific
|
||||
"s": ["vim::PushChangeSurrounds", {}]
|
||||
"s": [
|
||||
"vim::PushChangeSurrounds",
|
||||
{}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -483,7 +779,10 @@
|
||||
"context": "vim_operator == y",
|
||||
"bindings": {
|
||||
"y": "vim::CurrentLine",
|
||||
"s": ["vim::PushAddSurrounds", {}]
|
||||
"s": [
|
||||
"vim::PushAddSurrounds",
|
||||
{}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -522,47 +821,282 @@
|
||||
"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",
|
||||
"bindings": {
|
||||
"ctrl-@": ["vim::Literal", ["ctrl-@", "\u0000"]],
|
||||
"ctrl-a": ["vim::Literal", ["ctrl-a", "\u0001"]],
|
||||
"ctrl-b": ["vim::Literal", ["ctrl-b", "\u0002"]],
|
||||
"ctrl-c": ["vim::Literal", ["ctrl-c", "\u0003"]],
|
||||
"ctrl-d": ["vim::Literal", ["ctrl-d", "\u0004"]],
|
||||
"ctrl-e": ["vim::Literal", ["ctrl-e", "\u0005"]],
|
||||
"ctrl-f": ["vim::Literal", ["ctrl-f", "\u0006"]],
|
||||
"ctrl-g": ["vim::Literal", ["ctrl-g", "\u0007"]],
|
||||
"ctrl-h": ["vim::Literal", ["ctrl-h", "\u0008"]],
|
||||
"ctrl-i": ["vim::Literal", ["ctrl-i", "\u0009"]],
|
||||
"ctrl-j": ["vim::Literal", ["ctrl-j", "\u000A"]],
|
||||
"ctrl-k": ["vim::Literal", ["ctrl-k", "\u000B"]],
|
||||
"ctrl-l": ["vim::Literal", ["ctrl-l", "\u000C"]],
|
||||
"ctrl-m": ["vim::Literal", ["ctrl-m", "\u000D"]],
|
||||
"ctrl-n": ["vim::Literal", ["ctrl-n", "\u000E"]],
|
||||
"ctrl-o": ["vim::Literal", ["ctrl-o", "\u000F"]],
|
||||
"ctrl-p": ["vim::Literal", ["ctrl-p", "\u0010"]],
|
||||
"ctrl-q": ["vim::Literal", ["ctrl-q", "\u0011"]],
|
||||
"ctrl-r": ["vim::Literal", ["ctrl-r", "\u0012"]],
|
||||
"ctrl-s": ["vim::Literal", ["ctrl-s", "\u0013"]],
|
||||
"ctrl-t": ["vim::Literal", ["ctrl-t", "\u0014"]],
|
||||
"ctrl-u": ["vim::Literal", ["ctrl-u", "\u0015"]],
|
||||
"ctrl-v": ["vim::Literal", ["ctrl-v", "\u0016"]],
|
||||
"ctrl-w": ["vim::Literal", ["ctrl-w", "\u0017"]],
|
||||
"ctrl-x": ["vim::Literal", ["ctrl-x", "\u0018"]],
|
||||
"ctrl-y": ["vim::Literal", ["ctrl-y", "\u0019"]],
|
||||
"ctrl-z": ["vim::Literal", ["ctrl-z", "\u001A"]],
|
||||
"ctrl-[": ["vim::Literal", ["ctrl-[", "\u001B"]],
|
||||
"ctrl-\\": ["vim::Literal", ["ctrl-\\", "\u001C"]],
|
||||
"ctrl-]": ["vim::Literal", ["ctrl-]", "\u001D"]],
|
||||
"ctrl-^": ["vim::Literal", ["ctrl-^", "\u001E"]],
|
||||
"ctrl-_": ["vim::Literal", ["ctrl-_", "\u001F"]],
|
||||
"escape": ["vim::Literal", ["escape", "\u001B"]],
|
||||
"enter": ["vim::Literal", ["enter", "\u000D"]],
|
||||
"tab": ["vim::Literal", ["tab", "\u0009"]],
|
||||
"ctrl-@": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-@",
|
||||
"\u0000"
|
||||
]
|
||||
],
|
||||
"ctrl-a": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-a",
|
||||
"\u0001"
|
||||
]
|
||||
],
|
||||
"ctrl-b": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-b",
|
||||
"\u0002"
|
||||
]
|
||||
],
|
||||
"ctrl-c": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-c",
|
||||
"\u0003"
|
||||
]
|
||||
],
|
||||
"ctrl-d": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-d",
|
||||
"\u0004"
|
||||
]
|
||||
],
|
||||
"ctrl-e": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-e",
|
||||
"\u0005"
|
||||
]
|
||||
],
|
||||
"ctrl-f": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-f",
|
||||
"\u0006"
|
||||
]
|
||||
],
|
||||
"ctrl-g": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-g",
|
||||
"\u0007"
|
||||
]
|
||||
],
|
||||
"ctrl-h": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-h",
|
||||
"\u0008"
|
||||
]
|
||||
],
|
||||
"ctrl-i": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-i",
|
||||
"\u0009"
|
||||
]
|
||||
],
|
||||
"ctrl-j": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-j",
|
||||
"\u000A"
|
||||
]
|
||||
],
|
||||
"ctrl-k": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-k",
|
||||
"\u000B"
|
||||
]
|
||||
],
|
||||
"ctrl-l": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-l",
|
||||
"\u000C"
|
||||
]
|
||||
],
|
||||
"ctrl-m": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-m",
|
||||
"\u000D"
|
||||
]
|
||||
],
|
||||
"ctrl-n": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-n",
|
||||
"\u000E"
|
||||
]
|
||||
],
|
||||
"ctrl-o": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-o",
|
||||
"\u000F"
|
||||
]
|
||||
],
|
||||
"ctrl-p": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-p",
|
||||
"\u0010"
|
||||
]
|
||||
],
|
||||
"ctrl-q": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-q",
|
||||
"\u0011"
|
||||
]
|
||||
],
|
||||
"ctrl-r": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-r",
|
||||
"\u0012"
|
||||
]
|
||||
],
|
||||
"ctrl-s": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-s",
|
||||
"\u0013"
|
||||
]
|
||||
],
|
||||
"ctrl-t": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-t",
|
||||
"\u0014"
|
||||
]
|
||||
],
|
||||
"ctrl-u": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-u",
|
||||
"\u0015"
|
||||
]
|
||||
],
|
||||
"ctrl-v": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-v",
|
||||
"\u0016"
|
||||
]
|
||||
],
|
||||
"ctrl-w": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-w",
|
||||
"\u0017"
|
||||
]
|
||||
],
|
||||
"ctrl-x": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-x",
|
||||
"\u0018"
|
||||
]
|
||||
],
|
||||
"ctrl-y": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-y",
|
||||
"\u0019"
|
||||
]
|
||||
],
|
||||
"ctrl-z": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-z",
|
||||
"\u001A"
|
||||
]
|
||||
],
|
||||
"ctrl-[": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-[",
|
||||
"\u001B"
|
||||
]
|
||||
],
|
||||
"ctrl-\\": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-\\",
|
||||
"\u001C"
|
||||
]
|
||||
],
|
||||
"ctrl-]": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-]",
|
||||
"\u001D"
|
||||
]
|
||||
],
|
||||
"ctrl-^": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-^",
|
||||
"\u001E"
|
||||
]
|
||||
],
|
||||
"ctrl-_": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"ctrl-_",
|
||||
"\u001F"
|
||||
]
|
||||
],
|
||||
"escape": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"escape",
|
||||
"\u001B"
|
||||
]
|
||||
],
|
||||
"enter": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"enter",
|
||||
"\u000D"
|
||||
]
|
||||
],
|
||||
"tab": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"tab",
|
||||
"\u0009"
|
||||
]
|
||||
],
|
||||
// zed extensions:
|
||||
"backspace": ["vim::Literal", ["backspace", "\u0008"]],
|
||||
"delete": ["vim::Literal", ["delete", "\u007F"]]
|
||||
"backspace": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"backspace",
|
||||
"\u0008"
|
||||
]
|
||||
],
|
||||
"delete": [
|
||||
"vim::Literal",
|
||||
[
|
||||
"delete",
|
||||
"\u007F"
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -605,8 +1139,8 @@
|
||||
"ctrl-w =": "vim::ResetPaneSizes",
|
||||
"ctrl-w g t": "pane::ActivateNextItem",
|
||||
"ctrl-w ctrl-g t": "pane::ActivateNextItem",
|
||||
"ctrl-w g shift-t": "pane::ActivatePrevItem",
|
||||
"ctrl-w ctrl-g shift-t": "pane::ActivatePrevItem",
|
||||
"ctrl-w g shift-t": "pane::ActivatePreviousItem",
|
||||
"ctrl-w ctrl-g shift-t": "pane::ActivatePreviousItem",
|
||||
"ctrl-w w": "workspace::ActivateNextPane",
|
||||
"ctrl-w ctrl-w": "workspace::ActivateNextPane",
|
||||
"ctrl-w p": "workspace::ActivatePreviousPane",
|
||||
@@ -649,7 +1183,7 @@
|
||||
"escape": "project_panel::ToggleFocus",
|
||||
"h": "project_panel::CollapseSelectedEntry",
|
||||
"j": "menu::SelectNext",
|
||||
"k": "menu::SelectPrev",
|
||||
"k": "menu::SelectPrevious",
|
||||
"l": "project_panel::ExpandSelectedEntry",
|
||||
"o": "project_panel::OpenPermanent",
|
||||
"shift-d": "project_panel::Delete",
|
||||
@@ -675,7 +1209,7 @@
|
||||
"context": "OutlinePanel && not_editing",
|
||||
"bindings": {
|
||||
"j": "menu::SelectNext",
|
||||
"k": "menu::SelectPrev",
|
||||
"k": "menu::SelectPrevious",
|
||||
"shift-g": "menu::SelectLast",
|
||||
"g g": "menu::SelectFirst"
|
||||
}
|
||||
@@ -684,7 +1218,7 @@
|
||||
"context": "GitPanel && ChangesList",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"k": "menu::SelectPrev",
|
||||
"k": "menu::SelectPrevious",
|
||||
"j": "menu::SelectNext",
|
||||
"g g": "menu::SelectFirst",
|
||||
"shift-g": "menu::SelectLast",
|
||||
@@ -712,4 +1246,4 @@
|
||||
"alt-l": "editor::AcceptEditPrediction"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
@@ -581,7 +581,7 @@
|
||||
// The provider to use.
|
||||
"provider": "zed.dev",
|
||||
// The model to use.
|
||||
"model": "claude-3-5-sonnet"
|
||||
"model": "claude-3-5-sonnet-latest"
|
||||
}
|
||||
},
|
||||
// The settings for slash commands.
|
||||
@@ -758,7 +758,25 @@
|
||||
// Diagnostics configuration.
|
||||
"diagnostics": {
|
||||
// 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
|
||||
// scans, file searches, and not be displayed in the project file tree. Takes precedence over `file_scan_inclusions`.
|
||||
@@ -1075,6 +1093,7 @@
|
||||
"tab_size": 2
|
||||
},
|
||||
"Diff": {
|
||||
"show_edit_predictions": false,
|
||||
"remove_trailing_whitespace_on_save": false,
|
||||
"ensure_final_newline_on_save": false
|
||||
},
|
||||
@@ -1084,6 +1103,9 @@
|
||||
"Erlang": {
|
||||
"language_servers": ["erlang-ls", "!elp", "..."]
|
||||
},
|
||||
"Git Commit": {
|
||||
"allow_rewrap": "anywhere"
|
||||
},
|
||||
"Go": {
|
||||
"code_actions_on_format": {
|
||||
"source.organizeImports": true
|
||||
|
||||
@@ -379,7 +379,7 @@
|
||||
"font_weight": null
|
||||
},
|
||||
"variable": {
|
||||
"color": "#83a598ff",
|
||||
"color": "#ebdbb2ff",
|
||||
"font_style": null,
|
||||
"font_weight": null
|
||||
},
|
||||
@@ -767,7 +767,7 @@
|
||||
"font_weight": null
|
||||
},
|
||||
"variable": {
|
||||
"color": "#83a598ff",
|
||||
"color": "#ebdbb2ff",
|
||||
"font_style": null,
|
||||
"font_weight": null
|
||||
},
|
||||
@@ -1155,7 +1155,7 @@
|
||||
"font_weight": null
|
||||
},
|
||||
"variable": {
|
||||
"color": "#83a598ff",
|
||||
"color": "#ebdbb2ff",
|
||||
"font_style": null,
|
||||
"font_weight": null
|
||||
},
|
||||
@@ -1543,7 +1543,7 @@
|
||||
"font_weight": null
|
||||
},
|
||||
"variable": {
|
||||
"color": "#066578ff",
|
||||
"color": "#282828ff",
|
||||
"font_style": null,
|
||||
"font_weight": null
|
||||
},
|
||||
@@ -1931,7 +1931,7 @@
|
||||
"font_weight": null
|
||||
},
|
||||
"variable": {
|
||||
"color": "#066578ff",
|
||||
"color": "#282828ff",
|
||||
"font_style": null,
|
||||
"font_weight": null
|
||||
},
|
||||
@@ -2319,7 +2319,7 @@
|
||||
"font_weight": null
|
||||
},
|
||||
"variable": {
|
||||
"color": "#066578ff",
|
||||
"color": "#282828ff",
|
||||
"font_style": null,
|
||||
"font_weight": null
|
||||
},
|
||||
|
||||
@@ -365,7 +365,7 @@
|
||||
"font_weight": null
|
||||
},
|
||||
"variable": {
|
||||
"color": "#dce0e5ff",
|
||||
"color": "#acb2beff",
|
||||
"font_style": null,
|
||||
"font_weight": null
|
||||
},
|
||||
|
||||
@@ -30,6 +30,8 @@ pub enum Model {
|
||||
#[default]
|
||||
#[serde(rename = "claude-3-5-sonnet", 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-5-haiku", alias = "claude-3-5-haiku-latest")]
|
||||
Claude3_5Haiku,
|
||||
#[serde(rename = "claude-3-opus", alias = "claude-3-opus-latest")]
|
||||
@@ -59,6 +61,8 @@ impl Model {
|
||||
pub fn from_id(id: &str) -> Result<Self> {
|
||||
if id.starts_with("claude-3-5-sonnet") {
|
||||
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") {
|
||||
Ok(Self::Claude3_5Haiku)
|
||||
} else if id.starts_with("claude-3-opus") {
|
||||
@@ -75,6 +79,7 @@ impl Model {
|
||||
pub fn id(&self) -> &str {
|
||||
match self {
|
||||
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::Claude3Opus => "claude-3-opus-latest",
|
||||
Model::Claude3Sonnet => "claude-3-sonnet-20240229",
|
||||
@@ -85,6 +90,7 @@ impl Model {
|
||||
|
||||
pub fn display_name(&self) -> &str {
|
||||
match self {
|
||||
Self::Claude3_7Sonnet => "Claude 3.7 Sonnet",
|
||||
Self::Claude3_5Sonnet => "Claude 3.5 Sonnet",
|
||||
Self::Claude3_5Haiku => "Claude 3.5 Haiku",
|
||||
Self::Claude3Opus => "Claude 3 Opus",
|
||||
@@ -98,13 +104,14 @@ impl Model {
|
||||
|
||||
pub fn cache_configuration(&self) -> Option<AnthropicModelCacheConfiguration> {
|
||||
match self {
|
||||
Self::Claude3_5Sonnet | Self::Claude3_5Haiku | Self::Claude3Haiku => {
|
||||
Some(AnthropicModelCacheConfiguration {
|
||||
min_total_token: 2_048,
|
||||
should_speculate: true,
|
||||
max_cache_anchors: 4,
|
||||
})
|
||||
}
|
||||
Self::Claude3_5Sonnet
|
||||
| Self::Claude3_5Haiku
|
||||
| Self::Claude3_7Sonnet
|
||||
| Self::Claude3Haiku => Some(AnthropicModelCacheConfiguration {
|
||||
min_total_token: 2_048,
|
||||
should_speculate: true,
|
||||
max_cache_anchors: 4,
|
||||
}),
|
||||
Self::Custom {
|
||||
cache_configuration,
|
||||
..
|
||||
@@ -117,6 +124,7 @@ impl Model {
|
||||
match self {
|
||||
Self::Claude3_5Sonnet
|
||||
| Self::Claude3_5Haiku
|
||||
| Self::Claude3_7Sonnet
|
||||
| Self::Claude3Opus
|
||||
| Self::Claude3Sonnet
|
||||
| Self::Claude3Haiku => 200_000,
|
||||
@@ -127,7 +135,7 @@ impl Model {
|
||||
pub fn max_output_tokens(&self) -> u32 {
|
||||
match self {
|
||||
Self::Claude3Opus | Self::Claude3Sonnet | Self::Claude3Haiku => 4_096,
|
||||
Self::Claude3_5Sonnet | Self::Claude3_5Haiku => 8_192,
|
||||
Self::Claude3_5Sonnet | Self::Claude3_7Sonnet | Self::Claude3_5Haiku => 8_192,
|
||||
Self::Custom {
|
||||
max_output_tokens, ..
|
||||
} => max_output_tokens.unwrap_or(4_096),
|
||||
@@ -137,6 +145,7 @@ impl Model {
|
||||
pub fn default_temperature(&self) -> f32 {
|
||||
match self {
|
||||
Self::Claude3_5Sonnet
|
||||
| Self::Claude3_7Sonnet
|
||||
| Self::Claude3_5Haiku
|
||||
| Self::Claude3Opus
|
||||
| Self::Claude3Sonnet
|
||||
|
||||
@@ -43,7 +43,6 @@ indoc.workspace = true
|
||||
language.workspace = true
|
||||
language_model.workspace = true
|
||||
language_model_selector.workspace = true
|
||||
language_models.workspace = true
|
||||
log.workspace = true
|
||||
lsp.workspace = true
|
||||
menu.workspace = true
|
||||
|
||||
@@ -33,7 +33,7 @@ actions!(
|
||||
[
|
||||
InsertActivePrompt,
|
||||
DeployHistory,
|
||||
NewContext,
|
||||
NewChat,
|
||||
CycleNextInlineAssist,
|
||||
CyclePreviousInlineAssist
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::assistant_configuration::{ConfigurationView, ConfigurationViewEvent};
|
||||
use crate::{
|
||||
terminal_inline_assistant::TerminalInlineAssistant, DeployHistory, InlineAssistant, NewContext,
|
||||
terminal_inline_assistant::TerminalInlineAssistant, DeployHistory, InlineAssistant, NewChat,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use assistant_context_editor::{
|
||||
@@ -129,7 +129,7 @@ impl AssistantPanel {
|
||||
workspace.project().clone(),
|
||||
Default::default(),
|
||||
None,
|
||||
NewContext.boxed_clone(),
|
||||
NewChat.boxed_clone(),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
@@ -228,12 +228,12 @@ impl AssistantPanel {
|
||||
IconButton::new("new-chat", IconName::Plus)
|
||||
.icon_size(IconSize::Small)
|
||||
.on_click(cx.listener(|_, _, window, cx| {
|
||||
window.dispatch_action(NewContext.boxed_clone(), cx)
|
||||
window.dispatch_action(NewChat.boxed_clone(), cx)
|
||||
}))
|
||||
.tooltip(move |window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
"New Chat",
|
||||
&NewContext,
|
||||
&NewChat,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
@@ -256,7 +256,7 @@ impl AssistantPanel {
|
||||
let focus_handle = _pane.focus_handle(cx);
|
||||
Some(ContextMenu::build(window, cx, move |menu, _, _| {
|
||||
menu.context(focus_handle.clone())
|
||||
.action("New Chat", Box::new(NewContext))
|
||||
.action("New Chat", Box::new(NewChat))
|
||||
.action("History", Box::new(DeployHistory))
|
||||
.action("Prompt Library", Box::new(DeployPromptLibrary))
|
||||
.action("Configure", Box::new(ShowConfiguration))
|
||||
@@ -760,7 +760,7 @@ impl AssistantPanel {
|
||||
|
||||
pub fn create_new_context(
|
||||
workspace: &mut Workspace,
|
||||
_: &NewContext,
|
||||
_: &NewChat,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Workspace>,
|
||||
) {
|
||||
@@ -978,7 +978,7 @@ impl AssistantPanel {
|
||||
.active_provider()
|
||||
.map_or(true, |p| p.id() != provider.id())
|
||||
{
|
||||
if let Some(model) = provider.provided_models(cx).first().cloned() {
|
||||
if let Some(model) = provider.default_model(cx) {
|
||||
update_settings_file::<AssistantSettings>(
|
||||
this.fs.clone(),
|
||||
cx,
|
||||
@@ -1206,7 +1206,7 @@ impl Render for AssistantPanel {
|
||||
v_flex()
|
||||
.key_context("AssistantPanel")
|
||||
.size_full()
|
||||
.on_action(cx.listener(|this, _: &NewContext, window, cx| {
|
||||
.on_action(cx.listener(|this, _: &NewChat, window, cx| {
|
||||
this.new_context(window, cx);
|
||||
}))
|
||||
.on_action(cx.listener(|this, _: &ShowConfiguration, window, cx| {
|
||||
|
||||
@@ -32,11 +32,10 @@ use gpui::{
|
||||
};
|
||||
use language::{line_diff, Buffer, IndentKind, Point, Selection, TransactionId};
|
||||
use language_model::{
|
||||
LanguageModel, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage,
|
||||
LanguageModelTextStream, Role,
|
||||
report_assistant_event, LanguageModel, LanguageModelRegistry, LanguageModelRequest,
|
||||
LanguageModelRequestMessage, LanguageModelTextStream, Role,
|
||||
};
|
||||
use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};
|
||||
use language_models::report_assistant_event;
|
||||
use multi_buffer::MultiBufferRow;
|
||||
use parking_lot::Mutex;
|
||||
use project::{CodeAction, ProjectTransaction};
|
||||
@@ -2227,7 +2226,7 @@ impl PromptEditor {
|
||||
},
|
||||
font_family: settings.buffer_font.family.clone(),
|
||||
font_fallbacks: settings.buffer_font.fallbacks.clone(),
|
||||
font_size: settings.buffer_font_size.into(),
|
||||
font_size: settings.buffer_font_size(cx).into(),
|
||||
font_weight: settings.buffer_font.weight,
|
||||
line_height: relative(settings.buffer_line_height.value()),
|
||||
..Default::default()
|
||||
|
||||
@@ -16,10 +16,10 @@ use gpui::{
|
||||
};
|
||||
use language::Buffer;
|
||||
use language_model::{
|
||||
LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage, Role,
|
||||
report_assistant_event, LanguageModelRegistry, LanguageModelRequest,
|
||||
LanguageModelRequestMessage, Role,
|
||||
};
|
||||
use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};
|
||||
use language_models::report_assistant_event;
|
||||
use prompt_library::PromptBuilder;
|
||||
use settings::{update_settings_file, Settings};
|
||||
use std::{
|
||||
@@ -1049,7 +1049,7 @@ impl PromptEditor {
|
||||
},
|
||||
font_family: settings.buffer_font.family.clone(),
|
||||
font_fallbacks: settings.buffer_font.fallbacks.clone(),
|
||||
font_size: settings.buffer_font_size.into(),
|
||||
font_size: settings.buffer_font_size(cx).into(),
|
||||
font_weight: settings.buffer_font.weight,
|
||||
line_height: relative(settings.buffer_line_height.value()),
|
||||
..Default::default()
|
||||
|
||||
@@ -46,7 +46,6 @@ itertools.workspace = true
|
||||
language.workspace = true
|
||||
language_model.workspace = true
|
||||
language_model_selector.workspace = true
|
||||
language_models.workspace = true
|
||||
log.workspace = true
|
||||
lsp.workspace = true
|
||||
markdown.workspace = true
|
||||
|
||||
@@ -7,6 +7,7 @@ mod context;
|
||||
mod context_picker;
|
||||
mod context_store;
|
||||
mod context_strip;
|
||||
mod history_store;
|
||||
mod inline_assistant;
|
||||
mod inline_prompt_editor;
|
||||
mod message_editor;
|
||||
@@ -40,7 +41,6 @@ actions!(
|
||||
ToggleModelSelector,
|
||||
RemoveAllContext,
|
||||
OpenHistory,
|
||||
OpenPromptEditorHistory,
|
||||
OpenConfiguration,
|
||||
RemoveSelectedThread,
|
||||
Chat,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use collections::HashMap;
|
||||
use gpui::{AnyView, App, EventEmitter, FocusHandle, Focusable, Subscription};
|
||||
use gpui::{Action, AnyView, App, EventEmitter, FocusHandle, Focusable, Subscription};
|
||||
use language_model::{LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry};
|
||||
use ui::{prelude::*, Divider, DividerColor, ElevationIndex};
|
||||
use zed_actions::assistant::DeployPromptLibrary;
|
||||
@@ -168,8 +168,8 @@ impl Render for AssistantConfiguration {
|
||||
.icon(IconName::Book)
|
||||
.icon_size(IconSize::Small)
|
||||
.icon_position(IconPosition::Start)
|
||||
.on_click(|_event, _window, cx| {
|
||||
cx.dispatch_action(&DeployPromptLibrary)
|
||||
.on_click(|_event, window, cx| {
|
||||
window.dispatch_action(DeployPromptLibrary.boxed_clone(), cx)
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::sync::Arc;
|
||||
use anyhow::{anyhow, Result};
|
||||
use assistant_context_editor::{
|
||||
make_lsp_adapter_delegate, render_remaining_tokens, AssistantPanelDelegate, ConfigurationError,
|
||||
ContextEditor, ContextHistory, SlashCommandCompletionProvider,
|
||||
ContextEditor, SlashCommandCompletionProvider,
|
||||
};
|
||||
use assistant_settings::{AssistantDockPosition, AssistantSettings};
|
||||
use assistant_slash_command::SlashCommandWorkingSet;
|
||||
@@ -31,14 +31,12 @@ use zed_actions::assistant::{DeployPromptLibrary, ToggleFocus};
|
||||
|
||||
use crate::active_thread::ActiveThread;
|
||||
use crate::assistant_configuration::{AssistantConfiguration, AssistantConfigurationEvent};
|
||||
use crate::history_store::{HistoryEntry, HistoryStore};
|
||||
use crate::message_editor::MessageEditor;
|
||||
use crate::thread::{Thread, ThreadError, ThreadId};
|
||||
use crate::thread_history::{PastThread, ThreadHistory};
|
||||
use crate::thread_history::{PastContext, PastThread, ThreadHistory};
|
||||
use crate::thread_store::ThreadStore;
|
||||
use crate::{
|
||||
InlineAssistant, NewPromptEditor, NewThread, OpenConfiguration, OpenHistory,
|
||||
OpenPromptEditorHistory,
|
||||
};
|
||||
use crate::{InlineAssistant, NewPromptEditor, NewThread, OpenConfiguration, OpenHistory};
|
||||
|
||||
pub fn init(cx: &mut App) {
|
||||
cx.observe_new(
|
||||
@@ -62,12 +60,6 @@ pub fn init(cx: &mut App) {
|
||||
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| {
|
||||
if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {
|
||||
workspace.focus_panel::<AssistantPanel>(window, cx);
|
||||
@@ -83,7 +75,6 @@ enum ActiveView {
|
||||
Thread,
|
||||
PromptEditor,
|
||||
History,
|
||||
PromptEditorHistory,
|
||||
Configuration,
|
||||
}
|
||||
|
||||
@@ -97,15 +88,14 @@ pub struct AssistantPanel {
|
||||
message_editor: Entity<MessageEditor>,
|
||||
context_store: Entity<assistant_context_editor::ContextStore>,
|
||||
context_editor: Option<Entity<ContextEditor>>,
|
||||
context_history: Option<Entity<ContextHistory>>,
|
||||
configuration: Option<Entity<AssistantConfiguration>>,
|
||||
configuration_subscription: Option<Subscription>,
|
||||
tools: Arc<ToolWorkingSet>,
|
||||
local_timezone: UtcOffset,
|
||||
active_view: ActiveView,
|
||||
history_store: Entity<HistoryStore>,
|
||||
history: Entity<ThreadHistory>,
|
||||
new_item_context_menu_handle: PopoverMenuHandle<ContextMenu>,
|
||||
open_history_context_menu_handle: PopoverMenuHandle<ContextMenu>,
|
||||
width: Option<Pixels>,
|
||||
height: Option<Pixels>,
|
||||
}
|
||||
@@ -173,6 +163,9 @@ impl AssistantPanel {
|
||||
)
|
||||
});
|
||||
|
||||
let history_store =
|
||||
cx.new(|cx| HistoryStore::new(thread_store.clone(), context_store.clone(), cx));
|
||||
|
||||
Self {
|
||||
active_view: ActiveView::Thread,
|
||||
workspace: workspace.clone(),
|
||||
@@ -194,7 +187,6 @@ impl AssistantPanel {
|
||||
message_editor,
|
||||
context_store,
|
||||
context_editor: None,
|
||||
context_history: None,
|
||||
configuration: None,
|
||||
configuration_subscription: None,
|
||||
tools,
|
||||
@@ -202,9 +194,9 @@ impl AssistantPanel {
|
||||
chrono::Local::now().offset().local_minus_utc(),
|
||||
)
|
||||
.unwrap(),
|
||||
history: cx.new(|cx| ThreadHistory::new(weak_self, thread_store, cx)),
|
||||
history_store: history_store.clone(),
|
||||
history: cx.new(|cx| ThreadHistory::new(weak_self, history_store, cx)),
|
||||
new_item_context_menu_handle: PopoverMenuHandle::default(),
|
||||
open_history_context_menu_handle: PopoverMenuHandle::default(),
|
||||
width: None,
|
||||
height: None,
|
||||
}
|
||||
@@ -331,26 +323,7 @@ impl AssistantPanel {
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
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(
|
||||
pub(crate) fn open_saved_prompt_editor(
|
||||
&mut self,
|
||||
path: PathBuf,
|
||||
window: &mut Window,
|
||||
@@ -458,7 +431,7 @@ impl AssistantPanel {
|
||||
active_provider.id() != provider.id()
|
||||
})
|
||||
{
|
||||
if let Some(model) = provider.provided_models(cx).first().cloned() {
|
||||
if let Some(model) = provider.default_model(cx) {
|
||||
update_settings_file::<AssistantSettings>(
|
||||
self.fs.clone(),
|
||||
cx,
|
||||
@@ -499,13 +472,6 @@ impl Focusable for AssistantPanel {
|
||||
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 => {
|
||||
if let Some(configuration) = self.configuration.as_ref() {
|
||||
configuration.focus_handle(cx)
|
||||
@@ -618,18 +584,10 @@ impl AssistantPanel {
|
||||
SharedString::from(context_editor.read(cx).title(cx).to_string())
|
||||
})
|
||||
.unwrap_or_else(|| SharedString::from("Loading Summary…")),
|
||||
ActiveView::History | ActiveView::PromptEditorHistory => "History".into(),
|
||||
ActiveView::History => "History".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()
|
||||
.id("assistant-toolbar")
|
||||
.px(DynamicSpacing::Base08.rems(cx))
|
||||
@@ -645,24 +603,7 @@ impl AssistantPanel {
|
||||
.w_full()
|
||||
.gap_1()
|
||||
.justify_between()
|
||||
.child(
|
||||
h_flex()
|
||||
.child(Label::new(title))
|
||||
.when(sub_title.is_some(), |this| {
|
||||
this.child(
|
||||
h_flex()
|
||||
.pl_1p5()
|
||||
.gap_1p5()
|
||||
.child(
|
||||
Label::new("/")
|
||||
.size(LabelSize::Small)
|
||||
.color(Color::Disabled)
|
||||
.alpha(0.5),
|
||||
)
|
||||
.child(Label::new(sub_title.unwrap())),
|
||||
)
|
||||
}),
|
||||
)
|
||||
.child(Label::new(title))
|
||||
.children(if matches!(self.active_view, ActiveView::PromptEditor) {
|
||||
self.context_editor
|
||||
.as_ref()
|
||||
@@ -696,23 +637,23 @@ impl AssistantPanel {
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
PopoverMenu::new("assistant-toolbar-history-popover-menu")
|
||||
.trigger_with_tooltip(
|
||||
IconButton::new("open-history", IconName::HistoryRerun)
|
||||
.icon_size(IconSize::Small)
|
||||
.style(ButtonStyle::Subtle),
|
||||
Tooltip::text("History…"),
|
||||
)
|
||||
.anchor(Corner::TopRight)
|
||||
.with_handle(self.open_history_context_menu_handle.clone())
|
||||
.menu(move |window, cx| {
|
||||
Some(ContextMenu::build(window, cx, |menu, _window, _cx| {
|
||||
menu.action("Thread History", OpenHistory.boxed_clone())
|
||||
.action(
|
||||
"Prompt Editor History",
|
||||
OpenPromptEditorHistory.boxed_clone(),
|
||||
)
|
||||
}))
|
||||
IconButton::new("open-history", IconName::HistoryRerun)
|
||||
.icon_size(IconSize::Small)
|
||||
.style(ButtonStyle::Subtle)
|
||||
.tooltip({
|
||||
let focus_handle = self.focus_handle(cx);
|
||||
move |window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
"History",
|
||||
&OpenHistory,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
})
|
||||
.on_click(move |_event, window, cx| {
|
||||
window.dispatch_action(OpenHistory.boxed_clone(), cx);
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
@@ -762,9 +703,9 @@ impl AssistantPanel {
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> impl IntoElement {
|
||||
let recent_threads = self
|
||||
.thread_store
|
||||
.update(cx, |this, _cx| this.recent_threads(3));
|
||||
let recent_history = self
|
||||
.history_store
|
||||
.update(cx, |this, cx| this.recent_entries(3, cx));
|
||||
|
||||
let create_welcome_heading = || {
|
||||
h_flex()
|
||||
@@ -791,7 +732,8 @@ impl AssistantPanel {
|
||||
)
|
||||
.map(|parent| {
|
||||
match configuration_error {
|
||||
Some(ConfigurationError::ProviderNotAuthenticated) | Some(ConfigurationError::NoProvider) => {
|
||||
Some(ConfigurationError::ProviderNotAuthenticated)
|
||||
| Some(ConfigurationError::NoProvider) => {
|
||||
parent.child(
|
||||
v_flex()
|
||||
.gap_0p5()
|
||||
@@ -818,34 +760,24 @@ impl AssistantPanel {
|
||||
),
|
||||
)
|
||||
}
|
||||
Some(ConfigurationError::ProviderPendingTermsAcceptance(provider)) => {
|
||||
parent.child(
|
||||
v_flex()
|
||||
.gap_0p5()
|
||||
.child(create_welcome_heading())
|
||||
.children(provider.render_accept_terms(
|
||||
LanguageModelProviderTosView::ThreadEmptyState,
|
||||
cx,
|
||||
)),
|
||||
)
|
||||
}
|
||||
Some(ConfigurationError::ProviderPendingTermsAcceptance(provider)) => parent
|
||||
.child(v_flex().gap_0p5().child(create_welcome_heading()).children(
|
||||
provider.render_accept_terms(
|
||||
LanguageModelProviderTosView::ThreadEmptyState,
|
||||
cx,
|
||||
),
|
||||
)),
|
||||
None => parent,
|
||||
}
|
||||
})
|
||||
.when(
|
||||
recent_threads.is_empty() && no_error,
|
||||
|parent| {
|
||||
parent.child(
|
||||
v_flex().gap_0p5().child(create_welcome_heading()).child(
|
||||
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| {
|
||||
.when(recent_history.is_empty() && no_error, |parent| {
|
||||
parent.child(v_flex().gap_0p5().child(create_welcome_heading()).child(
|
||||
h_flex().w_full().justify_center().child(
|
||||
Label::new("Start typing to chat with your codebase").color(Color::Muted),
|
||||
),
|
||||
))
|
||||
})
|
||||
.when(!recent_history.is_empty(), |parent| {
|
||||
parent
|
||||
.child(
|
||||
h_flex().w_full().justify_center().child(
|
||||
@@ -855,9 +787,18 @@ impl AssistantPanel {
|
||||
),
|
||||
)
|
||||
.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)
|
||||
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()
|
||||
}
|
||||
}
|
||||
}),
|
||||
))
|
||||
.child(
|
||||
@@ -869,7 +810,7 @@ impl AssistantPanel {
|
||||
&OpenHistory,
|
||||
&self.focus_handle(cx),
|
||||
window,
|
||||
cx
|
||||
cx,
|
||||
))
|
||||
.on_click(move |_event, window, cx| {
|
||||
window.dispatch_action(OpenHistory.boxed_clone(), cx);
|
||||
@@ -1068,7 +1009,6 @@ impl Render for AssistantPanel {
|
||||
.children(self.render_last_error(cx)),
|
||||
ActiveView::History => parent.child(self.history.clone()),
|
||||
ActiveView::PromptEditor => parent.children(self.context_editor.clone()),
|
||||
ActiveView::PromptEditorHistory => parent.children(self.context_history.clone()),
|
||||
ActiveView::Configuration => parent.children(self.configuration.clone()),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -9,10 +9,9 @@ use futures::{channel::mpsc, future::LocalBoxFuture, join, SinkExt, Stream, Stre
|
||||
use gpui::{App, AppContext as _, Context, Entity, EventEmitter, Subscription, Task};
|
||||
use language::{line_diff, Buffer, IndentKind, Point, TransactionId};
|
||||
use language_model::{
|
||||
LanguageModel, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage,
|
||||
LanguageModelTextStream, Role,
|
||||
report_assistant_event, LanguageModel, LanguageModelRegistry, LanguageModelRequest,
|
||||
LanguageModelRequestMessage, LanguageModelTextStream, Role,
|
||||
};
|
||||
use language_models::report_assistant_event;
|
||||
use multi_buffer::MultiBufferRow;
|
||||
use parking_lot::Mutex;
|
||||
use prompt_library::PromptBuilder;
|
||||
|
||||
@@ -7,19 +7,19 @@ use std::sync::Arc;
|
||||
use editor::actions::FoldAt;
|
||||
use editor::display_map::{Crease, FoldId};
|
||||
use editor::scroll::Autoscroll;
|
||||
use editor::{Anchor, Editor, FoldPlaceholder, ToPoint};
|
||||
use editor::{Anchor, AnchorRangeExt, Editor, FoldPlaceholder, ToPoint};
|
||||
use file_icons::FileIcons;
|
||||
use fuzzy::PathMatch;
|
||||
use gpui::{
|
||||
AnyElement, App, DismissEvent, Empty, Entity, FocusHandle, Focusable, Stateful, Task,
|
||||
WeakEntity,
|
||||
AnyElement, App, AppContext, DismissEvent, Empty, Entity, FocusHandle, Focusable, Stateful,
|
||||
Task, WeakEntity,
|
||||
};
|
||||
use multi_buffer::{MultiBufferPoint, MultiBufferRow};
|
||||
use picker::{Picker, PickerDelegate};
|
||||
use project::{PathMatchCandidateSet, ProjectPath, WorktreeId};
|
||||
use rope::Point;
|
||||
use text::SelectionGoal;
|
||||
use ui::{prelude::*, ButtonLike, Disclosure, ElevationIndex, ListItem, Tooltip};
|
||||
use ui::{prelude::*, ButtonLike, Disclosure, ListItem, TintColor, Tooltip};
|
||||
use util::ResultExt as _;
|
||||
use workspace::{notifications::NotifyResultExt, Workspace};
|
||||
|
||||
@@ -238,11 +238,11 @@ impl PickerDelegate for FileContextPickerDelegate {
|
||||
path: mat.path.clone(),
|
||||
};
|
||||
|
||||
let Some(editor) = self.editor.upgrade() else {
|
||||
let Some(editor_entity) = self.editor.upgrade() else {
|
||||
return;
|
||||
};
|
||||
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor_entity.update(cx, |editor, 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.
|
||||
{
|
||||
@@ -292,7 +292,11 @@ impl PickerDelegate for FileContextPickerDelegate {
|
||||
.unwrap_or_else(|| SharedString::new(""));
|
||||
|
||||
let placeholder = FoldPlaceholder {
|
||||
render: render_fold_icon_button(file_icon, file_name.into()),
|
||||
render: render_fold_icon_button(
|
||||
file_icon,
|
||||
file_name.into(),
|
||||
editor_entity.downgrade(),
|
||||
),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@@ -464,11 +468,50 @@ pub fn render_file_context_entry(
|
||||
fn render_fold_icon_button(
|
||||
icon: SharedString,
|
||||
label: SharedString,
|
||||
) -> Arc<dyn Send + Sync + Fn(FoldId, Range<Anchor>, &mut Window, &mut App) -> AnyElement> {
|
||||
Arc::new(move |fold_id, _fold_range, _window, _cx| {
|
||||
editor: WeakEntity<Editor>,
|
||||
) -> Arc<dyn Send + Sync + Fn(FoldId, Range<Anchor>, &mut App) -> AnyElement> {
|
||||
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)
|
||||
.style(ButtonStyle::Filled)
|
||||
.layer(ElevationIndex::ElevatedSurface)
|
||||
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
|
||||
.toggle_state(is_in_text_selection)
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
|
||||
61
crates/assistant2/src/history_store.rs
Normal file
61
crates/assistant2/src/history_store.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
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,8 +24,7 @@ use gpui::{
|
||||
UpdateGlobal, WeakEntity, Window,
|
||||
};
|
||||
use language::{Buffer, Point, Selection, TransactionId};
|
||||
use language_model::LanguageModelRegistry;
|
||||
use language_models::report_assistant_event;
|
||||
use language_model::{report_assistant_event, LanguageModelRegistry};
|
||||
use multi_buffer::MultiBufferRow;
|
||||
use parking_lot::Mutex;
|
||||
use project::{CodeAction, ProjectTransaction};
|
||||
|
||||
@@ -56,7 +56,7 @@ impl<T: 'static> EventEmitter<PromptEditorEvent> for PromptEditor<T> {}
|
||||
|
||||
impl<T: 'static> Render for PromptEditor<T> {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let ui_font_size = ThemeSettings::get_global(cx).ui_font_size;
|
||||
let ui_font_size = ThemeSettings::get_global(cx).ui_font_size(cx);
|
||||
let mut buttons = Vec::new();
|
||||
|
||||
let left_gutter_width = match &self.mode {
|
||||
|
||||
@@ -13,7 +13,7 @@ use rope::Point;
|
||||
use settings::Settings;
|
||||
use std::time::Duration;
|
||||
use text::Bias;
|
||||
use theme::{get_ui_font_size, ThemeSettings};
|
||||
use theme::ThemeSettings;
|
||||
use ui::{
|
||||
prelude::*, ButtonLike, KeyBinding, PopoverMenu, PopoverMenuHandle, Switch, TintColor, Tooltip,
|
||||
};
|
||||
@@ -369,7 +369,7 @@ impl Render for MessageEditor {
|
||||
.anchor(gpui::Corner::BottomLeft)
|
||||
.offset(gpui::Point {
|
||||
x: px(0.0),
|
||||
y: (-get_ui_font_size(cx) * 2) - px(4.0),
|
||||
y: (-ThemeSettings::get_global(cx).ui_font_size(cx) * 2) - px(4.0),
|
||||
})
|
||||
.with_handle(self.inline_context_picker_menu_handle.clone()),
|
||||
)
|
||||
|
||||
@@ -2,8 +2,7 @@ use crate::inline_prompt_editor::CodegenStatus;
|
||||
use client::telemetry::Telemetry;
|
||||
use futures::{channel::mpsc, SinkExt, StreamExt};
|
||||
use gpui::{App, AppContext as _, Context, Entity, EventEmitter, Task};
|
||||
use language_model::{LanguageModelRegistry, LanguageModelRequest};
|
||||
use language_models::report_assistant_event;
|
||||
use language_model::{report_assistant_event, LanguageModelRegistry, LanguageModelRequest};
|
||||
use std::{sync::Arc, time::Instant};
|
||||
use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase};
|
||||
use terminal::Terminal;
|
||||
|
||||
@@ -13,9 +13,9 @@ use fs::Fs;
|
||||
use gpui::{App, Entity, Focusable, Global, Subscription, UpdateGlobal, WeakEntity};
|
||||
use language::Buffer;
|
||||
use language_model::{
|
||||
LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage, Role,
|
||||
report_assistant_event, LanguageModelRegistry, LanguageModelRequest,
|
||||
LanguageModelRequestMessage, Role,
|
||||
};
|
||||
use language_models::report_assistant_event;
|
||||
use prompt_library::PromptBuilder;
|
||||
use std::sync::Arc;
|
||||
use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase};
|
||||
|
||||
@@ -10,9 +10,9 @@ use gpui::{App, Context, EventEmitter, SharedString, Task};
|
||||
use language_model::{
|
||||
LanguageModel, LanguageModelCompletionEvent, LanguageModelRegistry, LanguageModelRequest,
|
||||
LanguageModelRequestMessage, LanguageModelToolResult, LanguageModelToolUse,
|
||||
LanguageModelToolUseId, MessageContent, Role, StopReason,
|
||||
LanguageModelToolUseId, MaxMonthlySpendReachedError, MessageContent, PaymentRequiredError,
|
||||
Role, StopReason,
|
||||
};
|
||||
use language_models::provider::cloud::{MaxMonthlySpendReachedError, PaymentRequiredError};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use util::{post_inc, TryFutureExt as _};
|
||||
use uuid::Uuid;
|
||||
@@ -518,7 +518,7 @@ impl Thread {
|
||||
match output {
|
||||
Ok(output) => {
|
||||
tool_results.push(LanguageModelToolResult {
|
||||
tool_use_id: tool_use_id.to_string(),
|
||||
tool_use_id: tool_use_id.clone(),
|
||||
content: output,
|
||||
is_error: false,
|
||||
});
|
||||
@@ -527,7 +527,7 @@ impl Thread {
|
||||
}
|
||||
Err(err) => {
|
||||
tool_results.push(LanguageModelToolResult {
|
||||
tool_use_id: tool_use_id.to_string(),
|
||||
tool_use_id: tool_use_id.clone(),
|
||||
content: err.to_string(),
|
||||
is_error: true,
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use assistant_context_editor::SavedContextMetadata;
|
||||
use gpui::{
|
||||
uniform_list, App, Entity, FocusHandle, Focusable, ScrollStrategy, UniformListScrollHandle,
|
||||
WeakEntity,
|
||||
@@ -5,13 +6,14 @@ use gpui::{
|
||||
use time::{OffsetDateTime, UtcOffset};
|
||||
use ui::{prelude::*, IconButtonShape, ListItem, ListItemSpacing, Tooltip};
|
||||
|
||||
use crate::thread_store::{SavedThreadMetadata, ThreadStore};
|
||||
use crate::history_store::{HistoryEntry, HistoryStore};
|
||||
use crate::thread_store::SavedThreadMetadata;
|
||||
use crate::{AssistantPanel, RemoveSelectedThread};
|
||||
|
||||
pub struct ThreadHistory {
|
||||
focus_handle: FocusHandle,
|
||||
assistant_panel: WeakEntity<AssistantPanel>,
|
||||
thread_store: Entity<ThreadStore>,
|
||||
history_store: Entity<HistoryStore>,
|
||||
scroll_handle: UniformListScrollHandle,
|
||||
selected_index: usize,
|
||||
}
|
||||
@@ -19,26 +21,27 @@ pub struct ThreadHistory {
|
||||
impl ThreadHistory {
|
||||
pub(crate) fn new(
|
||||
assistant_panel: WeakEntity<AssistantPanel>,
|
||||
thread_store: Entity<ThreadStore>,
|
||||
|
||||
history_store: Entity<HistoryStore>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
Self {
|
||||
focus_handle: cx.focus_handle(),
|
||||
assistant_panel,
|
||||
thread_store,
|
||||
history_store,
|
||||
scroll_handle: UniformListScrollHandle::default(),
|
||||
selected_index: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select_prev(
|
||||
pub fn select_previous(
|
||||
&mut self,
|
||||
_: &menu::SelectPrev,
|
||||
_: &menu::SelectPrevious,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let count = self.thread_store.read(cx).thread_count();
|
||||
let count = self
|
||||
.history_store
|
||||
.update(cx, |this, cx| this.entry_count(cx));
|
||||
if count > 0 {
|
||||
if self.selected_index == 0 {
|
||||
self.set_selected_index(count - 1, window, cx);
|
||||
@@ -54,7 +57,9 @@ impl ThreadHistory {
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let count = self.thread_store.read(cx).thread_count();
|
||||
let count = self
|
||||
.history_store
|
||||
.update(cx, |this, cx| this.entry_count(cx));
|
||||
if count > 0 {
|
||||
if self.selected_index == count - 1 {
|
||||
self.set_selected_index(0, window, cx);
|
||||
@@ -65,14 +70,18 @@ impl ThreadHistory {
|
||||
}
|
||||
|
||||
fn select_first(&mut self, _: &menu::SelectFirst, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let count = self.thread_store.read(cx).thread_count();
|
||||
let count = self
|
||||
.history_store
|
||||
.update(cx, |this, cx| this.entry_count(cx));
|
||||
if count > 0 {
|
||||
self.set_selected_index(0, window, cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn select_last(&mut self, _: &menu::SelectLast, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let count = self.thread_store.read(cx).thread_count();
|
||||
let count = self
|
||||
.history_store
|
||||
.update(cx, |this, cx| this.entry_count(cx));
|
||||
if count > 0 {
|
||||
self.set_selected_index(count - 1, window, cx);
|
||||
}
|
||||
@@ -86,12 +95,23 @@ impl ThreadHistory {
|
||||
}
|
||||
|
||||
fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let threads = self.thread_store.update(cx, |this, _cx| this.threads());
|
||||
let entries = self.history_store.update(cx, |this, cx| this.entries(cx));
|
||||
|
||||
if let Some(thread) = threads.get(self.selected_index) {
|
||||
self.assistant_panel
|
||||
.update(cx, move |this, cx| this.open_thread(&thread.id, window, cx))
|
||||
.ok();
|
||||
if let Some(entry) = entries.get(self.selected_index) {
|
||||
match entry {
|
||||
HistoryEntry::Thread(thread) => {
|
||||
self.assistant_panel
|
||||
.update(cx, move |this, cx| this.open_thread(&thread.id, window, cx))
|
||||
.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();
|
||||
}
|
||||
@@ -103,14 +123,19 @@ impl ThreadHistory {
|
||||
_window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let threads = self.thread_store.update(cx, |this, _cx| this.threads());
|
||||
let entries = self.history_store.update(cx, |this, cx| this.entries(cx));
|
||||
|
||||
if let Some(thread) = threads.get(self.selected_index) {
|
||||
self.assistant_panel
|
||||
.update(cx, |this, cx| {
|
||||
this.delete_thread(&thread.id, cx);
|
||||
})
|
||||
.ok();
|
||||
if let Some(entry) = entries.get(self.selected_index) {
|
||||
match entry {
|
||||
HistoryEntry::Thread(thread) => {
|
||||
self.assistant_panel
|
||||
.update(cx, |this, cx| {
|
||||
this.delete_thread(&thread.id, cx);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
HistoryEntry::Context(_context) => {}
|
||||
}
|
||||
|
||||
cx.notify();
|
||||
}
|
||||
@@ -125,7 +150,7 @@ impl Focusable for ThreadHistory {
|
||||
|
||||
impl Render for ThreadHistory {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let threads = self.thread_store.update(cx, |this, _cx| this.threads());
|
||||
let history_entries = self.history_store.update(cx, |this, cx| this.entries(cx));
|
||||
let selected_index = self.selected_index;
|
||||
|
||||
v_flex()
|
||||
@@ -135,14 +160,14 @@ impl Render for ThreadHistory {
|
||||
.overflow_y_scroll()
|
||||
.size_full()
|
||||
.p_1()
|
||||
.on_action(cx.listener(Self::select_prev))
|
||||
.on_action(cx.listener(Self::select_previous))
|
||||
.on_action(cx.listener(Self::select_next))
|
||||
.on_action(cx.listener(Self::select_first))
|
||||
.on_action(cx.listener(Self::select_last))
|
||||
.on_action(cx.listener(Self::confirm))
|
||||
.on_action(cx.listener(Self::remove_selected_thread))
|
||||
.map(|history| {
|
||||
if threads.is_empty() {
|
||||
if history_entries.is_empty() {
|
||||
history
|
||||
.justify_center()
|
||||
.child(
|
||||
@@ -156,17 +181,26 @@ impl Render for ThreadHistory {
|
||||
uniform_list(
|
||||
cx.entity().clone(),
|
||||
"thread-history",
|
||||
threads.len(),
|
||||
history_entries.len(),
|
||||
move |history, range, _window, _cx| {
|
||||
threads[range]
|
||||
history_entries[range]
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, thread)| {
|
||||
h_flex().w_full().pb_1().child(PastThread::new(
|
||||
thread.clone(),
|
||||
history.assistant_panel.clone(),
|
||||
selected_index == index,
|
||||
))
|
||||
.map(|(index, entry)| {
|
||||
h_flex().w_full().pb_1().child(match entry {
|
||||
HistoryEntry::Thread(thread) => PastThread::new(
|
||||
thread.clone(),
|
||||
history.assistant_panel.clone(),
|
||||
selected_index == index,
|
||||
)
|
||||
.into_any_element(),
|
||||
HistoryEntry::Context(context) => PastContext::new(
|
||||
context.clone(),
|
||||
history.assistant_panel.clone(),
|
||||
selected_index == index,
|
||||
)
|
||||
.into_any_element(),
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
@@ -262,3 +296,71 @@ 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(),
|
||||
))
|
||||
.outlined()
|
||||
.toggle_state(self.selected)
|
||||
.start_slot(
|
||||
Icon::new(IconName::Code)
|
||||
.size(IconSize::Small)
|
||||
.color(Color::Muted),
|
||||
)
|
||||
.spacing(ListItemSpacing::Sparse)
|
||||
.child(Label::new(summary).size(LabelSize::Small).text_ellipsis())
|
||||
.end_slot(
|
||||
h_flex().gap_1p5().child(
|
||||
Label::new(context_timestamp)
|
||||
.color(Color::Muted)
|
||||
.size(LabelSize::XSmall),
|
||||
),
|
||||
)
|
||||
.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();
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ indexed_docs.workspace = true
|
||||
language.workspace = true
|
||||
language_model.workspace = true
|
||||
language_model_selector.workspace = true
|
||||
language_models.workspace = true
|
||||
log.workspace = true
|
||||
multi_buffer.workspace = true
|
||||
open_ai.workspace = true
|
||||
|
||||
@@ -19,13 +19,10 @@ use gpui::{
|
||||
};
|
||||
use language::{AnchorRangeExt, Bias, Buffer, LanguageRegistry, OffsetRangeExt, Point, ToOffset};
|
||||
use language_model::{
|
||||
LanguageModel, LanguageModelCacheConfiguration, LanguageModelCompletionEvent,
|
||||
LanguageModelImage, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage,
|
||||
LanguageModelToolUseId, MessageContent, Role, StopReason,
|
||||
};
|
||||
use language_models::{
|
||||
provider::cloud::{MaxMonthlySpendReachedError, PaymentRequiredError},
|
||||
report_assistant_event,
|
||||
report_assistant_event, LanguageModel, LanguageModelCacheConfiguration,
|
||||
LanguageModelCompletionEvent, LanguageModelImage, LanguageModelRegistry, LanguageModelRequest,
|
||||
LanguageModelRequestMessage, LanguageModelToolUseId, MaxMonthlySpendReachedError,
|
||||
MessageContent, PaymentRequiredError, Role, StopReason,
|
||||
};
|
||||
use open_ai::Model as OpenAiModel;
|
||||
use paths::contexts_dir;
|
||||
@@ -3366,7 +3363,7 @@ impl SavedContextV0_1_0 {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SavedContextMetadata {
|
||||
pub title: String,
|
||||
pub path: PathBuf,
|
||||
|
||||
@@ -634,7 +634,7 @@ impl ContextEditor {
|
||||
}
|
||||
});
|
||||
let placeholder = FoldPlaceholder {
|
||||
render: Arc::new(move |_, _, _, _| Empty.into_any()),
|
||||
render: Arc::new(move |_, _, _| Empty.into_any()),
|
||||
..Default::default()
|
||||
};
|
||||
let render_toggle = {
|
||||
@@ -1087,7 +1087,7 @@ impl ContextEditor {
|
||||
patch: AssistantPatch,
|
||||
mut cx: AsyncWindowContext,
|
||||
) -> Result<()> {
|
||||
let project = this.update(&mut cx, |this, _| this.project.clone())?;
|
||||
let project = this.read_with(&cx, |this, _| this.project.clone())?;
|
||||
let resolved_patch = patch.resolve(project.clone(), &mut cx).await;
|
||||
|
||||
let editor = cx.new_window_entity(|window, cx| {
|
||||
@@ -1112,7 +1112,7 @@ impl ContextEditor {
|
||||
editor
|
||||
})?;
|
||||
|
||||
this.update_in(&mut cx, |this, window, cx| {
|
||||
this.update(&mut cx, |this, _| {
|
||||
if let Some(patch_state) = this.patches.get_mut(&patch.range) {
|
||||
patch_state.editor = Some(PatchEditorState {
|
||||
editor: editor.downgrade(),
|
||||
@@ -1120,19 +1120,12 @@ impl ContextEditor {
|
||||
});
|
||||
patch_state.update_task.take();
|
||||
}
|
||||
|
||||
this.workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
workspace.add_item_to_active_pane(
|
||||
Box::new(editor.clone()),
|
||||
None,
|
||||
false,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.log_err();
|
||||
})?;
|
||||
this.read_with(&cx, |this, _| this.workspace.clone())?
|
||||
.update_in(&mut cx, |workspace, window, cx| {
|
||||
workspace.add_item_to_active_pane(Box::new(editor.clone()), None, false, window, cx)
|
||||
})
|
||||
.log_err();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1234,8 +1227,8 @@ impl ContextEditor {
|
||||
.px_1()
|
||||
.mr_0p5()
|
||||
.border_1()
|
||||
.border_color(theme::color_alpha(colors.border_variant, 0.6))
|
||||
.bg(theme::color_alpha(colors.element_background, 0.6))
|
||||
.border_color(colors.border_variant.alpha(0.6))
|
||||
.bg(colors.element_background.alpha(0.6))
|
||||
.child("esc"),
|
||||
)
|
||||
.child("to cancel")
|
||||
@@ -1514,15 +1507,11 @@ impl ContextEditor {
|
||||
|
||||
(!text.is_empty()).then_some((text, true))
|
||||
} else {
|
||||
let anchor = context_editor.selections.newest_anchor();
|
||||
let text = context_editor
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.read(cx)
|
||||
.text_for_range(anchor.range())
|
||||
.collect::<String>();
|
||||
let selection = context_editor.selections.newest_adjusted(cx);
|
||||
let buffer = context_editor.buffer().read(cx).snapshot(cx);
|
||||
let selected_text = buffer.text_for_range(selection.range()).collect::<String>();
|
||||
|
||||
(!text.is_empty()).then_some((text, false))
|
||||
(!selected_text.is_empty()).then_some((selected_text, false))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1777,23 +1766,16 @@ impl ContextEditor {
|
||||
&mut self,
|
||||
cx: &mut Context<Self>,
|
||||
) -> (String, CopyMetadata, Vec<text::Selection<usize>>) {
|
||||
let (snapshot, selection, creases) = self.editor.update(cx, |editor, cx| {
|
||||
let mut selection = editor.selections.newest::<Point>(cx);
|
||||
let (selection, creases) = self.editor.update(cx, |editor, cx| {
|
||||
let mut selection = editor.selections.newest_adjusted(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);
|
||||
|
||||
(
|
||||
snapshot.clone(),
|
||||
selection.clone(),
|
||||
selection.map(|point| snapshot.point_to_offset(point)),
|
||||
editor.display_map.update(cx, |display_map, cx| {
|
||||
display_map
|
||||
.snapshot(cx)
|
||||
@@ -1833,7 +1815,6 @@ impl ContextEditor {
|
||||
)
|
||||
});
|
||||
|
||||
let selection = selection.map(|point| snapshot.point_to_offset(point));
|
||||
let context = self.context.read(cx);
|
||||
|
||||
let mut text = String::new();
|
||||
@@ -2043,6 +2024,15 @@ impl ContextEditor {
|
||||
});
|
||||
}
|
||||
|
||||
fn toggle_model_selector(
|
||||
&mut self,
|
||||
_: &ToggleModelSelector,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.language_model_selector_menu_handle.toggle(window, cx);
|
||||
}
|
||||
|
||||
fn save(&mut self, _: &Save, _window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.context.update(cx, |context, cx| {
|
||||
context.save(Some(Duration::from_millis(500)), self.fs.clone(), cx)
|
||||
@@ -2659,8 +2649,8 @@ fn render_fold_icon_button(
|
||||
editor: WeakEntity<Editor>,
|
||||
icon: IconName,
|
||||
label: SharedString,
|
||||
) -> Arc<dyn Send + Sync + Fn(FoldId, Range<Anchor>, &mut Window, &mut App) -> AnyElement> {
|
||||
Arc::new(move |fold_id, fold_range, _window, _cx| {
|
||||
) -> Arc<dyn Send + Sync + Fn(FoldId, Range<Anchor>, &mut App) -> AnyElement> {
|
||||
Arc::new(move |fold_id, fold_range, _cx| {
|
||||
let editor = editor.clone();
|
||||
ButtonLike::new(fold_id)
|
||||
.style(ButtonStyle::Filled)
|
||||
@@ -2720,7 +2710,7 @@ pub fn fold_toggle(
|
||||
fn quote_selection_fold_placeholder(title: String, editor: WeakEntity<Editor>) -> FoldPlaceholder {
|
||||
FoldPlaceholder {
|
||||
render: Arc::new({
|
||||
move |fold_id, fold_range, _window, _cx| {
|
||||
move |fold_id, fold_range, _cx| {
|
||||
let editor = editor.clone();
|
||||
ButtonLike::new(fold_id)
|
||||
.style(ButtonStyle::Filled)
|
||||
@@ -2886,6 +2876,7 @@ impl Render for ContextEditor {
|
||||
.on_action(cx.listener(ContextEditor::edit))
|
||||
.on_action(cx.listener(ContextEditor::assist))
|
||||
.on_action(cx.listener(ContextEditor::split))
|
||||
.on_action(cx.listener(ContextEditor::toggle_model_selector))
|
||||
.size_full()
|
||||
.children(self.render_notice(cx))
|
||||
.child(
|
||||
@@ -3403,7 +3394,7 @@ fn invoked_slash_command_fold_placeholder(
|
||||
FoldPlaceholder {
|
||||
constrain_width: false,
|
||||
merge_adjacent: false,
|
||||
render: Arc::new(move |fold_id, _, _window, cx| {
|
||||
render: Arc::new(move |fold_id, _, cx| {
|
||||
let Some(context) = context.upgrade() else {
|
||||
return Empty.into_any();
|
||||
};
|
||||
|
||||
@@ -350,6 +350,12 @@ 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> {
|
||||
let context = cx.new(|cx| {
|
||||
AssistantContext::local(
|
||||
|
||||
@@ -140,7 +140,7 @@ impl ResolvedPatch {
|
||||
buffer.edit(
|
||||
edits,
|
||||
Some(AutoindentMode::Block {
|
||||
original_indent_columns: Vec::new(),
|
||||
original_start_columns: Vec::new(),
|
||||
}),
|
||||
cx,
|
||||
);
|
||||
|
||||
@@ -359,6 +359,7 @@ fn providers_schema(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema:
|
||||
schemars::schema::SchemaObject {
|
||||
enum_values: Some(vec![
|
||||
"anthropic".into(),
|
||||
"bedrock".into(),
|
||||
"google".into(),
|
||||
"lmstudio".into(),
|
||||
"ollama".into(),
|
||||
@@ -512,7 +513,7 @@ mod tests {
|
||||
AssistantSettings::get_global(cx).default_model,
|
||||
LanguageModelSelection {
|
||||
provider: "zed.dev".into(),
|
||||
model: "claude-3-5-sonnet".into(),
|
||||
model: "claude-3-5-sonnet-latest".into(),
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -17,7 +17,7 @@ pub enum Timezone {
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct FileToolInput {
|
||||
pub struct NowToolInput {
|
||||
/// The timezone to use for the datetime.
|
||||
timezone: Timezone,
|
||||
}
|
||||
@@ -34,7 +34,7 @@ impl Tool for NowTool {
|
||||
}
|
||||
|
||||
fn input_schema(&self) -> serde_json::Value {
|
||||
let schema = schemars::schema_for!(FileToolInput);
|
||||
let schema = schemars::schema_for!(NowToolInput);
|
||||
serde_json::to_value(&schema).unwrap()
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ impl Tool for NowTool {
|
||||
_window: &mut Window,
|
||||
_cx: &mut App,
|
||||
) -> Task<Result<String>> {
|
||||
let input: FileToolInput = match serde_json::from_value(input) {
|
||||
let input: NowToolInput = match serde_json::from_value(input) {
|
||||
Ok(input) => input,
|
||||
Err(err) => return Task::ready(Err(anyhow!(err))),
|
||||
};
|
||||
|
||||
22
crates/aws_http_client/Cargo.toml
Normal file
22
crates/aws_http_client/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
||||
[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
crates/aws_http_client/LICENSE-GPL
Symbolic link
1
crates/aws_http_client/LICENSE-GPL
Symbolic link
@@ -0,0 +1 @@
|
||||
../../LICENSE-GPL
|
||||
118
crates/aws_http_client/src/aws_http_client.rs
Normal file
118
crates/aws_http_client/src/aws_http_client.rs
Normal file
@@ -0,0 +1,118 @@
|
||||
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(),
|
||||
}
|
||||
}
|
||||
28
crates/bedrock/Cargo.toml
Normal file
28
crates/bedrock/Cargo.toml
Normal file
@@ -0,0 +1,28 @@
|
||||
[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
crates/bedrock/LICENSE-GPL
Symbolic link
1
crates/bedrock/LICENSE-GPL
Symbolic link
@@ -0,0 +1 @@
|
||||
../../LICENSE-GPL
|
||||
166
crates/bedrock/src/bedrock.rs
Normal file
166
crates/bedrock/src/bedrock.rs
Normal file
@@ -0,0 +1,166 @@
|
||||
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),
|
||||
}
|
||||
207
crates/bedrock/src/models.rs
Normal file
207
crates/bedrock/src/models.rs
Normal file
@@ -0,0 +1,207 @@
|
||||
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);
|
||||
|
||||
StyledText::new(segment.text.replace('\n', ""))
|
||||
StyledText::new(segment.text.replace('\n', "⏎"))
|
||||
.with_highlights(&text_style, segment.highlights.unwrap_or_default())
|
||||
.into_any()
|
||||
});
|
||||
|
||||
@@ -3,7 +3,8 @@ use git2::{DiffLineType as GitDiffLineType, DiffOptions as GitOptions, Patch as
|
||||
use gpui::{App, AppContext as _, AsyncApp, Context, Entity, EventEmitter};
|
||||
use language::{Language, LanguageRegistry};
|
||||
use rope::Rope;
|
||||
use std::{cmp, future::Future, iter, ops::Range, sync::Arc};
|
||||
use std::cmp::Ordering;
|
||||
use std::{future::Future, iter, ops::Range, sync::Arc};
|
||||
use sum_tree::SumTree;
|
||||
use text::ToOffset as _;
|
||||
use text::{Anchor, Bias, BufferId, OffsetRangeExt, Point};
|
||||
@@ -68,7 +69,6 @@ pub struct DiffHunk {
|
||||
/// The range in the buffer's diff base text to which this hunk corresponds.
|
||||
pub diff_base_byte_range: Range<usize>,
|
||||
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.
|
||||
@@ -110,12 +110,17 @@ impl sum_tree::Summary for DiffHunkSummary {
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::SeekTarget<'a, DiffHunkSummary, DiffHunkSummary> for Anchor {
|
||||
fn cmp(
|
||||
&self,
|
||||
cursor_location: &DiffHunkSummary,
|
||||
buffer: &text::BufferSnapshot,
|
||||
) -> cmp::Ordering {
|
||||
self.cmp(&cursor_location.buffer_range.end, buffer)
|
||||
fn cmp(&self, cursor_location: &DiffHunkSummary, buffer: &text::BufferSnapshot) -> Ordering {
|
||||
if self
|
||||
.cmp(&cursor_location.buffer_range.start, buffer)
|
||||
.is_lt()
|
||||
{
|
||||
Ordering::Less
|
||||
} else if self.cmp(&cursor_location.buffer_range.end, buffer).is_gt() {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,97 +176,110 @@ impl BufferDiffSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
fn buffer_range_to_unchanged_diff_base_range(
|
||||
&self,
|
||||
buffer_range: Range<Anchor>,
|
||||
buffer: &text::BufferSnapshot,
|
||||
) -> Option<Range<usize>> {
|
||||
let mut hunks = self.inner.hunks.iter();
|
||||
let mut start = 0;
|
||||
let mut pos = buffer.anchor_before(0);
|
||||
while let Some(hunk) = hunks.next() {
|
||||
assert!(buffer_range.start.cmp(&pos, buffer).is_ge());
|
||||
assert!(hunk.buffer_range.start.cmp(&pos, buffer).is_ge());
|
||||
if hunk
|
||||
.buffer_range
|
||||
.start
|
||||
.cmp(&buffer_range.end, buffer)
|
||||
.is_ge()
|
||||
{
|
||||
// target buffer range is contained in the unchanged stretch leading up to this next hunk,
|
||||
// so do a final adjustment based on that
|
||||
break;
|
||||
}
|
||||
|
||||
// if the target buffer range intersects this hunk at all, no dice
|
||||
if buffer_range
|
||||
.start
|
||||
.cmp(&hunk.buffer_range.end, buffer)
|
||||
.is_lt()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
start += hunk.buffer_range.start.to_offset(buffer) - pos.to_offset(buffer);
|
||||
start += hunk.diff_base_byte_range.end - hunk.diff_base_byte_range.start;
|
||||
pos = hunk.buffer_range.end;
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
pub fn secondary_edits_for_stage_or_unstage(
|
||||
pub fn new_secondary_text_for_stage_or_unstage(
|
||||
&self,
|
||||
stage: bool,
|
||||
hunks: impl Iterator<Item = (Range<usize>, Option<Range<usize>>, Range<Anchor>)>,
|
||||
hunks: impl Iterator<Item = (Range<Anchor>, Range<usize>)>,
|
||||
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");
|
||||
let mut replacement_text = String::new();
|
||||
let Some(index_byte_range) = secondary_diff_base_byte_range.clone() else {
|
||||
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);
|
||||
cx: &mut App,
|
||||
) -> Option<Rope> {
|
||||
let secondary_diff = self.secondary_diff()?;
|
||||
let head_text = self.base_text().map(|text| text.as_rope().clone());
|
||||
let index_text = secondary_diff
|
||||
.base_text()
|
||||
.map(|text| text.as_rope().clone());
|
||||
let (index_text, head_text) = match (index_text, head_text) {
|
||||
(Some(index_text), Some(head_text)) => (index_text, head_text),
|
||||
// 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)
|
||||
}
|
||||
(index_byte_range, replacement_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
|
||||
.cmp(&buffer_range.end, buffer)
|
||||
.is_le()
|
||||
}) {
|
||||
let secondary_hunk_offset_range = secondary_hunk.buffer_range.to_offset(buffer);
|
||||
prev_secondary_hunk_base_text_offset = secondary_hunk.diff_base_byte_range.end;
|
||||
prev_secondary_hunk_buffer_offset = secondary_hunk_offset_range.end;
|
||||
|
||||
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
|
||||
.end
|
||||
.saturating_sub(prev_secondary_hunk_buffer_offset);
|
||||
let secondary_base_text_end = prev_secondary_hunk_base_text_offset + end_overshoot;
|
||||
|
||||
let secondary_base_text_range = secondary_base_text_start..secondary_base_text_end;
|
||||
buffer_offset_range.end = buffer_offset_range
|
||||
.end
|
||||
.max(prev_secondary_hunk_buffer_offset);
|
||||
|
||||
let replacement_text = if stage {
|
||||
log::debug!("staging");
|
||||
buffer
|
||||
.text_for_range(buffer_offset_range)
|
||||
.collect::<String>()
|
||||
} else {
|
||||
log::debug!("unstaging");
|
||||
let mut replacement_text = String::new();
|
||||
let Some(index_byte_range) = secondary_diff
|
||||
.buffer_range_to_unchanged_diff_base_range(buffer_range.clone(), &buffer)
|
||||
else {
|
||||
log::debug!("not an unstageable hunk");
|
||||
continue;
|
||||
};
|
||||
for chunk in head_base.chunks_in_range(diff_base_byte_range.clone()) {
|
||||
replacement_text.push_str(chunk);
|
||||
}
|
||||
(index_byte_range, replacement_text)
|
||||
head_text
|
||||
.chunks_in_range(diff_base_byte_range.clone())
|
||||
.collect::<String>()
|
||||
};
|
||||
edits.push((index_byte_range, replacement_text));
|
||||
edits.push((secondary_base_text_range, replacement_text));
|
||||
}
|
||||
log::debug!("edits: {edits:?}");
|
||||
edits
|
||||
|
||||
let buffer = cx.new(|cx| {
|
||||
language::Buffer::local_normalized(index_text, text::LineEnding::default(), cx)
|
||||
});
|
||||
let new_text = buffer.update(cx, |buffer, cx| {
|
||||
buffer.edit(edits, None, cx);
|
||||
buffer.as_rope().clone()
|
||||
});
|
||||
Some(new_text)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,13 +340,12 @@ impl BufferDiffInner {
|
||||
}
|
||||
|
||||
let mut secondary_status = DiffHunkSecondaryStatus::None;
|
||||
let mut secondary_diff_base_byte_range = None;
|
||||
if let Some(secondary_cursor) = secondary_cursor.as_mut() {
|
||||
if start_anchor
|
||||
.cmp(&secondary_cursor.start().buffer_range.start, buffer)
|
||||
.is_gt()
|
||||
{
|
||||
secondary_cursor.seek_forward(&end_anchor, Bias::Left, buffer);
|
||||
secondary_cursor.seek_forward(&start_anchor, Bias::Left, buffer);
|
||||
}
|
||||
|
||||
if let Some(secondary_hunk) = secondary_cursor.item() {
|
||||
@@ -339,12 +356,12 @@ impl BufferDiffInner {
|
||||
}
|
||||
if secondary_range == (start_point..end_point) {
|
||||
secondary_status = DiffHunkSecondaryStatus::HasSecondaryHunk;
|
||||
secondary_diff_base_byte_range =
|
||||
Some(secondary_hunk.diff_base_byte_range.clone());
|
||||
} else if secondary_range.start <= end_point {
|
||||
secondary_status = DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::debug!("no secondary cursor!!");
|
||||
}
|
||||
|
||||
return Some(DiffHunk {
|
||||
@@ -352,7 +369,6 @@ impl BufferDiffInner {
|
||||
diff_base_byte_range: start_base..end_base,
|
||||
buffer_range: start_anchor..end_anchor,
|
||||
secondary_status,
|
||||
secondary_diff_base_byte_range,
|
||||
});
|
||||
})
|
||||
}
|
||||
@@ -387,7 +403,6 @@ impl BufferDiffInner {
|
||||
buffer_range: hunk.buffer_range.clone(),
|
||||
// The secondary status is not used by callers of this method.
|
||||
secondary_status: DiffHunkSecondaryStatus::None,
|
||||
secondary_diff_base_byte_range: None,
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -408,12 +423,12 @@ impl BufferDiffInner {
|
||||
.start
|
||||
.cmp(&old_hunk.buffer_range.start, new_snapshot)
|
||||
{
|
||||
cmp::Ordering::Less => {
|
||||
Ordering::Less => {
|
||||
start.get_or_insert(new_hunk.buffer_range.start);
|
||||
end.replace(new_hunk.buffer_range.end);
|
||||
new_cursor.next(new_snapshot);
|
||||
}
|
||||
cmp::Ordering::Equal => {
|
||||
Ordering::Equal => {
|
||||
if new_hunk != old_hunk {
|
||||
start.get_or_insert(new_hunk.buffer_range.start);
|
||||
if old_hunk
|
||||
@@ -431,7 +446,7 @@ impl BufferDiffInner {
|
||||
new_cursor.next(new_snapshot);
|
||||
old_cursor.next(new_snapshot);
|
||||
}
|
||||
cmp::Ordering::Greater => {
|
||||
Ordering::Greater => {
|
||||
start.get_or_insert(old_hunk.buffer_range.start);
|
||||
end.replace(old_hunk.buffer_range.end);
|
||||
old_cursor.next(new_snapshot);
|
||||
@@ -1059,6 +1074,7 @@ mod tests {
|
||||
use rand::{rngs::StdRng, Rng as _};
|
||||
use text::{Buffer, BufferId, Rope};
|
||||
use unindent::Unindent as _;
|
||||
use util::test::marked_text_ranges;
|
||||
|
||||
#[ctor::ctor]
|
||||
fn init_logger() {
|
||||
@@ -1257,6 +1273,208 @@ 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]
|
||||
async fn test_buffer_diff_compare(cx: &mut TestAppContext) {
|
||||
let base_text = "
|
||||
@@ -1382,7 +1600,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[gpui::test(iterations = 100)]
|
||||
async fn test_secondary_edits_for_stage_unstage(cx: &mut TestAppContext, mut rng: StdRng) {
|
||||
async fn test_staging_and_unstaging_hunks(cx: &mut TestAppContext, mut rng: StdRng) {
|
||||
fn gen_line(rng: &mut StdRng) -> String {
|
||||
if rng.gen_bool(0.2) {
|
||||
"\n".to_owned()
|
||||
@@ -1447,7 +1665,7 @@ mod tests {
|
||||
|
||||
fn uncommitted_diff(
|
||||
working_copy: &language::BufferSnapshot,
|
||||
index_text: &Entity<language::Buffer>,
|
||||
index_text: &Rope,
|
||||
head_text: String,
|
||||
cx: &mut TestAppContext,
|
||||
) -> BufferDiff {
|
||||
@@ -1456,7 +1674,7 @@ mod tests {
|
||||
buffer_id: working_copy.remote_id(),
|
||||
inner: BufferDiff::build_sync(
|
||||
working_copy.text.clone(),
|
||||
index_text.read_with(cx, |index_text, _| index_text.text()),
|
||||
index_text.to_string(),
|
||||
cx,
|
||||
),
|
||||
secondary_diff: None,
|
||||
@@ -1487,17 +1705,11 @@ mod tests {
|
||||
)
|
||||
});
|
||||
let working_copy = working_copy.read_with(cx, |working_copy, _| working_copy.snapshot());
|
||||
let index_text = cx.new(|cx| {
|
||||
language::Buffer::local_normalized(
|
||||
if rng.gen() {
|
||||
Rope::from(head_text.as_str())
|
||||
} else {
|
||||
working_copy.as_rope().clone()
|
||||
},
|
||||
text::LineEnding::default(),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
let mut index_text = if rng.gen() {
|
||||
Rope::from(head_text.as_str())
|
||||
} else {
|
||||
working_copy.as_rope().clone()
|
||||
};
|
||||
|
||||
let mut diff = uncommitted_diff(&working_copy, &index_text, head_text.clone(), cx);
|
||||
let mut hunks = cx.update(|cx| {
|
||||
@@ -1511,37 +1723,29 @@ mod tests {
|
||||
for _ in 0..operations {
|
||||
let i = rng.gen_range(0..hunks.len());
|
||||
let hunk = &mut hunks[i];
|
||||
let hunk_fields = (
|
||||
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(_)) => {
|
||||
let stage = match hunk.secondary_status {
|
||||
DiffHunkSecondaryStatus::HasSecondaryHunk => {
|
||||
hunk.secondary_status = DiffHunkSecondaryStatus::None;
|
||||
hunk.secondary_diff_base_byte_range = None;
|
||||
true
|
||||
}
|
||||
(DiffHunkSecondaryStatus::None, None) => {
|
||||
DiffHunkSecondaryStatus::None => {
|
||||
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
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let snapshot = cx.update(|cx| diff.snapshot(cx));
|
||||
let edits = snapshot.secondary_edits_for_stage_or_unstage(
|
||||
stage,
|
||||
[hunk_fields].into_iter(),
|
||||
&working_copy,
|
||||
);
|
||||
index_text.update(cx, |index_text, cx| {
|
||||
index_text.edit(edits, None, cx);
|
||||
index_text = cx.update(|cx| {
|
||||
snapshot
|
||||
.new_secondary_text_for_stage_or_unstage(
|
||||
stage,
|
||||
[(hunk.buffer_range.clone(), hunk.diff_base_byte_range.clone())]
|
||||
.into_iter(),
|
||||
&working_copy,
|
||||
cx,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
diff = uncommitted_diff(&working_copy, &index_text, head_text.clone(), cx);
|
||||
@@ -1550,6 +1754,7 @@ mod tests {
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
assert_eq!(hunks.len(), found_hunks.len());
|
||||
|
||||
for (expected_hunk, found_hunk) in hunks.iter().zip(&found_hunks) {
|
||||
assert_eq!(
|
||||
expected_hunk.buffer_range.to_point(&working_copy),
|
||||
@@ -1560,10 +1765,6 @@ mod tests {
|
||||
found_hunk.diff_base_byte_range
|
||||
);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ async-tungstenite = { workspace = true, features = ["async-std", "async-tls"] }
|
||||
chrono = { workspace = true, features = ["serde"] }
|
||||
clock.workspace = true
|
||||
collections.workspace = true
|
||||
credentials_provider.workspace = true
|
||||
feature_flags.workspace = true
|
||||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
|
||||
@@ -15,6 +15,7 @@ use async_tungstenite::tungstenite::{
|
||||
};
|
||||
use chrono::{DateTime, Utc};
|
||||
use clock::SystemClock;
|
||||
use credentials_provider::CredentialsProvider;
|
||||
use futures::{
|
||||
channel::oneshot, future::BoxFuture, AsyncReadExt, FutureExt, SinkExt, Stream, StreamExt,
|
||||
TryFutureExt as _, TryStreamExt,
|
||||
@@ -57,14 +58,6 @@ static ZED_SERVER_URL: LazyLock<Option<String>> =
|
||||
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());
|
||||
|
||||
/// 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(|| {
|
||||
std::env::var("ZED_IMPERSONATE")
|
||||
.ok()
|
||||
@@ -193,7 +186,7 @@ pub struct Client {
|
||||
peer: Arc<Peer>,
|
||||
http: Arc<HttpClientWithUrl>,
|
||||
telemetry: Arc<Telemetry>,
|
||||
credentials_provider: Arc<dyn CredentialsProvider + Send + Sync + 'static>,
|
||||
credentials_provider: ClientCredentialsProvider,
|
||||
state: RwLock<ClientState>,
|
||||
handler_set: parking_lot::Mutex<ProtoMessageHandlerSet>,
|
||||
|
||||
@@ -304,16 +297,46 @@ impl Credentials {
|
||||
}
|
||||
}
|
||||
|
||||
/// A provider for [`Credentials`].
|
||||
///
|
||||
/// Used to abstract over reading and writing credentials to some form of
|
||||
/// persistence (like the system keychain).
|
||||
trait CredentialsProvider {
|
||||
pub struct ClientCredentialsProvider {
|
||||
provider: Arc<dyn CredentialsProvider>,
|
||||
}
|
||||
|
||||
impl ClientCredentialsProvider {
|
||||
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.
|
||||
fn read_credentials<'a>(
|
||||
&'a self,
|
||||
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.
|
||||
fn write_credentials<'a>(
|
||||
@@ -321,13 +344,32 @@ trait CredentialsProvider {
|
||||
user_id: u64,
|
||||
access_token: String,
|
||||
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.
|
||||
fn delete_credentials<'a>(
|
||||
&'a self,
|
||||
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 {
|
||||
@@ -484,27 +526,12 @@ impl Client {
|
||||
http: Arc<HttpClientWithUrl>,
|
||||
cx: &mut App,
|
||||
) -> 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 {
|
||||
id: AtomicU64::new(0),
|
||||
peer: Peer::new(0),
|
||||
telemetry: Telemetry::new(clock, http.clone(), cx),
|
||||
http,
|
||||
credentials_provider,
|
||||
credentials_provider: ClientCredentialsProvider::new(cx),
|
||||
state: Default::default(),
|
||||
handler_set: Default::default(),
|
||||
|
||||
@@ -842,8 +869,7 @@ impl Client {
|
||||
Ok(conn) => {
|
||||
self.state.write().credentials = Some(credentials.clone());
|
||||
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! {
|
||||
@@ -1588,130 +1614,6 @@ 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
|
||||
pub const ZED_URL_SCHEME: &str = "zed";
|
||||
|
||||
|
||||
@@ -97,6 +97,7 @@ extension.workspace = true
|
||||
file_finder.workspace = true
|
||||
fs = { workspace = true, features = ["test-support"] }
|
||||
git = { workspace = true, features = ["test-support"] }
|
||||
git_ui = { workspace = true, features = ["test-support"] }
|
||||
git_hosting_providers.workspace = true
|
||||
gpui = { workspace = true, features = ["test-support"] }
|
||||
hyper.workspace = true
|
||||
|
||||
@@ -256,6 +256,7 @@ async fn perform_completion(
|
||||
// so that users can use the new version, without having to update Zed.
|
||||
request.model = match model.as_str() {
|
||||
"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-haiku" => anthropic::Model::Claude3Haiku.id().to_string(),
|
||||
"claude-3-sonnet" => anthropic::Model::Claude3Sonnet.id().to_string(),
|
||||
@@ -450,19 +451,15 @@ async fn check_usage_limit(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let user_id = UserId::from_proto(claims.user_id);
|
||||
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();
|
||||
|
||||
if usage.spending_this_month >= free_tier {
|
||||
let spending_this_month = state
|
||||
.db
|
||||
.get_user_spending_for_month(user_id, Utc::now())
|
||||
.await?;
|
||||
if spending_this_month >= free_tier {
|
||||
if !claims.has_llm_subscription {
|
||||
return Err(Error::http(
|
||||
StatusCode::PAYMENT_REQUIRED,
|
||||
@@ -470,7 +467,8 @@ async fn check_usage_limit(
|
||||
));
|
||||
}
|
||||
|
||||
if (usage.spending_this_month - free_tier) >= Cents(claims.max_monthly_spend_in_cents) {
|
||||
let monthly_spend = spending_this_month.saturating_sub(free_tier);
|
||||
if monthly_spend >= Cents(claims.max_monthly_spend_in_cents) {
|
||||
return Err(Error::Http(
|
||||
StatusCode::FORBIDDEN,
|
||||
"Maximum spending limit reached for this month.".to_string(),
|
||||
@@ -495,6 +493,11 @@ async fn check_usage_limit(
|
||||
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 usage = state
|
||||
.db
|
||||
.get_usage(user_id, provider, model_name, Utc::now())
|
||||
.await?;
|
||||
|
||||
let checks = [
|
||||
(
|
||||
usage.requests_this_minute,
|
||||
|
||||
@@ -27,7 +27,7 @@ fn authorize_access_to_model(
|
||||
}
|
||||
|
||||
if provider == LanguageModelProvider::Anthropic {
|
||||
if model == "claude-3-5-sonnet" {
|
||||
if model == "claude-3-5-sonnet" || model == "claude-3-7-sonnet" {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
||||
@@ -393,10 +393,12 @@ impl Server {
|
||||
.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::Push>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::ForcePush>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::Pull>)
|
||||
.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::Unstage>)
|
||||
.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::GitReset>)
|
||||
.add_request_handler(forward_read_only_project_request::<proto::GitCheckoutFiles>)
|
||||
|
||||
@@ -13,6 +13,7 @@ mod channel_message_tests;
|
||||
mod channel_tests;
|
||||
mod editor_tests;
|
||||
mod following_tests;
|
||||
mod git_tests;
|
||||
mod integration_tests;
|
||||
mod notification_tests;
|
||||
mod random_channel_buffer_tests;
|
||||
|
||||
130
crates/collab/src/tests/git_tests.rs
Normal file
130
crates/collab/src/tests/git_tests.rs
Normal file
@@ -0,0 +1,130 @@
|
||||
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"]
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -17,7 +17,7 @@ use gpui::{
|
||||
ListState, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Render, SharedString,
|
||||
Styled, Subscription, Task, TextStyle, WeakEntity, Window,
|
||||
};
|
||||
use menu::{Cancel, Confirm, SecondaryConfirm, SelectNext, SelectPrev};
|
||||
use menu::{Cancel, Confirm, SecondaryConfirm, SelectNext, SelectPrevious};
|
||||
use project::{Fs, Project};
|
||||
use rpc::{
|
||||
proto::{self, ChannelVisibility, PeerId},
|
||||
@@ -1430,7 +1430,7 @@ impl CollabPanel {
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn select_prev(&mut self, _: &SelectPrev, _: &mut Window, cx: &mut Context<Self>) {
|
||||
fn select_previous(&mut self, _: &SelectPrevious, _: &mut Window, cx: &mut Context<Self>) {
|
||||
let ix = self.selection.take().unwrap_or(0);
|
||||
if ix > 0 {
|
||||
self.selection = Some(ix - 1);
|
||||
@@ -2458,8 +2458,8 @@ impl CollabPanel {
|
||||
Avatar::new(contact.user.avatar_uri.clone())
|
||||
.indicator::<AvatarAvailabilityIndicator>(if online {
|
||||
Some(AvatarAvailabilityIndicator::new(match busy {
|
||||
true => ui::Availability::Busy,
|
||||
false => ui::Availability::Free,
|
||||
true => ui::CollaboratorAvailability::Busy,
|
||||
false => ui::CollaboratorAvailability::Free,
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
@@ -2878,7 +2878,7 @@ impl Render for CollabPanel {
|
||||
.key_context("CollabPanel")
|
||||
.on_action(cx.listener(CollabPanel::cancel))
|
||||
.on_action(cx.listener(CollabPanel::select_next))
|
||||
.on_action(cx.listener(CollabPanel::select_prev))
|
||||
.on_action(cx.listener(CollabPanel::select_previous))
|
||||
.on_action(cx.listener(CollabPanel::confirm))
|
||||
.on_action(cx.listener(CollabPanel::insert_space))
|
||||
.on_action(cx.listener(CollabPanel::remove_selected_channel))
|
||||
|
||||
@@ -173,9 +173,9 @@ pub enum ExampleLabelSide {
|
||||
Left,
|
||||
/// Right side
|
||||
Right,
|
||||
#[default]
|
||||
/// Top side
|
||||
Top,
|
||||
#[default]
|
||||
/// Bottom side
|
||||
Bottom,
|
||||
}
|
||||
@@ -200,10 +200,10 @@ impl RenderOnce for ComponentExample {
|
||||
ExampleLabelSide::Top => base.flex_col_reverse(),
|
||||
};
|
||||
|
||||
base.gap_1()
|
||||
base.gap_2()
|
||||
.p_2()
|
||||
.text_sm()
|
||||
.text_color(cx.theme().colors().text)
|
||||
.text_size(px(10.))
|
||||
.text_color(cx.theme().colors().text_muted)
|
||||
.when(self.grow, |this| this.flex_1())
|
||||
.child(self.element)
|
||||
.child(self.variant_name)
|
||||
@@ -245,12 +245,13 @@ impl RenderOnce for ComponentExampleGroup {
|
||||
.text_color(cx.theme().colors().text_muted)
|
||||
.when(self.grow, |this| this.w_full().flex_1())
|
||||
.when_some(self.title, |this, title| {
|
||||
this.gap_4().pb_5().child(
|
||||
this.gap_4().child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_3()
|
||||
.child(div().h_px().w_4().bg(cx.theme().colors().border_variant))
|
||||
.pb_1()
|
||||
.child(div().h_px().w_4().bg(cx.theme().colors().border))
|
||||
.child(
|
||||
div()
|
||||
.flex_none()
|
||||
@@ -271,7 +272,7 @@ impl RenderOnce for ComponentExampleGroup {
|
||||
.flex()
|
||||
.items_start()
|
||||
.w_full()
|
||||
.gap_8()
|
||||
.gap_6()
|
||||
.children(self.examples)
|
||||
.into_any_element(),
|
||||
)
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
//! A view for exploring Zed components.
|
||||
|
||||
use component::{components, ComponentMetadata};
|
||||
use gpui::{prelude::*, App, EventEmitter, FocusHandle, Focusable, Window};
|
||||
use ui::prelude::*;
|
||||
use gpui::{list, prelude::*, uniform_list, App, EventEmitter, FocusHandle, Focusable, Window};
|
||||
use gpui::{ListState, ScrollHandle, UniformListScrollHandle};
|
||||
use ui::{prelude::*, ListItem};
|
||||
|
||||
use workspace::{item::ItemEvent, Item, Workspace, WorkspaceId};
|
||||
|
||||
@@ -12,7 +13,7 @@ pub fn init(cx: &mut App) {
|
||||
cx.observe_new(|workspace: &mut Workspace, _, _cx| {
|
||||
workspace.register_action(
|
||||
|workspace, _: &workspace::OpenComponentPreview, window, cx| {
|
||||
let component_preview = cx.new(ComponentPreview::new);
|
||||
let component_preview = cx.new(|cx| ComponentPreview::new(window, cx));
|
||||
workspace.add_item_to_active_pane(
|
||||
Box::new(component_preview),
|
||||
None,
|
||||
@@ -28,124 +29,161 @@ pub fn init(cx: &mut App) {
|
||||
|
||||
struct ComponentPreview {
|
||||
focus_handle: FocusHandle,
|
||||
_view_scroll_handle: ScrollHandle,
|
||||
nav_scroll_handle: UniformListScrollHandle,
|
||||
components: Vec<ComponentMetadata>,
|
||||
component_list: ListState,
|
||||
selected_index: usize,
|
||||
}
|
||||
|
||||
impl ComponentPreview {
|
||||
pub fn new(cx: &mut Context<Self>) -> Self {
|
||||
pub fn new(_window: &mut Window, 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 {
|
||||
focus_handle: cx.focus_handle(),
|
||||
_view_scroll_handle: ScrollHandle::new(),
|
||||
nav_scroll_handle: UniformListScrollHandle::new(),
|
||||
components,
|
||||
component_list,
|
||||
selected_index: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn render_sidebar(&self, _window: &Window, _cx: &Context<Self>) -> impl IntoElement {
|
||||
let components = components().all_sorted();
|
||||
let sorted_components = components.clone();
|
||||
fn scroll_to_preview(&mut self, ix: usize, cx: &mut Context<Self>) {
|
||||
self.component_list.scroll_to_reveal_item(ix);
|
||||
self.selected_index = ix;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
v_flex()
|
||||
.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 get_component(&self, ix: usize) -> ComponentMetadata {
|
||||
self.components[ix].clone()
|
||||
}
|
||||
|
||||
fn render_sidebar_entry(
|
||||
&self,
|
||||
component: &ComponentMetadata,
|
||||
_cx: &Context<Self>,
|
||||
ix: usize,
|
||||
selected: bool,
|
||||
cx: &Context<Self>,
|
||||
) -> impl IntoElement {
|
||||
h_flex()
|
||||
.w_40()
|
||||
.px_1p5()
|
||||
.py_0p5()
|
||||
.text_sm()
|
||||
.child(component.name().clone())
|
||||
let component = self.get_component(ix);
|
||||
|
||||
ListItem::new(ix)
|
||||
.child(Label::new(component.name().clone()).color(Color::Default))
|
||||
.selectable(true)
|
||||
.toggle_state(selected)
|
||||
.inset(true)
|
||||
.on_click(cx.listener(move |this, _, _, cx| {
|
||||
this.scroll_to_preview(ix, cx);
|
||||
}))
|
||||
}
|
||||
|
||||
fn render_preview(
|
||||
&self,
|
||||
component: &ComponentMetadata,
|
||||
ix: usize,
|
||||
window: &mut Window,
|
||||
cx: &Context<Self>,
|
||||
) -> impl IntoElement {
|
||||
let component = self.get_component(ix);
|
||||
|
||||
let name = component.name();
|
||||
let scope = component.scope();
|
||||
|
||||
let description = component.description();
|
||||
|
||||
v_flex()
|
||||
.border_b_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.w_full()
|
||||
.gap_3()
|
||||
.py_6()
|
||||
.py_2()
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.border_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.rounded_md()
|
||||
.w_full()
|
||||
.gap_4()
|
||||
.py_4()
|
||||
.px_6()
|
||||
.flex_none()
|
||||
.child(
|
||||
h_flex()
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.text_2xl()
|
||||
.child(div().child(name))
|
||||
.when_some(scope, |this, scope| {
|
||||
this.child(div().opacity(0.5).child(format!("({})", scope)))
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.text_xl()
|
||||
.child(div().child(name))
|
||||
.when_some(scope, |this, scope| {
|
||||
this.child(div().opacity(0.5).child(format!("({})", scope)))
|
||||
}),
|
||||
)
|
||||
.when_some(description, |this, description| {
|
||||
this.child(
|
||||
div()
|
||||
.text_ui_sm(cx)
|
||||
.text_color(cx.theme().colors().text_muted)
|
||||
.max_w(px(600.0))
|
||||
.child(description),
|
||||
)
|
||||
}),
|
||||
)
|
||||
.when_some(description, |this, description| {
|
||||
this.child(
|
||||
div()
|
||||
.text_ui_sm(cx)
|
||||
.text_color(cx.theme().colors().text_muted)
|
||||
.max_w(px(600.0))
|
||||
.child(description),
|
||||
)
|
||||
.when_some(component.preview(), |this, preview| {
|
||||
this.child(preview(window, cx))
|
||||
}),
|
||||
)
|
||||
.when_some(component.preview(), |this, preview| {
|
||||
this.child(preview(window, cx))
|
||||
})
|
||||
.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 {
|
||||
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()
|
||||
.id("component-preview")
|
||||
.key_context("ComponentPreview")
|
||||
.items_start()
|
||||
.overflow_hidden()
|
||||
.size_full()
|
||||
.max_h_full()
|
||||
.track_focus(&self.focus_handle)
|
||||
.px_2()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.child(self.render_sidebar(window, cx))
|
||||
.child(self.render_previews(window, cx))
|
||||
.child(
|
||||
uniform_list(
|
||||
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),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,13 +213,13 @@ impl Item for ComponentPreview {
|
||||
fn clone_on_split(
|
||||
&self,
|
||||
_workspace_id: Option<WorkspaceId>,
|
||||
_window: &mut Window,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Option<gpui::Entity<Self>>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Some(cx.new(Self::new))
|
||||
Some(cx.new(|cx| Self::new(window, cx)))
|
||||
}
|
||||
|
||||
fn to_item_events(event: &Self::Event, mut f: impl FnMut(workspace::item::ItemEvent)) {
|
||||
|
||||
@@ -14,6 +14,7 @@ path = "src/context_server.rs"
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
assistant_tool.workspace = true
|
||||
async-trait.workspace = true
|
||||
collections.workspace = true
|
||||
command_palette_hooks.workspace = true
|
||||
context_server_settings.workspace = true
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use collections::HashMap;
|
||||
use futures::{channel::oneshot, io::BufWriter, select, AsyncRead, AsyncWrite, FutureExt};
|
||||
use futures::{channel::oneshot, select, FutureExt, StreamExt};
|
||||
use gpui::{AppContext as _, AsyncApp, BackgroundExecutor, Task};
|
||||
use parking_lot::Mutex;
|
||||
use postage::barrier;
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use serde_json::{value::RawValue, Value};
|
||||
use smol::{
|
||||
channel,
|
||||
io::{AsyncBufReadExt, AsyncWriteExt, BufReader},
|
||||
process::Child,
|
||||
};
|
||||
use smol::channel;
|
||||
use std::{
|
||||
fmt,
|
||||
path::PathBuf,
|
||||
@@ -22,6 +18,8 @@ use std::{
|
||||
};
|
||||
use util::TryFutureExt;
|
||||
|
||||
use crate::transport::{StdioTransport, Transport};
|
||||
|
||||
const JSON_RPC_VERSION: &str = "2.0";
|
||||
const REQUEST_TIMEOUT: Duration = Duration::from_secs(60);
|
||||
|
||||
@@ -55,7 +53,8 @@ pub struct Client {
|
||||
#[allow(dead_code)]
|
||||
output_done_rx: Mutex<Option<barrier::Receiver>>,
|
||||
executor: BackgroundExecutor,
|
||||
server: Arc<Mutex<Option<Child>>>,
|
||||
#[allow(dead_code)]
|
||||
transport: Arc<dyn Transport>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
@@ -152,25 +151,13 @@ impl Client {
|
||||
&binary.args
|
||||
);
|
||||
|
||||
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 server_name = binary
|
||||
.executable
|
||||
.file_name()
|
||||
.map(|name| name.to_string_lossy().to_string())
|
||||
.unwrap_or_else(String::new);
|
||||
|
||||
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 transport = Arc::new(StdioTransport::new(binary, &cx)?);
|
||||
|
||||
let (outbound_tx, outbound_rx) = channel::unbounded::<String>();
|
||||
let (output_done_tx, output_done_rx) = barrier::channel();
|
||||
@@ -183,18 +170,22 @@ impl Client {
|
||||
let stdout_input_task = cx.spawn({
|
||||
let notification_handlers = notification_handlers.clone();
|
||||
let response_handlers = response_handlers.clone();
|
||||
let transport = transport.clone();
|
||||
move |cx| {
|
||||
Self::handle_input(stdout, notification_handlers, response_handlers, cx).log_err()
|
||||
Self::handle_input(transport, notification_handlers, response_handlers, cx)
|
||||
.log_err()
|
||||
}
|
||||
});
|
||||
let stderr_input_task = cx.spawn(|_| Self::handle_stderr(stderr).log_err());
|
||||
let stderr_input_task = cx.spawn(|_| Self::handle_stderr(transport.clone()).log_err());
|
||||
let input_task = cx.spawn(|_| async move {
|
||||
let (stdout, stderr) = futures::join!(stdout_input_task, stderr_input_task);
|
||||
stdout.or(stderr)
|
||||
});
|
||||
|
||||
let output_task = cx.background_spawn({
|
||||
let transport = transport.clone();
|
||||
Self::handle_output(
|
||||
stdin,
|
||||
transport,
|
||||
outbound_rx,
|
||||
output_done_tx,
|
||||
response_handlers.clone(),
|
||||
@@ -202,24 +193,18 @@ impl Client {
|
||||
.log_err()
|
||||
});
|
||||
|
||||
let mut context_server = Self {
|
||||
Ok(Self {
|
||||
server_id,
|
||||
notification_handlers,
|
||||
response_handlers,
|
||||
name: "".into(),
|
||||
name: server_name.into(),
|
||||
next_id: Default::default(),
|
||||
outbound_tx,
|
||||
executor: cx.background_executor().clone(),
|
||||
io_tasks: Mutex::new(Some((input_task, output_task))),
|
||||
output_done_rx: Mutex::new(Some(output_done_rx)),
|
||||
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)
|
||||
transport,
|
||||
})
|
||||
}
|
||||
|
||||
/// Handles input from the server's stdout.
|
||||
@@ -228,79 +213,53 @@ impl Client {
|
||||
/// parses them as JSON-RPC responses or notifications, and dispatches them
|
||||
/// to the appropriate handlers. It processes both responses (which are matched
|
||||
/// to pending requests) and notifications (which trigger registered handlers).
|
||||
async fn handle_input<Stdout>(
|
||||
stdout: Stdout,
|
||||
async fn handle_input(
|
||||
transport: Arc<dyn Transport>,
|
||||
notification_handlers: Arc<Mutex<HashMap<&'static str, NotificationHandler>>>,
|
||||
response_handlers: Arc<Mutex<Option<HashMap<RequestId, ResponseHandler>>>>,
|
||||
cx: AsyncApp,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
Stdout: AsyncRead + Unpin + Send + 'static,
|
||||
{
|
||||
let mut stdout = BufReader::new(stdout);
|
||||
let mut buffer = String::new();
|
||||
) -> anyhow::Result<()> {
|
||||
let mut receiver = transport.receive();
|
||||
|
||||
loop {
|
||||
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(handler) = handlers.remove(&response.id) {
|
||||
handler(Ok(content.to_string()));
|
||||
}
|
||||
}
|
||||
} else if let Ok(notification) = serde_json::from_str::<AnyNotification>(content) {
|
||||
let mut notification_handlers = notification_handlers.lock();
|
||||
if let Some(handler) =
|
||||
notification_handlers.get_mut(notification.method.as_str())
|
||||
{
|
||||
handler(notification.params.unwrap_or(Value::Null), cx.clone());
|
||||
while let Some(message) = receiver.next().await {
|
||||
if let Ok(response) = serde_json::from_str::<AnyResponse>(&message) {
|
||||
if let Some(handlers) = response_handlers.lock().as_mut() {
|
||||
if let Some(handler) = handlers.remove(&response.id) {
|
||||
handler(Ok(message.to_string()));
|
||||
}
|
||||
}
|
||||
} else if let Ok(notification) = serde_json::from_str::<AnyNotification>(&message) {
|
||||
let mut notification_handlers = notification_handlers.lock();
|
||||
if let Some(handler) = notification_handlers.get_mut(notification.method.as_str()) {
|
||||
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.
|
||||
/// Continuously reads and logs any error messages from the server.
|
||||
async fn handle_stderr<Stderr>(stderr: Stderr) -> anyhow::Result<()>
|
||||
where
|
||||
Stderr: AsyncRead + Unpin + Send + 'static,
|
||||
{
|
||||
let mut stderr = BufReader::new(stderr);
|
||||
let mut buffer = String::new();
|
||||
|
||||
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;
|
||||
async fn handle_stderr(transport: Arc<dyn Transport>) -> anyhow::Result<()> {
|
||||
while let Some(err) = transport.receive_err().next().await {
|
||||
log::warn!("context server stderr: {}", err.trim());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handles the output to the context server's stdin.
|
||||
/// This function continuously receives messages from the outbound channel,
|
||||
/// writes them to the server's stdin, and manages the lifecycle of response handlers.
|
||||
async fn handle_output<Stdin>(
|
||||
stdin: Stdin,
|
||||
async fn handle_output(
|
||||
transport: Arc<dyn Transport>,
|
||||
outbound_rx: channel::Receiver<String>,
|
||||
output_done_tx: barrier::Sender,
|
||||
response_handlers: Arc<Mutex<Option<HashMap<RequestId, ResponseHandler>>>>,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
Stdin: AsyncWrite + Unpin + Send + 'static,
|
||||
{
|
||||
let mut stdin = BufWriter::new(stdin);
|
||||
) -> anyhow::Result<()> {
|
||||
let _clear_response_handlers = util::defer({
|
||||
let response_handlers = response_handlers.clone();
|
||||
move || {
|
||||
@@ -309,10 +268,7 @@ impl Client {
|
||||
});
|
||||
while let Ok(message) = outbound_rx.recv().await {
|
||||
log::trace!("outgoing message: {}", message);
|
||||
|
||||
stdin.write_all(message.as_bytes()).await?;
|
||||
stdin.write_all(b"\n").await?;
|
||||
stdin.flush().await?;
|
||||
transport.send(message).await?;
|
||||
}
|
||||
drop(output_done_tx);
|
||||
Ok(())
|
||||
@@ -416,14 +372,6 @@ 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 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
|
||||
@@ -4,6 +4,7 @@ mod extension_context_server;
|
||||
pub mod manager;
|
||||
pub mod protocol;
|
||||
mod registry;
|
||||
mod transport;
|
||||
pub mod types;
|
||||
|
||||
use command_palette_hooks::CommandPaletteFilter;
|
||||
|
||||
16
crates/context_server/src/transport.rs
Normal file
16
crates/context_server/src/transport.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
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>>;
|
||||
}
|
||||
140
crates/context_server/src/transport/stdio_transport.rs
Normal file
140
crates/context_server/src/transport/stdio_transport.rs
Normal file
@@ -0,0 +1,140 @@
|
||||
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,6 +38,7 @@ gpui.workspace = true
|
||||
http_client.workspace = true
|
||||
inline_completion.workspace = true
|
||||
language.workspace = true
|
||||
log.workspace = true
|
||||
lsp.workspace = true
|
||||
menu.workspace = true
|
||||
node_runtime.workspace = true
|
||||
@@ -62,7 +63,9 @@ async-std = { version = "1.12.0", features = ["unstable"] }
|
||||
client = { workspace = true, features = ["test-support"] }
|
||||
clock = { workspace = true, features = ["test-support"] }
|
||||
collections = { workspace = true, features = ["test-support"] }
|
||||
ctor.workspace = true
|
||||
editor = { workspace = true, features = ["test-support"] }
|
||||
env_logger.workspace = true
|
||||
fs = { workspace = true, features = ["test-support"] }
|
||||
gpui = { workspace = true, features = ["test-support"] }
|
||||
http_client = { workspace = true, features = ["test-support"] }
|
||||
|
||||
@@ -16,6 +16,7 @@ use gpui::{
|
||||
};
|
||||
use http_client::github::get_release_by_tag_name;
|
||||
use http_client::HttpClient;
|
||||
use language::language_settings::CopilotSettings;
|
||||
use language::{
|
||||
language_settings::{all_language_settings, language_settings, EditPredictionProvider},
|
||||
point_from_lsp, point_to_lsp, Anchor, Bias, Buffer, BufferSnapshot, Language, PointUtf16,
|
||||
@@ -367,13 +368,13 @@ impl Copilot {
|
||||
let server_id = self.server_id;
|
||||
let http = self.http.clone();
|
||||
let node_runtime = self.node_runtime.clone();
|
||||
if all_language_settings(None, cx).edit_predictions.provider
|
||||
== EditPredictionProvider::Copilot
|
||||
{
|
||||
let language_settings = all_language_settings(None, cx);
|
||||
if language_settings.edit_predictions.provider == EditPredictionProvider::Copilot {
|
||||
if matches!(self.server, CopilotServer::Disabled) {
|
||||
let env = self.build_env(&language_settings.edit_predictions.copilot);
|
||||
let start_task = cx
|
||||
.spawn(move |this, cx| {
|
||||
Self::start_language_server(server_id, http, node_runtime, this, cx)
|
||||
Self::start_language_server(server_id, http, node_runtime, env, this, cx)
|
||||
})
|
||||
.shared();
|
||||
self.server = CopilotServer::Starting { task: start_task };
|
||||
@@ -385,6 +386,30 @@ 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"))]
|
||||
pub fn fake(cx: &mut gpui::TestAppContext) -> (Entity<Self>, lsp::FakeLanguageServer) {
|
||||
use lsp::FakeLanguageServer;
|
||||
@@ -422,6 +447,7 @@ impl Copilot {
|
||||
new_server_id: LanguageServerId,
|
||||
http: Arc<dyn HttpClient>,
|
||||
node_runtime: NodeRuntime,
|
||||
env: Option<HashMap<String, String>>,
|
||||
this: WeakEntity<Self>,
|
||||
mut cx: AsyncApp,
|
||||
) {
|
||||
@@ -432,8 +458,7 @@ impl Copilot {
|
||||
let binary = LanguageServerBinary {
|
||||
path: node_path,
|
||||
arguments,
|
||||
// TODO: We could set HTTP_PROXY etc here and fix the copilot issue.
|
||||
env: None,
|
||||
env,
|
||||
};
|
||||
|
||||
let root_path = if cfg!(target_os = "windows") {
|
||||
@@ -450,6 +475,7 @@ impl Copilot {
|
||||
binary,
|
||||
root_path,
|
||||
None,
|
||||
Default::default(),
|
||||
cx.clone(),
|
||||
)?;
|
||||
|
||||
@@ -611,6 +637,8 @@ impl Copilot {
|
||||
}
|
||||
|
||||
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
|
||||
.spawn({
|
||||
let http = self.http.clone();
|
||||
@@ -618,7 +646,7 @@ impl Copilot {
|
||||
let server_id = self.server_id;
|
||||
move |this, cx| async move {
|
||||
clear_copilot_dir().await;
|
||||
Self::start_language_server(server_id, http, node_runtime, this, cx).await
|
||||
Self::start_language_server(server_id, http, node_runtime, env, this, cx).await
|
||||
}
|
||||
})
|
||||
.shared();
|
||||
@@ -1279,3 +1307,11 @@ mod tests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[ctor::ctor]
|
||||
fn init_logger() {
|
||||
if std::env::var("RUST_LOG").is_ok() {
|
||||
env_logger::init();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ pub enum Model {
|
||||
O3Mini,
|
||||
#[serde(alias = "claude-3-5-sonnet", rename = "claude-3.5-sonnet")]
|
||||
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")]
|
||||
Gemini20Flash,
|
||||
}
|
||||
@@ -47,7 +49,11 @@ pub enum Model {
|
||||
impl Model {
|
||||
pub fn uses_streaming(&self) -> bool {
|
||||
match self {
|
||||
Self::Gpt4o | Self::Gpt4 | Self::Gpt3_5Turbo | Self::Claude3_5Sonnet => true,
|
||||
Self::Gpt4o
|
||||
| Self::Gpt4
|
||||
| Self::Gpt3_5Turbo
|
||||
| Self::Claude3_5Sonnet
|
||||
| Self::Claude3_7Sonnet => true,
|
||||
Self::O3Mini | Self::O1 | Self::Gemini20Flash => false,
|
||||
}
|
||||
}
|
||||
@@ -60,6 +66,7 @@ impl Model {
|
||||
"o1" => Ok(Self::O1),
|
||||
"o3-mini" => Ok(Self::O3Mini),
|
||||
"claude-3-5-sonnet" => Ok(Self::Claude3_5Sonnet),
|
||||
"claude-3-7-sonnet" => Ok(Self::Claude3_7Sonnet),
|
||||
"gemini-2.0-flash-001" => Ok(Self::Gemini20Flash),
|
||||
_ => Err(anyhow!("Invalid model id: {}", id)),
|
||||
}
|
||||
@@ -73,6 +80,7 @@ impl Model {
|
||||
Self::O3Mini => "o3-mini",
|
||||
Self::O1 => "o1",
|
||||
Self::Claude3_5Sonnet => "claude-3-5-sonnet",
|
||||
Self::Claude3_7Sonnet => "claude-3-7-sonnet",
|
||||
Self::Gemini20Flash => "gemini-2.0-flash-001",
|
||||
}
|
||||
}
|
||||
@@ -85,6 +93,7 @@ impl Model {
|
||||
Self::O3Mini => "o3-mini",
|
||||
Self::O1 => "o1",
|
||||
Self::Claude3_5Sonnet => "Claude 3.5 Sonnet",
|
||||
Self::Claude3_7Sonnet => "Claude 3.7 Sonnet",
|
||||
Self::Gemini20Flash => "Gemini 2.0 Flash",
|
||||
}
|
||||
}
|
||||
@@ -96,7 +105,8 @@ impl Model {
|
||||
Self::Gpt3_5Turbo => 12_288,
|
||||
Self::O3Mini => 64_000,
|
||||
Self::O1 => 20_000,
|
||||
Self::Claude3_5Sonnet => 128_000,
|
||||
Self::Claude3_5Sonnet => 200_000,
|
||||
Self::Claude3_7Sonnet => 90_000,
|
||||
Model::Gemini20Flash => 128_000,
|
||||
}
|
||||
}
|
||||
|
||||
21
crates/credentials_provider/Cargo.toml
Normal file
21
crates/credentials_provider/Cargo.toml
Normal file
@@ -0,0 +1,21 @@
|
||||
[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
crates/credentials_provider/LICENSE-GPL
Symbolic link
1
crates/credentials_provider/LICENSE-GPL
Symbolic link
@@ -0,0 +1 @@
|
||||
../../LICENSE-GPL
|
||||
199
crates/credentials_provider/src/credentials_provider.rs
Normal file
199
crates/credentials_provider/src/credentials_provider.rs
Normal file
@@ -0,0 +1,199 @@
|
||||
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,7 +24,6 @@ log.workspace = true
|
||||
lsp.workspace = true
|
||||
project.workspace = true
|
||||
rand.workspace = true
|
||||
schemars.workspace = true
|
||||
serde.workspace = true
|
||||
settings.workspace = true
|
||||
theme.workspace = true
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
pub mod items;
|
||||
mod project_diagnostics_settings;
|
||||
mod toolbar_controls;
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -15,17 +14,16 @@ use editor::{
|
||||
Editor, EditorEvent, ExcerptId, ExcerptRange, MultiBuffer, ToOffset,
|
||||
};
|
||||
use gpui::{
|
||||
actions, div, svg, AnyElement, AnyView, App, Context, Entity, EventEmitter, FocusHandle,
|
||||
Focusable, Global, HighlightStyle, InteractiveElement, IntoElement, ParentElement, Render,
|
||||
SharedString, Styled, StyledText, Subscription, Task, WeakEntity, Window,
|
||||
actions, div, svg, AnyElement, AnyView, App, AsyncApp, Context, Entity, EventEmitter,
|
||||
FocusHandle, Focusable, Global, HighlightStyle, InteractiveElement, IntoElement, ParentElement,
|
||||
Render, SharedString, Styled, StyledText, Subscription, Task, WeakEntity, Window,
|
||||
};
|
||||
use language::{
|
||||
Bias, Buffer, BufferRow, BufferSnapshot, Diagnostic, DiagnosticEntry, DiagnosticSeverity,
|
||||
Point, Selection, SelectionGoal, ToTreeSitterPoint,
|
||||
};
|
||||
use lsp::LanguageServerId;
|
||||
use project::{DiagnosticSummary, Project, ProjectPath};
|
||||
use project_diagnostics_settings::ProjectDiagnosticsSettings;
|
||||
use project::{project_settings::ProjectSettings, DiagnosticSummary, Project, ProjectPath};
|
||||
use settings::Settings;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
@@ -52,7 +50,6 @@ struct IncludeWarnings(bool);
|
||||
impl Global for IncludeWarnings {}
|
||||
|
||||
pub fn init(cx: &mut App) {
|
||||
ProjectDiagnosticsSettings::register(cx);
|
||||
cx.observe_new(ProjectDiagnosticsEditor::register).detach();
|
||||
}
|
||||
|
||||
@@ -91,15 +88,46 @@ const DIAGNOSTICS_UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
|
||||
|
||||
impl Render for ProjectDiagnosticsEditor {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let child = if self.path_states.is_empty() {
|
||||
div()
|
||||
let warning_count = if self.include_warnings {
|
||||
self.summary.warning_count
|
||||
} 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")
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.size_full()
|
||||
.child(Label::new("No problems in workspace"))
|
||||
.gap_1()
|
||||
.justify_center()
|
||||
.items_center()
|
||||
.text_center()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.child(Label::new(label).color(Color::Muted))
|
||||
.when(self.summary.warning_count > 0, |this| {
|
||||
let plural_suffix = if self.summary.warning_count > 1 {
|
||||
"s"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
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 {
|
||||
div().size_full().child(self.editor.clone())
|
||||
};
|
||||
@@ -178,6 +206,7 @@ impl ProjectDiagnosticsEditor {
|
||||
cx,
|
||||
);
|
||||
editor.set_vertical_scroll_margin(5, cx);
|
||||
editor.disable_inline_diagnostics();
|
||||
editor
|
||||
});
|
||||
cx.subscribe_in(
|
||||
@@ -249,8 +278,9 @@ impl ProjectDiagnosticsEditor {
|
||||
.log_err()
|
||||
{
|
||||
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(())
|
||||
@@ -287,7 +317,7 @@ impl ProjectDiagnosticsEditor {
|
||||
|
||||
let include_warnings = match cx.try_global::<IncludeWarnings>() {
|
||||
Some(include_warnings) => include_warnings.0,
|
||||
None => ProjectDiagnosticsSettings::get_global(cx).include_warnings,
|
||||
None => ProjectSettings::get_global(cx).diagnostics.include_warnings,
|
||||
};
|
||||
|
||||
let diagnostics = cx.new(|cx| {
|
||||
@@ -350,7 +380,7 @@ impl ProjectDiagnosticsEditor {
|
||||
buffer: Entity<Buffer>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
) -> Task<Result<()>> {
|
||||
let was_empty = self.path_states.is_empty();
|
||||
let snapshot = buffer.read(cx).snapshot();
|
||||
let path_ix = match self
|
||||
@@ -369,7 +399,6 @@ impl ProjectDiagnosticsEditor {
|
||||
ix
|
||||
}
|
||||
};
|
||||
|
||||
let mut prev_excerpt_id = if path_ix > 0 {
|
||||
let prev_path_last_group = &self.path_states[path_ix - 1]
|
||||
.diagnostic_groups
|
||||
@@ -380,7 +409,6 @@ impl ProjectDiagnosticsEditor {
|
||||
ExcerptId::min()
|
||||
};
|
||||
|
||||
let path_state = &mut self.path_states[path_ix];
|
||||
let mut new_group_ixs = Vec::new();
|
||||
let mut blocks_to_add = Vec::new();
|
||||
let mut blocks_to_remove = HashSet::default();
|
||||
@@ -390,8 +418,14 @@ impl ProjectDiagnosticsEditor {
|
||||
} else {
|
||||
DiagnosticSeverity::ERROR
|
||||
};
|
||||
let excerpts_snapshot = self.excerpts.update(cx, |excerpts, cx| {
|
||||
let mut old_groups = mem::take(&mut path_state.diagnostic_groups)
|
||||
let excerpts = self.excerpts.clone().downgrade();
|
||||
let context = self.context;
|
||||
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()
|
||||
.enumerate()
|
||||
.peekable();
|
||||
@@ -453,9 +487,19 @@ impl ProjectDiagnosticsEditor {
|
||||
let mut is_first_excerpt_for_group = true;
|
||||
for (ix, entry) in group.entries.iter().map(Some).chain([None]).enumerate() {
|
||||
let resolved_entry = entry.map(|e| e.resolve::<Point>(&snapshot));
|
||||
let expanded_range = resolved_entry.as_ref().map(|entry| {
|
||||
context_range_for_entry(entry, self.context, &snapshot, cx)
|
||||
});
|
||||
let expanded_range = if let Some(entry) = &resolved_entry {
|
||||
Some(
|
||||
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(expanded_range) = expanded_range.clone() {
|
||||
// If the entries are overlapping or next to each-other, merge them into one excerpt.
|
||||
@@ -465,18 +509,20 @@ impl ProjectDiagnosticsEditor {
|
||||
}
|
||||
}
|
||||
|
||||
let excerpt_id = excerpts
|
||||
.insert_excerpts_after(
|
||||
prev_excerpt_id,
|
||||
buffer.clone(),
|
||||
[ExcerptRange {
|
||||
context: context_range.clone(),
|
||||
primary: Some(range.clone()),
|
||||
}],
|
||||
cx,
|
||||
)
|
||||
.pop()
|
||||
.unwrap();
|
||||
let excerpt_id = excerpts.update(&mut cx, |excerpts, cx| {
|
||||
excerpts
|
||||
.insert_excerpts_after(
|
||||
prev_excerpt_id,
|
||||
buffer.clone(),
|
||||
[ExcerptRange {
|
||||
context: context_range.clone(),
|
||||
primary: Some(range.clone()),
|
||||
}],
|
||||
cx,
|
||||
)
|
||||
.pop()
|
||||
.unwrap()
|
||||
})?;
|
||||
|
||||
prev_excerpt_id = excerpt_id;
|
||||
first_excerpt_id.get_or_insert(prev_excerpt_id);
|
||||
@@ -533,128 +579,156 @@ impl ProjectDiagnosticsEditor {
|
||||
}
|
||||
}
|
||||
|
||||
new_group_ixs.push(path_state.diagnostic_groups.len());
|
||||
path_state.diagnostic_groups.push(group_state);
|
||||
this.update(&mut cx, |this, _| {
|
||||
new_group_ixs.push(this.path_states[path_ix].diagnostic_groups.len());
|
||||
this.path_states[path_ix]
|
||||
.diagnostic_groups
|
||||
.push(group_state);
|
||||
})?;
|
||||
} else if let Some((_, group_state)) = to_remove {
|
||||
excerpts.remove_excerpts(group_state.excerpts.iter().copied(), cx);
|
||||
excerpts.update(&mut cx, |excerpts, cx| {
|
||||
excerpts.remove_excerpts(group_state.excerpts.iter().copied(), cx)
|
||||
})?;
|
||||
blocks_to_remove.extend(group_state.blocks.iter().copied());
|
||||
} else if let Some((_, group_state)) = to_keep {
|
||||
prev_excerpt_id = *group_state.excerpts.last().unwrap();
|
||||
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)
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
excerpts.snapshot(cx)
|
||||
});
|
||||
let excerpts_snapshot =
|
||||
excerpts.update(&mut cx, |excerpts, cx| excerpts.snapshot(cx))?;
|
||||
editor.update(&mut cx, |editor, cx| {
|
||||
editor.remove_blocks(blocks_to_remove, None, cx);
|
||||
let block_ids = editor.insert_blocks(
|
||||
blocks_to_add.into_iter().flat_map(|block| {
|
||||
let placement = match block.placement {
|
||||
BlockPlacement::Above((excerpt_id, text_anchor)) => {
|
||||
BlockPlacement::Above(
|
||||
excerpts_snapshot.anchor_in_excerpt(excerpt_id, text_anchor)?,
|
||||
)
|
||||
}
|
||||
BlockPlacement::Below((excerpt_id, text_anchor)) => {
|
||||
BlockPlacement::Below(
|
||||
excerpts_snapshot.anchor_in_excerpt(excerpt_id, text_anchor)?,
|
||||
)
|
||||
}
|
||||
BlockPlacement::Replace(_) => {
|
||||
unreachable!(
|
||||
"no Replace block should have been pushed to blocks_to_add"
|
||||
)
|
||||
}
|
||||
};
|
||||
Some(BlockProperties {
|
||||
placement,
|
||||
height: block.height,
|
||||
style: block.style,
|
||||
render: block.render,
|
||||
priority: 0,
|
||||
})
|
||||
}),
|
||||
Some(Autoscroll::fit()),
|
||||
cx,
|
||||
);
|
||||
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
editor.remove_blocks(blocks_to_remove, None, cx);
|
||||
let block_ids = editor.insert_blocks(
|
||||
blocks_to_add.into_iter().flat_map(|block| {
|
||||
let placement = match block.placement {
|
||||
BlockPlacement::Above((excerpt_id, text_anchor)) => BlockPlacement::Above(
|
||||
excerpts_snapshot.anchor_in_excerpt(excerpt_id, text_anchor)?,
|
||||
),
|
||||
BlockPlacement::Below((excerpt_id, text_anchor)) => BlockPlacement::Below(
|
||||
excerpts_snapshot.anchor_in_excerpt(excerpt_id, text_anchor)?,
|
||||
),
|
||||
BlockPlacement::Replace(_) => {
|
||||
unreachable!(
|
||||
"no Replace block should have been pushed to blocks_to_add"
|
||||
)
|
||||
}
|
||||
};
|
||||
Some(BlockProperties {
|
||||
placement,
|
||||
height: block.height,
|
||||
style: block.style,
|
||||
render: block.render,
|
||||
priority: 0,
|
||||
})
|
||||
}),
|
||||
Some(Autoscroll::fit()),
|
||||
cx,
|
||||
);
|
||||
let mut block_ids = block_ids.into_iter();
|
||||
this.update(cx, |this, _| {
|
||||
for ix in new_group_ixs {
|
||||
let group_state = &mut this.path_states[path_ix].diagnostic_groups[ix];
|
||||
group_state.blocks =
|
||||
block_ids.by_ref().take(group_state.block_count).collect();
|
||||
}
|
||||
})?;
|
||||
Result::<(), anyhow::Error>::Ok(())
|
||||
})??;
|
||||
|
||||
let mut block_ids = block_ids.into_iter();
|
||||
for ix in new_group_ixs {
|
||||
let group_state = &mut path_state.diagnostic_groups[ix];
|
||||
group_state.blocks = block_ids.by_ref().take(group_state.block_count).collect();
|
||||
}
|
||||
});
|
||||
this.update_in(&mut cx, |this, window, cx| {
|
||||
if this.path_states[path_ix].diagnostic_groups.is_empty() {
|
||||
this.path_states.remove(path_ix);
|
||||
}
|
||||
|
||||
if path_state.diagnostic_groups.is_empty() {
|
||||
self.path_states.remove(path_ix);
|
||||
}
|
||||
this.editor.update(cx, |editor, cx| {
|
||||
let groups;
|
||||
let mut selections;
|
||||
let new_excerpt_ids_by_selection_id;
|
||||
if was_empty {
|
||||
groups = this.path_states.first()?.diagnostic_groups.as_slice();
|
||||
new_excerpt_ids_by_selection_id =
|
||||
[(0, ExcerptId::min())].into_iter().collect();
|
||||
selections = vec![Selection {
|
||||
id: 0,
|
||||
start: 0,
|
||||
end: 0,
|
||||
reversed: false,
|
||||
goal: SelectionGoal::None,
|
||||
}];
|
||||
} else {
|
||||
groups = this.path_states.get(path_ix)?.diagnostic_groups.as_slice();
|
||||
new_excerpt_ids_by_selection_id =
|
||||
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
|
||||
s.refresh()
|
||||
});
|
||||
selections = editor.selections.all::<usize>(cx);
|
||||
}
|
||||
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
let groups;
|
||||
let mut selections;
|
||||
let new_excerpt_ids_by_selection_id;
|
||||
if was_empty {
|
||||
groups = self.path_states.first()?.diagnostic_groups.as_slice();
|
||||
new_excerpt_ids_by_selection_id = [(0, ExcerptId::min())].into_iter().collect();
|
||||
selections = vec![Selection {
|
||||
id: 0,
|
||||
start: 0,
|
||||
end: 0,
|
||||
reversed: false,
|
||||
goal: SelectionGoal::None,
|
||||
}];
|
||||
} else {
|
||||
groups = self.path_states.get(path_ix)?.diagnostic_groups.as_slice();
|
||||
new_excerpt_ids_by_selection_id =
|
||||
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| s.refresh());
|
||||
selections = editor.selections.all::<usize>(cx);
|
||||
}
|
||||
|
||||
// If any selection has lost its position, move it to start of the next primary diagnostic.
|
||||
let snapshot = editor.snapshot(window, cx);
|
||||
for selection in &mut selections {
|
||||
if let Some(new_excerpt_id) = new_excerpt_ids_by_selection_id.get(&selection.id) {
|
||||
let group_ix = match groups.binary_search_by(|probe| {
|
||||
probe
|
||||
.excerpts
|
||||
.last()
|
||||
.unwrap()
|
||||
.cmp(new_excerpt_id, &snapshot.buffer_snapshot)
|
||||
}) {
|
||||
Ok(ix) | Err(ix) => ix,
|
||||
};
|
||||
if let Some(group) = groups.get(group_ix) {
|
||||
if let Some(offset) = excerpts_snapshot
|
||||
.anchor_in_excerpt(
|
||||
group.excerpts[group.primary_excerpt_ix],
|
||||
group.primary_diagnostic.range.start,
|
||||
)
|
||||
.map(|anchor| anchor.to_offset(&excerpts_snapshot))
|
||||
// If any selection has lost its position, move it to start of the next primary diagnostic.
|
||||
let snapshot = editor.snapshot(window, cx);
|
||||
for selection in &mut selections {
|
||||
if let Some(new_excerpt_id) =
|
||||
new_excerpt_ids_by_selection_id.get(&selection.id)
|
||||
{
|
||||
selection.start = offset;
|
||||
selection.end = offset;
|
||||
let group_ix = match groups.binary_search_by(|probe| {
|
||||
probe
|
||||
.excerpts
|
||||
.last()
|
||||
.unwrap()
|
||||
.cmp(new_excerpt_id, &snapshot.buffer_snapshot)
|
||||
}) {
|
||||
Ok(ix) | Err(ix) => ix,
|
||||
};
|
||||
if let Some(group) = groups.get(group_ix) {
|
||||
if let Some(offset) = excerpts_snapshot
|
||||
.anchor_in_excerpt(
|
||||
group.excerpts[group.primary_excerpt_ix],
|
||||
group.primary_diagnostic.range.start,
|
||||
)
|
||||
.map(|anchor| anchor.to_offset(&excerpts_snapshot))
|
||||
{
|
||||
selection.start = offset;
|
||||
selection.end = offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
editor.change_selections(None, window, cx, |s| {
|
||||
s.select(selections);
|
||||
});
|
||||
Some(())
|
||||
});
|
||||
})?;
|
||||
|
||||
this.update_in(&mut cx, |this, window, cx| {
|
||||
if this.path_states.is_empty() {
|
||||
if this.editor.focus_handle(cx).is_focused(window) {
|
||||
window.focus(&this.focus_handle);
|
||||
}
|
||||
} else if this.focus_handle.is_focused(window) {
|
||||
let focus_handle = this.editor.focus_handle(cx);
|
||||
window.focus(&focus_handle);
|
||||
}
|
||||
}
|
||||
editor.change_selections(None, window, cx, |s| {
|
||||
s.select(selections);
|
||||
});
|
||||
Some(())
|
||||
});
|
||||
|
||||
if self.path_states.is_empty() {
|
||||
if self.editor.focus_handle(cx).is_focused(window) {
|
||||
window.focus(&self.focus_handle);
|
||||
}
|
||||
} else if self.focus_handle.is_focused(window) {
|
||||
let focus_handle = self.editor.focus_handle(cx);
|
||||
window.focus(&focus_handle);
|
||||
}
|
||||
#[cfg(test)]
|
||||
this.check_invariants(cx);
|
||||
|
||||
#[cfg(test)]
|
||||
self.check_invariants(cx);
|
||||
|
||||
cx.notify();
|
||||
cx.notify();
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -978,29 +1052,30 @@ fn compare_diagnostics(
|
||||
const DIAGNOSTIC_EXPANSION_ROW_LIMIT: u32 = 32;
|
||||
|
||||
fn context_range_for_entry(
|
||||
entry: &DiagnosticEntry<Point>,
|
||||
range: Range<Point>,
|
||||
context: u32,
|
||||
snapshot: &BufferSnapshot,
|
||||
cx: &App,
|
||||
) -> Range<Point> {
|
||||
if let Some(rows) = heuristic_syntactic_expand(
|
||||
entry.range.clone(),
|
||||
DIAGNOSTIC_EXPANSION_ROW_LIMIT,
|
||||
snapshot,
|
||||
cx,
|
||||
) {
|
||||
return Range {
|
||||
start: Point::new(*rows.start(), 0),
|
||||
end: snapshot.clip_point(Point::new(*rows.end(), u32::MAX), Bias::Left),
|
||||
};
|
||||
}
|
||||
Range {
|
||||
start: Point::new(entry.range.start.row.saturating_sub(context), 0),
|
||||
end: snapshot.clip_point(
|
||||
Point::new(entry.range.end.row + context, u32::MAX),
|
||||
Bias::Left,
|
||||
),
|
||||
}
|
||||
snapshot: BufferSnapshot,
|
||||
cx: AsyncApp,
|
||||
) -> Task<Range<Point>> {
|
||||
cx.spawn(move |cx| async move {
|
||||
if let Some(rows) = heuristic_syntactic_expand(
|
||||
range.clone(),
|
||||
DIAGNOSTIC_EXPANSION_ROW_LIMIT,
|
||||
snapshot.clone(),
|
||||
cx,
|
||||
)
|
||||
.await
|
||||
{
|
||||
return Range {
|
||||
start: Point::new(*rows.start(), 0),
|
||||
end: snapshot.clip_point(Point::new(*rows.end(), u32::MAX), Bias::Left),
|
||||
};
|
||||
}
|
||||
Range {
|
||||
start: Point::new(range.start.row.saturating_sub(context), 0),
|
||||
end: snapshot.clip_point(Point::new(range.end.row + context, u32::MAX), Bias::Left),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Expands the input range using syntax information from TreeSitter. This expansion will be limited
|
||||
@@ -1008,11 +1083,11 @@ fn context_range_for_entry(
|
||||
///
|
||||
/// 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.
|
||||
fn heuristic_syntactic_expand<'a>(
|
||||
async fn heuristic_syntactic_expand(
|
||||
input_range: Range<Point>,
|
||||
max_row_count: u32,
|
||||
snapshot: &'a BufferSnapshot,
|
||||
cx: &'a App,
|
||||
snapshot: BufferSnapshot,
|
||||
cx: AsyncApp,
|
||||
) -> Option<RangeInclusive<BufferRow>> {
|
||||
let input_row_count = input_range.end.row - input_range.start.row;
|
||||
if input_row_count > max_row_count {
|
||||
@@ -1042,49 +1117,62 @@ fn heuristic_syntactic_expand<'a>(
|
||||
}
|
||||
|
||||
let mut node = snapshot.syntax_ancestor(input_range.clone())?;
|
||||
|
||||
loop {
|
||||
let node_start = Point::from_ts_point(node.start_position());
|
||||
let node_end = Point::from_ts_point(node.end_position());
|
||||
let node_range = node_start..node_end;
|
||||
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
|
||||
// of node children which contains the query range. For example, this allows just returning
|
||||
// the header of a declaration rather than the entire declaration.
|
||||
if row_count > max_row_count || outline_range == Some(node_range.clone()) {
|
||||
let mut cursor = node.walk();
|
||||
let mut included_child_start = None;
|
||||
let mut included_child_end = None;
|
||||
let mut previous_end = node_start;
|
||||
if cursor.goto_first_child() {
|
||||
loop {
|
||||
let child_node = cursor.node();
|
||||
let child_range = previous_end..Point::from_ts_point(child_node.end_position());
|
||||
if included_child_start.is_none() && child_range.contains(&input_range.start) {
|
||||
included_child_start = Some(child_range.start);
|
||||
}
|
||||
if child_range.contains(&input_range.end) {
|
||||
included_child_end = Some(child_range.end);
|
||||
}
|
||||
previous_end = child_range.end;
|
||||
if !cursor.goto_next_sibling() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let end = included_child_end.unwrap_or(node_range.end);
|
||||
if let Some(start) = included_child_start {
|
||||
let row_count = end.row - start.row;
|
||||
if row_count < max_row_count {
|
||||
*ancestor_range = Some(Some(RangeInclusive::new(start.row, end.row)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// the header of a declaration rather than the entire declaration.
|
||||
if row_count > max_row_count || outline_range == Some(node_range.clone()) {
|
||||
let mut cursor = node.walk();
|
||||
let mut included_child_start = None;
|
||||
let mut included_child_end = None;
|
||||
let mut previous_end = node_start;
|
||||
if cursor.goto_first_child() {
|
||||
loop {
|
||||
let child_node = cursor.node();
|
||||
let child_range = previous_end..Point::from_ts_point(child_node.end_position());
|
||||
if included_child_start.is_none() && child_range.contains(&input_range.start) {
|
||||
included_child_start = Some(child_range.start);
|
||||
log::info!(
|
||||
"Expanding to ancestor started on {} node exceeding row limit of {max_row_count}.",
|
||||
node.grammar_name()
|
||||
);
|
||||
*ancestor_range = Some(None);
|
||||
}
|
||||
if child_range.contains(&input_range.end) {
|
||||
included_child_end = Some(child_range.end);
|
||||
}
|
||||
previous_end = child_range.end;
|
||||
if !cursor.goto_next_sibling() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let end = included_child_end.unwrap_or(node_range.end);
|
||||
if let Some(start) = included_child_start {
|
||||
let row_count = end.row - start.row;
|
||||
if row_count < max_row_count {
|
||||
return Some(RangeInclusive::new(start.row, end.row));
|
||||
}
|
||||
}
|
||||
|
||||
log::info!(
|
||||
"Expanding to ancestor started on {} node exceeding row limit of {max_row_count}.",
|
||||
node.grammar_name()
|
||||
);
|
||||
return None;
|
||||
})
|
||||
}});
|
||||
reached_outline_node.await;
|
||||
if let Some(node) = ancestor_range {
|
||||
return node;
|
||||
}
|
||||
|
||||
let node_name = node.grammar_name();
|
||||
@@ -1093,7 +1181,9 @@ fn heuristic_syntactic_expand<'a>(
|
||||
return Some(node_row_range);
|
||||
} else if node_name.ends_with("statement") || node_name.ends_with("declaration") {
|
||||
// Expand to the nearest dedent or blank line for statements and declarations.
|
||||
let tab_size = snapshot.settings_at(node_range.start, cx).tab_size.get();
|
||||
let tab_size = cx
|
||||
.update(|cx| snapshot.settings_at(node_range.start, cx).tab_size.get())
|
||||
.ok()?;
|
||||
let indent_level = snapshot
|
||||
.line_indent_for_row(node_range.start.row)
|
||||
.len(tab_size);
|
||||
@@ -1101,7 +1191,9 @@ fn heuristic_syntactic_expand<'a>(
|
||||
let Some(start_row) = (node_range.start.row.saturating_sub(rows_remaining)
|
||||
..node_range.start.row)
|
||||
.rev()
|
||||
.find(|row| is_line_blank_or_indented_less(indent_level, *row, tab_size, snapshot))
|
||||
.find(|row| {
|
||||
is_line_blank_or_indented_less(indent_level, *row, tab_size, &snapshot.clone())
|
||||
})
|
||||
else {
|
||||
return Some(node_row_range);
|
||||
};
|
||||
@@ -1111,7 +1203,9 @@ fn heuristic_syntactic_expand<'a>(
|
||||
node_range.end.row + rows_remaining + 1,
|
||||
snapshot.row_count(),
|
||||
))
|
||||
.find(|row| is_line_blank_or_indented_less(indent_level, *row, tab_size, snapshot))
|
||||
.find(|row| {
|
||||
is_line_blank_or_indented_less(indent_level, *row, tab_size, &snapshot.clone())
|
||||
})
|
||||
else {
|
||||
return Some(node_row_range);
|
||||
};
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
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,6 +22,8 @@ pub struct SelectPrevious {
|
||||
pub struct MoveToBeginningOfLine {
|
||||
#[serde(default = "default_true")]
|
||||
pub stop_at_soft_wraps: bool,
|
||||
#[serde(default)]
|
||||
pub stop_at_indent: bool,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
|
||||
@@ -29,6 +31,8 @@ pub struct MoveToBeginningOfLine {
|
||||
pub struct SelectToBeginningOfLine {
|
||||
#[serde(default)]
|
||||
pub(super) stop_at_soft_wraps: bool,
|
||||
#[serde(default)]
|
||||
pub stop_at_indent: bool,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
|
||||
@@ -253,7 +257,7 @@ gpui::actions!(
|
||||
ContextMenuFirst,
|
||||
ContextMenuLast,
|
||||
ContextMenuNext,
|
||||
ContextMenuPrev,
|
||||
ContextMenuPrevious,
|
||||
ConvertToKebabCase,
|
||||
ConvertToLowerCamelCase,
|
||||
ConvertToLowerCase,
|
||||
@@ -280,7 +284,6 @@ gpui::actions!(
|
||||
DuplicateLineDown,
|
||||
DuplicateLineUp,
|
||||
DuplicateSelection,
|
||||
ExpandAllHunkDiffs,
|
||||
ExpandMacroRecursively,
|
||||
FindAllReferences,
|
||||
Fold,
|
||||
@@ -300,8 +303,8 @@ gpui::actions!(
|
||||
GoToHunk,
|
||||
GoToImplementation,
|
||||
GoToImplementationSplit,
|
||||
GoToPrevDiagnostic,
|
||||
GoToPrevHunk,
|
||||
GoToPreviousDiagnostic,
|
||||
GoToPreviousHunk,
|
||||
GoToTypeDefinition,
|
||||
GoToTypeDefinitionSplit,
|
||||
HalfPageDown,
|
||||
@@ -329,6 +332,8 @@ gpui::actions!(
|
||||
MoveToPreviousSubwordStart,
|
||||
MoveToPreviousWordStart,
|
||||
MoveToStartOfParagraph,
|
||||
MoveToStartOfExcerpt,
|
||||
MoveToEndOfExcerpt,
|
||||
MoveUp,
|
||||
Newline,
|
||||
NewlineAbove,
|
||||
@@ -364,6 +369,8 @@ gpui::actions!(
|
||||
ScrollCursorTop,
|
||||
SelectAll,
|
||||
SelectAllMatches,
|
||||
SelectToStartOfExcerpt,
|
||||
SelectToEndOfExcerpt,
|
||||
SelectDown,
|
||||
SelectEnclosingSymbol,
|
||||
SelectLargerSyntaxNode,
|
||||
@@ -391,12 +398,13 @@ gpui::actions!(
|
||||
SplitSelectionIntoLines,
|
||||
SwitchSourceHeader,
|
||||
Tab,
|
||||
TabPrev,
|
||||
Backtab,
|
||||
ToggleAutoSignatureHelp,
|
||||
ToggleGitBlame,
|
||||
ToggleGitBlameInline,
|
||||
ToggleIndentGuides,
|
||||
ToggleInlayHints,
|
||||
ToggleInlineDiagnostics,
|
||||
ToggleEditPrediction,
|
||||
ToggleLineNumbers,
|
||||
SwapSelectionEnds,
|
||||
@@ -420,3 +428,4 @@ action_as!(go_to_line, ToggleGoToLine as Toggle);
|
||||
|
||||
action_with_deprecated_aliases!(editor, OpenSelectedFilename, ["editor::OpenFile"]);
|
||||
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()
|
||||
.and_then(|details| details.pull_request.clone());
|
||||
|
||||
let ui_font_size = ThemeSettings::get_global(cx).ui_font_size;
|
||||
let ui_font_size = ThemeSettings::get_global(cx).ui_font_size(cx);
|
||||
let message_max_height = window.line_height() * 12 + (ui_font_size / 0.4);
|
||||
|
||||
tooltip_container(window, cx, move |this, _, cx| {
|
||||
|
||||
@@ -1911,6 +1911,67 @@ 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]
|
||||
async fn test_chunks(cx: &mut gpui::TestAppContext) {
|
||||
let text = r#"
|
||||
|
||||
@@ -638,10 +638,13 @@ impl BlockMap {
|
||||
self.custom_blocks[start_block_ix..end_block_ix]
|
||||
.iter()
|
||||
.filter_map(|block| {
|
||||
Some((
|
||||
block.placement.to_wrap_row(wrap_snapshot)?,
|
||||
Block::Custom(block.clone()),
|
||||
))
|
||||
let placement = block.placement.to_wrap_row(wrap_snapshot)?;
|
||||
if let BlockPlacement::Above(row) = placement {
|
||||
if row < new_start {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some((placement, Block::Custom(block.clone())))
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ use super::{
|
||||
inlay_map::{InlayBufferRows, InlayChunks, InlayEdit, InlayOffset, InlayPoint, InlaySnapshot},
|
||||
Highlights,
|
||||
};
|
||||
use gpui::{AnyElement, App, ElementId, Window};
|
||||
use gpui::{AnyElement, App, ElementId};
|
||||
use language::{Chunk, ChunkRenderer, Edit, Point, TextSummary};
|
||||
use multi_buffer::{
|
||||
Anchor, AnchorRangeExt, MultiBufferRow, MultiBufferSnapshot, RowInfo, ToOffset,
|
||||
@@ -21,8 +21,7 @@ use util::post_inc;
|
||||
#[derive(Clone)]
|
||||
pub struct FoldPlaceholder {
|
||||
/// Creates an element to represent this fold's placeholder.
|
||||
pub render:
|
||||
Arc<dyn Send + Sync + Fn(FoldId, Range<Anchor>, &mut Window, &mut App) -> AnyElement>,
|
||||
pub render: Arc<dyn Send + Sync + Fn(FoldId, Range<Anchor>, &mut App) -> AnyElement>,
|
||||
/// If true, the element is constrained to the shaped width of an ellipsis.
|
||||
pub constrain_width: bool,
|
||||
/// If true, merges the fold with an adjacent one.
|
||||
@@ -34,7 +33,7 @@ pub struct FoldPlaceholder {
|
||||
impl Default for FoldPlaceholder {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
render: Arc::new(|_, _, _, _| gpui::Empty.into_any_element()),
|
||||
render: Arc::new(|_, _, _| gpui::Empty.into_any_element()),
|
||||
constrain_width: true,
|
||||
merge_adjacent: true,
|
||||
type_tag: None,
|
||||
@@ -46,7 +45,7 @@ impl FoldPlaceholder {
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn test() -> Self {
|
||||
Self {
|
||||
render: Arc::new(|_id, _range, _window, _cx| gpui::Empty.into_any_element()),
|
||||
render: Arc::new(|_id, _range, _cx| gpui::Empty.into_any_element()),
|
||||
constrain_width: true,
|
||||
merge_adjacent: true,
|
||||
type_tag: None,
|
||||
@@ -486,7 +485,6 @@ impl FoldMap {
|
||||
(fold.placeholder.render)(
|
||||
fold_id,
|
||||
fold.range.0.clone(),
|
||||
cx.window,
|
||||
cx.context,
|
||||
)
|
||||
}),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -125,8 +125,7 @@ impl EditableSettingControl for BufferFontSizeControl {
|
||||
}
|
||||
|
||||
fn read(cx: &App) -> Self::Value {
|
||||
let settings = ThemeSettings::get_global(cx);
|
||||
settings.buffer_font_size
|
||||
ThemeSettings::get_global(cx).buffer_font_size(cx)
|
||||
}
|
||||
|
||||
fn apply(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,7 @@ use itertools::Itertools;
|
||||
use language::{DiagnosticEntry, Language, LanguageRegistry};
|
||||
use lsp::DiagnosticSeverity;
|
||||
use markdown::{Markdown, MarkdownStyle};
|
||||
use multi_buffer::ToOffset;
|
||||
use multi_buffer::{MultiOrSingleBufferOffsetRange, ToOffset};
|
||||
use project::{HoverBlock, HoverBlockKind, InlayHintLabelPart};
|
||||
use settings::Settings;
|
||||
use std::{borrow::Cow, cell::RefCell};
|
||||
@@ -339,7 +339,7 @@ fn show_hover(
|
||||
base_text_style.refine(&TextStyleRefinement {
|
||||
font_family: Some(settings.ui_font.family.clone()),
|
||||
font_fallbacks: settings.ui_font.fallbacks.clone(),
|
||||
font_size: Some(settings.ui_font_size.into()),
|
||||
font_size: Some(settings.ui_font_size(cx).into()),
|
||||
color: Some(cx.theme().colors().editor_foreground),
|
||||
background_color: Some(gpui::transparent_black()),
|
||||
|
||||
@@ -447,11 +447,13 @@ fn show_hover(
|
||||
})
|
||||
.or_else(|| {
|
||||
let snapshot = &snapshot.buffer_snapshot;
|
||||
let offset_range = snapshot.syntax_ancestor(anchor..anchor)?.1;
|
||||
Some(
|
||||
snapshot.anchor_before(offset_range.start)
|
||||
..snapshot.anchor_after(offset_range.end),
|
||||
)
|
||||
match snapshot.syntax_ancestor(anchor..anchor)?.1 {
|
||||
MultiOrSingleBufferOffsetRange::Multi(range) => Some(
|
||||
snapshot.anchor_before(range.start)
|
||||
..snapshot.anchor_after(range.end),
|
||||
),
|
||||
MultiOrSingleBufferOffsetRange::Single(_) => None,
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| anchor..anchor);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ use gpui::{Pixels, WindowTextSystem};
|
||||
use language::Point;
|
||||
use multi_buffer::{MultiBufferRow, MultiBufferSnapshot};
|
||||
use serde::Deserialize;
|
||||
use workspace::searchable::Direction;
|
||||
|
||||
use std::{ops::Range, sync::Arc};
|
||||
|
||||
@@ -213,6 +214,7 @@ pub fn indented_line_beginning(
|
||||
map: &DisplaySnapshot,
|
||||
display_point: DisplayPoint,
|
||||
stop_at_soft_boundaries: bool,
|
||||
stop_at_indent: bool,
|
||||
) -> DisplayPoint {
|
||||
let point = display_point.to_point(map);
|
||||
let soft_line_start = map.clip_point(DisplayPoint::new(display_point.row(), 0), Bias::Right);
|
||||
@@ -228,7 +230,7 @@ pub fn indented_line_beginning(
|
||||
if stop_at_soft_boundaries && soft_line_start > indent_start && display_point != soft_line_start
|
||||
{
|
||||
soft_line_start
|
||||
} else if stop_at_soft_boundaries && display_point != indent_start {
|
||||
} else if stop_at_indent && display_point != indent_start {
|
||||
indent_start
|
||||
} else {
|
||||
line_start
|
||||
@@ -403,6 +405,69 @@ pub fn end_of_paragraph(
|
||||
map.max_point()
|
||||
}
|
||||
|
||||
pub fn start_of_excerpt(
|
||||
map: &DisplaySnapshot,
|
||||
display_point: DisplayPoint,
|
||||
direction: Direction,
|
||||
) -> DisplayPoint {
|
||||
let point = map.display_point_to_point(display_point, Bias::Left);
|
||||
let Some(excerpt) = map.buffer_snapshot.excerpt_containing(point..point) else {
|
||||
return display_point;
|
||||
};
|
||||
match direction {
|
||||
Direction::Prev => {
|
||||
let mut start = excerpt.start_anchor().to_display_point(&map);
|
||||
if start >= display_point && start.row() > DisplayRow(0) {
|
||||
let Some(excerpt) = map.buffer_snapshot.excerpt_before(excerpt.id()) else {
|
||||
return display_point;
|
||||
};
|
||||
start = excerpt.start_anchor().to_display_point(&map);
|
||||
}
|
||||
start
|
||||
}
|
||||
Direction::Next => {
|
||||
let mut end = excerpt.end_anchor().to_display_point(&map);
|
||||
*end.row_mut() += 1;
|
||||
map.clip_point(end, Bias::Right)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn end_of_excerpt(
|
||||
map: &DisplaySnapshot,
|
||||
display_point: DisplayPoint,
|
||||
direction: Direction,
|
||||
) -> DisplayPoint {
|
||||
let point = map.display_point_to_point(display_point, Bias::Left);
|
||||
let Some(excerpt) = map.buffer_snapshot.excerpt_containing(point..point) else {
|
||||
return display_point;
|
||||
};
|
||||
match direction {
|
||||
Direction::Prev => {
|
||||
let mut start = excerpt.start_anchor().to_display_point(&map);
|
||||
if start.row() > DisplayRow(0) {
|
||||
*start.row_mut() -= 1;
|
||||
}
|
||||
map.clip_point(start, Bias::Left)
|
||||
}
|
||||
Direction::Next => {
|
||||
let mut end = excerpt.end_anchor().to_display_point(&map);
|
||||
*end.column_mut() = 0;
|
||||
if end <= display_point {
|
||||
*end.row_mut() += 1;
|
||||
let point_end = map.display_point_to_point(end, Bias::Right);
|
||||
let Some(excerpt) = map.buffer_snapshot.excerpt_containing(point_end..point_end)
|
||||
else {
|
||||
return display_point;
|
||||
};
|
||||
end = excerpt.end_anchor().to_display_point(&map);
|
||||
*end.column_mut() = 0;
|
||||
}
|
||||
end
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Scans for a boundary preceding the given start point `from` until a boundary is found,
|
||||
/// indicated by the given predicate returning true.
|
||||
/// The predicate is called with the character to the left and right of the candidate boundary location.
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::{fs, path::Path};
|
||||
|
||||
use anyhow::Context as _;
|
||||
use gpui::{App, AppContext as _, Context, Entity, Window};
|
||||
use language::Language;
|
||||
use language::{Capability, Language};
|
||||
use multi_buffer::MultiBuffer;
|
||||
use project::lsp_ext_command::ExpandMacro;
|
||||
use text::ToPointUtf16;
|
||||
@@ -80,14 +80,17 @@ pub fn expand_macro_recursively(
|
||||
.await?;
|
||||
workspace.update_in(&mut cx, |workspace, window, cx| {
|
||||
buffer.update(cx, |buffer, cx| {
|
||||
buffer.edit([(0..0, macro_expansion.expansion)], None, cx);
|
||||
buffer.set_language(Some(rust_language), cx)
|
||||
buffer.set_text(macro_expansion.expansion, cx);
|
||||
buffer.set_language(Some(rust_language), cx);
|
||||
buffer.set_capability(Capability::ReadOnly, cx);
|
||||
});
|
||||
let multibuffer =
|
||||
cx.new(|cx| MultiBuffer::singleton(buffer, cx).with_title(macro_expansion.name));
|
||||
workspace.add_item_to_active_pane(
|
||||
Box::new(cx.new(|cx| {
|
||||
Editor::for_multibuffer(multibuffer, Some(project), true, window, cx)
|
||||
let mut editor = Editor::for_multibuffer(multibuffer, None, false, window, cx);
|
||||
editor.set_read_only(true);
|
||||
editor
|
||||
})),
|
||||
None,
|
||||
true,
|
||||
|
||||
@@ -3,7 +3,6 @@ pub(crate) mod autoscroll;
|
||||
pub(crate) mod scroll_amount;
|
||||
|
||||
use crate::editor_settings::{ScrollBeyondLastLine, ScrollbarAxes};
|
||||
use crate::EditPredictionPreview;
|
||||
use crate::{
|
||||
display_map::{DisplaySnapshot, ToDisplayPoint},
|
||||
hover_popover::hide_hover,
|
||||
@@ -496,14 +495,8 @@ impl Editor {
|
||||
hide_hover(self, cx);
|
||||
let workspace_id = self.workspace.as_ref().and_then(|workspace| workspace.1);
|
||||
|
||||
if let EditPredictionPreview::Active {
|
||||
previous_scroll_position,
|
||||
} = &mut self.edit_prediction_preview
|
||||
{
|
||||
if !autoscroll {
|
||||
previous_scroll_position.take();
|
||||
}
|
||||
}
|
||||
self.edit_prediction_preview
|
||||
.set_previous_scroll_position(None);
|
||||
|
||||
self.scroll_manager.set_scroll_position(
|
||||
scroll_position,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user