Compare commits
130 Commits
cole/git-p
...
edit-predi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6d45ab41d | ||
|
|
af6548c745 | ||
|
|
6f467281e0 | ||
|
|
be4c3cfbd2 | ||
|
|
0ad2aeb2e9 | ||
|
|
f2b3f3a9ab | ||
|
|
e1af35aa15 | ||
|
|
cb15753694 | ||
|
|
5914ccdc51 | ||
|
|
8be73bf187 | ||
|
|
35fbe1ef3d | ||
|
|
517e519bdc | ||
|
|
ff43b6875b | ||
|
|
4c8b5ea4f7 | ||
|
|
f29b33ec85 | ||
|
|
e5bc0486b5 | ||
|
|
b6e54ae2f1 | ||
|
|
9c3482083b | ||
|
|
c28a4204ee | ||
|
|
4892286465 | ||
|
|
419780d702 | ||
|
|
7bf4fd6c46 | ||
|
|
2c950cf7f5 | ||
|
|
87b0f62041 | ||
|
|
9d6c0e57a0 | ||
|
|
399e2c1ed3 | ||
|
|
7adf9cb1a0 | ||
|
|
e23e03592b | ||
|
|
4ab372d6b5 | ||
|
|
d2828e8722 | ||
|
|
d1b8fedc9c | ||
|
|
429dbf7129 | ||
|
|
9e4555797d | ||
|
|
48dba9a9d9 | ||
|
|
51f07e3382 | ||
|
|
5e210c083f | ||
|
|
5e449c84fe | ||
|
|
41de83fe1f | ||
|
|
e721dac367 | ||
|
|
1bc54c2c20 | ||
|
|
e662e819fe | ||
|
|
b62812c49e | ||
|
|
154cffb9d5 | ||
|
|
a6c0388ce9 | ||
|
|
0434b4b9ae | ||
|
|
53f4ad8ad4 | ||
|
|
303cce0cbc | ||
|
|
19383036d5 | ||
|
|
ff72c6358e | ||
|
|
508c08bb86 | ||
|
|
e970690cfa | ||
|
|
e584586cb0 | ||
|
|
73c7f8aa8f | ||
|
|
ade3e45a36 | ||
|
|
974b9eec85 | ||
|
|
943d46c465 | ||
|
|
bd21334013 | ||
|
|
ee0d2a8d94 | ||
|
|
4cef772364 | ||
|
|
a62ce45126 | ||
|
|
b9e0aae49f | ||
|
|
31fa414422 | ||
|
|
706f7be5e7 | ||
|
|
baac01cea4 | ||
|
|
f8dddf0a5c | ||
|
|
a03b7624f1 | ||
|
|
8603a908c1 | ||
|
|
e5943975f9 | ||
|
|
8442e2b9d8 | ||
|
|
5ecff157aa | ||
|
|
fb9b4ee842 | ||
|
|
07161d65d0 | ||
|
|
9bf5e55233 | ||
|
|
46f45464be | ||
|
|
d2d9f492b9 | ||
|
|
6d4ccb0eb1 | ||
|
|
dbdf140ca1 | ||
|
|
06936c69f6 | ||
|
|
43f3491d50 | ||
|
|
16004d4c6a | ||
|
|
9e31b1019e | ||
|
|
442ea508c4 | ||
|
|
33d1145c3f | ||
|
|
92a1cb893f | ||
|
|
3b6e1be169 | ||
|
|
353ae316c9 | ||
|
|
e1646e6ff4 | ||
|
|
1973bf5268 | ||
|
|
22afec32cf | ||
|
|
bda269059b | ||
|
|
c4e6c619ba | ||
|
|
2b677736bf | ||
|
|
7b901caf8f | ||
|
|
47dcbdfe51 | ||
|
|
23672987ff | ||
|
|
070890d361 | ||
|
|
2b160f4f3c | ||
|
|
a5957bfaeb | ||
|
|
b74a273934 | ||
|
|
7105f9c68c | ||
|
|
fc5461adf4 | ||
|
|
57a3d8c491 | ||
|
|
dfed43ab24 | ||
|
|
b99159c59b | ||
|
|
bb59e7f217 | ||
|
|
b643080117 | ||
|
|
02af8dde16 | ||
|
|
34d0b57945 | ||
|
|
f314662048 | ||
|
|
5c650cdcb2 | ||
|
|
793873bdc9 | ||
|
|
79991650af | ||
|
|
e083679e0d | ||
|
|
1b88734c6c | ||
|
|
3eba831de8 | ||
|
|
02503cf3be | ||
|
|
d090caccd7 | ||
|
|
7deafdafae | ||
|
|
ee5f270f3f | ||
|
|
5331418f3a | ||
|
|
7a6223e71b | ||
|
|
06424c9608 | ||
|
|
15933d478f | ||
|
|
82b81ed6d6 | ||
|
|
23b92e3057 | ||
|
|
27d57ba3b6 | ||
|
|
a7c549b85b | ||
|
|
29bfb56739 | ||
|
|
f096a28a19 | ||
|
|
9705764892 |
34
.github/ISSUE_TEMPLATE/0_feature_request.yml
vendored
34
.github/ISSUE_TEMPLATE/0_feature_request.yml
vendored
@@ -1,34 +0,0 @@
|
||||
name: Feature Request
|
||||
description: "Tip: open this issue template from within Zed with the `request feature` command palette action"
|
||||
labels: ["admin read", "triage", "enhancement"]
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Check for existing issues
|
||||
description: Check the backlog of issues to reduce the chances of creating duplicates; if an issue already exists, place a `+1` (👍) on it.
|
||||
options:
|
||||
- label: Completed
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the feature
|
||||
description: A clear and concise description of what you want to happen.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: environment
|
||||
attributes:
|
||||
label: Zed Version and System Specs
|
||||
description: Zed version, release channel, architecture (x86_64 or aarch64), OS (macOS version / Linux distro and version) and RAM amount.
|
||||
placeholder: |
|
||||
<!-- In Zed run `copy system specs into clipboard` from the Zed command palette and paste here. -->
|
||||
<!-- Alternatively spawn `request feature` and this field will be autopopulated -->
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: |
|
||||
If applicable, add mockups / screenshots to help present your vision of the feature
|
||||
description: Drag images into the text input below
|
||||
validations:
|
||||
required: false
|
||||
60
.github/ISSUE_TEMPLATE/1_bug_report.yml
vendored
60
.github/ISSUE_TEMPLATE/1_bug_report.yml
vendored
@@ -1,54 +1,34 @@
|
||||
name: Bug Report
|
||||
description: |
|
||||
Use this template for **non-crash-related** bug reports.
|
||||
Tip: open this issue template from within Zed with the `file bug report` command palette action.
|
||||
labels: ["admin read", "triage", "bug"]
|
||||
Something is broken in Zed (exclude crashing).
|
||||
type: "Bug"
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Check for existing issues
|
||||
description: Check the backlog of issues to reduce the chances of creating duplicates; if an issue already exists, place a `+1` (👍) on it.
|
||||
options:
|
||||
- label: Completed
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the bug / provide steps to reproduce it
|
||||
description: A clear and concise description of what the bug is.
|
||||
label: Summary
|
||||
description: Describe the bug with a one line summary, and provide detailed reproduction steps
|
||||
value: |
|
||||
<!-- Please insert a one line summary of the issue below -->
|
||||
|
||||
<!-- Include all steps necessary to reproduce from a clean Zed installation. Be verbose -->
|
||||
Steps to trigger the problem:
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
Actual Behavior:
|
||||
|
||||
Expected Behavior:
|
||||
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: environment
|
||||
attributes:
|
||||
label: Zed Version and System Specs
|
||||
description: Zed version, release channel, architecture (x86_64 or aarch64), OS (macOS version / Linux distro and version) and RAM amount.
|
||||
description: 'Open Zed, and in the command palette select "zed: Copy System Specs Into Clipboard"'
|
||||
placeholder: |
|
||||
<!-- In Zed run `copy system specs into clipboard` from the Zed command palette and paste here. -->
|
||||
<!-- Alternatively spawn `file bug report` and this field will be autopopulated -->
|
||||
<!-- If Zed won't launch, include the equivalent with other relevant details (e.g. video card driver version for display bugs, etc) -->
|
||||
<!-- Zed Version: 0.xxx.x; Channel: Stable, OS: macOS xx.xx, RAM: XXGB, Architecture: x86_64"
|
||||
Output of "zed: Copy System Specs Into Clipboard"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: If applicable, add screenshots or screencasts of the incorrect state / behavior
|
||||
description: Drag images / videos into the text input below
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: If applicable, attach your Zed.log file to this issue.
|
||||
description: |
|
||||
macOS: `~/Library/Logs/Zed/Zed.log`
|
||||
Linux: `~/.local/share/zed/logs/Zed.log` or $XDG_DATA_HOME
|
||||
If you only need the most recent lines, you can run the `zed: open log` command palette action to see the last 1000.
|
||||
value: |
|
||||
<details><summary>Zed.log</summary>
|
||||
|
||||
<!-- Click below this line and paste or drag-and-drop your log-->
|
||||
```
|
||||
|
||||
```
|
||||
<!-- Click above this line and paste or drag-and-drop your log--></details>
|
||||
validations:
|
||||
required: false
|
||||
|
||||
35
.github/ISSUE_TEMPLATE/2_crash_report.yml
vendored
35
.github/ISSUE_TEMPLATE/2_crash_report.yml
vendored
@@ -1,26 +1,33 @@
|
||||
name: Crash Report
|
||||
description: |
|
||||
Use this template for crash reports.
|
||||
labels: ["admin read", "triage", "bug", "panic / crash"]
|
||||
description: Zed is Crashing or Hanging
|
||||
type: "Crash"
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Check for existing issues
|
||||
description: Check the backlog of issues to reduce the chances of creating duplicates; if an issue already exists, place a `+1` (👍) on it.
|
||||
options:
|
||||
- label: Completed
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the bug / provide steps to reproduce it
|
||||
description: A clear and concise description of what the bug is.
|
||||
label: Summary
|
||||
description: Describe the bug with a one line summary, and provide detailed reproduction steps
|
||||
value: |
|
||||
<!-- Please insert a one line summary of the issue below -->
|
||||
|
||||
<!-- Include all steps necessary to reproduce from a clean Zed installation. Be verbose -->
|
||||
Steps to trigger the problem:
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
Actual Behavior:
|
||||
|
||||
Expected Behavior:
|
||||
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: environment
|
||||
attributes:
|
||||
label: Environment
|
||||
description: Run the `copy system specs into clipboard` command palette action and paste the output in the field below. If you are unable to run the command, please include your Zed version and release channel, operating system and version, RAM amount, and architecture.
|
||||
label: Zed Version and System Specs
|
||||
description: 'Open Zed, and in the command palette select "zed: Copy System Specs Into Clipboard"'
|
||||
placeholder: |
|
||||
Output of "zed: Copy System Specs Into Clipboard"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
|
||||
24
.github/ISSUE_TEMPLATE/config.yml
vendored
24
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,18 +1,12 @@
|
||||
# yaml-language-server: $schema=https://json.schemastore.org/github-issue-config.json
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Language Request
|
||||
url: https://github.com/zed-industries/extensions/issues/new?assignees=&labels=language&projects=&template=1_language_request.yml&title=%3Cname_of_language%3E
|
||||
about: Request a language in the extensions repository
|
||||
- name: Theme Request
|
||||
url: https://github.com/zed-industries/extensions/issues/new?assignees=&labels=theme&projects=&template=0_theme_request.yml&title=%3Cname_of_theme%3E+theme
|
||||
about: Request a theme in the extensions repository
|
||||
- name: Top-Ranking Issues
|
||||
url: https://github.com/zed-industries/zed/issues/5393
|
||||
about: See an overview of the most popular Zed issues
|
||||
- name: Platform Support
|
||||
url: https://github.com/zed-industries/zed/issues/5391
|
||||
about: A quick note on platform support
|
||||
- name: Positive Feedback
|
||||
url: https://github.com/zed-industries/zed/discussions/5397
|
||||
about: A central location for kind words about Zed
|
||||
- name: Feature Request
|
||||
url: https://github.com/zed-industries/zed/discussions/new/choose
|
||||
about: To request a feature, open a new Discussion in one of the appropriate Discussion categories
|
||||
- name: Zed Discussion Forum
|
||||
url: https://github.com/zed-industries/zed/discussions
|
||||
about: A community discussion forum
|
||||
- name: "Zed Discord: #Support Channel"
|
||||
url: https://zed.dev/community-links
|
||||
about: Real-time discussion and user support
|
||||
|
||||
2
.github/actions/run_tests/action.yml
vendored
2
.github/actions/run_tests/action.yml
vendored
@@ -10,7 +10,7 @@ runs:
|
||||
cargo install cargo-nextest --locked
|
||||
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4
|
||||
with:
|
||||
node-version: "18"
|
||||
|
||||
|
||||
1
.github/workflows/bump_patch_version.yml
vendored
1
.github/workflows/bump_patch_version.yml
vendored
@@ -14,6 +14,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
bump_patch_version:
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on:
|
||||
- buildjet-16vcpu-ubuntu-2204
|
||||
steps:
|
||||
|
||||
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -283,7 +283,7 @@ jobs:
|
||||
DIGITALOCEAN_SPACES_SECRET_KEY: ${{ secrets.DIGITALOCEAN_SPACES_SECRET_KEY }}
|
||||
steps:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4
|
||||
with:
|
||||
node-version: "18"
|
||||
|
||||
|
||||
@@ -19,11 +19,7 @@ jobs:
|
||||
|
||||
Thanks for your help!
|
||||
close-issue-message: "This issue was closed due to inactivity. If you're still experiencing this problem, please open a new issue with a link to this issue."
|
||||
# We will increase `days-before-stale` to 365 on or after Jan 24th,
|
||||
# 2024. This date marks one year since migrating issues from
|
||||
# 'community' to 'zed' repository. The migration added activity to all
|
||||
# issues, preventing 365 days from working until then.
|
||||
days-before-stale: 180
|
||||
days-before-stale: 120
|
||||
days-before-close: 7
|
||||
any-of-issue-labels: "bug,panic / crash"
|
||||
operations-per-run: 1000
|
||||
|
||||
@@ -9,6 +9,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
delete_comment:
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check for specific strings in comment
|
||||
|
||||
@@ -6,6 +6,7 @@ on:
|
||||
|
||||
jobs:
|
||||
discord_release:
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get release URL
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
name: Update All Top Ranking Issues
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 */12 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
update_top_ranking_issues:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'zed-industries/zed'
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- name: Set up uv
|
||||
uses: astral-sh/setup-uv@caf0cab7a618c569241d31dcd442f54681755d39 # v3
|
||||
with:
|
||||
version: "latest"
|
||||
enable-cache: true
|
||||
cache-dependency-glob: "script/update_top_ranking_issues/pyproject.toml"
|
||||
- name: Install Python 3.13
|
||||
run: uv python install 3.13
|
||||
- name: Install dependencies
|
||||
run: uv sync --project script/update_top_ranking_issues -p 3.13
|
||||
- name: Run script
|
||||
run: uv run --project script/update_top_ranking_issues script/update_top_ranking_issues/main.py --github-token ${{ secrets.GITHUB_TOKEN }} --issue-reference-number 5393
|
||||
@@ -1,25 +0,0 @@
|
||||
name: Update Weekly Top Ranking Issues
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 15 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
update_top_ranking_issues:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'zed-industries/zed'
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- name: Set up uv
|
||||
uses: astral-sh/setup-uv@caf0cab7a618c569241d31dcd442f54681755d39 # v3
|
||||
with:
|
||||
version: "latest"
|
||||
enable-cache: true
|
||||
cache-dependency-glob: "script/update_top_ranking_issues/pyproject.toml"
|
||||
- name: Install Python 3.13
|
||||
run: uv python install 3.13
|
||||
- name: Install dependencies
|
||||
run: uv sync --project script/update_top_ranking_issues -p 3.13
|
||||
- name: Run script
|
||||
run: uv run --project script/update_top_ranking_issues script/update_top_ranking_issues/main.py --github-token ${{ secrets.GITHUB_TOKEN }} --issue-reference-number 6952 --query-day-interval 7
|
||||
3
.github/workflows/danger.yml
vendored
3
.github/workflows/danger.yml
vendored
@@ -11,6 +11,7 @@ on:
|
||||
|
||||
jobs:
|
||||
danger:
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@@ -21,7 +22,7 @@ jobs:
|
||||
version: 9
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4
|
||||
with:
|
||||
node-version: "20"
|
||||
cache: "pnpm"
|
||||
|
||||
1
.github/workflows/deploy_collab.yml
vendored
1
.github/workflows/deploy_collab.yml
vendored
@@ -12,6 +12,7 @@ env:
|
||||
jobs:
|
||||
style:
|
||||
name: Check formatting and Clippy lints
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- test
|
||||
|
||||
1
.github/workflows/publish_extension_cli.yml
vendored
1
.github/workflows/publish_extension_cli.yml
vendored
@@ -12,6 +12,7 @@ env:
|
||||
jobs:
|
||||
publish:
|
||||
name: Publish zed-extension CLI
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
steps:
|
||||
|
||||
3
.github/workflows/randomized_tests.yml
vendored
3
.github/workflows/randomized_tests.yml
vendored
@@ -18,11 +18,12 @@ env:
|
||||
jobs:
|
||||
tests:
|
||||
name: Run randomized tests
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on:
|
||||
- buildjet-16vcpu-ubuntu-2204
|
||||
steps:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4
|
||||
with:
|
||||
node-version: "18"
|
||||
|
||||
|
||||
2
.github/workflows/release_nightly.yml
vendored
2
.github/workflows/release_nightly.yml
vendored
@@ -70,7 +70,7 @@ jobs:
|
||||
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
|
||||
steps:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4
|
||||
with:
|
||||
node-version: "18"
|
||||
|
||||
|
||||
371
Cargo.lock
generated
371
Cargo.lock
generated
@@ -506,14 +506,12 @@ dependencies = [
|
||||
"assistant_settings",
|
||||
"assistant_slash_command",
|
||||
"assistant_slash_commands",
|
||||
"assistant_tool",
|
||||
"chrono",
|
||||
"client",
|
||||
"clock",
|
||||
"collections",
|
||||
"context_server",
|
||||
"editor",
|
||||
"feature_flags",
|
||||
"fs",
|
||||
"futures 0.3.31",
|
||||
"fuzzy",
|
||||
@@ -560,6 +558,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anthropic",
|
||||
"anyhow",
|
||||
"deepseek",
|
||||
"feature_flags",
|
||||
"fs",
|
||||
"gpui",
|
||||
@@ -636,6 +635,7 @@ dependencies = [
|
||||
"ui",
|
||||
"util",
|
||||
"workspace",
|
||||
"worktree",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1198,9 +1198,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-config"
|
||||
version = "1.5.14"
|
||||
version = "1.5.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f40e82e858e02445402906e454a73e244c7f501fcae198977585946c48e8697"
|
||||
checksum = "dc47e70fc35d054c8fcd296d47a61711f043ac80534a10b4f741904f81e73a90"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -1266,9 +1266,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-kinesis"
|
||||
version = "1.56.0"
|
||||
version = "1.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43d9c9144b6b00173d8f212a89d6bb252d48f88aeb2ae89c33c13b0a0fcd0ac9"
|
||||
checksum = "7963cf7a0f49ba4f8351044751f4d42c003c4a5f31d9e084f0d0e68b6fb8b8cf"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -1288,9 +1288,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-s3"
|
||||
version = "1.69.0"
|
||||
version = "1.72.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a88f1c30e4ffa2464f910297c24736ff68cca9e8d2b7d52596b54efd99b9c1e"
|
||||
checksum = "1c7ce6d85596c4bcb3aba8ad5bb134b08e204c8a475c9999c1af9290f80aa8ad"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -1322,9 +1322,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-sso"
|
||||
version = "1.54.0"
|
||||
version = "1.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "921a13ed6aabe2d1258f65ef7804946255c799224440774c30e1a2c65cdf983a"
|
||||
checksum = "c54bab121fe1881a74c338c5f723d1592bf3b53167f80268a1274f404e1acc38"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -1344,9 +1344,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-ssooidc"
|
||||
version = "1.55.0"
|
||||
version = "1.58.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "196c952738b05dfc917d82a3e9b5ba850822a6d6a86d677afda2a156cc172ceb"
|
||||
checksum = "8c8234fd024f7ac61c4e44ea008029bde934250f371efe7d4a39708397b1080c"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -1366,9 +1366,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-sts"
|
||||
version = "1.55.0"
|
||||
version = "1.58.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ef5b73a927ed80b44096f8c20fb4abae65469af15198367e179ae267256e9d"
|
||||
checksum = "ba60e1d519d6f23a9df712c04fdeadd7872ac911c84b2f62a8bda92e129b7962"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -1837,7 +1837,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "blade-graphics"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/kvark/blade?rev=091a8401033847bb9b6ace3fcf70448d069621c5#091a8401033847bb9b6ace3fcf70448d069621c5"
|
||||
source = "git+https://github.com/kvark/blade?rev=b16f5c7bd873c7126f48c82c39e7ae64602ae74f#b16f5c7bd873c7126f48c82c39e7ae64602ae74f"
|
||||
dependencies = [
|
||||
"ash",
|
||||
"ash-window",
|
||||
@@ -1869,7 +1869,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "blade-macros"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/kvark/blade?rev=091a8401033847bb9b6ace3fcf70448d069621c5#091a8401033847bb9b6ace3fcf70448d069621c5"
|
||||
source = "git+https://github.com/kvark/blade?rev=b16f5c7bd873c7126f48c82c39e7ae64602ae74f#b16f5c7bd873c7126f48c82c39e7ae64602ae74f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1879,7 +1879,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "blade-util"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/kvark/blade?rev=091a8401033847bb9b6ace3fcf70448d069621c5#091a8401033847bb9b6ace3fcf70448d069621c5"
|
||||
source = "git+https://github.com/kvark/blade?rev=b16f5c7bd873c7126f48c82c39e7ae64602ae74f#b16f5c7bd873c7126f48c82c39e7ae64602ae74f"
|
||||
dependencies = [
|
||||
"blade-graphics",
|
||||
"bytemuck",
|
||||
@@ -2182,7 +2182,7 @@ dependencies = [
|
||||
"cap-primitives",
|
||||
"cap-std",
|
||||
"io-lifetimes",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2210,7 +2210,7 @@ dependencies = [
|
||||
"ipnet",
|
||||
"maybe-owned",
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
"winx",
|
||||
]
|
||||
|
||||
@@ -2718,7 +2718,6 @@ dependencies = [
|
||||
"envy",
|
||||
"extension",
|
||||
"file_finder",
|
||||
"fireworks",
|
||||
"fs",
|
||||
"futures 0.3.31",
|
||||
"git",
|
||||
@@ -3685,6 +3684,18 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deepseek"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures 0.3.31",
|
||||
"http_client",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deflate64"
|
||||
version = "0.1.9"
|
||||
@@ -4009,7 +4020,7 @@ dependencies = [
|
||||
"util",
|
||||
"uuid",
|
||||
"workspace",
|
||||
"zed_predict_tos",
|
||||
"zed_predict_onboarding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4209,7 +4220,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4645,17 +4656,6 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fireworks"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures 0.3.31",
|
||||
"http_client",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.4.2"
|
||||
@@ -4684,6 +4684,12 @@ version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d"
|
||||
|
||||
[[package]]
|
||||
name = "float_next_after"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8"
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.11.1"
|
||||
@@ -4852,7 +4858,7 @@ dependencies = [
|
||||
"gpui",
|
||||
"libc",
|
||||
"log",
|
||||
"notify",
|
||||
"notify 6.1.1",
|
||||
"objc",
|
||||
"parking_lot",
|
||||
"paths",
|
||||
@@ -4876,7 +4882,7 @@ checksum = "5e2e6123af26f0f2c51cc66869137080199406754903cc926a7690401ce09cb4"
|
||||
dependencies = [
|
||||
"io-lifetimes",
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5149,6 +5155,18 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi 0.13.3+wasi-0.2.2",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gif"
|
||||
version = "0.13.1"
|
||||
@@ -5412,8 +5430,10 @@ dependencies = [
|
||||
"itertools 0.14.0",
|
||||
"linkme",
|
||||
"log",
|
||||
"lyon",
|
||||
"media",
|
||||
"metal",
|
||||
"naga",
|
||||
"num_cpus",
|
||||
"objc",
|
||||
"objc2",
|
||||
@@ -5470,6 +5490,15 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gpui_tokio"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"gpui",
|
||||
"tokio",
|
||||
"util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "grid"
|
||||
version = "0.13.0"
|
||||
@@ -5615,11 +5644,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hashlink"
|
||||
version = "0.9.1"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
|
||||
checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
|
||||
dependencies = [
|
||||
"hashbrown 0.14.5",
|
||||
"hashbrown 0.15.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6374,7 +6403,7 @@ dependencies = [
|
||||
"ui",
|
||||
"workspace",
|
||||
"zed_actions",
|
||||
"zed_predict_tos",
|
||||
"zed_predict_onboarding",
|
||||
"zeta",
|
||||
]
|
||||
|
||||
@@ -6389,6 +6418,17 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inotify"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"inotify-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inotify-sys"
|
||||
version = "0.1.5"
|
||||
@@ -6445,7 +6485,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2285ddfe3054097ef4b2fe909ef8c3bcd1ea52a8f0d274416caebeef39f04a65"
|
||||
dependencies = [
|
||||
"io-lifetimes",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6808,6 +6848,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
"collections",
|
||||
"deepseek",
|
||||
"futures 0.3.31",
|
||||
"google_ai",
|
||||
"gpui",
|
||||
@@ -6851,6 +6892,7 @@ dependencies = [
|
||||
"client",
|
||||
"collections",
|
||||
"copilot",
|
||||
"deepseek",
|
||||
"editor",
|
||||
"feature_flags",
|
||||
"fs",
|
||||
@@ -7413,6 +7455,69 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lyon"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91e7f9cda98b5430809e63ca5197b06c7d191bf7e26dfc467d5a3f0290e2a74f"
|
||||
dependencies = [
|
||||
"lyon_algorithms",
|
||||
"lyon_extra",
|
||||
"lyon_tessellation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lyon_algorithms"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f13c9be19d257c7d37e70608ed858e8eab4b2afcea2e3c9a622e892acbf43c08"
|
||||
dependencies = [
|
||||
"lyon_path",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lyon_extra"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ca94c7bf1e2557c2798989c43416822c12fc5dcc5e17cc3307ef0e71894a955"
|
||||
dependencies = [
|
||||
"lyon_path",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lyon_geom"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8af69edc087272df438b3ee436c4bb6d7c04aa8af665cfd398feae627dbd8570"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"euclid",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lyon_path"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e0b8aec2f58586f6eef237985b9a9b7cb3a3aff4417c575075cf95bf925252e"
|
||||
dependencies = [
|
||||
"lyon_geom",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lyon_tessellation"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "579d42360a4b09846eff2feef28f538696c7d6c7439bfa65874ff3cbe0951b2c"
|
||||
dependencies = [
|
||||
"float_next_after",
|
||||
"lyon_path",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mac"
|
||||
version = "0.1.1"
|
||||
@@ -7554,9 +7659,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mdbook"
|
||||
version = "0.4.43"
|
||||
version = "0.4.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe1f98b8d66e537d2f0ba06e7dec4f44001deec539a2d18bfc102d6a86189148"
|
||||
checksum = "f9da1e54401fe5d45a664c57e112e70f18e8c5a73e268c179305b932ee864574"
|
||||
dependencies = [
|
||||
"ammonia",
|
||||
"anyhow",
|
||||
@@ -7570,7 +7675,7 @@ dependencies = [
|
||||
"ignore",
|
||||
"log",
|
||||
"memchr",
|
||||
"notify",
|
||||
"notify 8.0.0",
|
||||
"notify-debouncer-mini",
|
||||
"once_cell",
|
||||
"opener",
|
||||
@@ -7986,7 +8091,7 @@ dependencies = [
|
||||
"crossbeam-channel",
|
||||
"filetime",
|
||||
"fsevent-sys 4.1.0",
|
||||
"inotify",
|
||||
"inotify 0.9.6",
|
||||
"kqueue",
|
||||
"libc",
|
||||
"log",
|
||||
@@ -7996,16 +8101,42 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "notify-debouncer-mini"
|
||||
version = "0.4.1"
|
||||
name = "notify"
|
||||
version = "8.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d40b221972a1fc5ef4d858a2f671fb34c75983eb385463dff3780eeff6a9d43"
|
||||
checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"bitflags 2.8.0",
|
||||
"filetime",
|
||||
"fsevent-sys 4.1.0",
|
||||
"inotify 0.11.0",
|
||||
"kqueue",
|
||||
"libc",
|
||||
"log",
|
||||
"notify",
|
||||
"mio 1.0.3",
|
||||
"notify-types",
|
||||
"walkdir",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "notify-debouncer-mini"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a689eb4262184d9a1727f9087cd03883ea716682ab03ed24efec57d7716dccb8"
|
||||
dependencies = [
|
||||
"log",
|
||||
"notify 8.0.0",
|
||||
"notify-types",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "notify-types"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d"
|
||||
|
||||
[[package]]
|
||||
name = "ntapi"
|
||||
version = "0.4.1"
|
||||
@@ -10241,7 +10372,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"socket2",
|
||||
"tracing",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10623,6 +10754,7 @@ dependencies = [
|
||||
"git",
|
||||
"git_hosting_providers",
|
||||
"gpui",
|
||||
"gpui_tokio",
|
||||
"http_client",
|
||||
"language",
|
||||
"language_extension",
|
||||
@@ -11126,7 +11258,7 @@ dependencies = [
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"once_cell",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11294,6 +11426,19 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schema_generator"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"env_logger 0.11.6",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"theme",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schemars"
|
||||
version = "0.8.21"
|
||||
@@ -11635,9 +11780,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.137"
|
||||
version = "1.0.138"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b"
|
||||
checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itoa",
|
||||
@@ -12062,7 +12207,7 @@ dependencies = [
|
||||
"paths",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_json_lenient",
|
||||
"snippet",
|
||||
"util",
|
||||
]
|
||||
@@ -12182,9 +12327,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sqlx"
|
||||
version = "0.8.2"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93334716a037193fac19df402f8571269c84a00852f6a7066b5d2616dcd64d3e"
|
||||
checksum = "4410e73b3c0d8442c5f99b425d7a435b5ee0ae4167b3196771dd3f7a01be745f"
|
||||
dependencies = [
|
||||
"sqlx-core",
|
||||
"sqlx-macros",
|
||||
@@ -12195,32 +12340,27 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-core"
|
||||
version = "0.8.2"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4d8060b456358185f7d50c55d9b5066ad956956fddec42ee2e8567134a8936e"
|
||||
checksum = "6a007b6936676aa9ab40207cde35daab0a04b823be8ae004368c0793b96a61e0"
|
||||
dependencies = [
|
||||
"atoi",
|
||||
"bigdecimal",
|
||||
"byteorder",
|
||||
"bytes 1.9.0",
|
||||
"chrono",
|
||||
"crc",
|
||||
"crossbeam-queue",
|
||||
"either",
|
||||
"event-listener 5.3.1",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-intrusive",
|
||||
"futures-io",
|
||||
"futures-util",
|
||||
"hashbrown 0.14.5",
|
||||
"hashlink 0.9.1",
|
||||
"hex",
|
||||
"hashbrown 0.15.2",
|
||||
"hashlink 0.10.0",
|
||||
"indexmap",
|
||||
"log",
|
||||
"memchr",
|
||||
"once_cell",
|
||||
"paste",
|
||||
"percent-encoding",
|
||||
"rust_decimal",
|
||||
"rustls 0.23.20",
|
||||
@@ -12229,8 +12369,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"smallvec",
|
||||
"sqlformat",
|
||||
"thiserror 1.0.69",
|
||||
"thiserror 2.0.6",
|
||||
"time",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
@@ -12242,9 +12381,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-macros"
|
||||
version = "0.8.2"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cac0692bcc9de3b073e8d747391827297e075c7710ff6276d9f7a1f3d58c6657"
|
||||
checksum = "3112e2ad78643fef903618d78cf0aec1cb3134b019730edb039b69eaf531f310"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -12255,9 +12394,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-macros-core"
|
||||
version = "0.8.2"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1804e8a7c7865599c9c79be146dc8a9fd8cc86935fa641d3ea58e5f0688abaa5"
|
||||
checksum = "4e9f90acc5ab146a99bf5061a7eb4976b573f560bc898ef3bf8435448dd5e7ad"
|
||||
dependencies = [
|
||||
"dotenvy",
|
||||
"either",
|
||||
@@ -12281,9 +12420,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-mysql"
|
||||
version = "0.8.2"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64bb4714269afa44aef2755150a0fc19d756fb580a67db8885608cf02f47d06a"
|
||||
checksum = "4560278f0e00ce64938540546f59f590d60beee33fffbd3b9cd47851e5fff233"
|
||||
dependencies = [
|
||||
"atoi",
|
||||
"base64 0.22.1",
|
||||
@@ -12319,7 +12458,7 @@ dependencies = [
|
||||
"smallvec",
|
||||
"sqlx-core",
|
||||
"stringprep",
|
||||
"thiserror 1.0.69",
|
||||
"thiserror 2.0.6",
|
||||
"time",
|
||||
"tracing",
|
||||
"uuid",
|
||||
@@ -12328,9 +12467,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-postgres"
|
||||
version = "0.8.2"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fa91a732d854c5d7726349bb4bb879bb9478993ceb764247660aee25f67c2f8"
|
||||
checksum = "c5b98a57f363ed6764d5b3a12bfedf62f07aa16e1856a7ddc2a0bb190a959613"
|
||||
dependencies = [
|
||||
"atoi",
|
||||
"base64 0.22.1",
|
||||
@@ -12343,7 +12482,6 @@ dependencies = [
|
||||
"etcetera",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-util",
|
||||
"hex",
|
||||
"hkdf",
|
||||
@@ -12363,7 +12501,7 @@ dependencies = [
|
||||
"smallvec",
|
||||
"sqlx-core",
|
||||
"stringprep",
|
||||
"thiserror 1.0.69",
|
||||
"thiserror 2.0.6",
|
||||
"time",
|
||||
"tracing",
|
||||
"uuid",
|
||||
@@ -12372,9 +12510,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-sqlite"
|
||||
version = "0.8.2"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5b2cf34a45953bfd3daaf3db0f7a7878ab9b7a6b91b422d24a7a9e4c857b680"
|
||||
checksum = "f85ca71d3a5b24e64e1d08dd8fe36c6c95c339a896cc33068148906784620540"
|
||||
dependencies = [
|
||||
"atoi",
|
||||
"chrono",
|
||||
@@ -12842,7 +12980,7 @@ dependencies = [
|
||||
"fd-lock",
|
||||
"io-lifetimes",
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
"winx",
|
||||
]
|
||||
|
||||
@@ -12967,16 +13105,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.15.0"
|
||||
version = "3.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704"
|
||||
checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand 2.3.0",
|
||||
"getrandom 0.2.15",
|
||||
"getrandom 0.3.1",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -13141,7 +13279,6 @@ dependencies = [
|
||||
"log",
|
||||
"palette",
|
||||
"rust-embed",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_json_lenient",
|
||||
@@ -13403,6 +13540,7 @@ dependencies = [
|
||||
"windows 0.58.0",
|
||||
"workspace",
|
||||
"zed_actions",
|
||||
"zed_predict_onboarding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -13921,9 +14059,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-regex"
|
||||
version = "0.23.0"
|
||||
version = "0.24.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b9a7087b1cf769c96b7e74414947df067fb6135f04d176fd23be08b9396cc0e"
|
||||
checksum = "712656f8c262a5a4b7d6026e6246950787d178d613864952554e1516a33ab0c1"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"tree-sitter-language",
|
||||
@@ -14189,9 +14327,9 @@ checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
|
||||
|
||||
[[package]]
|
||||
name = "unindent"
|
||||
version = "0.1.11"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c"
|
||||
checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
@@ -14293,9 +14431,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.11.0"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a"
|
||||
checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b"
|
||||
dependencies = [
|
||||
"getrandom 0.2.15",
|
||||
"serde",
|
||||
@@ -14402,6 +14540,7 @@ dependencies = [
|
||||
"command_palette_hooks",
|
||||
"editor",
|
||||
"futures 0.3.31",
|
||||
"git_ui",
|
||||
"gpui",
|
||||
"indoc",
|
||||
"itertools 0.14.0",
|
||||
@@ -14553,6 +14692,15 @@ version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.13.3+wasi-0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt 0.33.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasite"
|
||||
version = "0.1.0"
|
||||
@@ -15724,7 +15872,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f3fd376f71958b862e7afb20cfe5a22830e1963462f3a17f49d82a6c1d1f42d"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -15743,7 +15891,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "288f992ea30e6b5c531b52cdd5f3be81c148554b09ea416f058d16556ba92c27"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"wit-bindgen-rt",
|
||||
"wit-bindgen-rt 0.22.0",
|
||||
"wit-bindgen-rust-macro",
|
||||
]
|
||||
|
||||
@@ -15763,6 +15911,15 @@ version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcb8738270f32a2d6739973cbbb7c1b6dd8959ce515578a6e19165853272ee64"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.33.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust"
|
||||
version = "0.22.0"
|
||||
@@ -16276,7 +16433,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed"
|
||||
version = "0.172.0"
|
||||
version = "0.173.0"
|
||||
dependencies = [
|
||||
"activity_indicator",
|
||||
"anyhow",
|
||||
@@ -16322,6 +16479,7 @@ dependencies = [
|
||||
"git_ui",
|
||||
"go_to_line",
|
||||
"gpui",
|
||||
"gpui_tokio",
|
||||
"http_client",
|
||||
"image_viewer",
|
||||
"inline_completion_button",
|
||||
@@ -16399,7 +16557,7 @@ dependencies = [
|
||||
"winresource",
|
||||
"workspace",
|
||||
"zed_actions",
|
||||
"zed_predict_tos",
|
||||
"zed_predict_onboarding",
|
||||
"zeta",
|
||||
]
|
||||
|
||||
@@ -16514,23 +16672,24 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zed_predict_tos"
|
||||
name = "zed_predict_onboarding"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"client",
|
||||
"db",
|
||||
"feature_flags",
|
||||
"fs",
|
||||
"gpui",
|
||||
"language",
|
||||
"menu",
|
||||
"settings",
|
||||
"theme",
|
||||
"ui",
|
||||
"util",
|
||||
"workspace",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zed_prisma"
|
||||
version = "0.0.4"
|
||||
dependencies = [
|
||||
"zed_extension_api 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zed_proto"
|
||||
version = "0.2.1"
|
||||
@@ -16719,9 +16878,12 @@ dependencies = [
|
||||
"client",
|
||||
"clock",
|
||||
"collections",
|
||||
"command_palette_hooks",
|
||||
"ctor",
|
||||
"db",
|
||||
"editor",
|
||||
"env_logger 0.11.6",
|
||||
"feature_flags",
|
||||
"futures 0.3.31",
|
||||
"gpui",
|
||||
"http_client",
|
||||
@@ -16733,6 +16895,7 @@ dependencies = [
|
||||
"menu",
|
||||
"reqwest_client",
|
||||
"rpc",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"settings",
|
||||
"similar",
|
||||
|
||||
27
Cargo.toml
27
Cargo.toml
@@ -2,7 +2,6 @@
|
||||
resolver = "2"
|
||||
members = [
|
||||
"crates/activity_indicator",
|
||||
"crates/zed_predict_tos",
|
||||
"crates/anthropic",
|
||||
"crates/assets",
|
||||
"crates/assistant",
|
||||
@@ -31,6 +30,7 @@ members = [
|
||||
"crates/context_server_settings",
|
||||
"crates/copilot",
|
||||
"crates/db",
|
||||
"crates/deepseek",
|
||||
"crates/diagnostics",
|
||||
"crates/docs_preprocessor",
|
||||
"crates/editor",
|
||||
@@ -44,16 +44,17 @@ members = [
|
||||
"crates/feedback",
|
||||
"crates/file_finder",
|
||||
"crates/file_icons",
|
||||
"crates/fireworks",
|
||||
"crates/fs",
|
||||
"crates/fsevent",
|
||||
"crates/fuzzy",
|
||||
"crates/git",
|
||||
"crates/git_hosting_providers",
|
||||
"crates/git_ui",
|
||||
"crates/go_to_line",
|
||||
"crates/google_ai",
|
||||
"crates/gpui",
|
||||
"crates/gpui_macros",
|
||||
"crates/gpui_tokio",
|
||||
"crates/html_to_markdown",
|
||||
"crates/http_client",
|
||||
"crates/image_viewer",
|
||||
@@ -102,9 +103,11 @@ members = [
|
||||
"crates/remote_server",
|
||||
"crates/repl",
|
||||
"crates/reqwest_client",
|
||||
"crates/reqwest_client",
|
||||
"crates/rich_text",
|
||||
"crates/rope",
|
||||
"crates/rpc",
|
||||
"crates/schema_generator",
|
||||
"crates/search",
|
||||
"crates/semantic_index",
|
||||
"crates/semantic_version",
|
||||
@@ -140,7 +143,6 @@ members = [
|
||||
"crates/ui",
|
||||
"crates/ui_input",
|
||||
"crates/ui_macros",
|
||||
"crates/reqwest_client",
|
||||
"crates/util",
|
||||
"crates/vcs_menu",
|
||||
"crates/vim",
|
||||
@@ -150,8 +152,8 @@ members = [
|
||||
"crates/worktree",
|
||||
"crates/zed",
|
||||
"crates/zed_actions",
|
||||
"crates/zed_predict_onboarding",
|
||||
"crates/zeta",
|
||||
"crates/git_ui",
|
||||
|
||||
#
|
||||
# Extensions
|
||||
@@ -168,7 +170,6 @@ members = [
|
||||
"extensions/lua",
|
||||
"extensions/php",
|
||||
"extensions/perplexity",
|
||||
"extensions/prisma",
|
||||
"extensions/proto",
|
||||
"extensions/purescript",
|
||||
"extensions/ruff",
|
||||
@@ -200,7 +201,6 @@ edition = "2021"
|
||||
|
||||
activity_indicator = { path = "crates/activity_indicator" }
|
||||
ai = { path = "crates/ai" }
|
||||
zed_predict_tos = { path = "crates/zed_predict_tos" }
|
||||
anthropic = { path = "crates/anthropic" }
|
||||
assets = { path = "crates/assets" }
|
||||
assistant = { path = "crates/assistant" }
|
||||
@@ -229,6 +229,7 @@ context_server = { path = "crates/context_server" }
|
||||
context_server_settings = { path = "crates/context_server_settings" }
|
||||
copilot = { path = "crates/copilot" }
|
||||
db = { path = "crates/db" }
|
||||
deepseek = { path = "crates/deepseek" }
|
||||
diagnostics = { path = "crates/diagnostics" }
|
||||
editor = { path = "crates/editor" }
|
||||
extension = { path = "crates/extension" }
|
||||
@@ -238,7 +239,6 @@ feature_flags = { path = "crates/feature_flags" }
|
||||
feedback = { path = "crates/feedback" }
|
||||
file_finder = { path = "crates/file_finder" }
|
||||
file_icons = { path = "crates/file_icons" }
|
||||
fireworks = { path = "crates/fireworks" }
|
||||
fs = { path = "crates/fs" }
|
||||
fsevent = { path = "crates/fsevent" }
|
||||
fuzzy = { path = "crates/fuzzy" }
|
||||
@@ -251,6 +251,7 @@ gpui = { path = "crates/gpui", default-features = false, features = [
|
||||
"http_client",
|
||||
] }
|
||||
gpui_macros = { path = "crates/gpui_macros" }
|
||||
gpui_tokio = { path = "crates/gpui_tokio" }
|
||||
html_to_markdown = { path = "crates/html_to_markdown" }
|
||||
http_client = { path = "crates/http_client" }
|
||||
image_viewer = { path = "crates/image_viewer" }
|
||||
@@ -347,6 +348,7 @@ workspace = { path = "crates/workspace" }
|
||||
worktree = { path = "crates/worktree" }
|
||||
zed = { path = "crates/zed" }
|
||||
zed_actions = { path = "crates/zed_actions" }
|
||||
zed_predict_onboarding = { path = "crates/zed_predict_onboarding" }
|
||||
zeta = { path = "crates/zeta" }
|
||||
|
||||
#
|
||||
@@ -373,9 +375,10 @@ async-watch = "0.3.1"
|
||||
async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
|
||||
base64 = "0.22"
|
||||
bitflags = "2.6.0"
|
||||
blade-graphics = { git = "https://github.com/kvark/blade", rev = "091a8401033847bb9b6ace3fcf70448d069621c5" }
|
||||
blade-macros = { git = "https://github.com/kvark/blade", rev = "091a8401033847bb9b6ace3fcf70448d069621c5" }
|
||||
blade-util = { git = "https://github.com/kvark/blade", rev = "091a8401033847bb9b6ace3fcf70448d069621c5" }
|
||||
blade-graphics = { git = "https://github.com/kvark/blade", rev = "b16f5c7bd873c7126f48c82c39e7ae64602ae74f" }
|
||||
blade-macros = { git = "https://github.com/kvark/blade", rev = "b16f5c7bd873c7126f48c82c39e7ae64602ae74f" }
|
||||
blade-util = { git = "https://github.com/kvark/blade", rev = "b16f5c7bd873c7126f48c82c39e7ae64602ae74f" }
|
||||
naga = { version = "23.1.0", features = ["wgsl-in"] }
|
||||
blake3 = "1.5.3"
|
||||
bytes = "1.0"
|
||||
cargo_metadata = "0.19"
|
||||
@@ -522,13 +525,13 @@ tree-sitter-jsdoc = "0.23"
|
||||
tree-sitter-json = "0.24"
|
||||
tree-sitter-md = { git = "https://github.com/tree-sitter-grammars/tree-sitter-markdown", rev = "9a23c1a96c0513d8fc6520972beedd419a973539" }
|
||||
tree-sitter-python = "0.23"
|
||||
tree-sitter-regex = "0.23"
|
||||
tree-sitter-regex = "0.24"
|
||||
tree-sitter-ruby = "0.23"
|
||||
tree-sitter-rust = "0.23"
|
||||
tree-sitter-typescript = "0.23"
|
||||
tree-sitter-yaml = { git = "https://github.com/zed-industries/tree-sitter-yaml", rev = "baff0b51c64ef6a1fb1f8390f3ad6015b83ec13a" }
|
||||
unicase = "2.6"
|
||||
unindent = "0.1.7"
|
||||
unindent = "0.2.0"
|
||||
unicode-segmentation = "1.10"
|
||||
unicode-script = "0.5.7"
|
||||
url = "2.2"
|
||||
|
||||
1
assets/icons/ai_deep_seek.svg
Normal file
1
assets/icons/ai_deep_seek.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>DeepSeek</title><path d="M23.748 4.482c-.254-.124-.364.113-.512.234-.051.039-.094.09-.137.136-.372.397-.806.657-1.373.626-.829-.046-1.537.214-2.163.848-.133-.782-.575-1.248-1.247-1.548-.352-.156-.708-.311-.955-.65-.172-.241-.219-.51-.305-.774-.055-.16-.11-.323-.293-.35-.2-.031-.278.136-.356.276-.313.572-.434 1.202-.422 1.84.027 1.436.633 2.58 1.838 3.393.137.093.172.187.129.323-.082.28-.18.552-.266.833-.055.179-.137.217-.329.14a5.526 5.526 0 01-1.736-1.18c-.857-.828-1.631-1.742-2.597-2.458a11.365 11.365 0 00-.689-.471c-.985-.957.13-1.743.388-1.836.27-.098.093-.432-.779-.428-.872.004-1.67.295-2.687.684a3.055 3.055 0 01-.465.137 9.597 9.597 0 00-2.883-.102c-1.885.21-3.39 1.102-4.497 2.623C.082 8.606-.231 10.684.152 12.85c.403 2.284 1.569 4.175 3.36 5.653 1.858 1.533 3.997 2.284 6.438 2.14 1.482-.085 3.133-.284 4.994-1.86.47.234.962.327 1.78.397.63.059 1.236-.03 1.705-.128.735-.156.684-.837.419-.961-2.155-1.004-1.682-.595-2.113-.926 1.096-1.296 2.746-2.642 3.392-7.003.05-.347.007-.565 0-.845-.004-.17.035-.237.23-.256a4.173 4.173 0 001.545-.475c1.396-.763 1.96-2.015 2.093-3.517.02-.23-.004-.467-.247-.588zM11.581 18c-2.089-1.642-3.102-2.183-3.52-2.16-.392.024-.321.471-.235.763.09.288.207.486.371.739.114.167.192.416-.113.603-.673.416-1.842-.14-1.897-.167-1.361-.802-2.5-1.86-3.301-3.307-.774-1.393-1.224-2.887-1.298-4.482-.02-.386.093-.522.477-.592a4.696 4.696 0 011.529-.039c2.132.312 3.946 1.265 5.468 2.774.868.86 1.525 1.887 2.202 2.891.72 1.066 1.494 2.082 2.48 2.914.348.292.625.514.891.677-.802.09-2.14.11-3.054-.614zm1-6.44a.306.306 0 01.415-.287.302.302 0 01.2.288.306.306 0 01-.31.307.303.303 0 01-.304-.308zm3.11 1.596c-.2.081-.399.151-.59.16a1.245 1.245 0 01-.798-.254c-.274-.23-.47-.358-.552-.758a1.73 1.73 0 01.016-.588c.07-.327-.008-.537-.239-.727-.187-.156-.426-.199-.688-.199a.559.559 0 01-.254-.078c-.11-.054-.2-.19-.114-.358.028-.054.16-.186.192-.21.356-.202.767-.136 1.146.016.352.144.618.408 1.001.782.391.451.462.576.685.914.176.265.336.537.445.848.067.195-.019.354-.25.452z" fill="black"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -42,6 +42,12 @@
|
||||
"elm": "elm",
|
||||
"erl": "erlang",
|
||||
"escript": "erlang",
|
||||
"eslint.config.cjs": "eslint",
|
||||
"eslint.config.cts": "eslint",
|
||||
"eslint.config.js": "eslint",
|
||||
"eslint.config.mjs": "eslint",
|
||||
"eslint.config.mts": "eslint",
|
||||
"eslint.config.ts": "eslint",
|
||||
"eslintrc": "eslint",
|
||||
"eslintrc.js": "eslint",
|
||||
"eslintrc.json": "eslint",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7 8.9V11C5.34478 11 4.65522 11 3 11V10.4L7 5.6V5H3V7.1" stroke="black" stroke-width="1.5"/>
|
||||
<path d="M12 5L14 8L12 11" stroke="black" stroke-width="1.5"/>
|
||||
<path d="M10 6.5L11 8L10 9.5" stroke="black" stroke-width="1.5"/>
|
||||
<path d="M7.5 8.9V11C5.43097 11 4.56903 11 2.5 11V10.4L7.5 5.6V5H2.5V7.1" stroke="black" stroke-width="1.5"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 334 B After Width: | Height: | Size: 342 B |
19
assets/icons/zed_predict_bg.svg
Normal file
19
assets/icons/zed_predict_bg.svg
Normal file
@@ -0,0 +1,19 @@
|
||||
<svg width="420" height="128" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<pattern id="tilePattern" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 5L14 8L12 11" stroke="black" stroke-width="1.5"/>
|
||||
<path d="M10 6.5L11 8L10 9.5" stroke="black" stroke-width="1.5"/>
|
||||
<path d="M7.5 8.9V11C5.43097 11 4.56903 11 2.5 11V10.4L7.5 5.6V5H2.5V7.1" stroke="black" stroke-width="1.5"/>
|
||||
</svg>
|
||||
</pattern>
|
||||
<linearGradient id="fade" y2="1" x2="0">
|
||||
<stop offset="0" stop-color="white" stop-opacity=".24"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<mask id="fadeMask" maskContentUnits="objectBoundingBox">
|
||||
<rect width="1" height="1" fill="url(#fade)"/>
|
||||
</mask>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" fill="url(#tilePattern)" mask="url(#fadeMask)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 971 B |
@@ -122,8 +122,7 @@
|
||||
"ctrl-i": "editor::ShowSignatureHelp",
|
||||
"alt-g b": "editor::ToggleGitBlame",
|
||||
"menu": "editor::OpenContextMenu",
|
||||
"shift-f10": "editor::OpenContextMenu",
|
||||
"alt-enter": "editor::OpenSelectionsInMultibuffer"
|
||||
"shift-f10": "editor::OpenContextMenu"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -137,11 +136,12 @@
|
||||
"ctrl-k z": "editor::ToggleSoftWrap",
|
||||
"find": "buffer_search::Deploy",
|
||||
"ctrl-f": "buffer_search::Deploy",
|
||||
"ctrl-h": ["buffer_search::Deploy", { "replace_enabled": true }],
|
||||
"ctrl-h": "buffer_search::DeployReplace",
|
||||
// "cmd-e": ["buffer_search::Deploy", { "focus": false }],
|
||||
"ctrl->": "assistant::QuoteSelection",
|
||||
"ctrl-<": "assistant::InsertIntoEditor",
|
||||
"ctrl-alt-e": "editor::SelectEnclosingSymbol"
|
||||
"ctrl-alt-e": "editor::SelectEnclosingSymbol",
|
||||
"alt-enter": "editor::OpenSelectionsInMultibuffer"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -426,6 +426,7 @@
|
||||
"ctrl-shift-m": "diagnostics::Deploy",
|
||||
"ctrl-shift-e": "project_panel::ToggleFocus",
|
||||
"ctrl-shift-b": "outline_panel::ToggleFocus",
|
||||
"ctrl-shift-g": "git_panel::ToggleFocus",
|
||||
"ctrl-?": "assistant::ToggleFocus",
|
||||
"alt-save": "workspace::SaveAll",
|
||||
"ctrl-alt-s": "workspace::SaveAll",
|
||||
@@ -692,6 +693,34 @@
|
||||
"space": "project_panel::Open"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "GitPanel && !CommitEditor",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"escape": "git_panel::Close"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "GitPanel && ChangesList",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"up": "menu::SelectPrev",
|
||||
"down": "menu::SelectNext",
|
||||
"enter": "menu::Confirm",
|
||||
"space": "git::ToggleStaged",
|
||||
"ctrl-space": "git::StageAll",
|
||||
"ctrl-shift-space": "git::UnstageAll"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "GitPanel && CommitEditor > Editor",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"escape": "git_panel::FocusChanges",
|
||||
"ctrl-enter": "git::CommitChanges",
|
||||
"ctrl-shift-enter": "git::CommitAllChanges"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "CollabPanel && not_editing",
|
||||
"bindings": {
|
||||
@@ -769,6 +798,8 @@
|
||||
"shift-insert": "terminal::Paste",
|
||||
"ctrl-shift-v": "terminal::Paste",
|
||||
"ctrl-enter": "assistant::InlineAssist",
|
||||
"alt-b": ["terminal::SendText", "\u001bb"],
|
||||
"alt-f": ["terminal::SendText", "\u001bf"],
|
||||
// Overrides for conflicting keybindings
|
||||
"ctrl-w": ["terminal::SendKeystroke", "ctrl-w"],
|
||||
"ctrl-shift-a": "editor::SelectAll",
|
||||
@@ -792,5 +823,12 @@
|
||||
"shift-end": "terminal::ScrollToBottom",
|
||||
"ctrl-shift-space": "terminal::ToggleViMode"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "ZedPredictModal",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"escape": "menu::Cancel"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -132,8 +132,7 @@
|
||||
"cmd-alt-g b": "editor::ToggleGitBlame",
|
||||
"cmd-i": "editor::ShowSignatureHelp",
|
||||
"ctrl-f12": "editor::GoToDeclaration",
|
||||
"alt-ctrl-f12": "editor::GoToDeclarationSplit",
|
||||
"alt-enter": "editor::OpenSelectionsInMultibuffer"
|
||||
"alt-ctrl-f12": "editor::GoToDeclarationSplit"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -146,12 +145,13 @@
|
||||
"cmd-shift-enter": "editor::NewlineAbove",
|
||||
"cmd-k z": "editor::ToggleSoftWrap",
|
||||
"cmd-f": "buffer_search::Deploy",
|
||||
"cmd-alt-f": ["buffer_search::Deploy", { "replace_enabled": true }],
|
||||
"cmd-alt-f": "buffer_search::DeployReplace",
|
||||
"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"
|
||||
"cmd-alt-e": "editor::SelectEnclosingSymbol",
|
||||
"alt-enter": "editor::OpenSelectionsInMultibuffer"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -834,6 +834,8 @@
|
||||
// Terminal.app compatibility
|
||||
"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"],
|
||||
@@ -881,7 +883,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "ZedPredictTos",
|
||||
"context": "ZedPredictModal",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"escape": "menu::Cancel"
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"cmd-b": "editor::GoToDefinition",
|
||||
"cmd-j": "editor::ScrollCursorCenter",
|
||||
"cmd-enter": "editor::NewlineBelow",
|
||||
"cmd-alt-enter": "editor::NewLineAbove",
|
||||
"cmd-alt-enter": "editor::NewlineAbove",
|
||||
"cmd-shift-l": "editor::SelectLine",
|
||||
"cmd-shift-t": "outline::Toggle",
|
||||
"alt-backspace": "editor::DeleteToPreviousWordStart",
|
||||
@@ -70,7 +70,7 @@
|
||||
"bindings": {
|
||||
"cmd-backspace": ["project_panel::Trash", { "skip_prompt": true }],
|
||||
"cmd-d": "project_panel::Duplicate",
|
||||
"cmd-n": "project_panel::NewFolder",
|
||||
"cmd-n": "project_panel::NewDirectory",
|
||||
"return": "project_panel::Rename",
|
||||
"cmd-c": "project_panel::Copy",
|
||||
"cmd-v": "project_panel::Paste",
|
||||
|
||||
@@ -408,6 +408,7 @@
|
||||
"(": "vim::Parentheses",
|
||||
")": "vim::Parentheses",
|
||||
"b": "vim::Parentheses",
|
||||
// "b": "vim::AnyBrackets",
|
||||
"[": "vim::SquareBrackets",
|
||||
"]": "vim::SquareBrackets",
|
||||
"r": "vim::SquareBrackets",
|
||||
@@ -668,5 +669,20 @@
|
||||
"shift-g": "menu::SelectLast",
|
||||
"g g": "menu::SelectFirst"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "GitPanel && ChangesList",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"k": "menu::SelectPrev",
|
||||
"j": "menu::SelectNext",
|
||||
"g g": "menu::SelectFirst",
|
||||
"shift-g": "menu::SelectLast",
|
||||
"g f": "menu::Confirm",
|
||||
"i": "git_panel::FocusEditor",
|
||||
"x": "git::ToggleStaged",
|
||||
"shift-x": "git::StageAll",
|
||||
"shift-u": "git::UnstageAll"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -10,8 +10,9 @@
|
||||
"light": "One Light",
|
||||
"dark": "One Dark"
|
||||
},
|
||||
"icon_theme": "Zed (Default)",
|
||||
// The name of a base set of key bindings to use.
|
||||
// This setting can take four values, each named after another
|
||||
// This setting can take six values, each named after another
|
||||
// text editor:
|
||||
//
|
||||
// 1. "VSCode"
|
||||
@@ -202,7 +203,7 @@
|
||||
// Example: ["string", "comment"]
|
||||
"inline_completions_disabled_in": [],
|
||||
// Whether to show tabs and spaces in the editor.
|
||||
// This setting can take three values:
|
||||
// This setting can take four values:
|
||||
//
|
||||
// 1. Draw tabs and spaces only for the selected text (default):
|
||||
// "selection"
|
||||
@@ -392,7 +393,7 @@
|
||||
/// Scrollbar-related settings
|
||||
"scrollbar": {
|
||||
/// When to show the scrollbar in the project panel.
|
||||
/// This setting can take four values:
|
||||
/// This setting can take five values:
|
||||
///
|
||||
/// 1. null (default): Inherit editor settings
|
||||
/// 2. Show the scrollbar if there's important information or
|
||||
@@ -464,7 +465,7 @@
|
||||
/// Scrollbar-related settings
|
||||
"scrollbar": {
|
||||
/// When to show the scrollbar in the project panel.
|
||||
/// This setting can take four values:
|
||||
/// This setting can take five values:
|
||||
///
|
||||
/// 1. null (default): Inherit editor settings
|
||||
/// 2. Show the scrollbar if there's important information or
|
||||
@@ -592,7 +593,9 @@
|
||||
// Whether or not to show the tab bar in the editor
|
||||
"show": true,
|
||||
// Whether or not to show the navigation history buttons.
|
||||
"show_nav_history_buttons": true
|
||||
"show_nav_history_buttons": true,
|
||||
/// Whether or not to show the tab bar buttons.
|
||||
"show_tab_bar_buttons": true
|
||||
},
|
||||
// Settings related to the editor's tabs
|
||||
"tabs": {
|
||||
@@ -912,7 +915,7 @@
|
||||
/// Scrollbar-related settings
|
||||
"scrollbar": {
|
||||
/// When to show the scrollbar in the terminal.
|
||||
/// This setting can take four values:
|
||||
/// This setting can take five values:
|
||||
///
|
||||
/// 1. null (default): Inherit editor settings
|
||||
/// 2. Show the scrollbar if there's important information or
|
||||
@@ -1166,6 +1169,9 @@
|
||||
},
|
||||
"lmstudio": {
|
||||
"api_url": "http://localhost:1234/api/v0"
|
||||
},
|
||||
"deepseek": {
|
||||
"api_url": "https://api.deepseek.com"
|
||||
}
|
||||
},
|
||||
// Zed's Prettier integration settings.
|
||||
|
||||
@@ -3,7 +3,7 @@ name = "anthropic"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
publish.workspace = true
|
||||
license = "AGPL-3.0-or-later"
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
../../LICENSE-AGPL
|
||||
@@ -148,8 +148,13 @@ impl Model {
|
||||
}
|
||||
}
|
||||
|
||||
pub const DEFAULT_BETA_HEADERS: &[&str] = &["prompt-caching-2024-07-31"];
|
||||
|
||||
pub fn beta_headers(&self) -> String {
|
||||
let mut headers = vec!["prompt-caching-2024-07-31".to_string()];
|
||||
let mut headers = Self::DEFAULT_BETA_HEADERS
|
||||
.into_iter()
|
||||
.map(|header| header.to_string())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if let Self::Custom {
|
||||
extra_beta_headers, ..
|
||||
@@ -186,12 +191,14 @@ pub async fn complete(
|
||||
request: Request,
|
||||
) -> Result<Response, AnthropicError> {
|
||||
let uri = format!("{api_url}/v1/messages");
|
||||
let model = Model::from_id(&request.model)?;
|
||||
let beta_headers = Model::from_id(&request.model)
|
||||
.map(|model| model.beta_headers())
|
||||
.unwrap_or_else(|_err| Model::DEFAULT_BETA_HEADERS.join(","));
|
||||
let request_builder = HttpRequest::builder()
|
||||
.method(Method::POST)
|
||||
.uri(uri)
|
||||
.header("Anthropic-Version", "2023-06-01")
|
||||
.header("Anthropic-Beta", model.beta_headers())
|
||||
.header("Anthropic-Beta", beta_headers)
|
||||
.header("X-Api-Key", api_key)
|
||||
.header("Content-Type", "application/json");
|
||||
|
||||
@@ -302,12 +309,14 @@ pub async fn stream_completion_with_rate_limit_info(
|
||||
stream: true,
|
||||
};
|
||||
let uri = format!("{api_url}/v1/messages");
|
||||
let model = Model::from_id(&request.base.model)?;
|
||||
let beta_headers = Model::from_id(&request.base.model)
|
||||
.map(|model| model.beta_headers())
|
||||
.unwrap_or_else(|_err| Model::DEFAULT_BETA_HEADERS.join(","));
|
||||
let request_builder = HttpRequest::builder()
|
||||
.method(Method::POST)
|
||||
.uri(uri)
|
||||
.header("Anthropic-Version", "2023-06-01")
|
||||
.header("Anthropic-Beta", model.beta_headers())
|
||||
.header("Anthropic-Beta", beta_headers)
|
||||
.header("X-Api-Key", api_key)
|
||||
.header("Content-Type", "application/json");
|
||||
let serialized_request =
|
||||
|
||||
@@ -11,7 +11,6 @@ use assistant_context_editor::{
|
||||
};
|
||||
use assistant_settings::{AssistantDockPosition, AssistantSettings};
|
||||
use assistant_slash_command::SlashCommandWorkingSet;
|
||||
use assistant_tool::ToolWorkingSet;
|
||||
use client::{proto, Client, Status};
|
||||
use editor::{Editor, EditorEvent};
|
||||
use fs::Fs;
|
||||
@@ -100,11 +99,10 @@ impl AssistantPanel {
|
||||
) -> Task<Result<Entity<Self>>> {
|
||||
cx.spawn(|mut cx| async move {
|
||||
let slash_commands = Arc::new(SlashCommandWorkingSet::default());
|
||||
let tools = Arc::new(ToolWorkingSet::default());
|
||||
let context_store = workspace
|
||||
.update(&mut cx, |workspace, cx| {
|
||||
let project = workspace.project().clone();
|
||||
ContextStore::new(project, prompt_builder.clone(), slash_commands, tools, cx)
|
||||
ContextStore::new(project, prompt_builder.clone(), slash_commands, cx)
|
||||
})?
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -1878,19 +1878,17 @@ impl PromptEditor {
|
||||
) {
|
||||
match event {
|
||||
EditorEvent::Edited { .. } => {
|
||||
if let Some(workspace) = window.window_handle().downcast::<Workspace>() {
|
||||
workspace
|
||||
.update(cx, |workspace, _, cx| {
|
||||
let is_via_ssh = workspace
|
||||
.project()
|
||||
.update(cx, |project, _| project.is_via_ssh());
|
||||
if let Some(workspace) = window.root::<Workspace>().flatten() {
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
let is_via_ssh = workspace
|
||||
.project()
|
||||
.update(cx, |project, _| project.is_via_ssh());
|
||||
|
||||
workspace
|
||||
.client()
|
||||
.telemetry()
|
||||
.log_edit_event("inline assist", is_via_ssh);
|
||||
})
|
||||
.log_err();
|
||||
workspace
|
||||
.client()
|
||||
.telemetry()
|
||||
.log_edit_event("inline assist", is_via_ssh);
|
||||
});
|
||||
}
|
||||
let prompt = self.editor.read(cx).text(cx);
|
||||
if self
|
||||
|
||||
@@ -298,7 +298,7 @@ impl ActiveThread {
|
||||
let styled_message = match message.role {
|
||||
Role::User => v_flex()
|
||||
.id(("message-container", ix))
|
||||
.py_1()
|
||||
.pt_2p5()
|
||||
.px_2p5()
|
||||
.child(
|
||||
v_flex()
|
||||
@@ -350,7 +350,6 @@ impl Render for ActiveThread {
|
||||
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
||||
v_flex()
|
||||
.size_full()
|
||||
.pt_1p5()
|
||||
.child(list(self.list_state.clone()).flex_grow())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::sync::Arc;
|
||||
use collections::HashMap;
|
||||
use gpui::{AnyView, App, EventEmitter, FocusHandle, Focusable, Subscription};
|
||||
use language_model::{LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry};
|
||||
use ui::{prelude::*, ElevationIndex};
|
||||
use ui::{prelude::*, Divider, DividerColor, ElevationIndex};
|
||||
use zed_actions::assistant::DeployPromptLibrary;
|
||||
|
||||
pub struct AssistantConfiguration {
|
||||
@@ -91,38 +91,47 @@ impl AssistantConfiguration {
|
||||
.cloned();
|
||||
|
||||
v_flex()
|
||||
.gap_2()
|
||||
.gap_1p5()
|
||||
.child(
|
||||
h_flex()
|
||||
.justify_between()
|
||||
.child(Headline::new(provider_name.clone()).size(HeadlineSize::Small))
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_2()
|
||||
.child(
|
||||
Icon::new(provider.icon())
|
||||
.size(IconSize::Small)
|
||||
.color(Color::Muted),
|
||||
)
|
||||
.child(Label::new(provider_name.clone())),
|
||||
)
|
||||
.when(provider.is_authenticated(cx), |parent| {
|
||||
parent.child(
|
||||
h_flex().justify_end().child(
|
||||
Button::new(
|
||||
SharedString::from(format!("new-thread-{provider_id}")),
|
||||
"Open New Thread",
|
||||
)
|
||||
.icon_position(IconPosition::Start)
|
||||
.icon(IconName::Plus)
|
||||
.style(ButtonStyle::Filled)
|
||||
.layer(ElevationIndex::ModalSurface)
|
||||
.on_click(cx.listener({
|
||||
let provider = provider.clone();
|
||||
move |_this, _event, _window, cx| {
|
||||
cx.emit(AssistantConfigurationEvent::NewThread(
|
||||
provider.clone(),
|
||||
))
|
||||
}
|
||||
})),
|
||||
),
|
||||
Button::new(
|
||||
SharedString::from(format!("new-thread-{provider_id}")),
|
||||
"Start New Thread",
|
||||
)
|
||||
.icon_position(IconPosition::Start)
|
||||
.icon(IconName::Plus)
|
||||
.icon_size(IconSize::Small)
|
||||
.style(ButtonStyle::Filled)
|
||||
.layer(ElevationIndex::ModalSurface)
|
||||
.label_size(LabelSize::Small)
|
||||
.on_click(cx.listener({
|
||||
let provider = provider.clone();
|
||||
move |_this, _event, _window, cx| {
|
||||
cx.emit(AssistantConfigurationEvent::NewThread(
|
||||
provider.clone(),
|
||||
))
|
||||
}
|
||||
})),
|
||||
)
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.p(DynamicSpacing::Base08.rems(cx))
|
||||
.bg(cx.theme().colors().surface_background)
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.border_1()
|
||||
.border_color(cx.theme().colors().border_variant)
|
||||
.rounded_md()
|
||||
@@ -143,26 +152,43 @@ impl Render for AssistantConfiguration {
|
||||
v_flex()
|
||||
.id("assistant-configuration")
|
||||
.track_focus(&self.focus_handle(cx))
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.bg(cx.theme().colors().panel_background)
|
||||
.size_full()
|
||||
.overflow_y_scroll()
|
||||
.child(
|
||||
h_flex().p(DynamicSpacing::Base16.rems(cx)).child(
|
||||
Button::new("open-prompt-library", "Open Prompt Library")
|
||||
.style(ButtonStyle::Filled)
|
||||
.full_width()
|
||||
.icon(IconName::Book)
|
||||
.icon_size(IconSize::Small)
|
||||
.icon_position(IconPosition::Start)
|
||||
.on_click(|_event, _window, cx| cx.dispatch_action(&DeployPromptLibrary)),
|
||||
),
|
||||
v_flex()
|
||||
.p(DynamicSpacing::Base16.rems(cx))
|
||||
.gap_1()
|
||||
.child(Headline::new("Prompt Library").size(HeadlineSize::Small))
|
||||
.child(
|
||||
Button::new("open-prompt-library", "Open Prompt Library")
|
||||
.style(ButtonStyle::Filled)
|
||||
.layer(ElevationIndex::ModalSurface)
|
||||
.full_width()
|
||||
.icon(IconName::Book)
|
||||
.icon_size(IconSize::Small)
|
||||
.icon_position(IconPosition::Start)
|
||||
.on_click(|_event, _window, cx| {
|
||||
cx.dispatch_action(&DeployPromptLibrary)
|
||||
}),
|
||||
),
|
||||
)
|
||||
.child(Divider::horizontal().color(DividerColor::Border))
|
||||
.child(
|
||||
v_flex()
|
||||
.p(DynamicSpacing::Base16.rems(cx))
|
||||
.mt_1()
|
||||
.gap_6()
|
||||
.flex_1()
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_0p5()
|
||||
.child(Headline::new("LLM Providers").size(HeadlineSize::Small))
|
||||
.child(
|
||||
Label::new("Add at least one provider to use AI-powered features.")
|
||||
.color(Color::Muted),
|
||||
),
|
||||
)
|
||||
.children(
|
||||
providers
|
||||
.into_iter()
|
||||
|
||||
@@ -119,12 +119,10 @@ impl AssistantPanel {
|
||||
cx.spawn(|mut cx| async move {
|
||||
let tools = Arc::new(ToolWorkingSet::default());
|
||||
log::info!("[assistant2-debug] initializing ThreadStore");
|
||||
let thread_store = workspace
|
||||
.update(&mut cx, |workspace, cx| {
|
||||
let project = workspace.project().clone();
|
||||
ThreadStore::new(project, tools.clone(), cx)
|
||||
})?
|
||||
.await?;
|
||||
let thread_store = workspace.update(&mut cx, |workspace, cx| {
|
||||
let project = workspace.project().clone();
|
||||
ThreadStore::new(project, tools.clone(), cx)
|
||||
})??;
|
||||
log::info!("[assistant2-debug] finished initializing ThreadStore");
|
||||
|
||||
let slash_commands = Arc::new(SlashCommandWorkingSet::default());
|
||||
@@ -136,7 +134,6 @@ impl AssistantPanel {
|
||||
project,
|
||||
prompt_builder.clone(),
|
||||
slash_commands,
|
||||
tools.clone(),
|
||||
cx,
|
||||
)
|
||||
})?
|
||||
@@ -614,9 +611,16 @@ impl AssistantPanel {
|
||||
SharedString::from(context_editor.read(cx).title(cx).to_string())
|
||||
})
|
||||
.unwrap_or_else(|| SharedString::from("Loading Summary…")),
|
||||
ActiveView::History => "History / Thread".into(),
|
||||
ActiveView::PromptEditorHistory => "History / Prompt Editor".into(),
|
||||
ActiveView::Configuration => "Configuration".into(),
|
||||
ActiveView::History | ActiveView::PromptEditorHistory => "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()
|
||||
@@ -629,7 +633,24 @@ impl AssistantPanel {
|
||||
.bg(cx.theme().colors().tab_bar_background)
|
||||
.border_b_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.child(h_flex().child(Label::new(title)))
|
||||
.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(
|
||||
h_flex()
|
||||
.h_full()
|
||||
@@ -678,9 +699,9 @@ impl AssistantPanel {
|
||||
IconButton::new("configure-assistant", IconName::Settings)
|
||||
.icon_size(IconSize::Small)
|
||||
.style(ButtonStyle::Subtle)
|
||||
.tooltip(Tooltip::text("Configure Assistant"))
|
||||
.on_click(move |_event, _window, cx| {
|
||||
cx.dispatch_action(&OpenConfiguration);
|
||||
.tooltip(Tooltip::text("Assistant Settings"))
|
||||
.on_click(move |_event, window, cx| {
|
||||
window.dispatch_action(OpenConfiguration.boxed_clone(), cx);
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -124,7 +124,7 @@ impl FileContextPickerDelegate {
|
||||
let file_matches = project.worktrees(cx).flat_map(|worktree| {
|
||||
let worktree = worktree.read(cx);
|
||||
let path_prefix: Arc<str> = worktree.root_name().into();
|
||||
worktree.files(true, 0).map(move |entry| PathMatch {
|
||||
worktree.files(false, 0).map(move |entry| PathMatch {
|
||||
score: 0.,
|
||||
positions: Vec::new(),
|
||||
worktree_id: worktree.id().to_usize(),
|
||||
|
||||
@@ -304,19 +304,17 @@ impl<T: 'static> PromptEditor<T> {
|
||||
) {
|
||||
match event {
|
||||
EditorEvent::Edited { .. } => {
|
||||
if let Some(workspace) = window.window_handle().downcast::<Workspace>() {
|
||||
workspace
|
||||
.update(cx, |workspace, _, cx| {
|
||||
let is_via_ssh = workspace
|
||||
.project()
|
||||
.update(cx, |project, _| project.is_via_ssh());
|
||||
if let Some(workspace) = window.root::<Workspace>().flatten() {
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
let is_via_ssh = workspace
|
||||
.project()
|
||||
.update(cx, |project, _| project.is_via_ssh());
|
||||
|
||||
workspace
|
||||
.client()
|
||||
.telemetry()
|
||||
.log_edit_event("inline assist", is_via_ssh);
|
||||
})
|
||||
.log_err();
|
||||
workspace
|
||||
.client()
|
||||
.telemetry()
|
||||
.log_edit_event("inline assist", is_via_ssh);
|
||||
});
|
||||
}
|
||||
let prompt = self.editor.read(cx).text(cx);
|
||||
if self
|
||||
|
||||
@@ -225,16 +225,16 @@ impl RenderOnce for PastThread {
|
||||
.child(Label::new(summary).size(LabelSize::Small).text_ellipsis())
|
||||
.end_slot(
|
||||
h_flex()
|
||||
.gap_2()
|
||||
.gap_1p5()
|
||||
.child(
|
||||
Label::new(thread_timestamp)
|
||||
.color(Color::Disabled)
|
||||
.size(LabelSize::Small),
|
||||
.color(Color::Muted)
|
||||
.size(LabelSize::XSmall),
|
||||
)
|
||||
.child(
|
||||
IconButton::new("delete", IconName::TrashAlt)
|
||||
.shape(IconButtonShape::Square)
|
||||
.icon_size(IconSize::Small)
|
||||
.icon_size(IconSize::XSmall)
|
||||
.tooltip(Tooltip::text("Delete Thread"))
|
||||
.on_click({
|
||||
let assistant_panel = self.assistant_panel.clone();
|
||||
|
||||
@@ -34,45 +34,39 @@ impl ThreadStore {
|
||||
project: Entity<Project>,
|
||||
tools: Arc<ToolWorkingSet>,
|
||||
cx: &mut App,
|
||||
) -> Task<Result<Entity<Self>>> {
|
||||
cx.spawn(|mut cx| async move {
|
||||
let this = cx.new(|cx: &mut Context<Self>| {
|
||||
let context_server_factory_registry =
|
||||
ContextServerFactoryRegistry::default_global(cx);
|
||||
let context_server_manager = cx.new(|cx| {
|
||||
ContextServerManager::new(context_server_factory_registry, project.clone(), cx)
|
||||
});
|
||||
) -> Result<Entity<Self>> {
|
||||
let this = cx.new(|cx| {
|
||||
let context_server_factory_registry = ContextServerFactoryRegistry::default_global(cx);
|
||||
let context_server_manager = cx.new(|cx| {
|
||||
ContextServerManager::new(context_server_factory_registry, project.clone(), cx)
|
||||
});
|
||||
|
||||
let executor = cx.background_executor().clone();
|
||||
let database_future = executor
|
||||
.spawn({
|
||||
let executor = executor.clone();
|
||||
let database_path = paths::support_dir().join("threads/threads-db.0.mdb");
|
||||
async move { ThreadsDatabase::new(database_path, executor) }
|
||||
})
|
||||
.then(|result| future::ready(result.map(Arc::new).map_err(Arc::new)))
|
||||
.boxed()
|
||||
.shared();
|
||||
let executor = cx.background_executor().clone();
|
||||
let database_future = executor
|
||||
.spawn({
|
||||
let executor = executor.clone();
|
||||
let database_path = paths::support_dir().join("threads/threads-db.0.mdb");
|
||||
async move { ThreadsDatabase::new(database_path, executor) }
|
||||
})
|
||||
.then(|result| future::ready(result.map(Arc::new).map_err(Arc::new)))
|
||||
.boxed()
|
||||
.shared();
|
||||
|
||||
let this = Self {
|
||||
project,
|
||||
tools,
|
||||
context_server_manager,
|
||||
context_server_tool_ids: HashMap::default(),
|
||||
threads: Vec::new(),
|
||||
database_future,
|
||||
};
|
||||
this.register_context_server_handlers(cx);
|
||||
let this = Self {
|
||||
project,
|
||||
tools,
|
||||
context_server_manager,
|
||||
context_server_tool_ids: HashMap::default(),
|
||||
threads: Vec::new(),
|
||||
database_future,
|
||||
};
|
||||
this.register_context_server_handlers(cx);
|
||||
this.reload(cx).detach_and_log_err(cx);
|
||||
|
||||
this
|
||||
})?;
|
||||
this
|
||||
});
|
||||
|
||||
log::info!("[assistant2-debug] reloading threads");
|
||||
this.update(&mut cx, |this, cx| this.reload(cx))?.await?;
|
||||
log::info!("[assistant2-debug] finished reloading threads");
|
||||
|
||||
Ok(this)
|
||||
})
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
/// Returns the number of threads.
|
||||
|
||||
@@ -16,14 +16,12 @@ anyhow.workspace = true
|
||||
assistant_settings.workspace = true
|
||||
assistant_slash_command.workspace = true
|
||||
assistant_slash_commands.workspace = true
|
||||
assistant_tool.workspace = true
|
||||
chrono.workspace = true
|
||||
client.workspace = true
|
||||
clock.workspace = true
|
||||
collections.workspace = true
|
||||
context_server.workspace = true
|
||||
editor.workspace = true
|
||||
feature_flags.workspace = true
|
||||
fs.workspace = true
|
||||
futures.workspace = true
|
||||
fuzzy.workspace = true
|
||||
|
||||
@@ -8,11 +8,9 @@ use assistant_slash_command::{
|
||||
SlashCommandResult, SlashCommandWorkingSet,
|
||||
};
|
||||
use assistant_slash_commands::FileCommandMetadata;
|
||||
use assistant_tool::ToolWorkingSet;
|
||||
use client::{self, proto, telemetry::Telemetry};
|
||||
use clock::ReplicaId;
|
||||
use collections::{HashMap, HashSet};
|
||||
use feature_flags::{FeatureFlagAppExt, ToolUseFeatureFlag};
|
||||
use fs::{Fs, RemoveOptions};
|
||||
use futures::{future::Shared, FutureExt, StreamExt};
|
||||
use gpui::{
|
||||
@@ -23,7 +21,6 @@ use language::{AnchorRangeExt, Bias, Buffer, LanguageRegistry, OffsetRangeExt, P
|
||||
use language_model::{
|
||||
LanguageModel, LanguageModelCacheConfiguration, LanguageModelCompletionEvent,
|
||||
LanguageModelImage, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage,
|
||||
LanguageModelRequestTool, LanguageModelToolResult, LanguageModelToolUse,
|
||||
LanguageModelToolUseId, MessageContent, Role, StopReason,
|
||||
};
|
||||
use language_models::{
|
||||
@@ -438,11 +435,6 @@ pub enum ContextEvent {
|
||||
SlashCommandOutputSectionAdded {
|
||||
section: SlashCommandOutputSection<language::Anchor>,
|
||||
},
|
||||
UsePendingTools,
|
||||
ToolFinished {
|
||||
tool_use_id: LanguageModelToolUseId,
|
||||
output_range: Range<language::Anchor>,
|
||||
},
|
||||
Operation(ContextOperation),
|
||||
}
|
||||
|
||||
@@ -528,21 +520,12 @@ pub enum Content {
|
||||
render_image: Arc<RenderImage>,
|
||||
image: Shared<Task<Option<LanguageModelImage>>>,
|
||||
},
|
||||
ToolUse {
|
||||
range: Range<language::Anchor>,
|
||||
tool_use: LanguageModelToolUse,
|
||||
},
|
||||
ToolResult {
|
||||
range: Range<language::Anchor>,
|
||||
tool_use_id: LanguageModelToolUseId,
|
||||
},
|
||||
}
|
||||
|
||||
impl Content {
|
||||
fn range(&self) -> Range<language::Anchor> {
|
||||
match self {
|
||||
Self::Image { anchor, .. } => *anchor..*anchor,
|
||||
Self::ToolUse { range, .. } | Self::ToolResult { range, .. } => range.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -599,9 +582,7 @@ pub struct AssistantContext {
|
||||
invoked_slash_commands: HashMap<InvokedSlashCommandId, InvokedSlashCommand>,
|
||||
edits_since_last_parse: language::Subscription,
|
||||
slash_commands: Arc<SlashCommandWorkingSet>,
|
||||
tools: Arc<ToolWorkingSet>,
|
||||
slash_command_output_sections: Vec<SlashCommandOutputSection<language::Anchor>>,
|
||||
pending_tool_uses_by_id: HashMap<LanguageModelToolUseId, PendingToolUse>,
|
||||
message_anchors: Vec<MessageAnchor>,
|
||||
contents: Vec<Content>,
|
||||
messages_metadata: HashMap<MessageId, MessageMetadata>,
|
||||
@@ -654,7 +635,6 @@ impl AssistantContext {
|
||||
telemetry: Option<Arc<Telemetry>>,
|
||||
prompt_builder: Arc<PromptBuilder>,
|
||||
slash_commands: Arc<SlashCommandWorkingSet>,
|
||||
tools: Arc<ToolWorkingSet>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
Self::new(
|
||||
@@ -664,7 +644,6 @@ impl AssistantContext {
|
||||
language_registry,
|
||||
prompt_builder,
|
||||
slash_commands,
|
||||
tools,
|
||||
project,
|
||||
telemetry,
|
||||
cx,
|
||||
@@ -679,7 +658,6 @@ impl AssistantContext {
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
prompt_builder: Arc<PromptBuilder>,
|
||||
slash_commands: Arc<SlashCommandWorkingSet>,
|
||||
tools: Arc<ToolWorkingSet>,
|
||||
project: Option<Entity<Project>>,
|
||||
telemetry: Option<Arc<Telemetry>>,
|
||||
cx: &mut Context<Self>,
|
||||
@@ -707,7 +685,6 @@ impl AssistantContext {
|
||||
messages_metadata: Default::default(),
|
||||
parsed_slash_commands: Vec::new(),
|
||||
invoked_slash_commands: HashMap::default(),
|
||||
pending_tool_uses_by_id: HashMap::default(),
|
||||
slash_command_output_sections: Vec::new(),
|
||||
edits_since_last_parse: edits_since_last_slash_command_parse,
|
||||
summary: None,
|
||||
@@ -725,7 +702,6 @@ impl AssistantContext {
|
||||
project,
|
||||
language_registry,
|
||||
slash_commands,
|
||||
tools,
|
||||
patches: Vec::new(),
|
||||
xml_tags: Vec::new(),
|
||||
prompt_builder,
|
||||
@@ -802,7 +778,6 @@ impl AssistantContext {
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
prompt_builder: Arc<PromptBuilder>,
|
||||
slash_commands: Arc<SlashCommandWorkingSet>,
|
||||
tools: Arc<ToolWorkingSet>,
|
||||
project: Option<Entity<Project>>,
|
||||
telemetry: Option<Arc<Telemetry>>,
|
||||
cx: &mut Context<Self>,
|
||||
@@ -815,7 +790,6 @@ impl AssistantContext {
|
||||
language_registry,
|
||||
prompt_builder,
|
||||
slash_commands,
|
||||
tools,
|
||||
project,
|
||||
telemetry,
|
||||
cx,
|
||||
@@ -848,10 +822,6 @@ impl AssistantContext {
|
||||
&self.slash_commands
|
||||
}
|
||||
|
||||
pub fn tools(&self) -> &Arc<ToolWorkingSet> {
|
||||
&self.tools
|
||||
}
|
||||
|
||||
pub fn set_capability(&mut self, capability: language::Capability, cx: &mut Context<Self>) {
|
||||
self.buffer
|
||||
.update(cx, |buffer, cx| buffer.set_capability(capability, cx));
|
||||
@@ -1177,14 +1147,6 @@ impl AssistantContext {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn pending_tool_uses(&self) -> Vec<&PendingToolUse> {
|
||||
self.pending_tool_uses_by_id.values().collect()
|
||||
}
|
||||
|
||||
pub fn get_tool_use_by_id(&self, id: &LanguageModelToolUseId) -> Option<&PendingToolUse> {
|
||||
self.pending_tool_uses_by_id.get(id)
|
||||
}
|
||||
|
||||
fn set_language(&mut self, cx: &mut Context<Self>) {
|
||||
let markdown = self.language_registry.language_for_name("Markdown");
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
@@ -2206,68 +2168,6 @@ impl AssistantContext {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn insert_tool_output(
|
||||
&mut self,
|
||||
tool_use_id: LanguageModelToolUseId,
|
||||
output: Task<Result<String>>,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let insert_output_task = cx.spawn(|this, mut cx| {
|
||||
let tool_use_id = tool_use_id.clone();
|
||||
async move {
|
||||
let output = output.await;
|
||||
this.update(&mut cx, |this, cx| match output {
|
||||
Ok(mut output) => {
|
||||
const NEWLINE: char = '\n';
|
||||
|
||||
if !output.ends_with(NEWLINE) {
|
||||
output.push(NEWLINE);
|
||||
}
|
||||
|
||||
let anchor_range = this.buffer.update(cx, |buffer, cx| {
|
||||
let insert_start = buffer.len().to_offset(buffer);
|
||||
let insert_end = insert_start;
|
||||
|
||||
let start = insert_start;
|
||||
let end = start + output.len() - NEWLINE.len_utf8();
|
||||
|
||||
buffer.edit([(insert_start..insert_end, output)], None, cx);
|
||||
|
||||
let output_range = buffer.anchor_after(start)..buffer.anchor_after(end);
|
||||
|
||||
output_range
|
||||
});
|
||||
|
||||
this.insert_content(
|
||||
Content::ToolResult {
|
||||
range: anchor_range.clone(),
|
||||
tool_use_id: tool_use_id.clone(),
|
||||
},
|
||||
cx,
|
||||
);
|
||||
|
||||
cx.emit(ContextEvent::ToolFinished {
|
||||
tool_use_id,
|
||||
output_range: anchor_range,
|
||||
});
|
||||
}
|
||||
Err(err) => {
|
||||
if let Some(tool_use) = this.pending_tool_uses_by_id.get_mut(&tool_use_id) {
|
||||
tool_use.status = PendingToolUseStatus::Error(err.to_string());
|
||||
}
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(tool_use) = self.pending_tool_uses_by_id.get_mut(&tool_use_id) {
|
||||
tool_use.status = PendingToolUseStatus::Running {
|
||||
_task: insert_output_task.shared(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn completion_provider_changed(&mut self, cx: &mut Context<Self>) {
|
||||
self.count_remaining_tokens(cx);
|
||||
}
|
||||
@@ -2298,23 +2198,7 @@ impl AssistantContext {
|
||||
// Compute which messages to cache, including the last one.
|
||||
self.mark_cache_anchors(&model.cache_configuration(), false, cx);
|
||||
|
||||
let mut request = self.to_completion_request(request_type, cx);
|
||||
|
||||
// Don't attach tools for now; we'll be removing tool use from
|
||||
// Assistant1 shortly.
|
||||
#[allow(clippy::overly_complex_bool_expr)]
|
||||
if false && cx.has_flag::<ToolUseFeatureFlag>() {
|
||||
request.tools = self
|
||||
.tools
|
||||
.tools(cx)
|
||||
.into_iter()
|
||||
.map(|tool| LanguageModelRequestTool {
|
||||
name: tool.name(),
|
||||
description: tool.description(),
|
||||
input_schema: tool.input_schema(),
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
let request = self.to_completion_request(request_type, cx);
|
||||
|
||||
let assistant_message = self
|
||||
.insert_message_after(last_message_id, Role::Assistant, MessageStatus::Pending, cx)
|
||||
@@ -2371,44 +2255,7 @@ impl AssistantContext {
|
||||
cx,
|
||||
);
|
||||
}
|
||||
LanguageModelCompletionEvent::ToolUse(tool_use) => {
|
||||
const NEWLINE: char = '\n';
|
||||
|
||||
let mut text = String::new();
|
||||
text.push(NEWLINE);
|
||||
text.push_str(
|
||||
&serde_json::to_string_pretty(&tool_use)
|
||||
.expect("failed to serialize tool use to JSON"),
|
||||
);
|
||||
text.push(NEWLINE);
|
||||
let text_len = text.len();
|
||||
|
||||
buffer.edit(
|
||||
[(
|
||||
message_old_end_offset..message_old_end_offset,
|
||||
text,
|
||||
)],
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
|
||||
let start_ix = message_old_end_offset + NEWLINE.len_utf8();
|
||||
let end_ix =
|
||||
message_old_end_offset + text_len - NEWLINE.len_utf8();
|
||||
let source_range = buffer.anchor_after(start_ix)
|
||||
..buffer.anchor_after(end_ix);
|
||||
|
||||
this.pending_tool_uses_by_id.insert(
|
||||
tool_use.id.clone(),
|
||||
PendingToolUse {
|
||||
id: tool_use.id,
|
||||
name: tool_use.name,
|
||||
input: tool_use.input,
|
||||
status: PendingToolUseStatus::Idle,
|
||||
source_range,
|
||||
},
|
||||
);
|
||||
}
|
||||
LanguageModelCompletionEvent::ToolUse(_) => {}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2491,9 +2338,7 @@ impl AssistantContext {
|
||||
|
||||
if let Ok(stop_reason) = result {
|
||||
match stop_reason {
|
||||
StopReason::ToolUse => {
|
||||
cx.emit(ContextEvent::UsePendingTools);
|
||||
}
|
||||
StopReason::ToolUse => {}
|
||||
StopReason::EndTurn => {}
|
||||
StopReason::MaxTokens => {}
|
||||
}
|
||||
@@ -2572,23 +2417,6 @@ impl AssistantContext {
|
||||
.push(language_model::MessageContent::Image(image));
|
||||
}
|
||||
}
|
||||
Content::ToolUse { tool_use, .. } => {
|
||||
request_message
|
||||
.content
|
||||
.push(language_model::MessageContent::ToolUse(tool_use.clone()));
|
||||
}
|
||||
Content::ToolResult { tool_use_id, .. } => {
|
||||
request_message.content.push(
|
||||
language_model::MessageContent::ToolResult(
|
||||
LanguageModelToolResult {
|
||||
tool_use_id: tool_use_id.to_string(),
|
||||
is_error: false,
|
||||
content: collect_text_content(buffer, range.clone())
|
||||
.unwrap_or_default(),
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
offset = range.end;
|
||||
|
||||
@@ -8,7 +8,6 @@ use assistant_slash_command::{
|
||||
SlashCommandOutputSection, SlashCommandRegistry, SlashCommandResult, SlashCommandWorkingSet,
|
||||
};
|
||||
use assistant_slash_commands::FileSlashCommand;
|
||||
use assistant_tool::ToolWorkingSet;
|
||||
use collections::{HashMap, HashSet};
|
||||
use fs::FakeFs;
|
||||
use futures::{
|
||||
@@ -56,7 +55,6 @@ fn test_inserting_and_removing_messages(cx: &mut App) {
|
||||
None,
|
||||
prompt_builder.clone(),
|
||||
Arc::new(SlashCommandWorkingSet::default()),
|
||||
Arc::new(ToolWorkingSet::default()),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
@@ -197,7 +195,6 @@ fn test_message_splitting(cx: &mut App) {
|
||||
None,
|
||||
prompt_builder.clone(),
|
||||
Arc::new(SlashCommandWorkingSet::default()),
|
||||
Arc::new(ToolWorkingSet::default()),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
@@ -300,7 +297,6 @@ fn test_messages_for_offsets(cx: &mut App) {
|
||||
None,
|
||||
prompt_builder.clone(),
|
||||
Arc::new(SlashCommandWorkingSet::default()),
|
||||
Arc::new(ToolWorkingSet::default()),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
@@ -414,7 +410,6 @@ async fn test_slash_commands(cx: &mut TestAppContext) {
|
||||
None,
|
||||
prompt_builder.clone(),
|
||||
Arc::new(SlashCommandWorkingSet::default()),
|
||||
Arc::new(ToolWorkingSet::default()),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
@@ -704,7 +699,6 @@ async fn test_workflow_step_parsing(cx: &mut TestAppContext) {
|
||||
None,
|
||||
prompt_builder.clone(),
|
||||
Arc::new(SlashCommandWorkingSet::default()),
|
||||
Arc::new(ToolWorkingSet::default()),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
@@ -969,7 +963,6 @@ async fn test_workflow_step_parsing(cx: &mut TestAppContext) {
|
||||
registry.clone(),
|
||||
prompt_builder.clone(),
|
||||
Arc::new(SlashCommandWorkingSet::default()),
|
||||
Arc::new(ToolWorkingSet::default()),
|
||||
None,
|
||||
None,
|
||||
cx,
|
||||
@@ -1088,7 +1081,6 @@ async fn test_serialization(cx: &mut TestAppContext) {
|
||||
None,
|
||||
prompt_builder.clone(),
|
||||
Arc::new(SlashCommandWorkingSet::default()),
|
||||
Arc::new(ToolWorkingSet::default()),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
@@ -1132,7 +1124,6 @@ async fn test_serialization(cx: &mut TestAppContext) {
|
||||
registry.clone(),
|
||||
prompt_builder.clone(),
|
||||
Arc::new(SlashCommandWorkingSet::default()),
|
||||
Arc::new(ToolWorkingSet::default()),
|
||||
None,
|
||||
None,
|
||||
cx,
|
||||
@@ -1191,7 +1182,6 @@ async fn test_random_context_collaboration(cx: &mut TestAppContext, mut rng: Std
|
||||
registry.clone(),
|
||||
prompt_builder.clone(),
|
||||
Arc::new(SlashCommandWorkingSet::default()),
|
||||
Arc::new(ToolWorkingSet::default()),
|
||||
None,
|
||||
None,
|
||||
cx,
|
||||
@@ -1451,7 +1441,6 @@ fn test_mark_cache_anchors(cx: &mut App) {
|
||||
None,
|
||||
prompt_builder.clone(),
|
||||
Arc::new(SlashCommandWorkingSet::default()),
|
||||
Arc::new(ToolWorkingSet::default()),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
@@ -5,7 +5,6 @@ use assistant_slash_commands::{
|
||||
selections_creases, DefaultSlashCommand, DocsSlashCommand, DocsSlashCommandArgs,
|
||||
FileSlashCommand,
|
||||
};
|
||||
use assistant_tool::ToolWorkingSet;
|
||||
use client::{proto, zed_urls};
|
||||
use collections::{hash_map, BTreeSet, HashMap, HashSet};
|
||||
use editor::{
|
||||
@@ -23,17 +22,17 @@ use fs::Fs;
|
||||
use futures::FutureExt;
|
||||
use gpui::{
|
||||
actions, div, img, impl_internal_actions, percentage, point, prelude::*, pulsating_between,
|
||||
size, Animation, AnimationExt, AnyElement, AnyView, AnyWindowHandle, App, AsyncWindowContext,
|
||||
ClipboardEntry, ClipboardItem, CursorStyle, Empty, Entity, EventEmitter, FocusHandle,
|
||||
Focusable, FontWeight, Global, InteractiveElement, IntoElement, ParentElement, Pixels, Render,
|
||||
RenderImage, SharedString, Size, StatefulInteractiveElement, Styled, Subscription, Task,
|
||||
Transformation, WeakEntity,
|
||||
size, Animation, AnimationExt, AnyElement, AnyView, App, AsyncWindowContext, ClipboardEntry,
|
||||
ClipboardItem, CursorStyle, Empty, Entity, EventEmitter, FocusHandle, Focusable, FontWeight,
|
||||
Global, InteractiveElement, IntoElement, ParentElement, Pixels, Render, RenderImage,
|
||||
SharedString, Size, StatefulInteractiveElement, Styled, Subscription, Task, Transformation,
|
||||
WeakEntity,
|
||||
};
|
||||
use indexed_docs::IndexedDocsStore;
|
||||
use language::{language_settings::SoftWrap, BufferSnapshot, LspAdapterDelegate, ToOffset};
|
||||
use language_model::{
|
||||
LanguageModelImage, LanguageModelProvider, LanguageModelProviderTosView, LanguageModelRegistry,
|
||||
LanguageModelToolUse, Role,
|
||||
Role,
|
||||
};
|
||||
use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};
|
||||
use multi_buffer::MultiBufferRow;
|
||||
@@ -171,7 +170,6 @@ pub struct ContextEditor {
|
||||
context: Entity<AssistantContext>,
|
||||
fs: Arc<dyn Fs>,
|
||||
slash_commands: Arc<SlashCommandWorkingSet>,
|
||||
tools: Arc<ToolWorkingSet>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
project: Entity<Project>,
|
||||
lsp_adapter_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||
@@ -182,7 +180,6 @@ pub struct ContextEditor {
|
||||
remote_id: Option<workspace::ViewId>,
|
||||
pending_slash_command_creases: HashMap<Range<language::Anchor>, CreaseId>,
|
||||
invoked_slash_command_creases: HashMap<InvokedSlashCommandId, CreaseId>,
|
||||
pending_tool_use_creases: HashMap<Range<language::Anchor>, CreaseId>,
|
||||
_subscriptions: Vec<Subscription>,
|
||||
patches: HashMap<Range<language::Anchor>, PatchViewState>,
|
||||
active_patch: Option<Range<language::Anchor>>,
|
||||
@@ -244,11 +241,9 @@ impl ContextEditor {
|
||||
let sections = context.read(cx).slash_command_output_sections().to_vec();
|
||||
let patch_ranges = context.read(cx).patch_ranges().collect::<Vec<_>>();
|
||||
let slash_commands = context.read(cx).slash_commands().clone();
|
||||
let tools = context.read(cx).tools().clone();
|
||||
let mut this = Self {
|
||||
context,
|
||||
slash_commands,
|
||||
tools,
|
||||
editor,
|
||||
lsp_adapter_delegate,
|
||||
blocks: Default::default(),
|
||||
@@ -260,7 +255,6 @@ impl ContextEditor {
|
||||
project,
|
||||
pending_slash_command_creases: HashMap::default(),
|
||||
invoked_slash_command_creases: HashMap::default(),
|
||||
pending_tool_use_creases: HashMap::default(),
|
||||
_subscriptions,
|
||||
patches: HashMap::default(),
|
||||
active_patch: None,
|
||||
@@ -580,87 +574,6 @@ impl ContextEditor {
|
||||
cx,
|
||||
);
|
||||
}
|
||||
|
||||
let new_tool_uses = self
|
||||
.context
|
||||
.read(cx)
|
||||
.pending_tool_uses()
|
||||
.into_iter()
|
||||
.filter(|tool_use| {
|
||||
!self
|
||||
.pending_tool_use_creases
|
||||
.contains_key(&tool_use.source_range)
|
||||
})
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let buffer = editor.buffer().read(cx).snapshot(cx);
|
||||
let (excerpt_id, _buffer_id, _) = buffer.as_singleton().unwrap();
|
||||
let excerpt_id = *excerpt_id;
|
||||
|
||||
let mut buffer_rows_to_fold = BTreeSet::new();
|
||||
|
||||
let creases = new_tool_uses
|
||||
.iter()
|
||||
.map(|tool_use| {
|
||||
let placeholder = FoldPlaceholder {
|
||||
render: render_fold_icon_button(
|
||||
cx.entity().downgrade(),
|
||||
IconName::PocketKnife,
|
||||
tool_use.name.clone().into(),
|
||||
),
|
||||
..Default::default()
|
||||
};
|
||||
let render_trailer =
|
||||
move |_row, _unfold, _window: &mut Window, _cx: &mut App| {
|
||||
Empty.into_any()
|
||||
};
|
||||
|
||||
let start = buffer
|
||||
.anchor_in_excerpt(excerpt_id, tool_use.source_range.start)
|
||||
.unwrap();
|
||||
let end = buffer
|
||||
.anchor_in_excerpt(excerpt_id, tool_use.source_range.end)
|
||||
.unwrap();
|
||||
|
||||
let buffer_row = MultiBufferRow(start.to_point(&buffer).row);
|
||||
buffer_rows_to_fold.insert(buffer_row);
|
||||
|
||||
self.context.update(cx, |context, cx| {
|
||||
context.insert_content(
|
||||
Content::ToolUse {
|
||||
range: tool_use.source_range.clone(),
|
||||
tool_use: LanguageModelToolUse {
|
||||
id: tool_use.id.clone(),
|
||||
name: tool_use.name.clone(),
|
||||
input: tool_use.input.clone(),
|
||||
},
|
||||
},
|
||||
cx,
|
||||
);
|
||||
});
|
||||
|
||||
Crease::inline(
|
||||
start..end,
|
||||
placeholder,
|
||||
fold_toggle("tool-use"),
|
||||
render_trailer,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let crease_ids = editor.insert_creases(creases, cx);
|
||||
|
||||
for buffer_row in buffer_rows_to_fold.into_iter().rev() {
|
||||
editor.fold_at(&FoldAt { buffer_row }, window, cx);
|
||||
}
|
||||
|
||||
self.pending_tool_use_creases.extend(
|
||||
new_tool_uses
|
||||
.iter()
|
||||
.map(|tool_use| tool_use.source_range.clone())
|
||||
.zip(crease_ids),
|
||||
);
|
||||
});
|
||||
}
|
||||
ContextEvent::PatchesUpdated { removed, updated } => {
|
||||
@@ -758,66 +671,6 @@ impl ContextEditor {
|
||||
ContextEvent::SlashCommandOutputSectionAdded { section } => {
|
||||
self.insert_slash_command_output_sections([section.clone()], false, window, cx);
|
||||
}
|
||||
ContextEvent::UsePendingTools => {
|
||||
let pending_tool_uses = self
|
||||
.context
|
||||
.read(cx)
|
||||
.pending_tool_uses()
|
||||
.into_iter()
|
||||
.filter(|tool_use| tool_use.status.is_idle())
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for tool_use in pending_tool_uses {
|
||||
if let Some(tool) = self.tools.tool(&tool_use.name, cx) {
|
||||
let task = tool.run(tool_use.input, self.workspace.clone(), window, cx);
|
||||
|
||||
self.context.update(cx, |context, cx| {
|
||||
context.insert_tool_output(tool_use.id.clone(), task, cx);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
ContextEvent::ToolFinished {
|
||||
tool_use_id,
|
||||
output_range,
|
||||
} => {
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
let buffer = editor.buffer().read(cx).snapshot(cx);
|
||||
let (excerpt_id, _buffer_id, _) = buffer.as_singleton().unwrap();
|
||||
let excerpt_id = *excerpt_id;
|
||||
|
||||
let placeholder = FoldPlaceholder {
|
||||
render: render_fold_icon_button(
|
||||
cx.entity().downgrade(),
|
||||
IconName::PocketKnife,
|
||||
format!("Tool Result: {tool_use_id}").into(),
|
||||
),
|
||||
..Default::default()
|
||||
};
|
||||
let render_trailer =
|
||||
move |_row, _unfold, _window: &mut Window, _cx: &mut App| Empty.into_any();
|
||||
|
||||
let start = buffer
|
||||
.anchor_in_excerpt(excerpt_id, output_range.start)
|
||||
.unwrap();
|
||||
let end = buffer
|
||||
.anchor_in_excerpt(excerpt_id, output_range.end)
|
||||
.unwrap();
|
||||
|
||||
let buffer_row = MultiBufferRow(start.to_point(&buffer).row);
|
||||
|
||||
let crease = Crease::inline(
|
||||
start..end,
|
||||
placeholder,
|
||||
fold_toggle("tool-use"),
|
||||
render_trailer,
|
||||
);
|
||||
|
||||
editor.insert_creases([crease], cx);
|
||||
editor.fold_at(&FoldAt { buffer_row }, window, cx);
|
||||
});
|
||||
}
|
||||
ContextEvent::Operation(_) => {}
|
||||
ContextEvent::ShowAssistError(error_message) => {
|
||||
self.last_error = Some(AssistError::Message(error_message.clone()));
|
||||
@@ -978,21 +831,20 @@ impl ContextEditor {
|
||||
.unwrap();
|
||||
let render_block: RenderBlock = Arc::new({
|
||||
let this = this.clone();
|
||||
let window_handle = window.window_handle();
|
||||
let patch_range = range.clone();
|
||||
move |cx: &mut BlockContext<'_, '_>| {
|
||||
let max_width = cx.max_width;
|
||||
let gutter_width = cx.gutter_dimensions.full_width();
|
||||
let block_id = cx.block_id;
|
||||
let selected = cx.selected;
|
||||
this.update(&mut **cx, |this, cx| {
|
||||
this.update_in(cx, |this, window, cx| {
|
||||
this.render_patch_block(
|
||||
patch_range.clone(),
|
||||
max_width,
|
||||
gutter_width,
|
||||
block_id,
|
||||
selected,
|
||||
window_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
@@ -2113,18 +1965,13 @@ impl ContextEditor {
|
||||
.context
|
||||
.read(cx)
|
||||
.contents(cx)
|
||||
.filter_map(|content| {
|
||||
if let Content::Image {
|
||||
anchor,
|
||||
render_image,
|
||||
..
|
||||
} = content
|
||||
{
|
||||
Some((anchor, render_image))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.map(
|
||||
|Content::Image {
|
||||
anchor,
|
||||
render_image,
|
||||
..
|
||||
}| (anchor, render_image),
|
||||
)
|
||||
.filter_map(|(anchor, render_image)| {
|
||||
const MAX_HEIGHT_IN_LINES: u32 = 8;
|
||||
let anchor = buffer.anchor_in_excerpt(excerpt_id, anchor).unwrap();
|
||||
@@ -2198,15 +2045,12 @@ impl ContextEditor {
|
||||
gutter_width: Pixels,
|
||||
id: BlockId,
|
||||
selected: bool,
|
||||
window_handle: AnyWindowHandle,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Option<AnyElement> {
|
||||
let snapshot = window_handle
|
||||
.update(cx, |_, window, cx| {
|
||||
self.editor
|
||||
.update(cx, |editor, cx| editor.snapshot(window, cx))
|
||||
})
|
||||
.ok()?;
|
||||
let snapshot = self
|
||||
.editor
|
||||
.update(cx, |editor, cx| editor.snapshot(window, cx));
|
||||
let (excerpt_id, _buffer_id, _) = snapshot.buffer_snapshot.as_singleton().unwrap();
|
||||
let excerpt_id = *excerpt_id;
|
||||
let anchor = snapshot
|
||||
|
||||
@@ -4,12 +4,11 @@ use crate::{
|
||||
};
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use assistant_slash_command::{SlashCommandId, SlashCommandWorkingSet};
|
||||
use assistant_tool::{ToolId, ToolWorkingSet};
|
||||
use client::{proto, telemetry::Telemetry, Client, TypedEnvelope};
|
||||
use clock::ReplicaId;
|
||||
use collections::HashMap;
|
||||
use context_server::manager::ContextServerManager;
|
||||
use context_server::{ContextServerFactoryRegistry, ContextServerTool};
|
||||
use context_server::ContextServerFactoryRegistry;
|
||||
use fs::Fs;
|
||||
use futures::StreamExt;
|
||||
use fuzzy::StringMatchCandidate;
|
||||
@@ -50,12 +49,10 @@ pub struct ContextStore {
|
||||
contexts_metadata: Vec<SavedContextMetadata>,
|
||||
context_server_manager: Entity<ContextServerManager>,
|
||||
context_server_slash_command_ids: HashMap<Arc<str>, Vec<SlashCommandId>>,
|
||||
context_server_tool_ids: HashMap<Arc<str>, Vec<ToolId>>,
|
||||
host_contexts: Vec<RemoteContextMetadata>,
|
||||
fs: Arc<dyn Fs>,
|
||||
languages: Arc<LanguageRegistry>,
|
||||
slash_commands: Arc<SlashCommandWorkingSet>,
|
||||
tools: Arc<ToolWorkingSet>,
|
||||
telemetry: Arc<Telemetry>,
|
||||
_watch_updates: Task<Option<()>>,
|
||||
client: Arc<Client>,
|
||||
@@ -98,7 +95,6 @@ impl ContextStore {
|
||||
project: Entity<Project>,
|
||||
prompt_builder: Arc<PromptBuilder>,
|
||||
slash_commands: Arc<SlashCommandWorkingSet>,
|
||||
tools: Arc<ToolWorkingSet>,
|
||||
cx: &mut App,
|
||||
) -> Task<Result<Entity<Self>>> {
|
||||
let fs = project.read(cx).fs().clone();
|
||||
@@ -119,12 +115,10 @@ impl ContextStore {
|
||||
contexts_metadata: Vec::new(),
|
||||
context_server_manager,
|
||||
context_server_slash_command_ids: HashMap::default(),
|
||||
context_server_tool_ids: HashMap::default(),
|
||||
host_contexts: Vec::new(),
|
||||
fs,
|
||||
languages,
|
||||
slash_commands,
|
||||
tools,
|
||||
telemetry,
|
||||
_watch_updates: cx.spawn(|this, mut cx| {
|
||||
async move {
|
||||
@@ -367,7 +361,6 @@ impl ContextStore {
|
||||
Some(self.telemetry.clone()),
|
||||
self.prompt_builder.clone(),
|
||||
self.slash_commands.clone(),
|
||||
self.tools.clone(),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
@@ -391,7 +384,6 @@ impl ContextStore {
|
||||
let telemetry = self.telemetry.clone();
|
||||
let prompt_builder = self.prompt_builder.clone();
|
||||
let slash_commands = self.slash_commands.clone();
|
||||
let tools = self.tools.clone();
|
||||
let request = self.client.request(proto::CreateContext { project_id });
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
let response = request.await?;
|
||||
@@ -405,7 +397,6 @@ impl ContextStore {
|
||||
language_registry,
|
||||
prompt_builder,
|
||||
slash_commands,
|
||||
tools,
|
||||
Some(project),
|
||||
Some(telemetry),
|
||||
cx,
|
||||
@@ -456,7 +447,6 @@ impl ContextStore {
|
||||
});
|
||||
let prompt_builder = self.prompt_builder.clone();
|
||||
let slash_commands = self.slash_commands.clone();
|
||||
let tools = self.tools.clone();
|
||||
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
let saved_context = load.await?;
|
||||
@@ -467,7 +457,6 @@ impl ContextStore {
|
||||
languages,
|
||||
prompt_builder,
|
||||
slash_commands,
|
||||
tools,
|
||||
Some(project),
|
||||
Some(telemetry),
|
||||
cx,
|
||||
@@ -535,7 +524,6 @@ impl ContextStore {
|
||||
});
|
||||
let prompt_builder = self.prompt_builder.clone();
|
||||
let slash_commands = self.slash_commands.clone();
|
||||
let tools = self.tools.clone();
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
let response = request.await?;
|
||||
let context_proto = response.context.context("invalid context")?;
|
||||
@@ -547,7 +535,6 @@ impl ContextStore {
|
||||
language_registry,
|
||||
prompt_builder,
|
||||
slash_commands,
|
||||
tools,
|
||||
Some(project),
|
||||
Some(telemetry),
|
||||
cx,
|
||||
@@ -816,7 +803,6 @@ impl ContextStore {
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let slash_command_working_set = self.slash_commands.clone();
|
||||
let tool_working_set = self.tools.clone();
|
||||
match event {
|
||||
context_server::manager::Event::ServerStarted { server_id } => {
|
||||
if let Some(server) = context_server_manager.read(cx).get_server(server_id) {
|
||||
@@ -856,29 +842,6 @@ impl ContextStore {
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
|
||||
if protocol.capable(context_server::protocol::ServerCapability::Tools) {
|
||||
if let Some(tools) = protocol.list_tools().await.log_err() {
|
||||
let tool_ids = tools.tools.into_iter().map(|tool| {
|
||||
log::info!("registering context server tool: {:?}", tool.name);
|
||||
tool_working_set.insert(
|
||||
Arc::new(ContextServerTool::new(
|
||||
context_server_manager.clone(),
|
||||
server.id(),
|
||||
tool,
|
||||
)),
|
||||
)
|
||||
|
||||
}).collect::<Vec<_>>();
|
||||
|
||||
|
||||
this.update(&mut cx, |this, _cx| {
|
||||
this.context_server_tool_ids
|
||||
.insert(server_id, tool_ids);
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
@@ -890,10 +853,6 @@ impl ContextStore {
|
||||
{
|
||||
slash_command_working_set.remove(&slash_command_ids);
|
||||
}
|
||||
|
||||
if let Some(tool_ids) = self.context_server_tool_ids.remove(server_id) {
|
||||
tool_working_set.remove(&tool_ids);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ lmstudio = { workspace = true, features = ["schemars"] }
|
||||
log.workspace = true
|
||||
ollama = { workspace = true, features = ["schemars"] }
|
||||
open_ai = { workspace = true, features = ["schemars"] }
|
||||
deepseek = { workspace = true, features = ["schemars"] }
|
||||
schemars.workspace = true
|
||||
serde.workspace = true
|
||||
settings.workspace = true
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::sync::Arc;
|
||||
|
||||
use ::open_ai::Model as OpenAiModel;
|
||||
use anthropic::Model as AnthropicModel;
|
||||
use deepseek::Model as DeepseekModel;
|
||||
use feature_flags::FeatureFlagAppExt;
|
||||
use gpui::{App, Pixels};
|
||||
use language_model::{CloudModel, LanguageModel};
|
||||
@@ -46,6 +47,11 @@ pub enum AssistantProviderContentV1 {
|
||||
default_model: Option<LmStudioModel>,
|
||||
api_url: Option<String>,
|
||||
},
|
||||
#[serde(rename = "deepseek")]
|
||||
DeepSeek {
|
||||
default_model: Option<DeepseekModel>,
|
||||
api_url: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
@@ -149,6 +155,12 @@ impl AssistantSettingsContent {
|
||||
model: model.id().to_string(),
|
||||
})
|
||||
}
|
||||
AssistantProviderContentV1::DeepSeek { default_model, .. } => {
|
||||
default_model.map(|model| LanguageModelSelection {
|
||||
provider: "deepseek".to_string(),
|
||||
model: model.id().to_string(),
|
||||
})
|
||||
}
|
||||
}),
|
||||
inline_alternatives: None,
|
||||
enable_experimental_live_diffs: None,
|
||||
@@ -253,6 +265,18 @@ impl AssistantSettingsContent {
|
||||
available_models,
|
||||
});
|
||||
}
|
||||
"deepseek" => {
|
||||
let api_url = match &settings.provider {
|
||||
Some(AssistantProviderContentV1::DeepSeek { api_url, .. }) => {
|
||||
api_url.clone()
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
settings.provider = Some(AssistantProviderContentV1::DeepSeek {
|
||||
default_model: DeepseekModel::from_id(&model).ok(),
|
||||
api_url,
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
VersionedAssistantSettingsContent::V2(settings) => {
|
||||
@@ -341,6 +365,7 @@ fn providers_schema(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema:
|
||||
"openai".into(),
|
||||
"zed.dev".into(),
|
||||
"copilot_chat".into(),
|
||||
"deepseek".into(),
|
||||
]),
|
||||
..Default::default()
|
||||
}
|
||||
@@ -380,7 +405,7 @@ pub struct AssistantSettingsContentV1 {
|
||||
default_height: Option<f32>,
|
||||
/// The provider of the assistant service.
|
||||
///
|
||||
/// This can be "openai", "anthropic", "ollama", "lmstudio", "zed.dev"
|
||||
/// This can be "openai", "anthropic", "ollama", "lmstudio", "deepseek", "zed.dev"
|
||||
/// each with their respective default models and configurations.
|
||||
provider: Option<AssistantProviderContentV1>,
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ toml.workspace = true
|
||||
ui.workspace = true
|
||||
util.workspace = true
|
||||
workspace.workspace = true
|
||||
worktree.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger.workspace = true
|
||||
|
||||
@@ -20,6 +20,7 @@ use std::{
|
||||
use ui::prelude::*;
|
||||
use util::ResultExt;
|
||||
use workspace::Workspace;
|
||||
use worktree::ChildEntriesOptions;
|
||||
|
||||
pub struct FileSlashCommand;
|
||||
|
||||
@@ -42,7 +43,13 @@ impl FileSlashCommand {
|
||||
.chain(project.worktrees(cx).flat_map(|worktree| {
|
||||
let worktree = worktree.read(cx);
|
||||
let id = worktree.id();
|
||||
worktree.child_entries(Path::new("")).map(move |entry| {
|
||||
let options = ChildEntriesOptions {
|
||||
include_files: true,
|
||||
include_dirs: true,
|
||||
include_ignored: false,
|
||||
};
|
||||
let entries = worktree.child_entries_with_options(Path::new(""), options);
|
||||
entries.map(move |entry| {
|
||||
(
|
||||
project::ProjectPath {
|
||||
worktree_id: id,
|
||||
|
||||
@@ -121,9 +121,7 @@ pub enum Event {
|
||||
},
|
||||
ShowContacts,
|
||||
ParticipantIndicesChanged,
|
||||
TermsStatusUpdated {
|
||||
accepted: bool,
|
||||
},
|
||||
PrivateUserInfoUpdated,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
@@ -227,9 +225,7 @@ impl UserStore {
|
||||
};
|
||||
|
||||
this.set_current_user_accepted_tos_at(accepted_tos_at);
|
||||
cx.emit(Event::TermsStatusUpdated {
|
||||
accepted: accepted_tos_at.is_some(),
|
||||
});
|
||||
cx.emit(Event::PrivateUserInfoUpdated);
|
||||
})
|
||||
} else {
|
||||
anyhow::Ok(())
|
||||
@@ -244,6 +240,8 @@ impl UserStore {
|
||||
Status::SignedOut => {
|
||||
current_user_tx.send(None).await.ok();
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.accepted_tos_at = None;
|
||||
cx.emit(Event::PrivateUserInfoUpdated);
|
||||
cx.notify();
|
||||
this.clear_contacts()
|
||||
})?
|
||||
@@ -714,7 +712,7 @@ impl UserStore {
|
||||
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.set_current_user_accepted_tos_at(Some(response.accepted_tos_at));
|
||||
cx.emit(Event::TermsStatusUpdated { accepted: true });
|
||||
cx.emit(Event::PrivateUserInfoUpdated);
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!("client not found"))
|
||||
|
||||
@@ -34,7 +34,6 @@ collections.workspace = true
|
||||
dashmap.workspace = true
|
||||
derive_more.workspace = true
|
||||
envy = "0.4.2"
|
||||
fireworks.workspace = true
|
||||
futures.workspace = true
|
||||
google_ai.workspace = true
|
||||
hex.workspace = true
|
||||
|
||||
@@ -5,6 +5,7 @@ pub mod extensions;
|
||||
pub mod ips_file;
|
||||
pub mod slack;
|
||||
|
||||
use crate::api::events::SnowflakeRow;
|
||||
use crate::{
|
||||
auth,
|
||||
db::{User, UserId},
|
||||
@@ -99,6 +100,7 @@ pub fn routes(rpc_server: Arc<rpc::Server>) -> Router<(), Body> {
|
||||
.route("/user", get(get_authenticated_user))
|
||||
.route("/users/:id/access_tokens", post(create_access_token))
|
||||
.route("/rpc_server_snapshot", get(get_rpc_server_snapshot))
|
||||
.route("/snowflake/events", post(write_snowflake_event))
|
||||
.merge(billing::router())
|
||||
.merge(contributors::router())
|
||||
.layer(
|
||||
@@ -245,3 +247,19 @@ async fn create_access_token(
|
||||
encrypted_access_token,
|
||||
}))
|
||||
}
|
||||
|
||||
/// An endpoint that writes a Snowflake event to our event stream.
|
||||
///
|
||||
/// This endpoint is exposed such that other internal services can write
|
||||
/// telemetry events without needing to talk to AWS Kinesis directly.
|
||||
async fn write_snowflake_event(
|
||||
Extension(app): Extension<Arc<AppState>>,
|
||||
Json(event): Json<SnowflakeRow>,
|
||||
) -> Result<()> {
|
||||
let kinesis_client = app.kinesis_client.clone();
|
||||
let kinesis_stream = app.config.kinesis_stream.clone();
|
||||
|
||||
event.write(&kinesis_client, &kinesis_stream).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -506,7 +506,7 @@ fn for_snowflake(
|
||||
),
|
||||
Event::InlineCompletion(e) => (
|
||||
format!(
|
||||
"Inline Completion {}",
|
||||
"Edit Prediction {}",
|
||||
if e.suggestion_accepted {
|
||||
"Accepted"
|
||||
} else {
|
||||
@@ -516,7 +516,7 @@ fn for_snowflake(
|
||||
serde_json::to_value(e).unwrap(),
|
||||
),
|
||||
Event::InlineCompletionRating(e) => (
|
||||
"Inline Completion Rated".to_string(),
|
||||
"Edit Prediction Rated".to_string(),
|
||||
serde_json::to_value(e).unwrap(),
|
||||
),
|
||||
Event::Call(e) => {
|
||||
|
||||
@@ -407,7 +407,7 @@ async fn build_kinesis_client(config: &Config) -> anyhow::Result<aws_sdk_kinesis
|
||||
config
|
||||
.kinesis_region
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow!("missing blob_store_region"))?,
|
||||
.ok_or_else(|| anyhow!("missing kinesis_region"))?,
|
||||
))
|
||||
.credentials_provider(keys)
|
||||
.load()
|
||||
|
||||
@@ -21,15 +21,12 @@ use chrono::{DateTime, Duration, Utc};
|
||||
use collections::HashMap;
|
||||
use db::TokenUsage;
|
||||
use db::{usage_measure::UsageMeasure, ActiveUserCount, LlmDatabase};
|
||||
use futures::{FutureExt, Stream, StreamExt as _};
|
||||
use futures::{Stream, StreamExt as _};
|
||||
use reqwest_client::ReqwestClient;
|
||||
use rpc::{
|
||||
proto::Plan, LanguageModelProvider, PerformCompletionParams, EXPIRED_LLM_TOKEN_HEADER_NAME,
|
||||
};
|
||||
use rpc::{
|
||||
ListModelsResponse, PredictEditsParams, PredictEditsResponse,
|
||||
MAX_LLM_MONTHLY_SPEND_REACHED_HEADER_NAME,
|
||||
};
|
||||
use rpc::{ListModelsResponse, MAX_LLM_MONTHLY_SPEND_REACHED_HEADER_NAME};
|
||||
use serde_json::json;
|
||||
use std::{
|
||||
pin::Pin,
|
||||
@@ -42,6 +39,8 @@ use util::ResultExt;
|
||||
|
||||
pub use token::*;
|
||||
|
||||
const ACTIVE_USER_COUNT_CACHE_DURATION: Duration = Duration::seconds(30);
|
||||
|
||||
pub struct LlmState {
|
||||
pub config: Config,
|
||||
pub executor: Executor,
|
||||
@@ -52,8 +51,6 @@ pub struct LlmState {
|
||||
RwLock<HashMap<(LanguageModelProvider, String), (DateTime<Utc>, ActiveUserCount)>>,
|
||||
}
|
||||
|
||||
const ACTIVE_USER_COUNT_CACHE_DURATION: Duration = Duration::seconds(30);
|
||||
|
||||
impl LlmState {
|
||||
pub async fn new(config: Config, executor: Executor) -> Result<Arc<Self>> {
|
||||
let database_url = config
|
||||
@@ -120,7 +117,6 @@ pub fn routes() -> Router<(), Body> {
|
||||
Router::new()
|
||||
.route("/models", get(list_models))
|
||||
.route("/completion", post(perform_completion))
|
||||
.route("/predict_edits", post(predict_edits))
|
||||
.layer(middleware::from_fn(validate_api_token))
|
||||
}
|
||||
|
||||
@@ -434,156 +430,6 @@ fn normalize_model_name(known_models: Vec<String>, name: String) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
async fn predict_edits(
|
||||
Extension(state): Extension<Arc<LlmState>>,
|
||||
Extension(claims): Extension<LlmTokenClaims>,
|
||||
_country_code_header: Option<TypedHeader<CloudflareIpCountryHeader>>,
|
||||
Json(params): Json<PredictEditsParams>,
|
||||
) -> Result<impl IntoResponse> {
|
||||
if !claims.is_staff && !claims.has_predict_edits_feature_flag {
|
||||
return Err(Error::http(
|
||||
StatusCode::FORBIDDEN,
|
||||
"no access to Zed's edit prediction feature".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let sample_input_output = claims.is_staff && rand::random::<f32>() < 0.1;
|
||||
|
||||
let api_url = state
|
||||
.config
|
||||
.prediction_api_url
|
||||
.as_ref()
|
||||
.context("no PREDICTION_API_URL configured on the server")?;
|
||||
let api_key = state
|
||||
.config
|
||||
.prediction_api_key
|
||||
.as_ref()
|
||||
.context("no PREDICTION_API_KEY configured on the server")?;
|
||||
let model = state
|
||||
.config
|
||||
.prediction_model
|
||||
.as_ref()
|
||||
.context("no PREDICTION_MODEL configured on the server")?;
|
||||
|
||||
let outline_prefix = params
|
||||
.outline
|
||||
.as_ref()
|
||||
.map(|outline| format!("### Outline for current file:\n{}\n", outline))
|
||||
.unwrap_or_default();
|
||||
|
||||
let prompt = include_str!("./llm/prediction_prompt.md")
|
||||
.replace("<outline>", &outline_prefix)
|
||||
.replace("<events>", ¶ms.input_events)
|
||||
.replace("<excerpt>", ¶ms.input_excerpt);
|
||||
|
||||
let request_start = std::time::Instant::now();
|
||||
let timeout = state
|
||||
.executor
|
||||
.sleep(std::time::Duration::from_secs(2))
|
||||
.fuse();
|
||||
let response = fireworks::complete(
|
||||
&state.http_client,
|
||||
api_url,
|
||||
api_key,
|
||||
fireworks::CompletionRequest {
|
||||
model: model.to_string(),
|
||||
prompt: prompt.clone(),
|
||||
max_tokens: 2048,
|
||||
temperature: 0.,
|
||||
prediction: Some(fireworks::Prediction::Content {
|
||||
content: params.input_excerpt.clone(),
|
||||
}),
|
||||
rewrite_speculation: Some(true),
|
||||
},
|
||||
)
|
||||
.fuse();
|
||||
futures::pin_mut!(timeout);
|
||||
futures::pin_mut!(response);
|
||||
|
||||
futures::select! {
|
||||
_ = timeout => {
|
||||
state.executor.spawn_detached({
|
||||
let kinesis_client = state.kinesis_client.clone();
|
||||
let kinesis_stream = state.config.kinesis_stream.clone();
|
||||
let model = model.clone();
|
||||
async move {
|
||||
SnowflakeRow::new(
|
||||
"Fireworks Completion Timeout",
|
||||
claims.metrics_id,
|
||||
claims.is_staff,
|
||||
claims.system_id.clone(),
|
||||
json!({
|
||||
"model": model.to_string(),
|
||||
"prompt": prompt,
|
||||
}),
|
||||
)
|
||||
.write(&kinesis_client, &kinesis_stream)
|
||||
.await
|
||||
.log_err();
|
||||
}
|
||||
});
|
||||
Err(anyhow!("request timed out"))?
|
||||
},
|
||||
response = response => {
|
||||
let duration = request_start.elapsed();
|
||||
|
||||
let mut response = response?;
|
||||
let choice = response
|
||||
.completion
|
||||
.choices
|
||||
.pop()
|
||||
.context("no output from completion response")?;
|
||||
|
||||
state.executor.spawn_detached({
|
||||
let kinesis_client = state.kinesis_client.clone();
|
||||
let kinesis_stream = state.config.kinesis_stream.clone();
|
||||
let model = model.clone();
|
||||
let output = choice.text.clone();
|
||||
|
||||
async move {
|
||||
let properties = if sample_input_output {
|
||||
json!({
|
||||
"model": model.to_string(),
|
||||
"headers": response.headers,
|
||||
"usage": response.completion.usage,
|
||||
"duration": duration.as_secs_f64(),
|
||||
"prompt": prompt,
|
||||
"input_excerpt": params.input_excerpt,
|
||||
"input_events": params.input_events,
|
||||
"outline": params.outline,
|
||||
"output": output,
|
||||
"is_sampled": true,
|
||||
})
|
||||
} else {
|
||||
json!({
|
||||
"model": model.to_string(),
|
||||
"headers": response.headers,
|
||||
"usage": response.completion.usage,
|
||||
"duration": duration.as_secs_f64(),
|
||||
"is_sampled": false,
|
||||
})
|
||||
};
|
||||
|
||||
SnowflakeRow::new(
|
||||
"Fireworks Completion Requested",
|
||||
claims.metrics_id,
|
||||
claims.is_staff,
|
||||
claims.system_id.clone(),
|
||||
properties,
|
||||
)
|
||||
.write(&kinesis_client, &kinesis_stream)
|
||||
.await
|
||||
.log_err();
|
||||
}
|
||||
});
|
||||
|
||||
Ok(Json(PredictEditsResponse {
|
||||
output_excerpt: choice.text,
|
||||
}))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// The maximum monthly spending an individual user can reach on the free tier
|
||||
/// before they have to pay.
|
||||
pub const FREE_TIER_MONTHLY_SPENDING_LIMIT: Cents = Cents::from_dollars(10);
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
<outline>## Task
|
||||
Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request.
|
||||
|
||||
### Instruction:
|
||||
You are a code completion assistant and your task is to analyze user edits and then rewrite an excerpt that the user provides, suggesting the appropriate edits within the excerpt, taking into account the cursor location.
|
||||
|
||||
### Events:
|
||||
<events>
|
||||
|
||||
### Input:
|
||||
<excerpt>
|
||||
|
||||
### Response:
|
||||
@@ -333,6 +333,9 @@ impl Server {
|
||||
.add_request_handler(forward_mutating_project_request::<proto::CopyProjectEntry>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::DeleteProjectEntry>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::ExpandProjectEntry>)
|
||||
.add_request_handler(
|
||||
forward_mutating_project_request::<proto::ExpandAllForProjectEntry>,
|
||||
)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::OnTypeFormatting>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::SaveBuffer>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::BlameBuffer>)
|
||||
@@ -388,6 +391,9 @@ impl Server {
|
||||
.add_request_handler(forward_mutating_project_request::<proto::OpenContext>)
|
||||
.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::Stage>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::Unstage>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::Commit>)
|
||||
.add_message_handler(broadcast_project_message_from_host::<proto::AdvertiseContexts>)
|
||||
.add_message_handler(update_context)
|
||||
.add_request_handler({
|
||||
|
||||
@@ -8,7 +8,6 @@ use crate::{
|
||||
use anyhow::{anyhow, Result};
|
||||
use assistant_context_editor::ContextStore;
|
||||
use assistant_slash_command::SlashCommandWorkingSet;
|
||||
use assistant_tool::ToolWorkingSet;
|
||||
use call::{room, ActiveCall, ParticipantLocation, Room};
|
||||
use client::{User, RECEIVE_TIMEOUT};
|
||||
use collections::{HashMap, HashSet};
|
||||
@@ -6547,7 +6546,6 @@ async fn test_context_collaboration_with_reconnect(
|
||||
project_a.clone(),
|
||||
prompt_builder.clone(),
|
||||
Arc::new(SlashCommandWorkingSet::default()),
|
||||
Arc::new(ToolWorkingSet::default()),
|
||||
cx,
|
||||
)
|
||||
})
|
||||
@@ -6559,7 +6557,6 @@ async fn test_context_collaboration_with_reconnect(
|
||||
project_b.clone(),
|
||||
prompt_builder.clone(),
|
||||
Arc::new(SlashCommandWorkingSet::default()),
|
||||
Arc::new(ToolWorkingSet::default()),
|
||||
cx,
|
||||
)
|
||||
})
|
||||
|
||||
@@ -18,16 +18,12 @@ pub fn initiate_sign_in(window: &mut Window, cx: &mut App) {
|
||||
return;
|
||||
};
|
||||
let status = copilot.read(cx).status();
|
||||
let Some(workspace) = window.window_handle().downcast::<Workspace>() else {
|
||||
let Some(workspace) = window.root::<Workspace>().flatten() else {
|
||||
return;
|
||||
};
|
||||
match status {
|
||||
Status::Starting { task } => {
|
||||
let Some(workspace) = window.window_handle().downcast::<Workspace>() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Ok(workspace) = workspace.update(cx, |workspace, _window, cx| {
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
workspace.show_toast(
|
||||
Toast::new(
|
||||
NotificationId::unique::<CopilotStartingToast>(),
|
||||
@@ -35,11 +31,9 @@ pub fn initiate_sign_in(window: &mut Window, cx: &mut App) {
|
||||
),
|
||||
cx,
|
||||
);
|
||||
workspace.weak_handle()
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
});
|
||||
|
||||
let workspace = workspace.downgrade();
|
||||
cx.spawn(|mut cx| async move {
|
||||
task.await;
|
||||
if let Some(copilot) = cx.update(|cx| Copilot::global(cx)).ok().flatten() {
|
||||
@@ -69,13 +63,11 @@ pub fn initiate_sign_in(window: &mut Window, cx: &mut App) {
|
||||
}
|
||||
_ => {
|
||||
copilot.update(cx, |this, cx| this.sign_in(cx)).detach();
|
||||
workspace
|
||||
.update(cx, |this, window, cx| {
|
||||
this.toggle_modal(window, cx, |_, cx| {
|
||||
CopilotCodeVerification::new(&copilot, cx)
|
||||
});
|
||||
})
|
||||
.ok();
|
||||
workspace.update(cx, |this, cx| {
|
||||
this.toggle_modal(window, cx, |_, cx| {
|
||||
CopilotCodeVerification::new(&copilot, cx)
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "fireworks"
|
||||
name = "deepseek"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
publish.workspace = true
|
||||
@@ -9,11 +9,16 @@ license = "GPL-3.0-or-later"
|
||||
workspace = true
|
||||
|
||||
[lib]
|
||||
path = "src/fireworks.rs"
|
||||
path = "src/deepseek.rs"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
schemars = ["dep:schemars"]
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
futures.workspace = true
|
||||
http_client.workspace = true
|
||||
schemars = { workspace = true, optional = true }
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
301
crates/deepseek/src/deepseek.rs
Normal file
301
crates/deepseek/src/deepseek.rs
Normal file
@@ -0,0 +1,301 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use futures::{
|
||||
io::BufReader,
|
||||
stream::{BoxStream, StreamExt},
|
||||
AsyncBufReadExt, AsyncReadExt,
|
||||
};
|
||||
use http_client::{AsyncBody, HttpClient, Method, Request as HttpRequest};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
pub const DEEPSEEK_API_URL: &str = "https://api.deepseek.com";
|
||||
|
||||
#[derive(Clone, Copy, Serialize, Deserialize, Debug, Eq, PartialEq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum Role {
|
||||
User,
|
||||
Assistant,
|
||||
System,
|
||||
Tool,
|
||||
}
|
||||
|
||||
impl TryFrom<String> for Role {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: String) -> Result<Self> {
|
||||
match value.as_str() {
|
||||
"user" => Ok(Self::User),
|
||||
"assistant" => Ok(Self::Assistant),
|
||||
"system" => Ok(Self::System),
|
||||
"tool" => Ok(Self::Tool),
|
||||
_ => Err(anyhow!("invalid role '{value}'")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Role> for String {
|
||||
fn from(val: Role) -> Self {
|
||||
match val {
|
||||
Role::User => "user".to_owned(),
|
||||
Role::Assistant => "assistant".to_owned(),
|
||||
Role::System => "system".to_owned(),
|
||||
Role::Tool => "tool".to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
|
||||
pub enum Model {
|
||||
#[serde(rename = "deepseek-chat")]
|
||||
#[default]
|
||||
Chat,
|
||||
#[serde(rename = "deepseek-reasoner")]
|
||||
Reasoner,
|
||||
#[serde(rename = "custom")]
|
||||
Custom {
|
||||
name: String,
|
||||
/// The name displayed in the UI, such as in the assistant panel model dropdown menu.
|
||||
display_name: Option<String>,
|
||||
max_tokens: usize,
|
||||
max_output_tokens: Option<u32>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Model {
|
||||
pub fn from_id(id: &str) -> Result<Self> {
|
||||
match id {
|
||||
"deepseek-chat" => Ok(Self::Chat),
|
||||
"deepseek-reasoner" => Ok(Self::Reasoner),
|
||||
_ => Err(anyhow!("invalid model id")),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> &str {
|
||||
match self {
|
||||
Self::Chat => "deepseek-chat",
|
||||
Self::Reasoner => "deepseek-reasoner",
|
||||
Self::Custom { name, .. } => name,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn display_name(&self) -> &str {
|
||||
match self {
|
||||
Self::Chat => "DeepSeek Chat",
|
||||
Self::Reasoner => "DeepSeek Reasoner",
|
||||
Self::Custom {
|
||||
name, display_name, ..
|
||||
} => display_name.as_ref().unwrap_or(name).as_str(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max_token_count(&self) -> usize {
|
||||
match self {
|
||||
Self::Chat | Self::Reasoner => 64_000,
|
||||
Self::Custom { max_tokens, .. } => *max_tokens,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max_output_tokens(&self) -> Option<u32> {
|
||||
match self {
|
||||
Self::Chat => Some(8_192),
|
||||
Self::Reasoner => Some(8_192),
|
||||
Self::Custom {
|
||||
max_output_tokens, ..
|
||||
} => *max_output_tokens,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Request {
|
||||
pub model: String,
|
||||
pub messages: Vec<RequestMessage>,
|
||||
pub stream: bool,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub max_tokens: Option<u32>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub temperature: Option<f32>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub response_format: Option<ResponseFormat>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub tools: Vec<ToolDefinition>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ResponseFormat {
|
||||
Text,
|
||||
#[serde(rename = "json_object")]
|
||||
JsonObject,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ToolDefinition {
|
||||
Function { function: FunctionDefinition },
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct FunctionDefinition {
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
pub parameters: Option<Value>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
||||
#[serde(tag = "role", rename_all = "lowercase")]
|
||||
pub enum RequestMessage {
|
||||
Assistant {
|
||||
content: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
tool_calls: Vec<ToolCall>,
|
||||
},
|
||||
User {
|
||||
content: String,
|
||||
},
|
||||
System {
|
||||
content: String,
|
||||
},
|
||||
Tool {
|
||||
content: String,
|
||||
tool_call_id: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
||||
pub struct ToolCall {
|
||||
pub id: String,
|
||||
#[serde(flatten)]
|
||||
pub content: ToolCallContent,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
||||
#[serde(tag = "type", rename_all = "lowercase")]
|
||||
pub enum ToolCallContent {
|
||||
Function { function: FunctionContent },
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
||||
pub struct FunctionContent {
|
||||
pub name: String,
|
||||
pub arguments: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Response {
|
||||
pub id: String,
|
||||
pub object: String,
|
||||
pub created: u64,
|
||||
pub model: String,
|
||||
pub choices: Vec<Choice>,
|
||||
pub usage: Usage,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub reasoning_content: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Usage {
|
||||
pub prompt_tokens: u32,
|
||||
pub completion_tokens: u32,
|
||||
pub total_tokens: u32,
|
||||
#[serde(default)]
|
||||
pub prompt_cache_hit_tokens: u32,
|
||||
#[serde(default)]
|
||||
pub prompt_cache_miss_tokens: u32,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Choice {
|
||||
pub index: u32,
|
||||
pub message: RequestMessage,
|
||||
pub finish_reason: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct StreamResponse {
|
||||
pub id: String,
|
||||
pub object: String,
|
||||
pub created: u64,
|
||||
pub model: String,
|
||||
pub choices: Vec<StreamChoice>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct StreamChoice {
|
||||
pub index: u32,
|
||||
pub delta: StreamDelta,
|
||||
pub finish_reason: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct StreamDelta {
|
||||
pub role: Option<Role>,
|
||||
pub content: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub tool_calls: Option<Vec<ToolCallChunk>>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub reasoning_content: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct ToolCallChunk {
|
||||
pub index: usize,
|
||||
pub id: Option<String>,
|
||||
pub function: Option<FunctionChunk>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct FunctionChunk {
|
||||
pub name: Option<String>,
|
||||
pub arguments: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn stream_completion(
|
||||
client: &dyn HttpClient,
|
||||
api_url: &str,
|
||||
api_key: &str,
|
||||
request: Request,
|
||||
) -> Result<BoxStream<'static, Result<StreamResponse>>> {
|
||||
let uri = format!("{api_url}/v1/chat/completions");
|
||||
let request_builder = HttpRequest::builder()
|
||||
.method(Method::POST)
|
||||
.uri(uri)
|
||||
.header("Content-Type", "application/json")
|
||||
.header("Authorization", format!("Bearer {}", api_key));
|
||||
|
||||
let request = request_builder.body(AsyncBody::from(serde_json::to_string(&request)?))?;
|
||||
let mut response = client.send(request).await?;
|
||||
|
||||
if response.status().is_success() {
|
||||
let reader = BufReader::new(response.into_body());
|
||||
Ok(reader
|
||||
.lines()
|
||||
.filter_map(|line| async move {
|
||||
match line {
|
||||
Ok(line) => {
|
||||
let line = line.strip_prefix("data: ")?;
|
||||
if line == "[DONE]" {
|
||||
None
|
||||
} else {
|
||||
match serde_json::from_str(line) {
|
||||
Ok(response) => Some(Ok(response)),
|
||||
Err(error) => Some(Err(anyhow!(error))),
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(error) => Some(Err(anyhow!(error))),
|
||||
}
|
||||
})
|
||||
.boxed())
|
||||
} else {
|
||||
let mut body = String::new();
|
||||
response.body_mut().read_to_string(&mut body).await?;
|
||||
Err(anyhow!(
|
||||
"Failed to connect to DeepSeek API: {} {}",
|
||||
response.status(),
|
||||
body,
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -88,7 +88,7 @@ url.workspace = true
|
||||
util.workspace = true
|
||||
uuid.workspace = true
|
||||
workspace.workspace = true
|
||||
zed_predict_tos.workspace = true
|
||||
zed_predict_onboarding.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
ctor.workspace = true
|
||||
|
||||
@@ -397,4 +397,4 @@ gpui::actions!(
|
||||
action_as!(go_to_line, ToggleGoToLine as Toggle);
|
||||
|
||||
action_with_deprecated_aliases!(editor, OpenSelectedFilename, ["editor::OpenFile"]);
|
||||
action_with_deprecated_aliases!(editor, ToggleSelectedDiffHunks, ["editor::ToggleDiffHunk"]);
|
||||
action_with_deprecated_aliases!(editor, ToggleSelectedDiffHunks, ["editor::ToggleHunkDiff"]);
|
||||
|
||||
@@ -652,7 +652,7 @@ impl CompletionsMenu {
|
||||
)
|
||||
.on_click(cx.listener(move |editor, _event, window, cx| {
|
||||
cx.stop_propagation();
|
||||
editor.toggle_zed_predict_tos(window, cx);
|
||||
editor.toggle_zed_predict_onboarding(window, cx);
|
||||
})),
|
||||
),
|
||||
|
||||
|
||||
@@ -1068,13 +1068,15 @@ impl DisplaySnapshot {
|
||||
DisplayPoint(self.block_snapshot.clip_point(point.0, bias))
|
||||
}
|
||||
|
||||
pub fn clip_at_line_end(&self, point: DisplayPoint) -> DisplayPoint {
|
||||
let mut point = point.0;
|
||||
if point.column == self.line_len(DisplayRow(point.row)) {
|
||||
point.column = point.column.saturating_sub(1);
|
||||
point = self.block_snapshot.clip_point(point, Bias::Left);
|
||||
pub fn clip_at_line_end(&self, display_point: DisplayPoint) -> DisplayPoint {
|
||||
let mut point = self.display_point_to_point(display_point, Bias::Left);
|
||||
|
||||
if point.column != self.buffer_snapshot.line_len(MultiBufferRow(point.row)) {
|
||||
return display_point;
|
||||
}
|
||||
DisplayPoint(point)
|
||||
point.column = point.column.saturating_sub(1);
|
||||
point = self.buffer_snapshot.clip_point(point, Bias::Left);
|
||||
self.point_to_display_point(point, Bias::Left)
|
||||
}
|
||||
|
||||
pub fn folds_in_range<T>(&self, range: Range<T>) -> impl Iterator<Item = &Fold>
|
||||
|
||||
@@ -225,8 +225,11 @@ pub enum BlockStyle {
|
||||
Sticky,
|
||||
}
|
||||
|
||||
#[derive(gpui::AppContext, gpui::VisualContext)]
|
||||
pub struct BlockContext<'a, 'b> {
|
||||
#[window]
|
||||
pub window: &'a mut Window,
|
||||
#[app]
|
||||
pub app: &'b mut App,
|
||||
pub anchor_x: Pixels,
|
||||
pub max_width: Pixels,
|
||||
|
||||
@@ -69,7 +69,7 @@ pub use element::{
|
||||
};
|
||||
use futures::{future, FutureExt};
|
||||
use fuzzy::StringMatchCandidate;
|
||||
use zed_predict_tos::ZedPredictTos;
|
||||
use zed_predict_onboarding::ZedPredictModal;
|
||||
|
||||
use code_context_menus::{
|
||||
AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
|
||||
@@ -81,9 +81,9 @@ use gpui::{
|
||||
AsyncWindowContext, AvailableSpace, Bounds, ClipboardEntry, ClipboardItem, Context,
|
||||
DispatchPhase, ElementId, Entity, EntityInputHandler, EventEmitter, FocusHandle, FocusOutEvent,
|
||||
Focusable, FontId, FontWeight, Global, HighlightStyle, Hsla, InteractiveText, KeyContext,
|
||||
MouseButton, PaintQuad, ParentElement, Pixels, Render, SharedString, Size, Styled, StyledText,
|
||||
Subscription, Task, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle,
|
||||
UniformListScrollHandle, WeakEntity, WeakFocusHandle, Window,
|
||||
MouseButton, MouseDownEvent, PaintQuad, ParentElement, Pixels, Render, SharedString, Size,
|
||||
Styled, StyledText, Subscription, Task, TextStyle, TextStyleRefinement, UTF16Selection,
|
||||
UnderlineStyle, UniformListScrollHandle, WeakEntity, WeakFocusHandle, Window,
|
||||
};
|
||||
use highlight_matching_bracket::refresh_matching_bracket_highlights;
|
||||
use hover_popover::{hide_hover, HoverState};
|
||||
@@ -681,6 +681,7 @@ pub struct Editor {
|
||||
leader_peer_id: Option<PeerId>,
|
||||
remote_id: Option<ViewId>,
|
||||
hover_state: HoverState,
|
||||
pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
|
||||
gutter_hovered: bool,
|
||||
hovered_link_state: Option<HoveredLinkState>,
|
||||
inline_completion_provider: Option<RegisteredInlineCompletionProvider>,
|
||||
@@ -727,6 +728,7 @@ pub struct Editor {
|
||||
expect_bounds_change: Option<Bounds<Pixels>>,
|
||||
tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
|
||||
tasks_update_task: Option<Task<()>>,
|
||||
in_project_search: bool,
|
||||
previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
|
||||
breadcrumb_header: Option<String>,
|
||||
focused_block: Option<FocusedBlock>,
|
||||
@@ -1375,6 +1377,7 @@ impl Editor {
|
||||
leader_peer_id: None,
|
||||
remote_id: None,
|
||||
hover_state: Default::default(),
|
||||
pending_mouse_down: None,
|
||||
hovered_link_state: Default::default(),
|
||||
inline_completion_provider: None,
|
||||
active_inline_completion: None,
|
||||
@@ -1424,6 +1427,7 @@ impl Editor {
|
||||
],
|
||||
tasks_update_task: None,
|
||||
linked_edit_ranges: Default::default(),
|
||||
in_project_search: false,
|
||||
previous_search_ranges: None,
|
||||
breadcrumb_header: None,
|
||||
focused_block: None,
|
||||
@@ -1701,6 +1705,10 @@ impl Editor {
|
||||
self.collaboration_hub = Some(hub);
|
||||
}
|
||||
|
||||
pub fn set_in_project_search(&mut self, in_project_search: bool) {
|
||||
self.in_project_search = in_project_search;
|
||||
}
|
||||
|
||||
pub fn set_custom_context_menu(
|
||||
&mut self,
|
||||
f: impl 'static
|
||||
@@ -3948,12 +3956,21 @@ impl Editor {
|
||||
self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
|
||||
}
|
||||
|
||||
fn toggle_zed_predict_tos(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
fn toggle_zed_predict_onboarding(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let (Some(workspace), Some(project)) = (self.workspace(), self.project.as_ref()) else {
|
||||
return;
|
||||
};
|
||||
|
||||
ZedPredictTos::toggle(workspace, project.read(cx).user_store().clone(), window, cx);
|
||||
let project = project.read(cx);
|
||||
|
||||
ZedPredictModal::toggle(
|
||||
workspace,
|
||||
project.user_store().clone(),
|
||||
project.client().clone(),
|
||||
project.fs().clone(),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
|
||||
fn do_completion(
|
||||
@@ -3985,7 +4002,7 @@ impl Editor {
|
||||
)) => {
|
||||
drop(entries);
|
||||
drop(context_menu);
|
||||
self.toggle_zed_predict_tos(window, cx);
|
||||
self.toggle_zed_predict_onboarding(window, cx);
|
||||
return Some(Task::ready(Ok(())));
|
||||
}
|
||||
_ => {}
|
||||
@@ -4974,8 +4991,8 @@ impl Editor {
|
||||
.and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
|
||||
|
||||
let event_type = match accepted {
|
||||
true => "Inline Completion Accepted",
|
||||
false => "Inline Completion Discarded",
|
||||
true => "Edit Prediction Accepted",
|
||||
false => "Edit Prediction Discarded",
|
||||
};
|
||||
telemetry::event!(
|
||||
event_type,
|
||||
@@ -7042,7 +7059,7 @@ impl Editor {
|
||||
let mut should_rewrap = is_vim_mode == IsVimMode::Yes;
|
||||
|
||||
if let Some(language_scope) = buffer.language_scope_at(selection.head()) {
|
||||
match language_scope.language_name().0.as_ref() {
|
||||
match language_scope.language_name().as_ref() {
|
||||
"Markdown" | "Plain Text" => {
|
||||
should_rewrap = true;
|
||||
}
|
||||
@@ -8816,6 +8833,12 @@ impl Editor {
|
||||
}
|
||||
}
|
||||
|
||||
let reversed = self.selections.oldest::<usize>(cx).reversed;
|
||||
|
||||
for selection in new_selections.iter_mut() {
|
||||
selection.reversed = reversed;
|
||||
}
|
||||
|
||||
select_next_state.done = true;
|
||||
self.unfold_ranges(
|
||||
&new_selections
|
||||
@@ -12144,6 +12167,10 @@ impl Editor {
|
||||
.update(cx, |map, cx| map.set_wrap_width(width, cx))
|
||||
}
|
||||
|
||||
pub fn set_soft_wrap(&mut self) {
|
||||
self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
|
||||
}
|
||||
|
||||
pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
|
||||
if self.soft_wrap_mode_override.is_some() {
|
||||
self.soft_wrap_mode_override.take();
|
||||
@@ -13381,6 +13408,9 @@ impl Editor {
|
||||
|
||||
cx.emit(EditorEvent::Reparsed(*buffer_id));
|
||||
}
|
||||
multi_buffer::Event::DiffHunksToggled => {
|
||||
self.tasks_update_task = Some(self.refresh_runnables(window, cx));
|
||||
}
|
||||
multi_buffer::Event::LanguageChanged(buffer_id) => {
|
||||
linked_editing_ranges::refresh_linked_ranges(self, window, cx);
|
||||
cx.emit(EditorEvent::Reparsed(*buffer_id));
|
||||
|
||||
@@ -5236,11 +5236,27 @@ async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx).await;
|
||||
|
||||
// Test caret-only selections
|
||||
cx.set_state("abc\nˇabc abc\ndefabc\nabc");
|
||||
|
||||
cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
|
||||
.unwrap();
|
||||
cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
|
||||
|
||||
// Test left-to-right selections
|
||||
cx.set_state("abc\n«abcˇ»\nabc");
|
||||
|
||||
cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
|
||||
.unwrap();
|
||||
cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
|
||||
|
||||
// Test right-to-left selections
|
||||
cx.set_state("abc\n«ˇabc»\nabc");
|
||||
|
||||
cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
|
||||
.unwrap();
|
||||
cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
@@ -11283,7 +11299,7 @@ async fn test_completions_resolve_updates_labels_if_filter_text_matches(
|
||||
cx.simulate_keystroke(".");
|
||||
|
||||
let item1 = lsp::CompletionItem {
|
||||
label: "id".to_string(),
|
||||
label: "method id()".to_string(),
|
||||
filter_text: Some("id".to_string()),
|
||||
detail: None,
|
||||
documentation: None,
|
||||
@@ -11333,7 +11349,7 @@ async fn test_completions_resolve_updates_labels_if_filter_text_matches(
|
||||
.iter()
|
||||
.map(|completion| &completion.label.text)
|
||||
.collect::<Vec<_>>(),
|
||||
vec!["id", "other"]
|
||||
vec!["method id()", "other"]
|
||||
)
|
||||
}
|
||||
CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
|
||||
@@ -11388,7 +11404,7 @@ async fn test_completions_resolve_updates_labels_if_filter_text_matches(
|
||||
.iter()
|
||||
.map(|completion| &completion.label.text)
|
||||
.collect::<Vec<_>>(),
|
||||
vec!["method id()", "other"],
|
||||
vec!["method id() Now resolved!", "other"],
|
||||
"Should update first completion label, but not second as the filter text did not match."
|
||||
);
|
||||
}
|
||||
@@ -14397,12 +14413,8 @@ async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut gpui::TestAppContex
|
||||
let buffer = multibuffer.as_singleton().unwrap();
|
||||
let change_set = cx.new(|cx| {
|
||||
let mut change_set = BufferChangeSet::new(&buffer, cx);
|
||||
change_set.recalculate_diff_sync(
|
||||
base_text.into(),
|
||||
buffer.read(cx).text_snapshot(),
|
||||
true,
|
||||
cx,
|
||||
);
|
||||
let _ =
|
||||
change_set.set_base_text(base_text.into(), buffer.read(cx).text_snapshot(), cx);
|
||||
change_set
|
||||
});
|
||||
|
||||
@@ -14412,6 +14424,7 @@ async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut gpui::TestAppContex
|
||||
buffer.read(cx).remote_id()
|
||||
})
|
||||
});
|
||||
cx.run_until_parked();
|
||||
|
||||
cx.assert_state_with_diff(
|
||||
indoc! { "
|
||||
|
||||
@@ -75,8 +75,10 @@ use unicode_segmentation::UnicodeSegmentation;
|
||||
use util::{RangeExt, ResultExt};
|
||||
use workspace::{item::Item, notifications::NotifyTaskExt, Workspace};
|
||||
|
||||
const INLINE_BLAME_PADDING_EM_WIDTHS: f32 = 7.;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum DisplayDiffHunk {
|
||||
enum DisplayDiffHunk {
|
||||
Folded {
|
||||
display_row: DisplayRow,
|
||||
},
|
||||
@@ -331,14 +333,44 @@ impl EditorElement {
|
||||
register_action(editor, window, Editor::go_to_prev_diagnostic);
|
||||
register_action(editor, window, Editor::go_to_next_hunk);
|
||||
register_action(editor, window, Editor::go_to_prev_hunk);
|
||||
register_action(editor, window, |editor, a, window, cx| {
|
||||
register_action(editor, window, |editor, action, window, cx| {
|
||||
editor
|
||||
.go_to_definition(a, window, cx)
|
||||
.go_to_definition(action, window, cx)
|
||||
.detach_and_log_err(cx);
|
||||
});
|
||||
register_action(editor, window, |editor, a, window, cx| {
|
||||
register_action(editor, window, |editor, action, window, cx| {
|
||||
editor
|
||||
.go_to_definition_split(a, window, cx)
|
||||
.go_to_definition_split(action, window, cx)
|
||||
.detach_and_log_err(cx);
|
||||
});
|
||||
register_action(editor, window, |editor, action, window, cx| {
|
||||
editor
|
||||
.go_to_declaration(action, window, cx)
|
||||
.detach_and_log_err(cx);
|
||||
});
|
||||
register_action(editor, window, |editor, action, window, cx| {
|
||||
editor
|
||||
.go_to_declaration_split(action, window, cx)
|
||||
.detach_and_log_err(cx);
|
||||
});
|
||||
register_action(editor, window, |editor, action, window, cx| {
|
||||
editor
|
||||
.go_to_implementation(action, window, cx)
|
||||
.detach_and_log_err(cx);
|
||||
});
|
||||
register_action(editor, window, |editor, action, window, cx| {
|
||||
editor
|
||||
.go_to_implementation_split(action, window, cx)
|
||||
.detach_and_log_err(cx);
|
||||
});
|
||||
register_action(editor, window, |editor, action, window, cx| {
|
||||
editor
|
||||
.go_to_type_definition(action, window, cx)
|
||||
.detach_and_log_err(cx);
|
||||
});
|
||||
register_action(editor, window, |editor, action, window, cx| {
|
||||
editor
|
||||
.go_to_type_definition_split(action, window, cx)
|
||||
.detach_and_log_err(cx);
|
||||
});
|
||||
register_action(editor, window, Editor::open_url);
|
||||
@@ -704,6 +736,10 @@ impl EditorElement {
|
||||
fn mouse_up(
|
||||
editor: &mut Editor,
|
||||
event: &MouseUpEvent,
|
||||
#[cfg_attr(
|
||||
not(any(target_os = "linux", target_os = "freebsd")),
|
||||
allow(unused_variables)
|
||||
)]
|
||||
position_map: &PositionMap,
|
||||
text_hitbox: &Hitbox,
|
||||
window: &mut Window,
|
||||
@@ -716,18 +752,7 @@ impl EditorElement {
|
||||
editor.select(SelectPhase::End, window, cx);
|
||||
}
|
||||
|
||||
let multi_cursor_setting = EditorSettings::get_global(cx).multi_cursor_modifier;
|
||||
let multi_cursor_modifier = match multi_cursor_setting {
|
||||
MultiCursorModifier::Alt => event.modifiers.secondary(),
|
||||
MultiCursorModifier::CmdOrCtrl => event.modifiers.alt,
|
||||
};
|
||||
|
||||
if !pending_nonempty_selections && multi_cursor_modifier && text_hitbox.is_hovered(window) {
|
||||
let point = position_map.point_for_position(text_hitbox.bounds, event.position);
|
||||
editor.handle_click_hovered_link(point, event.modifiers, window, cx);
|
||||
|
||||
cx.stop_propagation();
|
||||
} else if end_selection && pending_nonempty_selections {
|
||||
if end_selection && pending_nonempty_selections {
|
||||
cx.stop_propagation();
|
||||
} else if cfg!(any(target_os = "linux", target_os = "freebsd"))
|
||||
&& event.button == MouseButton::Middle
|
||||
@@ -759,6 +784,30 @@ impl EditorElement {
|
||||
}
|
||||
}
|
||||
|
||||
fn click(
|
||||
editor: &mut Editor,
|
||||
event: &ClickEvent,
|
||||
position_map: &PositionMap,
|
||||
text_hitbox: &Hitbox,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Editor>,
|
||||
) {
|
||||
let pending_nonempty_selections = editor.has_pending_nonempty_selection();
|
||||
|
||||
let multi_cursor_setting = EditorSettings::get_global(cx).multi_cursor_modifier;
|
||||
let multi_cursor_modifier = match multi_cursor_setting {
|
||||
MultiCursorModifier::Alt => event.modifiers().secondary(),
|
||||
MultiCursorModifier::CmdOrCtrl => event.modifiers().alt,
|
||||
};
|
||||
|
||||
if !pending_nonempty_selections && multi_cursor_modifier && text_hitbox.is_hovered(window) {
|
||||
let point = position_map.point_for_position(text_hitbox.bounds, event.up.position);
|
||||
editor.handle_click_hovered_link(point, event.modifiers(), window, cx);
|
||||
|
||||
cx.stop_propagation();
|
||||
}
|
||||
}
|
||||
|
||||
fn mouse_dragged(
|
||||
editor: &mut Editor,
|
||||
event: &MouseMoveEvent,
|
||||
@@ -1550,16 +1599,19 @@ impl EditorElement {
|
||||
let hunk_display_start = snapshot.point_to_display_point(hunk_start_point, Bias::Left);
|
||||
let hunk_display_end = snapshot.point_to_display_point(hunk_end_point, Bias::Right);
|
||||
|
||||
let display_hunk = if hunk_display_start.column() != 0 || hunk_display_end.column() != 0
|
||||
{
|
||||
let display_hunk = if hunk_display_start.column() != 0 {
|
||||
DisplayDiffHunk::Folded {
|
||||
display_row: hunk_display_start.row(),
|
||||
}
|
||||
} else {
|
||||
let mut end_row = hunk_display_end.row();
|
||||
if hunk_display_end.column() > 0 {
|
||||
end_row.0 += 1;
|
||||
}
|
||||
DisplayDiffHunk::Unfolded {
|
||||
status: hunk.status(),
|
||||
diff_base_byte_range: hunk.diff_base_byte_range,
|
||||
display_row_range: hunk_display_start.row()..hunk_display_end.row(),
|
||||
display_row_range: hunk_display_start.row()..end_row,
|
||||
multi_buffer_range: Anchor::range_in_buffer(
|
||||
hunk.excerpt_id,
|
||||
hunk.buffer_id,
|
||||
@@ -3375,6 +3427,7 @@ impl EditorElement {
|
||||
line_layouts: &[LineWithInvisibles],
|
||||
line_height: Pixels,
|
||||
scroll_pixel_position: gpui::Point<Pixels>,
|
||||
newest_selection_head: Option<DisplayPoint>,
|
||||
editor_width: Pixels,
|
||||
style: &EditorStyle,
|
||||
window: &mut Window,
|
||||
@@ -3491,7 +3544,7 @@ impl EditorElement {
|
||||
crate::inline_completion_edit_text(&snapshot, edits, edit_preview, false, cx)
|
||||
})?;
|
||||
|
||||
let line_count = highlighted_edits.text.lines().count() + 1;
|
||||
let line_count = highlighted_edits.text.lines().count();
|
||||
|
||||
let longest_row =
|
||||
editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
|
||||
@@ -3521,25 +3574,58 @@ impl EditorElement {
|
||||
.child(styled_text)
|
||||
.into_any();
|
||||
|
||||
let viewport_bounds = Bounds::new(Default::default(), window.viewport_size())
|
||||
.extend(Edges {
|
||||
right: -Self::SCROLLBAR_WIDTH,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let x_after_longest =
|
||||
text_bounds.origin.x + longest_line_width + PADDING_X - scroll_pixel_position.x;
|
||||
|
||||
let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
|
||||
let is_fully_visible =
|
||||
editor_width >= longest_line_width + PADDING_X + element_bounds.width;
|
||||
|
||||
// Fully visible if it can be displayed within the window (allow overlapping other
|
||||
// panes). However, this is only allowed if the popover starts within text_bounds.
|
||||
let is_fully_visible = x_after_longest < text_bounds.right()
|
||||
&& x_after_longest + element_bounds.width < viewport_bounds.right();
|
||||
|
||||
let origin = if is_fully_visible {
|
||||
text_bounds.origin
|
||||
+ point(
|
||||
longest_line_width + PADDING_X - scroll_pixel_position.x,
|
||||
edit_start.row().as_f32() * line_height - scroll_pixel_position.y,
|
||||
)
|
||||
point(
|
||||
x_after_longest,
|
||||
text_bounds.origin.y + edit_start.row().as_f32() * line_height
|
||||
- scroll_pixel_position.y,
|
||||
)
|
||||
} else {
|
||||
let target_above =
|
||||
DisplayRow(edit_start.row().0.saturating_sub(line_count as u32));
|
||||
let row_target = if visible_row_range
|
||||
.contains(&DisplayRow(target_above.0.saturating_sub(1)))
|
||||
{
|
||||
target_above
|
||||
// Avoid overlapping both the edited rows and the user's cursor.
|
||||
let target_above = DisplayRow(
|
||||
edit_start
|
||||
.row()
|
||||
.0
|
||||
.min(
|
||||
newest_selection_head
|
||||
.map_or(u32::MAX, |cursor_row| cursor_row.row().0),
|
||||
)
|
||||
.saturating_sub(line_count as u32),
|
||||
);
|
||||
let mut row_target;
|
||||
if visible_row_range.contains(&DisplayRow(target_above.0.saturating_sub(1))) {
|
||||
row_target = target_above;
|
||||
} else {
|
||||
DisplayRow(edit_end.row().0 + 1)
|
||||
row_target = DisplayRow(
|
||||
edit_end.row().0.max(
|
||||
newest_selection_head.map_or(0, |cursor_row| cursor_row.row().0),
|
||||
) + 1,
|
||||
);
|
||||
if !visible_row_range.contains(&row_target) {
|
||||
// Not visible, so fallback on displaying immediately below the cursor.
|
||||
if let Some(cursor) = newest_selection_head {
|
||||
row_target = DisplayRow(cursor.row().0 + 1);
|
||||
} else {
|
||||
// Not visible and no cursor visible, so fallback on displaying at the top of the editor.
|
||||
row_target = DisplayRow(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
text_bounds.origin
|
||||
@@ -3549,8 +3635,10 @@ impl EditorElement {
|
||||
)
|
||||
};
|
||||
|
||||
element.prepaint_as_root(origin, element_bounds.into(), window, cx);
|
||||
Some(element)
|
||||
window.defer_draw(element, origin, 1);
|
||||
|
||||
// Do not return an element, since it will already be drawn due to defer_draw.
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3794,8 +3882,13 @@ impl EditorElement {
|
||||
- scroll_pixel_position.y;
|
||||
let x = text_hitbox.bounds.right() - px(100.);
|
||||
|
||||
let mut element =
|
||||
diff_hunk_controls(multi_buffer_range.clone(), line_height, &editor, cx);
|
||||
let mut element = diff_hunk_controls(
|
||||
display_row_range.start.0,
|
||||
multi_buffer_range.clone(),
|
||||
line_height,
|
||||
&editor,
|
||||
cx,
|
||||
);
|
||||
element.prepaint_as_root(
|
||||
gpui::Point::new(x, y),
|
||||
size(px(100.0), line_height).into(),
|
||||
@@ -4147,9 +4240,9 @@ impl EditorElement {
|
||||
// In singleton buffers, we select corresponding lines on the line number click, so use | -like cursor.
|
||||
// In multi buffers, we open file at the line number clicked, so use a pointing hand cursor.
|
||||
if is_singleton {
|
||||
window.set_cursor_style(CursorStyle::IBeam, hitbox);
|
||||
window.set_cursor_style(CursorStyle::IBeam, &hitbox);
|
||||
} else {
|
||||
window.set_cursor_style(CursorStyle::PointingHand, hitbox);
|
||||
window.set_cursor_style(CursorStyle::PointingHand, &hitbox);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4223,7 +4316,7 @@ impl EditorElement {
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn diff_hunk_bounds(
|
||||
fn diff_hunk_bounds(
|
||||
snapshot: &EditorSnapshot,
|
||||
line_height: Pixels,
|
||||
gutter_bounds: Bounds<Pixels>,
|
||||
@@ -4231,15 +4324,14 @@ impl EditorElement {
|
||||
) -> Bounds<Pixels> {
|
||||
let scroll_position = snapshot.scroll_position();
|
||||
let scroll_top = scroll_position.y * line_height;
|
||||
let gutter_strip_width = (0.275 * line_height).floor();
|
||||
|
||||
match hunk {
|
||||
DisplayDiffHunk::Folded { display_row, .. } => {
|
||||
let start_y = display_row.as_f32() * line_height - scroll_top;
|
||||
let end_y = start_y + line_height;
|
||||
|
||||
let width = Self::diff_hunk_strip_width(line_height);
|
||||
let highlight_origin = gutter_bounds.origin + point(px(0.), start_y);
|
||||
let highlight_size = size(width, end_y - start_y);
|
||||
let highlight_size = size(gutter_strip_width, end_y - start_y);
|
||||
Bounds::new(highlight_origin, highlight_size)
|
||||
}
|
||||
DisplayDiffHunk::Unfolded {
|
||||
@@ -4281,21 +4373,14 @@ impl EditorElement {
|
||||
let start_y = start_row.as_f32() * line_height - scroll_top;
|
||||
let end_y = end_row_in_current_excerpt.as_f32() * line_height - scroll_top;
|
||||
|
||||
let width = Self::diff_hunk_strip_width(line_height);
|
||||
let highlight_origin = gutter_bounds.origin + point(px(0.), start_y);
|
||||
let highlight_size = size(width, end_y - start_y);
|
||||
let highlight_size = size(gutter_strip_width, end_y - start_y);
|
||||
Bounds::new(highlight_origin, highlight_size)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the width of the diff strip that will be displayed in the gutter.
|
||||
pub(super) fn diff_hunk_strip_width(line_height: Pixels) -> Pixels {
|
||||
// We floor the value to prevent pixel rounding.
|
||||
(0.275 * line_height).floor()
|
||||
}
|
||||
|
||||
fn paint_gutter_indicators(
|
||||
&self,
|
||||
layout: &mut EditorLayout,
|
||||
@@ -5211,7 +5296,7 @@ impl EditorElement {
|
||||
let editor = self.editor.clone();
|
||||
let text_hitbox = layout.text_hitbox.clone();
|
||||
let gutter_hitbox = layout.gutter_hitbox.clone();
|
||||
let hovered_hunk =
|
||||
let multi_buffer_range =
|
||||
layout
|
||||
.display_hunks
|
||||
.iter()
|
||||
@@ -5237,10 +5322,17 @@ impl EditorElement {
|
||||
if phase == DispatchPhase::Bubble {
|
||||
match event.button {
|
||||
MouseButton::Left => editor.update(cx, |editor, cx| {
|
||||
let pending_mouse_down = editor
|
||||
.pending_mouse_down
|
||||
.get_or_insert_with(Default::default)
|
||||
.clone();
|
||||
|
||||
*pending_mouse_down.borrow_mut() = Some(event.clone());
|
||||
|
||||
Self::mouse_left_down(
|
||||
editor,
|
||||
event,
|
||||
hovered_hunk.clone(),
|
||||
multi_buffer_range.clone(),
|
||||
&position_map,
|
||||
&text_hitbox,
|
||||
&gutter_hitbox,
|
||||
@@ -5288,6 +5380,43 @@ impl EditorElement {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
window.on_mouse_event({
|
||||
let editor = self.editor.clone();
|
||||
let position_map = layout.position_map.clone();
|
||||
let text_hitbox = layout.text_hitbox.clone();
|
||||
|
||||
let mut captured_mouse_down = None;
|
||||
|
||||
move |event: &MouseUpEvent, phase, window, cx| match phase {
|
||||
// Clear the pending mouse down during the capture phase,
|
||||
// so that it happens even if another event handler stops
|
||||
// propagation.
|
||||
DispatchPhase::Capture => editor.update(cx, |editor, _cx| {
|
||||
let pending_mouse_down = editor
|
||||
.pending_mouse_down
|
||||
.get_or_insert_with(Default::default)
|
||||
.clone();
|
||||
|
||||
let mut pending_mouse_down = pending_mouse_down.borrow_mut();
|
||||
if pending_mouse_down.is_some() && text_hitbox.is_hovered(window) {
|
||||
captured_mouse_down = pending_mouse_down.take();
|
||||
window.refresh();
|
||||
}
|
||||
}),
|
||||
// Fire click handlers during the bubble phase.
|
||||
DispatchPhase::Bubble => editor.update(cx, |editor, cx| {
|
||||
if let Some(mouse_down) = captured_mouse_down.take() {
|
||||
let event = ClickEvent {
|
||||
down: mouse_down,
|
||||
up: event.clone(),
|
||||
};
|
||||
Self::click(editor, &event, &position_map, &text_hitbox, window, cx);
|
||||
}
|
||||
}),
|
||||
}
|
||||
});
|
||||
|
||||
window.on_mouse_event({
|
||||
let position_map = layout.position_map.clone();
|
||||
let editor = self.editor.clone();
|
||||
@@ -6623,7 +6752,6 @@ impl Element for EditorElement {
|
||||
cx,
|
||||
);
|
||||
|
||||
let mut max_visible_line_width = Pixels::ZERO;
|
||||
let mut line_layouts = Self::layout_lines(
|
||||
start_row..end_row,
|
||||
&snapshot,
|
||||
@@ -6633,11 +6761,38 @@ impl Element for EditorElement {
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
for line_with_invisibles in &line_layouts {
|
||||
if line_with_invisibles.width > max_visible_line_width {
|
||||
max_visible_line_width = line_with_invisibles.width;
|
||||
}
|
||||
}
|
||||
|
||||
let longest_line_blame_width = self
|
||||
.editor
|
||||
.update(cx, |editor, cx| {
|
||||
if !editor.show_git_blame_inline {
|
||||
return None;
|
||||
}
|
||||
let blame = editor.blame.as_ref()?;
|
||||
let blame_entry = blame
|
||||
.update(cx, |blame, cx| {
|
||||
let row_infos =
|
||||
snapshot.row_infos(snapshot.longest_row()).next()?;
|
||||
blame.blame_for_rows(&[row_infos], cx).next()
|
||||
})
|
||||
.flatten()?;
|
||||
let workspace = editor.workspace.as_ref().map(|(w, _)| w.to_owned());
|
||||
let mut element = render_inline_blame_entry(
|
||||
blame,
|
||||
blame_entry,
|
||||
&style,
|
||||
workspace,
|
||||
cx,
|
||||
);
|
||||
let inline_blame_padding = INLINE_BLAME_PADDING_EM_WIDTHS * em_advance;
|
||||
Some(
|
||||
element
|
||||
.layout_as_root(AvailableSpace::min_size(), window, cx)
|
||||
.width
|
||||
+ inline_blame_padding,
|
||||
)
|
||||
})
|
||||
.unwrap_or(Pixels::ZERO);
|
||||
|
||||
let longest_line_width = layout_line(
|
||||
snapshot.longest_row(),
|
||||
@@ -6655,6 +6810,7 @@ impl Element for EditorElement {
|
||||
letter_size,
|
||||
&snapshot,
|
||||
longest_line_width,
|
||||
longest_line_blame_width,
|
||||
&style,
|
||||
cx,
|
||||
);
|
||||
@@ -7029,6 +7185,7 @@ impl Element for EditorElement {
|
||||
&line_layouts,
|
||||
line_height,
|
||||
scroll_pixel_position,
|
||||
newest_selection_head,
|
||||
editor_width,
|
||||
&style,
|
||||
window,
|
||||
@@ -7247,6 +7404,7 @@ impl ScrollbarRangeData {
|
||||
letter_size: Size<Pixels>,
|
||||
snapshot: &EditorSnapshot,
|
||||
longest_line_width: Pixels,
|
||||
longest_line_blame_width: Pixels,
|
||||
style: &EditorStyle,
|
||||
|
||||
cx: &mut App,
|
||||
@@ -7265,7 +7423,7 @@ impl ScrollbarRangeData {
|
||||
};
|
||||
|
||||
let overscroll = size(
|
||||
scrollbar_width + (letter_size.width / 2.0),
|
||||
scrollbar_width + (letter_size.width / 2.0) + longest_line_blame_width,
|
||||
letter_size.height * scroll_beyond_last_line,
|
||||
);
|
||||
|
||||
@@ -7776,8 +7934,8 @@ impl HighlightedRange {
|
||||
};
|
||||
|
||||
let top_curve_width = curve_width(first_line.start_x, first_line.end_x);
|
||||
let mut path = gpui::Path::new(first_top_right - top_curve_width);
|
||||
path.curve_to(first_top_right + curve_height, first_top_right);
|
||||
let mut builder = gpui::PathBuilder::fill();
|
||||
builder.curve_to(first_top_right + curve_height, first_top_right);
|
||||
|
||||
let mut iter = lines.iter().enumerate().peekable();
|
||||
while let Some((ix, line)) = iter.next() {
|
||||
@@ -7788,42 +7946,42 @@ impl HighlightedRange {
|
||||
|
||||
match next_top_right.x.partial_cmp(&bottom_right.x).unwrap() {
|
||||
Ordering::Equal => {
|
||||
path.line_to(bottom_right);
|
||||
builder.line_to(bottom_right);
|
||||
}
|
||||
Ordering::Less => {
|
||||
let curve_width = curve_width(next_top_right.x, bottom_right.x);
|
||||
path.line_to(bottom_right - curve_height);
|
||||
builder.line_to(bottom_right - curve_height);
|
||||
if self.corner_radius > Pixels::ZERO {
|
||||
path.curve_to(bottom_right - curve_width, bottom_right);
|
||||
builder.curve_to(bottom_right - curve_width, bottom_right);
|
||||
}
|
||||
path.line_to(next_top_right + curve_width);
|
||||
builder.line_to(next_top_right + curve_width);
|
||||
if self.corner_radius > Pixels::ZERO {
|
||||
path.curve_to(next_top_right + curve_height, next_top_right);
|
||||
builder.curve_to(next_top_right + curve_height, next_top_right);
|
||||
}
|
||||
}
|
||||
Ordering::Greater => {
|
||||
let curve_width = curve_width(bottom_right.x, next_top_right.x);
|
||||
path.line_to(bottom_right - curve_height);
|
||||
builder.line_to(bottom_right - curve_height);
|
||||
if self.corner_radius > Pixels::ZERO {
|
||||
path.curve_to(bottom_right + curve_width, bottom_right);
|
||||
builder.curve_to(bottom_right + curve_width, bottom_right);
|
||||
}
|
||||
path.line_to(next_top_right - curve_width);
|
||||
builder.line_to(next_top_right - curve_width);
|
||||
if self.corner_radius > Pixels::ZERO {
|
||||
path.curve_to(next_top_right + curve_height, next_top_right);
|
||||
builder.curve_to(next_top_right + curve_height, next_top_right);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let curve_width = curve_width(line.start_x, line.end_x);
|
||||
path.line_to(bottom_right - curve_height);
|
||||
builder.line_to(bottom_right - curve_height);
|
||||
if self.corner_radius > Pixels::ZERO {
|
||||
path.curve_to(bottom_right - curve_width, bottom_right);
|
||||
builder.curve_to(bottom_right - curve_width, bottom_right);
|
||||
}
|
||||
|
||||
let bottom_left = point(line.start_x, bottom_right.y);
|
||||
path.line_to(bottom_left + curve_width);
|
||||
builder.line_to(bottom_left + curve_width);
|
||||
if self.corner_radius > Pixels::ZERO {
|
||||
path.curve_to(bottom_left - curve_height, bottom_left);
|
||||
builder.curve_to(bottom_left - curve_height, bottom_left);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7831,24 +7989,26 @@ impl HighlightedRange {
|
||||
if first_line.start_x > last_line.start_x {
|
||||
let curve_width = curve_width(last_line.start_x, first_line.start_x);
|
||||
let second_top_left = point(last_line.start_x, start_y + self.line_height);
|
||||
path.line_to(second_top_left + curve_height);
|
||||
builder.line_to(second_top_left + curve_height);
|
||||
if self.corner_radius > Pixels::ZERO {
|
||||
path.curve_to(second_top_left + curve_width, second_top_left);
|
||||
builder.curve_to(second_top_left + curve_width, second_top_left);
|
||||
}
|
||||
let first_bottom_left = point(first_line.start_x, second_top_left.y);
|
||||
path.line_to(first_bottom_left - curve_width);
|
||||
builder.line_to(first_bottom_left - curve_width);
|
||||
if self.corner_radius > Pixels::ZERO {
|
||||
path.curve_to(first_bottom_left - curve_height, first_bottom_left);
|
||||
builder.curve_to(first_bottom_left - curve_height, first_bottom_left);
|
||||
}
|
||||
}
|
||||
|
||||
path.line_to(first_top_left + curve_height);
|
||||
builder.line_to(first_top_left + curve_height);
|
||||
if self.corner_radius > Pixels::ZERO {
|
||||
path.curve_to(first_top_left + top_curve_width, first_top_left);
|
||||
builder.curve_to(first_top_left + top_curve_width, first_top_left);
|
||||
}
|
||||
path.line_to(first_top_right - top_curve_width);
|
||||
builder.line_to(first_top_right - top_curve_width);
|
||||
|
||||
window.paint_path(path, self.color);
|
||||
if let Ok(path) = builder.build() {
|
||||
window.paint_path(path, self.color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8469,6 +8629,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn diff_hunk_controls(
|
||||
row: u32,
|
||||
hunk_range: Range<Anchor>,
|
||||
line_height: Pixels,
|
||||
editor: &Entity<Editor>,
|
||||
@@ -8486,7 +8647,7 @@ fn diff_hunk_controls(
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.gap_1()
|
||||
.child(
|
||||
IconButton::new("next-hunk", IconName::ArrowDown)
|
||||
IconButton::new(("next-hunk", row as u64), IconName::ArrowDown)
|
||||
.shape(IconButtonShape::Square)
|
||||
.icon_size(IconSize::Small)
|
||||
// .disabled(!has_multiple_hunks)
|
||||
@@ -8509,7 +8670,7 @@ fn diff_hunk_controls(
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
IconButton::new("prev-hunk", IconName::ArrowUp)
|
||||
IconButton::new(("prev-hunk", row as u64), IconName::ArrowUp)
|
||||
.shape(IconButtonShape::Square)
|
||||
.icon_size(IconSize::Small)
|
||||
// .disabled(!has_multiple_hunks)
|
||||
|
||||
@@ -38,8 +38,11 @@ use text::{BufferId, Selection};
|
||||
use theme::{Theme, ThemeSettings};
|
||||
use ui::{h_flex, prelude::*, IconDecorationKind, Label};
|
||||
use util::{paths::PathExt, ResultExt, TryFutureExt};
|
||||
use workspace::item::{BreadcrumbText, FollowEvent};
|
||||
use workspace::item::{Dedup, ItemSettings, SerializableItem, TabContentParams};
|
||||
use workspace::{
|
||||
item::{BreadcrumbText, FollowEvent},
|
||||
searchable::SearchOptions,
|
||||
};
|
||||
use workspace::{
|
||||
item::{FollowableItem, Item, ItemEvent, ProjectItem},
|
||||
searchable::{Direction, SearchEvent, SearchableItem, SearchableItemHandle},
|
||||
@@ -1040,10 +1043,10 @@ impl SerializableItem for Editor {
|
||||
} => window.spawn(cx, |mut cx| {
|
||||
let project = project.clone();
|
||||
async move {
|
||||
let language = if let Some(language_name) = language {
|
||||
let language_registry =
|
||||
project.update(&mut cx, |project, _| project.languages().clone())?;
|
||||
let language_registry =
|
||||
project.update(&mut cx, |project, _| project.languages().clone())?;
|
||||
|
||||
let language = if let Some(language_name) = language {
|
||||
// We don't fail here, because we'd rather not set the language if the name changed
|
||||
// than fail to restore the buffer.
|
||||
language_registry
|
||||
@@ -1061,6 +1064,7 @@ impl SerializableItem for Editor {
|
||||
|
||||
// Then set the text so that the dirty bit is set correctly
|
||||
buffer.update(&mut cx, |buffer, cx| {
|
||||
buffer.set_language_registry(language_registry);
|
||||
if let Some(language) = language {
|
||||
buffer.set_language(Some(language), cx);
|
||||
}
|
||||
@@ -1323,6 +1327,28 @@ impl SearchableItem for Editor {
|
||||
}
|
||||
}
|
||||
|
||||
fn supported_options(&self) -> SearchOptions {
|
||||
if self.in_project_search {
|
||||
SearchOptions {
|
||||
case: true,
|
||||
word: true,
|
||||
regex: true,
|
||||
replacement: false,
|
||||
selection: false,
|
||||
find_in_results: true,
|
||||
}
|
||||
} else {
|
||||
SearchOptions {
|
||||
case: true,
|
||||
word: true,
|
||||
regex: true,
|
||||
replacement: true,
|
||||
selection: true,
|
||||
find_in_results: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn query_suggestion(&mut self, window: &mut Window, cx: &mut Context<Self>) -> String {
|
||||
let setting = EditorSettings::get_global(cx).seed_search_query_from_cursor;
|
||||
let snapshot = &self.snapshot(window, cx).buffer_snapshot;
|
||||
@@ -1507,26 +1533,42 @@ impl SearchableItem for Editor {
|
||||
search_within_ranges
|
||||
};
|
||||
|
||||
for (search_buffer, search_range, excerpt_id) in
|
||||
buffer.ranges_to_buffer_ranges(search_within_ranges.into_iter())
|
||||
{
|
||||
ranges.extend(
|
||||
query
|
||||
.search(search_buffer, Some(search_range.clone()))
|
||||
.await
|
||||
.into_iter()
|
||||
.map(|match_range| {
|
||||
let start =
|
||||
search_buffer.anchor_after(search_range.start + match_range.start);
|
||||
let end =
|
||||
search_buffer.anchor_before(search_range.start + match_range.end);
|
||||
Anchor::range_in_buffer(
|
||||
excerpt_id,
|
||||
search_buffer.remote_id(),
|
||||
start..end,
|
||||
)
|
||||
}),
|
||||
);
|
||||
for range in search_within_ranges {
|
||||
for (search_buffer, search_range, excerpt_id, deleted_hunk_anchor) in
|
||||
buffer.range_to_buffer_ranges_with_deleted_hunks(range)
|
||||
{
|
||||
ranges.extend(
|
||||
query
|
||||
.search(search_buffer, Some(search_range.clone()))
|
||||
.await
|
||||
.into_iter()
|
||||
.map(|match_range| {
|
||||
if let Some(deleted_hunk_anchor) = deleted_hunk_anchor {
|
||||
let start = search_buffer
|
||||
.anchor_after(search_range.start + match_range.start);
|
||||
let end = search_buffer
|
||||
.anchor_before(search_range.start + match_range.end);
|
||||
Anchor {
|
||||
diff_base_anchor: Some(start),
|
||||
..deleted_hunk_anchor
|
||||
}..Anchor {
|
||||
diff_base_anchor: Some(end),
|
||||
..deleted_hunk_anchor
|
||||
}
|
||||
} else {
|
||||
let start = search_buffer
|
||||
.anchor_after(search_range.start + match_range.start);
|
||||
let end = search_buffer
|
||||
.anchor_before(search_range.start + match_range.end);
|
||||
Anchor::range_in_buffer(
|
||||
excerpt_id,
|
||||
search_buffer.remote_id(),
|
||||
start..end,
|
||||
)
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ranges
|
||||
|
||||
@@ -87,8 +87,8 @@ define_connection!(
|
||||
// mtime_seconds: Option<i64>,
|
||||
// mtime_nanos: Option<i32>,
|
||||
// )
|
||||
pub static ref DB: EditorDb<WorkspaceDb> =
|
||||
&[sql! (
|
||||
pub static ref DB: EditorDb<WorkspaceDb> = &[
|
||||
sql! (
|
||||
CREATE TABLE editors(
|
||||
item_id INTEGER NOT NULL,
|
||||
workspace_id INTEGER NOT NULL,
|
||||
@@ -134,7 +134,7 @@ define_connection!(
|
||||
ALTER TABLE editors ADD COLUMN mtime_seconds INTEGER DEFAULT NULL;
|
||||
ALTER TABLE editors ADD COLUMN mtime_nanos INTEGER DEFAULT NULL;
|
||||
),
|
||||
];
|
||||
];
|
||||
);
|
||||
|
||||
impl EditorDb {
|
||||
|
||||
@@ -167,6 +167,38 @@ async fn copy_extension_resources(
|
||||
}
|
||||
}
|
||||
|
||||
if !manifest.icon_themes.is_empty() {
|
||||
let output_icon_themes_dir = output_dir.join("icon_themes");
|
||||
fs::create_dir_all(&output_icon_themes_dir)?;
|
||||
for icon_theme_path in &manifest.icon_themes {
|
||||
fs::copy(
|
||||
extension_path.join(icon_theme_path),
|
||||
output_icon_themes_dir.join(
|
||||
icon_theme_path
|
||||
.file_name()
|
||||
.ok_or_else(|| anyhow!("invalid icon theme path"))?,
|
||||
),
|
||||
)
|
||||
.with_context(|| {
|
||||
format!("failed to copy icon theme '{}'", icon_theme_path.display())
|
||||
})?;
|
||||
}
|
||||
|
||||
let output_icons_dir = output_dir.join("icons");
|
||||
fs::create_dir_all(&output_icons_dir)?;
|
||||
copy_recursive(
|
||||
fs.as_ref(),
|
||||
&extension_path.join("icons"),
|
||||
&output_icons_dir,
|
||||
CopyOptions {
|
||||
overwrite: true,
|
||||
ignore_if_exists: false,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.with_context(|| "failed to copy icons")?;
|
||||
}
|
||||
|
||||
if !manifest.languages.is_empty() {
|
||||
let output_languages_dir = output_dir.join("languages");
|
||||
fs::create_dir_all(&output_languages_dir)?;
|
||||
|
||||
@@ -444,6 +444,23 @@ impl ExtensionStore {
|
||||
.filter_map(|(name, theme)| theme.extension.as_ref().eq(extension_id).then_some(name))
|
||||
}
|
||||
|
||||
/// Returns the names of icon themes provided by extensions.
|
||||
pub fn extension_icon_themes<'a>(
|
||||
&'a self,
|
||||
extension_id: &'a str,
|
||||
) -> impl Iterator<Item = &'a Arc<str>> {
|
||||
self.extension_index
|
||||
.icon_themes
|
||||
.iter()
|
||||
.filter_map(|(name, icon_theme)| {
|
||||
icon_theme
|
||||
.extension
|
||||
.as_ref()
|
||||
.eq(extension_id)
|
||||
.then_some(name)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn fetch_extensions(
|
||||
&self,
|
||||
search: Option<&str>,
|
||||
|
||||
@@ -295,6 +295,25 @@ impl ExtensionsPage {
|
||||
);
|
||||
})
|
||||
.ok();
|
||||
return;
|
||||
}
|
||||
|
||||
let icon_themes = extension_store
|
||||
.extension_icon_themes(extension_id)
|
||||
.map(|name| name.to_string())
|
||||
.collect::<Vec<_>>();
|
||||
if !icon_themes.is_empty() {
|
||||
workspace
|
||||
.update(cx, |_workspace, cx| {
|
||||
window.dispatch_action(
|
||||
zed_actions::icon_theme_selector::Toggle {
|
||||
themes_filter: Some(icon_themes),
|
||||
}
|
||||
.boxed_clone(),
|
||||
cx,
|
||||
);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,21 +41,16 @@ impl FeatureFlag for Assistant2FeatureFlag {
|
||||
const NAME: &'static str = "assistant2";
|
||||
}
|
||||
|
||||
pub struct ToolUseFeatureFlag;
|
||||
|
||||
impl FeatureFlag for ToolUseFeatureFlag {
|
||||
const NAME: &'static str = "assistant-tool-use";
|
||||
|
||||
fn enabled_for_staff() -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PredictEditsFeatureFlag;
|
||||
impl FeatureFlag for PredictEditsFeatureFlag {
|
||||
const NAME: &'static str = "predict-edits";
|
||||
}
|
||||
|
||||
pub struct PredictEditsRateCompletionsFeatureFlag;
|
||||
impl FeatureFlag for PredictEditsRateCompletionsFeatureFlag {
|
||||
const NAME: &'static str = "predict-edits-rate-completions";
|
||||
}
|
||||
|
||||
pub struct GitUiFeatureFlag;
|
||||
impl FeatureFlag for GitUiFeatureFlag {
|
||||
const NAME: &'static str = "git-ui";
|
||||
|
||||
@@ -21,25 +21,18 @@ const fn zed_repo_url() -> &'static str {
|
||||
"https://github.com/zed-industries/zed"
|
||||
}
|
||||
|
||||
fn request_feature_url(specs: &SystemSpecs) -> String {
|
||||
format!(
|
||||
concat!(
|
||||
"https://github.com/zed-industries/zed/issues/new",
|
||||
"?labels=admin+read%2Ctriage%2Cenhancement",
|
||||
"&template=0_feature_request.yml",
|
||||
"&environment={}"
|
||||
),
|
||||
urlencoding::encode(&specs.to_string())
|
||||
)
|
||||
fn request_feature_url() -> String {
|
||||
"https://github.com/zed-industries/zed/discussions/new/choose".to_string()
|
||||
}
|
||||
|
||||
fn file_bug_report_url(specs: &SystemSpecs) -> String {
|
||||
format!(
|
||||
concat!(
|
||||
"https://github.com/zed-industries/zed/issues/new",
|
||||
"?labels=admin+read%2Ctriage%2Cbug",
|
||||
"&template=1_bug_report.yml",
|
||||
"&environment={}"
|
||||
"?",
|
||||
"template=1_bug_report.yml",
|
||||
"&",
|
||||
"environment={}"
|
||||
),
|
||||
urlencoding::encode(&specs.to_string())
|
||||
)
|
||||
@@ -74,11 +67,9 @@ pub fn init(cx: &mut App) {
|
||||
.detach();
|
||||
})
|
||||
.register_action(|_, _: &RequestFeature, window, cx| {
|
||||
let specs = SystemSpecs::new(window, cx);
|
||||
cx.spawn_in(window, |_, mut cx| async move {
|
||||
let specs = specs.await;
|
||||
cx.update(|_, cx| {
|
||||
cx.open_url(&request_feature_url(&specs));
|
||||
cx.open_url(&request_feature_url());
|
||||
})
|
||||
.log_err();
|
||||
})
|
||||
|
||||
@@ -194,6 +194,7 @@ impl FeedbackModal {
|
||||
editor.set_show_inline_completions(Some(false), window, cx);
|
||||
editor.set_vertical_scroll_margin(5, cx);
|
||||
editor.set_use_modal_editing(false);
|
||||
editor.set_soft_wrap();
|
||||
editor
|
||||
});
|
||||
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use futures::AsyncReadExt;
|
||||
use http_client::{http::HeaderMap, AsyncBody, HttpClient, Method, Request as HttpRequest};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub const FIREWORKS_API_URL: &str = "https://api.openai.com/v1";
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CompletionRequest {
|
||||
pub model: String,
|
||||
pub prompt: String,
|
||||
pub max_tokens: u32,
|
||||
pub temperature: f32,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub prediction: Option<Prediction>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub rewrite_speculation: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize, Debug)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum Prediction {
|
||||
Content { content: String },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Response {
|
||||
pub completion: CompletionResponse,
|
||||
pub headers: Headers,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct CompletionResponse {
|
||||
pub id: String,
|
||||
pub object: String,
|
||||
pub created: u64,
|
||||
pub model: String,
|
||||
pub choices: Vec<CompletionChoice>,
|
||||
pub usage: Usage,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct CompletionChoice {
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Usage {
|
||||
pub prompt_tokens: u32,
|
||||
pub completion_tokens: u32,
|
||||
pub total_tokens: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize)]
|
||||
pub struct Headers {
|
||||
pub server_processing_time: Option<f64>,
|
||||
pub request_id: Option<String>,
|
||||
pub prompt_tokens: Option<u32>,
|
||||
pub speculation_generated_tokens: Option<u32>,
|
||||
pub cached_prompt_tokens: Option<u32>,
|
||||
pub backend_host: Option<String>,
|
||||
pub num_concurrent_requests: Option<u32>,
|
||||
pub deployment: Option<String>,
|
||||
pub tokenizer_queue_duration: Option<f64>,
|
||||
pub tokenizer_duration: Option<f64>,
|
||||
pub prefill_queue_duration: Option<f64>,
|
||||
pub prefill_duration: Option<f64>,
|
||||
pub generation_queue_duration: Option<f64>,
|
||||
}
|
||||
|
||||
impl Headers {
|
||||
pub fn parse(headers: &HeaderMap) -> Self {
|
||||
Headers {
|
||||
request_id: headers
|
||||
.get("x-request-id")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.map(String::from),
|
||||
server_processing_time: headers
|
||||
.get("fireworks-server-processing-time")
|
||||
.and_then(|v| v.to_str().ok()?.parse().ok()),
|
||||
prompt_tokens: headers
|
||||
.get("fireworks-prompt-tokens")
|
||||
.and_then(|v| v.to_str().ok()?.parse().ok()),
|
||||
speculation_generated_tokens: headers
|
||||
.get("fireworks-speculation-generated-tokens")
|
||||
.and_then(|v| v.to_str().ok()?.parse().ok()),
|
||||
cached_prompt_tokens: headers
|
||||
.get("fireworks-cached-prompt-tokens")
|
||||
.and_then(|v| v.to_str().ok()?.parse().ok()),
|
||||
backend_host: headers
|
||||
.get("fireworks-backend-host")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.map(String::from),
|
||||
num_concurrent_requests: headers
|
||||
.get("fireworks-num-concurrent-requests")
|
||||
.and_then(|v| v.to_str().ok()?.parse().ok()),
|
||||
deployment: headers
|
||||
.get("fireworks-deployment")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.map(String::from),
|
||||
tokenizer_queue_duration: headers
|
||||
.get("fireworks-tokenizer-queue-duration")
|
||||
.and_then(|v| v.to_str().ok()?.parse().ok()),
|
||||
tokenizer_duration: headers
|
||||
.get("fireworks-tokenizer-duration")
|
||||
.and_then(|v| v.to_str().ok()?.parse().ok()),
|
||||
prefill_queue_duration: headers
|
||||
.get("fireworks-prefill-queue-duration")
|
||||
.and_then(|v| v.to_str().ok()?.parse().ok()),
|
||||
prefill_duration: headers
|
||||
.get("fireworks-prefill-duration")
|
||||
.and_then(|v| v.to_str().ok()?.parse().ok()),
|
||||
generation_queue_duration: headers
|
||||
.get("fireworks-generation-queue-duration")
|
||||
.and_then(|v| v.to_str().ok()?.parse().ok()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn complete(
|
||||
client: &dyn HttpClient,
|
||||
api_url: &str,
|
||||
api_key: &str,
|
||||
request: CompletionRequest,
|
||||
) -> Result<Response> {
|
||||
let uri = format!("{api_url}/completions");
|
||||
let request_builder = HttpRequest::builder()
|
||||
.method(Method::POST)
|
||||
.uri(uri)
|
||||
.header("Content-Type", "application/json")
|
||||
.header("Authorization", format!("Bearer {}", api_key));
|
||||
|
||||
let request = request_builder.body(AsyncBody::from(serde_json::to_string(&request)?))?;
|
||||
let mut response = client.send(request).await?;
|
||||
|
||||
if response.status().is_success() {
|
||||
let headers = Headers::parse(response.headers());
|
||||
|
||||
let mut body = String::new();
|
||||
response.body_mut().read_to_string(&mut body).await?;
|
||||
|
||||
Ok(Response {
|
||||
completion: serde_json::from_str(&body)?,
|
||||
headers,
|
||||
})
|
||||
} else {
|
||||
let mut body = String::new();
|
||||
response.body_mut().read_to_string(&mut body).await?;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct FireworksResponse {
|
||||
error: FireworksError,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct FireworksError {
|
||||
message: String,
|
||||
}
|
||||
|
||||
match serde_json::from_str::<FireworksResponse>(&body) {
|
||||
Ok(response) if !response.error.message.is_empty() => Err(anyhow!(
|
||||
"Failed to connect to Fireworks API: {}",
|
||||
response.error.message,
|
||||
)),
|
||||
|
||||
_ => Err(anyhow!(
|
||||
"Failed to connect to Fireworks API: {} {}",
|
||||
response.status(),
|
||||
body,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -398,8 +398,15 @@ mod tests {
|
||||
assert!(event.flags.contains(StreamFlags::ITEM_CREATED));
|
||||
|
||||
fs::remove_file(path.join("existing-file-5")).unwrap();
|
||||
let events = rx.recv_timeout(Duration::from_secs(2)).unwrap();
|
||||
let event = events.last().unwrap();
|
||||
let mut events = rx.recv_timeout(Duration::from_secs(2)).unwrap();
|
||||
let mut event = events.last().unwrap();
|
||||
// we see this duplicate about 1/100 test runs.
|
||||
if event.path == path.join("new-file")
|
||||
&& event.flags.contains(StreamFlags::ITEM_CREATED)
|
||||
{
|
||||
events = rx.recv_timeout(Duration::from_secs(2)).unwrap();
|
||||
event = events.last().unwrap();
|
||||
}
|
||||
assert_eq!(event.path, path.join("existing-file-5"));
|
||||
assert!(event.flags.contains(StreamFlags::ITEM_REMOVED));
|
||||
drop(handle);
|
||||
|
||||
@@ -35,6 +35,7 @@ util.workspace = true
|
||||
unindent.workspace = true
|
||||
serde_json.workspace = true
|
||||
pretty_assertions.workspace = true
|
||||
text = {workspace = true, features = ["test-support"]}
|
||||
|
||||
[features]
|
||||
test-support = []
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use rope::Rope;
|
||||
use std::{iter, ops::Range};
|
||||
use std::{cmp, iter, ops::Range};
|
||||
use sum_tree::SumTree;
|
||||
use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point};
|
||||
|
||||
@@ -25,7 +25,7 @@ pub struct DiffHunk {
|
||||
}
|
||||
|
||||
/// We store [`InternalDiffHunk`]s internally so we don't need to store the additional row range.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct InternalDiffHunk {
|
||||
buffer_range: Range<Anchor>,
|
||||
diff_base_byte_range: Range<usize>,
|
||||
@@ -187,6 +187,69 @@ impl BufferDiff {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compare(&self, old: &Self, new_snapshot: &BufferSnapshot) -> Option<Range<Anchor>> {
|
||||
let mut new_cursor = self.tree.cursor::<()>(new_snapshot);
|
||||
let mut old_cursor = old.tree.cursor::<()>(new_snapshot);
|
||||
old_cursor.next(new_snapshot);
|
||||
new_cursor.next(new_snapshot);
|
||||
let mut start = None;
|
||||
let mut end = None;
|
||||
|
||||
loop {
|
||||
match (new_cursor.item(), old_cursor.item()) {
|
||||
(Some(new_hunk), Some(old_hunk)) => {
|
||||
match new_hunk
|
||||
.buffer_range
|
||||
.start
|
||||
.cmp(&old_hunk.buffer_range.start, new_snapshot)
|
||||
{
|
||||
cmp::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 => {
|
||||
if new_hunk != old_hunk {
|
||||
start.get_or_insert(new_hunk.buffer_range.start);
|
||||
if old_hunk
|
||||
.buffer_range
|
||||
.end
|
||||
.cmp(&new_hunk.buffer_range.end, new_snapshot)
|
||||
.is_ge()
|
||||
{
|
||||
end.replace(old_hunk.buffer_range.end);
|
||||
} else {
|
||||
end.replace(new_hunk.buffer_range.end);
|
||||
}
|
||||
}
|
||||
|
||||
new_cursor.next(new_snapshot);
|
||||
old_cursor.next(new_snapshot);
|
||||
}
|
||||
cmp::Ordering::Greater => {
|
||||
start.get_or_insert(old_hunk.buffer_range.start);
|
||||
end.replace(old_hunk.buffer_range.end);
|
||||
old_cursor.next(new_snapshot);
|
||||
}
|
||||
}
|
||||
}
|
||||
(Some(new_hunk), None) => {
|
||||
start.get_or_insert(new_hunk.buffer_range.start);
|
||||
end.replace(new_hunk.buffer_range.end);
|
||||
new_cursor.next(new_snapshot);
|
||||
}
|
||||
(None, Some(old_hunk)) => {
|
||||
start.get_or_insert(old_hunk.buffer_range.start);
|
||||
end.replace(old_hunk.buffer_range.end);
|
||||
old_cursor.next(new_snapshot);
|
||||
}
|
||||
(None, None) => break,
|
||||
}
|
||||
}
|
||||
|
||||
start.zip(end).map(|(start, end)| start..end)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn clear(&mut self, buffer: &text::BufferSnapshot) {
|
||||
self.tree = SumTree::new(buffer);
|
||||
@@ -427,4 +490,128 @@ mod tests {
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buffer_diff_compare() {
|
||||
let base_text = "
|
||||
zero
|
||||
one
|
||||
two
|
||||
three
|
||||
four
|
||||
five
|
||||
six
|
||||
seven
|
||||
eight
|
||||
nine
|
||||
"
|
||||
.unindent();
|
||||
|
||||
let buffer_text_1 = "
|
||||
one
|
||||
three
|
||||
four
|
||||
five
|
||||
SIX
|
||||
seven
|
||||
eight
|
||||
NINE
|
||||
"
|
||||
.unindent();
|
||||
|
||||
let mut buffer = Buffer::new(0, BufferId::new(1).unwrap(), buffer_text_1);
|
||||
|
||||
let empty_diff = BufferDiff::new(&buffer);
|
||||
let diff_1 = BufferDiff::build(&base_text, &buffer);
|
||||
let range = diff_1.compare(&empty_diff, &buffer).unwrap();
|
||||
assert_eq!(range.to_point(&buffer), Point::new(0, 0)..Point::new(8, 0));
|
||||
|
||||
// Edit does not affect the diff.
|
||||
buffer.edit_via_marked_text(
|
||||
&"
|
||||
one
|
||||
three
|
||||
four
|
||||
five
|
||||
«SIX.5»
|
||||
seven
|
||||
eight
|
||||
NINE
|
||||
"
|
||||
.unindent(),
|
||||
);
|
||||
let diff_2 = BufferDiff::build(&base_text, &buffer);
|
||||
assert_eq!(None, diff_2.compare(&diff_1, &buffer));
|
||||
|
||||
// Edit turns a deletion hunk into a modification.
|
||||
buffer.edit_via_marked_text(
|
||||
&"
|
||||
one
|
||||
«THREE»
|
||||
four
|
||||
five
|
||||
SIX.5
|
||||
seven
|
||||
eight
|
||||
NINE
|
||||
"
|
||||
.unindent(),
|
||||
);
|
||||
let diff_3 = BufferDiff::build(&base_text, &buffer);
|
||||
let range = diff_3.compare(&diff_2, &buffer).unwrap();
|
||||
assert_eq!(range.to_point(&buffer), Point::new(1, 0)..Point::new(2, 0));
|
||||
|
||||
// Edit turns a modification hunk into a deletion.
|
||||
buffer.edit_via_marked_text(
|
||||
&"
|
||||
one
|
||||
THREE
|
||||
four
|
||||
five«»
|
||||
seven
|
||||
eight
|
||||
NINE
|
||||
"
|
||||
.unindent(),
|
||||
);
|
||||
let diff_4 = BufferDiff::build(&base_text, &buffer);
|
||||
let range = diff_4.compare(&diff_3, &buffer).unwrap();
|
||||
assert_eq!(range.to_point(&buffer), Point::new(3, 4)..Point::new(4, 0));
|
||||
|
||||
// Edit introduces a new insertion hunk.
|
||||
buffer.edit_via_marked_text(
|
||||
&"
|
||||
one
|
||||
THREE
|
||||
four«
|
||||
FOUR.5
|
||||
»five
|
||||
seven
|
||||
eight
|
||||
NINE
|
||||
"
|
||||
.unindent(),
|
||||
);
|
||||
let diff_5 = BufferDiff::build(&base_text, &buffer);
|
||||
let range = diff_5.compare(&diff_4, &buffer).unwrap();
|
||||
assert_eq!(range.to_point(&buffer), Point::new(3, 0)..Point::new(4, 0));
|
||||
|
||||
// Edit removes a hunk.
|
||||
buffer.edit_via_marked_text(
|
||||
&"
|
||||
one
|
||||
THREE
|
||||
four
|
||||
FOUR.5
|
||||
five
|
||||
seven
|
||||
eight
|
||||
«nine»
|
||||
"
|
||||
.unindent(),
|
||||
);
|
||||
let diff_6 = BufferDiff::build(&base_text, &buffer);
|
||||
let range = diff_6.compare(&diff_5, &buffer).unwrap();
|
||||
assert_eq!(range.to_point(&buffer), Point::new(7, 0)..Point::new(8, 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -827,6 +827,7 @@ impl GitPanel {
|
||||
pub fn render_panel_header(
|
||||
&self,
|
||||
window: &mut Window,
|
||||
has_write_access: bool,
|
||||
cx: &mut Context<Self>,
|
||||
) -> impl IntoElement {
|
||||
let focus_handle = self.focus_handle(cx).clone();
|
||||
@@ -869,7 +870,7 @@ impl GitPanel {
|
||||
} else {
|
||||
Tooltip::text("Stage all changes")
|
||||
})
|
||||
.disabled(entry_count == 0)
|
||||
.disabled(!has_write_access || entry_count == 0)
|
||||
.on_click(cx.listener(
|
||||
move |git_panel, _, window, cx| match all_staged {
|
||||
true => git_panel.unstage_all(&UnstageAll, window, cx),
|
||||
@@ -960,7 +961,11 @@ impl GitPanel {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn render_commit_editor(&self, cx: &Context<Self>) -> impl IntoElement {
|
||||
pub fn render_commit_editor(
|
||||
&self,
|
||||
has_write_access: bool,
|
||||
cx: &Context<Self>,
|
||||
) -> impl IntoElement {
|
||||
let editor = self.commit_editor.clone();
|
||||
let editor_focus_handle = editor.read(cx).focus_handle(cx).clone();
|
||||
let (can_commit, can_commit_all) =
|
||||
@@ -968,8 +973,8 @@ impl GitPanel {
|
||||
.as_ref()
|
||||
.map_or((false, false), |active_repository| {
|
||||
(
|
||||
active_repository.can_commit(false, cx),
|
||||
active_repository.can_commit(true, cx),
|
||||
has_write_access && active_repository.can_commit(false, cx),
|
||||
has_write_access && active_repository.can_commit(true, cx),
|
||||
)
|
||||
});
|
||||
|
||||
@@ -1107,7 +1112,7 @@ impl GitPanel {
|
||||
)
|
||||
}
|
||||
|
||||
fn render_entries(&self, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
fn render_entries(&self, has_write_access: bool, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let entry_count = self.visible_entries.len();
|
||||
|
||||
h_flex()
|
||||
@@ -1118,7 +1123,7 @@ impl GitPanel {
|
||||
move |git_panel, range, _window, cx| {
|
||||
let mut items = Vec::with_capacity(range.end - range.start);
|
||||
git_panel.for_each_visible_entry(range, cx, |ix, details, cx| {
|
||||
items.push(git_panel.render_entry(ix, details, cx));
|
||||
items.push(git_panel.render_entry(ix, details, has_write_access, cx));
|
||||
});
|
||||
items
|
||||
}
|
||||
@@ -1136,6 +1141,7 @@ impl GitPanel {
|
||||
&self,
|
||||
ix: usize,
|
||||
entry_details: GitListEntry,
|
||||
has_write_access: bool,
|
||||
cx: &Context<Self>,
|
||||
) -> impl IntoElement {
|
||||
let repo_path = entry_details.repo_path.clone();
|
||||
@@ -1215,6 +1221,7 @@ impl GitPanel {
|
||||
.is_staged
|
||||
.map_or(ToggleState::Indeterminate, ToggleState::from),
|
||||
)
|
||||
.disabled(!has_write_access)
|
||||
.fill()
|
||||
.elevation(ElevationIndex::Surface)
|
||||
.on_click({
|
||||
@@ -1296,19 +1303,23 @@ impl Render for GitPanel {
|
||||
.map_or(false, |active_repository| {
|
||||
active_repository.entry_count() > 0
|
||||
});
|
||||
let has_co_authors = self
|
||||
let room = self
|
||||
.workspace
|
||||
.upgrade()
|
||||
.and_then(|workspace| workspace.read(cx).active_call()?.read(cx).room().cloned())
|
||||
.map(|room| {
|
||||
let room = room.read(cx);
|
||||
room.local_participant().can_write()
|
||||
&& room
|
||||
.remote_participants()
|
||||
.values()
|
||||
.any(|remote_participant| remote_participant.can_write())
|
||||
})
|
||||
.unwrap_or(false);
|
||||
.and_then(|workspace| workspace.read(cx).active_call()?.read(cx).room().cloned());
|
||||
|
||||
let has_write_access = room
|
||||
.as_ref()
|
||||
.map_or(true, |room| room.read(cx).local_participant().can_write());
|
||||
|
||||
let has_co_authors = room.map_or(false, |room| {
|
||||
has_write_access
|
||||
&& room
|
||||
.read(cx)
|
||||
.remote_participants()
|
||||
.values()
|
||||
.any(|remote_participant| remote_participant.can_write())
|
||||
});
|
||||
|
||||
v_flex()
|
||||
.id("git_panel")
|
||||
@@ -1366,15 +1377,15 @@ impl Render for GitPanel {
|
||||
.font_buffer(cx)
|
||||
.py_1()
|
||||
.bg(ElevationIndex::Surface.bg(cx))
|
||||
.child(self.render_panel_header(window, cx))
|
||||
.child(self.render_panel_header(window, has_write_access, cx))
|
||||
.child(self.render_divider(cx))
|
||||
.child(if has_entries {
|
||||
self.render_entries(cx).into_any_element()
|
||||
self.render_entries(has_write_access, cx).into_any_element()
|
||||
} else {
|
||||
self.render_empty_state(cx).into_any_element()
|
||||
})
|
||||
.child(self.render_divider(cx))
|
||||
.child(self.render_commit_editor(cx))
|
||||
.child(self.render_commit_editor(has_write_access, cx))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ use editor::{
|
||||
actions::Tab, scroll::Autoscroll, Anchor, Editor, MultiBufferSnapshot, ToOffset, ToPoint,
|
||||
};
|
||||
use gpui::{
|
||||
div, prelude::*, AnyWindowHandle, App, DismissEvent, Entity, EventEmitter, FocusHandle,
|
||||
Focusable, Render, SharedString, Styled, Subscription,
|
||||
div, prelude::*, App, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Render,
|
||||
SharedString, Styled, Subscription,
|
||||
};
|
||||
use language::Buffer;
|
||||
use settings::Settings;
|
||||
@@ -133,19 +133,15 @@ impl GoToLine {
|
||||
}
|
||||
}
|
||||
|
||||
fn release(&mut self, window: AnyWindowHandle, cx: &mut App) {
|
||||
window
|
||||
.update(cx, |_, window, cx| {
|
||||
let scroll_position = self.prev_scroll_position.take();
|
||||
self.active_editor.update(cx, |editor, cx| {
|
||||
editor.clear_row_highlights::<GoToLineRowHighlights>();
|
||||
if let Some(scroll_position) = scroll_position {
|
||||
editor.set_scroll_position(scroll_position, window, cx);
|
||||
}
|
||||
cx.notify();
|
||||
})
|
||||
})
|
||||
.ok();
|
||||
fn release(&mut self, window: &mut Window, cx: &mut App) {
|
||||
let scroll_position = self.prev_scroll_position.take();
|
||||
self.active_editor.update(cx, |editor, cx| {
|
||||
editor.clear_row_highlights::<GoToLineRowHighlights>();
|
||||
if let Some(scroll_position) = scroll_position {
|
||||
editor.set_scroll_position(scroll_position, window, cx);
|
||||
}
|
||||
cx.notify();
|
||||
})
|
||||
}
|
||||
|
||||
fn on_line_editor_event(
|
||||
|
||||
@@ -108,22 +108,7 @@ thiserror.workspace = true
|
||||
util.workspace = true
|
||||
uuid.workspace = true
|
||||
waker-fn = "1.2.0"
|
||||
|
||||
[dev-dependencies]
|
||||
backtrace = "0.3"
|
||||
collections = { workspace = true, features = ["test-support"] }
|
||||
env_logger.workspace = true
|
||||
rand.workspace = true
|
||||
util = { workspace = true, features = ["test-support"] }
|
||||
http_client = { workspace = true, features = ["test-support"] }
|
||||
unicode-segmentation.workspace = true
|
||||
|
||||
[target.'cfg(target_os = "windows")'.build-dependencies]
|
||||
embed-resource = "3.0"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.build-dependencies]
|
||||
bindgen = "0.70.0"
|
||||
cbindgen = { version = "0.28.0", default-features = false }
|
||||
lyon = "1.0"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
block = "0.1"
|
||||
@@ -213,6 +198,29 @@ rand.workspace = true
|
||||
windows.workspace = true
|
||||
windows-core = "0.58"
|
||||
|
||||
[dev-dependencies]
|
||||
backtrace = "0.3"
|
||||
collections = { workspace = true, features = ["test-support"] }
|
||||
env_logger.workspace = true
|
||||
rand.workspace = true
|
||||
util = { workspace = true, features = ["test-support"] }
|
||||
http_client = { workspace = true, features = ["test-support"] }
|
||||
unicode-segmentation.workspace = true
|
||||
lyon = { version = "1.0", features = ["extra"] }
|
||||
|
||||
[target.'cfg(target_os = "windows")'.build-dependencies]
|
||||
embed-resource = "3.0"
|
||||
naga.workspace = true
|
||||
|
||||
[target.'cfg(target_os = "macos")'.build-dependencies]
|
||||
bindgen = "0.70.0"
|
||||
cbindgen = { version = "0.28.0", default-features = false }
|
||||
naga.workspace = true
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.build-dependencies]
|
||||
naga.workspace = true
|
||||
|
||||
|
||||
[[example]]
|
||||
name = "hello_world"
|
||||
path = "examples/hello_world.rs"
|
||||
@@ -221,18 +229,22 @@ path = "examples/hello_world.rs"
|
||||
name = "image"
|
||||
path = "examples/image/image.rs"
|
||||
|
||||
[[example]]
|
||||
name = "set_menus"
|
||||
path = "examples/set_menus.rs"
|
||||
|
||||
[[example]]
|
||||
name = "window_shadow"
|
||||
path = "examples/window_shadow.rs"
|
||||
|
||||
[[example]]
|
||||
name = "input"
|
||||
path = "examples/input.rs"
|
||||
|
||||
[[example]]
|
||||
name = "opacity"
|
||||
path = "examples/opacity.rs"
|
||||
|
||||
[[example]]
|
||||
name = "pattern"
|
||||
path = "examples/pattern.rs"
|
||||
|
||||
[[example]]
|
||||
name = "set_menus"
|
||||
path = "examples/set_menus.rs"
|
||||
|
||||
[[example]]
|
||||
name = "shadow"
|
||||
path = "examples/shadow.rs"
|
||||
@@ -245,10 +257,10 @@ path = "examples/svg/svg.rs"
|
||||
name = "text_wrapper"
|
||||
path = "examples/text_wrapper.rs"
|
||||
|
||||
[[example]]
|
||||
name = "opacity"
|
||||
path = "examples/opacity.rs"
|
||||
|
||||
[[example]]
|
||||
name = "uniform_list"
|
||||
path = "examples/uniform_list.rs"
|
||||
|
||||
[[example]]
|
||||
name = "window_shadow"
|
||||
path = "examples/window_shadow.rs"
|
||||
|
||||
@@ -8,6 +8,10 @@ use std::env;
|
||||
fn main() {
|
||||
let target = env::var("CARGO_CFG_TARGET_OS");
|
||||
println!("cargo::rustc-check-cfg=cfg(gles)");
|
||||
|
||||
#[cfg(any(not(target_os = "macos"), feature = "macos-blade"))]
|
||||
check_wgsl_shaders();
|
||||
|
||||
match target.as_deref() {
|
||||
Ok("macos") => {
|
||||
#[cfg(target_os = "macos")]
|
||||
@@ -27,6 +31,28 @@ fn main() {
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn check_wgsl_shaders() {
|
||||
use std::path::PathBuf;
|
||||
use std::process;
|
||||
use std::str::FromStr;
|
||||
|
||||
let shader_source_path = "./src/platform/blade/shaders.wgsl";
|
||||
let shader_path = PathBuf::from_str(shader_source_path).unwrap();
|
||||
println!("cargo:rerun-if-changed={}", &shader_path.display());
|
||||
|
||||
let shader_source = std::fs::read_to_string(&shader_path).unwrap();
|
||||
|
||||
match naga::front::wgsl::parse_str(&shader_source) {
|
||||
Ok(_) => {
|
||||
// All clear
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("WGSL shader compilation failed:\n{}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
mod macos {
|
||||
use std::{
|
||||
|
||||
@@ -218,13 +218,17 @@ impl Render for GradientViewer {
|
||||
let height = square_bounds.size.height;
|
||||
let horizontal_offset = height;
|
||||
let vertical_offset = px(30.);
|
||||
let mut path = gpui::Path::new(square_bounds.bottom_left());
|
||||
path.line_to(square_bounds.origin + point(horizontal_offset, vertical_offset));
|
||||
path.line_to(
|
||||
let mut builder = gpui::PathBuilder::fill();
|
||||
builder.move_to(square_bounds.bottom_left());
|
||||
builder
|
||||
.line_to(square_bounds.origin + point(horizontal_offset, vertical_offset));
|
||||
builder.line_to(
|
||||
square_bounds.top_right() + point(-horizontal_offset, vertical_offset),
|
||||
);
|
||||
path.line_to(square_bounds.bottom_right());
|
||||
path.line_to(square_bounds.bottom_left());
|
||||
|
||||
builder.line_to(square_bounds.bottom_right());
|
||||
builder.line_to(square_bounds.bottom_left());
|
||||
let path = builder.build().unwrap();
|
||||
window.paint_path(
|
||||
path,
|
||||
linear_gradient(
|
||||
|
||||
@@ -1,46 +1,62 @@
|
||||
use gpui::{
|
||||
canvas, div, point, prelude::*, px, size, App, Application, Bounds, Context, MouseDownEvent,
|
||||
Path, Pixels, Point, Render, Window, WindowOptions,
|
||||
canvas, div, linear_color_stop, linear_gradient, point, prelude::*, px, rgb, size, Application,
|
||||
Background, Bounds, ColorSpace, Context, MouseDownEvent, Path, PathBuilder, PathStyle, Pixels,
|
||||
Point, Render, StrokeOptions, Window, WindowOptions,
|
||||
};
|
||||
|
||||
struct PaintingViewer {
|
||||
default_lines: Vec<Path<Pixels>>,
|
||||
default_lines: Vec<(Path<Pixels>, Background)>,
|
||||
lines: Vec<Vec<Point<Pixels>>>,
|
||||
start: Point<Pixels>,
|
||||
_painting: bool,
|
||||
}
|
||||
|
||||
impl PaintingViewer {
|
||||
fn new() -> Self {
|
||||
fn new(_window: &mut Window, _cx: &mut Context<Self>) -> Self {
|
||||
let mut lines = vec![];
|
||||
|
||||
// draw a line
|
||||
let mut path = Path::new(point(px(50.), px(180.)));
|
||||
path.line_to(point(px(100.), px(120.)));
|
||||
// go back to close the path
|
||||
path.line_to(point(px(100.), px(121.)));
|
||||
path.line_to(point(px(50.), px(181.)));
|
||||
lines.push(path);
|
||||
// draw a Rust logo
|
||||
let mut builder = lyon::path::Path::svg_builder();
|
||||
lyon::extra::rust_logo::build_logo_path(&mut builder);
|
||||
// move down the Path
|
||||
let mut builder: PathBuilder = builder.into();
|
||||
builder.translate(point(px(10.), px(100.)));
|
||||
builder.scale(0.9);
|
||||
let path = builder.build().unwrap();
|
||||
lines.push((path, gpui::black().into()));
|
||||
|
||||
// draw a lightening bolt ⚡
|
||||
let mut path = Path::new(point(px(150.), px(200.)));
|
||||
path.line_to(point(px(200.), px(125.)));
|
||||
path.line_to(point(px(200.), px(175.)));
|
||||
path.line_to(point(px(250.), px(100.)));
|
||||
lines.push(path);
|
||||
let mut builder = PathBuilder::fill();
|
||||
builder.move_to(point(px(150.), px(200.)));
|
||||
builder.line_to(point(px(200.), px(125.)));
|
||||
builder.line_to(point(px(200.), px(175.)));
|
||||
builder.line_to(point(px(250.), px(100.)));
|
||||
let path = builder.build().unwrap();
|
||||
lines.push((path, rgb(0x1d4ed8).into()));
|
||||
|
||||
// draw a ⭐
|
||||
let mut path = Path::new(point(px(350.), px(100.)));
|
||||
path.line_to(point(px(370.), px(160.)));
|
||||
path.line_to(point(px(430.), px(160.)));
|
||||
path.line_to(point(px(380.), px(200.)));
|
||||
path.line_to(point(px(400.), px(260.)));
|
||||
path.line_to(point(px(350.), px(220.)));
|
||||
path.line_to(point(px(300.), px(260.)));
|
||||
path.line_to(point(px(320.), px(200.)));
|
||||
path.line_to(point(px(270.), px(160.)));
|
||||
path.line_to(point(px(330.), px(160.)));
|
||||
path.line_to(point(px(350.), px(100.)));
|
||||
lines.push(path);
|
||||
let mut builder = PathBuilder::fill();
|
||||
builder.move_to(point(px(350.), px(100.)));
|
||||
builder.line_to(point(px(370.), px(160.)));
|
||||
builder.line_to(point(px(430.), px(160.)));
|
||||
builder.line_to(point(px(380.), px(200.)));
|
||||
builder.line_to(point(px(400.), px(260.)));
|
||||
builder.line_to(point(px(350.), px(220.)));
|
||||
builder.line_to(point(px(300.), px(260.)));
|
||||
builder.line_to(point(px(320.), px(200.)));
|
||||
builder.line_to(point(px(270.), px(160.)));
|
||||
builder.line_to(point(px(330.), px(160.)));
|
||||
builder.line_to(point(px(350.), px(100.)));
|
||||
let path = builder.build().unwrap();
|
||||
lines.push((
|
||||
path,
|
||||
linear_gradient(
|
||||
180.,
|
||||
linear_color_stop(rgb(0xFACC15), 0.7),
|
||||
linear_color_stop(rgb(0xD56D0C), 1.),
|
||||
)
|
||||
.color_space(ColorSpace::Oklab),
|
||||
));
|
||||
|
||||
let square_bounds = Bounds {
|
||||
origin: point(px(450.), px(100.)),
|
||||
@@ -49,18 +65,42 @@ impl PaintingViewer {
|
||||
let height = square_bounds.size.height;
|
||||
let horizontal_offset = height;
|
||||
let vertical_offset = px(30.);
|
||||
let mut path = Path::new(square_bounds.bottom_left());
|
||||
path.curve_to(
|
||||
let mut builder = PathBuilder::fill();
|
||||
builder.move_to(square_bounds.bottom_left());
|
||||
builder.curve_to(
|
||||
square_bounds.origin + point(horizontal_offset, vertical_offset),
|
||||
square_bounds.origin + point(px(0.0), vertical_offset),
|
||||
);
|
||||
path.line_to(square_bounds.top_right() + point(-horizontal_offset, vertical_offset));
|
||||
path.curve_to(
|
||||
builder.line_to(square_bounds.top_right() + point(-horizontal_offset, vertical_offset));
|
||||
builder.curve_to(
|
||||
square_bounds.bottom_right(),
|
||||
square_bounds.top_right() + point(px(0.0), vertical_offset),
|
||||
);
|
||||
path.line_to(square_bounds.bottom_left());
|
||||
lines.push(path);
|
||||
builder.line_to(square_bounds.bottom_left());
|
||||
let path = builder.build().unwrap();
|
||||
lines.push((
|
||||
path,
|
||||
linear_gradient(
|
||||
180.,
|
||||
linear_color_stop(gpui::blue(), 0.4),
|
||||
linear_color_stop(gpui::red(), 1.),
|
||||
),
|
||||
));
|
||||
|
||||
// draw a wave
|
||||
let options = StrokeOptions::default()
|
||||
.with_line_width(1.)
|
||||
.with_line_join(lyon::path::LineJoin::Bevel);
|
||||
let mut builder = PathBuilder::stroke(px(1.)).with_style(PathStyle::Stroke(options));
|
||||
builder.move_to(point(px(40.), px(320.)));
|
||||
for i in 0..50 {
|
||||
builder.line_to(point(
|
||||
px(40.0 + i as f32 * 10.0),
|
||||
px(320.0 + (i as f32 * 10.0).sin() * 40.0),
|
||||
));
|
||||
}
|
||||
let path = builder.build().unwrap();
|
||||
lines.push((path, gpui::green().into()));
|
||||
|
||||
Self {
|
||||
default_lines: lines.clone(),
|
||||
@@ -115,27 +155,28 @@ impl Render for PaintingViewer {
|
||||
canvas(
|
||||
move |_, _, _| {},
|
||||
move |_, _, window, _| {
|
||||
const STROKE_WIDTH: Pixels = px(2.0);
|
||||
for path in default_lines {
|
||||
window.paint_path(path, gpui::black());
|
||||
|
||||
for (path, color) in default_lines {
|
||||
window.paint_path(path, color);
|
||||
}
|
||||
|
||||
for points in lines {
|
||||
let mut path = Path::new(points[0]);
|
||||
for p in points.iter().skip(1) {
|
||||
path.line_to(*p);
|
||||
if points.len() < 2 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut last = points.last().unwrap();
|
||||
for p in points.iter().rev() {
|
||||
let mut offset_x = px(0.);
|
||||
if last.x == p.x {
|
||||
offset_x = STROKE_WIDTH;
|
||||
let mut builder = PathBuilder::stroke(px(1.));
|
||||
for (i, p) in points.into_iter().enumerate() {
|
||||
if i == 0 {
|
||||
builder.move_to(p);
|
||||
} else {
|
||||
builder.line_to(p);
|
||||
}
|
||||
path.line_to(point(p.x + offset_x, p.y + STROKE_WIDTH));
|
||||
last = p;
|
||||
}
|
||||
|
||||
window.paint_path(path, gpui::black());
|
||||
if let Ok(path) = builder.build() {
|
||||
window.paint_path(path, gpui::black());
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -185,13 +226,13 @@ impl Render for PaintingViewer {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
Application::new().run(|cx: &mut App| {
|
||||
Application::new().run(|cx| {
|
||||
cx.open_window(
|
||||
WindowOptions {
|
||||
focus: true,
|
||||
..Default::default()
|
||||
},
|
||||
|_, cx| cx.new(|_| PaintingViewer::new()),
|
||||
|window, cx| cx.new(|cx| PaintingViewer::new(window, cx)),
|
||||
)
|
||||
.unwrap();
|
||||
cx.activate(true);
|
||||
|
||||
103
crates/gpui/examples/pattern.rs
Normal file
103
crates/gpui/examples/pattern.rs
Normal file
@@ -0,0 +1,103 @@
|
||||
use gpui::{
|
||||
div, linear_color_stop, linear_gradient, pattern_slash, prelude::*, px, rgb, size, App,
|
||||
AppContext, Application, Bounds, Context, Window, WindowBounds, WindowOptions,
|
||||
};
|
||||
|
||||
struct PatternExample;
|
||||
|
||||
impl Render for PatternExample {
|
||||
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
.bg(rgb(0xffffff))
|
||||
.size(px(600.0))
|
||||
.justify_center()
|
||||
.items_center()
|
||||
.shadow_lg()
|
||||
.text_xl()
|
||||
.text_color(rgb(0x000000))
|
||||
.child("Pattern Example")
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.border_1()
|
||||
.border_color(gpui::blue())
|
||||
.child(div().w(px(54.0)).h(px(18.0)).bg(pattern_slash(gpui::red())))
|
||||
.child(div().w(px(54.0)).h(px(18.0)).bg(pattern_slash(gpui::red())))
|
||||
.child(div().w(px(54.0)).h(px(18.0)).bg(pattern_slash(gpui::red())))
|
||||
.child(div().w(px(54.0)).h(px(18.0)).bg(pattern_slash(gpui::red()))),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.border_1()
|
||||
.border_color(gpui::blue())
|
||||
.bg(gpui::green().opacity(0.16))
|
||||
.child("Elements the same height should align")
|
||||
.child(
|
||||
div()
|
||||
.w(px(256.0))
|
||||
.h(px(56.0))
|
||||
.bg(pattern_slash(gpui::red())),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.w(px(256.0))
|
||||
.h(px(56.0))
|
||||
.bg(pattern_slash(gpui::green())),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.w(px(256.0))
|
||||
.h(px(56.0))
|
||||
.bg(pattern_slash(gpui::blue())),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.w(px(256.0))
|
||||
.h(px(26.0))
|
||||
.bg(pattern_slash(gpui::yellow())),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.border_1()
|
||||
.border_color(gpui::blue())
|
||||
.w(px(240.0))
|
||||
.h(px(40.0))
|
||||
.bg(gpui::red()),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.border_1()
|
||||
.border_color(gpui::blue())
|
||||
.w(px(240.0))
|
||||
.h(px(40.0))
|
||||
.bg(linear_gradient(
|
||||
45.,
|
||||
linear_color_stop(gpui::red(), 0.),
|
||||
linear_color_stop(gpui::blue(), 1.),
|
||||
)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
Application::new().run(|cx: &mut App| {
|
||||
let bounds = Bounds::centered(None, size(px(600.0), px(600.0)), cx);
|
||||
cx.open_window(
|
||||
WindowOptions {
|
||||
window_bounds: Some(WindowBounds::Windowed(bounds)),
|
||||
..Default::default()
|
||||
},
|
||||
|_window, cx| cx.new(|_cx| PatternExample),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
cx.activate(true);
|
||||
});
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use gpui::{
|
||||
div, prelude::*, px, size, App, Application, Bounds, Context, Window, WindowBounds,
|
||||
WindowOptions,
|
||||
div, prelude::*, px, size, App, Application, Bounds, Context, TextOverflow, Window,
|
||||
WindowBounds, WindowOptions,
|
||||
};
|
||||
|
||||
struct HelloWorld {}
|
||||
@@ -20,6 +20,7 @@ impl Render for HelloWorld {
|
||||
div()
|
||||
.flex()
|
||||
.flex_row()
|
||||
.flex_shrink_0()
|
||||
.gap_2()
|
||||
.child(
|
||||
div()
|
||||
@@ -49,29 +50,53 @@ impl Render for HelloWorld {
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex_shrink_0()
|
||||
.text_xl()
|
||||
.overflow_hidden()
|
||||
.text_ellipsis()
|
||||
.truncate()
|
||||
.border_1()
|
||||
.border_color(gpui::red())
|
||||
.border_color(gpui::blue())
|
||||
.child("ELLIPSIS: ".to_owned() + text),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex_shrink_0()
|
||||
.text_xl()
|
||||
.overflow_hidden()
|
||||
.truncate()
|
||||
.text_ellipsis()
|
||||
.line_clamp(2)
|
||||
.border_1()
|
||||
.border_color(gpui::blue())
|
||||
.child("ELLIPSIS 2 lines: ".to_owned() + text),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex_shrink_0()
|
||||
.text_xl()
|
||||
.overflow_hidden()
|
||||
.text_overflow(TextOverflow::Ellipsis(""))
|
||||
.border_1()
|
||||
.border_color(gpui::green())
|
||||
.child("TRUNCATE: ".to_owned() + text),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex_shrink_0()
|
||||
.text_xl()
|
||||
.overflow_hidden()
|
||||
.text_overflow(TextOverflow::Ellipsis(""))
|
||||
.line_clamp(3)
|
||||
.border_1()
|
||||
.border_color(gpui::green())
|
||||
.child("TRUNCATE 3 lines: ".to_owned() + text),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex_shrink_0()
|
||||
.text_xl()
|
||||
.whitespace_nowrap()
|
||||
.overflow_hidden()
|
||||
.border_1()
|
||||
.border_color(gpui::blue())
|
||||
.border_color(gpui::black())
|
||||
.child("NOWRAP: ".to_owned() + text),
|
||||
)
|
||||
.child(div().text_xl().w_full().child(text))
|
||||
@@ -80,7 +105,7 @@ impl Render for HelloWorld {
|
||||
|
||||
fn main() {
|
||||
Application::new().run(|cx: &mut App| {
|
||||
let bounds = Bounds::centered(None, size(px(600.0), px(480.0)), cx);
|
||||
let bounds = Bounds::centered(None, size(px(800.0), px(600.0)), cx);
|
||||
cx.open_window(
|
||||
WindowOptions {
|
||||
window_bounds: Some(WindowBounds::Windowed(bounds)),
|
||||
@@ -89,5 +114,6 @@ fn main() {
|
||||
|_, cx| cx.new(|_| HelloWorld {}),
|
||||
)
|
||||
.unwrap();
|
||||
cx.activate(true);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -22,9 +22,9 @@ use slotmap::SlotMap;
|
||||
|
||||
pub use async_context::*;
|
||||
use collections::{FxHashMap, FxHashSet, HashMap, VecDeque};
|
||||
pub use context::*;
|
||||
pub use entity_map::*;
|
||||
use http_client::HttpClient;
|
||||
pub use model_context::*;
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub use test_context::*;
|
||||
use util::ResultExt;
|
||||
@@ -41,8 +41,8 @@ use crate::{
|
||||
};
|
||||
|
||||
mod async_context;
|
||||
mod context;
|
||||
mod entity_map;
|
||||
mod model_context;
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
mod test_context;
|
||||
|
||||
@@ -1236,15 +1236,9 @@ impl App {
|
||||
T: 'static,
|
||||
{
|
||||
let window_handle = window.handle;
|
||||
let (subscription, activate) = self.release_listeners.insert(
|
||||
handle.entity_id(),
|
||||
Box::new(move |entity, cx| {
|
||||
let entity = entity.downcast_mut().expect("invalid entity type");
|
||||
let _ = window_handle.update(cx, |_, window, cx| on_release(entity, window, cx));
|
||||
}),
|
||||
);
|
||||
activate();
|
||||
subscription
|
||||
self.observe_release(&handle, move |entity, cx| {
|
||||
let _ = window_handle.update(cx, |_, window, cx| on_release(entity, window, cx));
|
||||
})
|
||||
}
|
||||
|
||||
/// Register a callback to be invoked when a keystroke is received by the application
|
||||
@@ -1673,6 +1667,21 @@ impl AppContext for App {
|
||||
|
||||
Ok(read(view, self))
|
||||
}
|
||||
|
||||
fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
|
||||
where
|
||||
R: Send + 'static,
|
||||
{
|
||||
self.background_executor.spawn(future)
|
||||
}
|
||||
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result<R>
|
||||
where
|
||||
G: Global,
|
||||
{
|
||||
let mut g = self.global::<G>();
|
||||
callback(&g, self)
|
||||
}
|
||||
}
|
||||
|
||||
/// These effects are processed at the end of each application update cycle.
|
||||
|
||||
@@ -104,6 +104,22 @@ impl AppContext for AsyncApp {
|
||||
let lock = app.borrow();
|
||||
lock.read_window(window, read)
|
||||
}
|
||||
|
||||
fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
|
||||
where
|
||||
R: Send + 'static,
|
||||
{
|
||||
self.background_executor.spawn(future)
|
||||
}
|
||||
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result<R>
|
||||
where
|
||||
G: Global,
|
||||
{
|
||||
let app = self.app.upgrade().context("app was released")?;
|
||||
let mut lock = app.borrow_mut();
|
||||
Ok(lock.update(|this| this.read_global(callback)))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncApp {
|
||||
@@ -367,6 +383,20 @@ impl AppContext for AsyncWindowContext {
|
||||
{
|
||||
self.app.read_window(window, read)
|
||||
}
|
||||
|
||||
fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
|
||||
where
|
||||
R: Send + 'static,
|
||||
{
|
||||
self.app.background_executor.spawn(future)
|
||||
}
|
||||
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Result<R>
|
||||
where
|
||||
G: Global,
|
||||
{
|
||||
self.app.read_global(callback)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisualContext for AsyncWindowContext {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
AnyView, AnyWindowHandle, App, AppContext, AsyncApp, DispatchPhase, Effect, EntityId,
|
||||
EventEmitter, FocusHandle, FocusOutEvent, Focusable, Global, KeystrokeObserver, Reservation,
|
||||
SubscriberSet, Subscription, Task, WeakEntity, WeakFocusHandle, Window, WindowHandle,
|
||||
AnyView, AnyWindowHandle, AppContext, AsyncApp, DispatchPhase, Effect, EntityId, EventEmitter,
|
||||
FocusHandle, FocusOutEvent, Focusable, Global, KeystrokeObserver, Reservation, SubscriberSet,
|
||||
Subscription, Task, WeakEntity, WeakFocusHandle, Window, WindowHandle,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use derive_more::{Deref, DerefMut};
|
||||
@@ -13,7 +13,7 @@ use std::{
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use super::{AsyncWindowContext, Entity, KeystrokeEvent};
|
||||
use super::{App, AsyncWindowContext, Entity, KeystrokeEvent};
|
||||
|
||||
/// The app context, with specialized behavior for the given model.
|
||||
#[derive(Deref, DerefMut)]
|
||||
@@ -323,46 +323,32 @@ impl<'a, T: 'static> Context<'a, T> {
|
||||
pub fn on_release_in(
|
||||
&mut self,
|
||||
window: &Window,
|
||||
on_release: impl FnOnce(&mut T, AnyWindowHandle, &mut App) + 'static,
|
||||
on_release: impl FnOnce(&mut T, &mut Window, &mut App) + 'static,
|
||||
) -> Subscription {
|
||||
let window_handle = window.handle;
|
||||
let (subscription, activate) = self.release_listeners.insert(
|
||||
self.entity_id(),
|
||||
Box::new(move |this, cx| {
|
||||
let this = this.downcast_mut().expect("invalid entity type");
|
||||
on_release(this, window_handle, cx)
|
||||
}),
|
||||
);
|
||||
activate();
|
||||
subscription
|
||||
let entity = self.entity();
|
||||
self.app.observe_release_in(&entity, window, on_release)
|
||||
}
|
||||
|
||||
/// Register a callback to be invoked when the given Model or View is released.
|
||||
pub fn observe_release_in<V2>(
|
||||
pub fn observe_release_in<T2>(
|
||||
&self,
|
||||
observed: &Entity<V2>,
|
||||
observed: &Entity<T2>,
|
||||
window: &Window,
|
||||
mut on_release: impl FnMut(&mut T, &mut V2, &mut Window, &mut Context<'_, T>) + 'static,
|
||||
mut on_release: impl FnMut(&mut T, &mut T2, &mut Window, &mut Context<'_, T>) + 'static,
|
||||
) -> Subscription
|
||||
where
|
||||
T: 'static,
|
||||
V2: 'static,
|
||||
T2: 'static,
|
||||
{
|
||||
let observer = self.weak_entity();
|
||||
let window_handle = window.handle;
|
||||
let (subscription, activate) = self.release_listeners.insert(
|
||||
observed.entity_id(),
|
||||
Box::new(move |observed, cx| {
|
||||
let observed = observed
|
||||
.downcast_mut()
|
||||
.expect("invalid observed entity type");
|
||||
let _ = window_handle.update(cx, |_, window, cx| {
|
||||
observer.update(cx, |this, cx| on_release(this, observed, window, cx))
|
||||
});
|
||||
}),
|
||||
);
|
||||
activate();
|
||||
subscription
|
||||
self.app
|
||||
.observe_release_in(observed, window, move |observed, window, cx| {
|
||||
observer
|
||||
.update(cx, |observer, cx| {
|
||||
on_release(observer, observed, window, cx)
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
}
|
||||
|
||||
/// Register a callback to be invoked when the window is resized.
|
||||
@@ -731,6 +717,20 @@ impl<'a, T> AppContext for Context<'a, T> {
|
||||
{
|
||||
self.app.read_window(window, read)
|
||||
}
|
||||
|
||||
fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
|
||||
where
|
||||
R: Send + 'static,
|
||||
{
|
||||
self.app.background_executor.spawn(future)
|
||||
}
|
||||
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result<R>
|
||||
where
|
||||
G: Global,
|
||||
{
|
||||
self.app.read_global(callback)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Borrow<App> for Context<'_, T> {
|
||||
@@ -94,6 +94,21 @@ impl AppContext for TestAppContext {
|
||||
let app = self.app.borrow();
|
||||
app.read_window(window, read)
|
||||
}
|
||||
|
||||
fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
|
||||
where
|
||||
R: Send + 'static,
|
||||
{
|
||||
self.background_executor.spawn(future)
|
||||
}
|
||||
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result<R>
|
||||
where
|
||||
G: Global,
|
||||
{
|
||||
let app = self.app.borrow();
|
||||
app.read_global(callback)
|
||||
}
|
||||
}
|
||||
|
||||
impl TestAppContext {
|
||||
@@ -906,6 +921,20 @@ impl AppContext for VisualTestContext {
|
||||
{
|
||||
self.cx.read_window(window, read)
|
||||
}
|
||||
|
||||
fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
|
||||
where
|
||||
R: Send + 'static,
|
||||
{
|
||||
self.cx.background_spawn(future)
|
||||
}
|
||||
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result<R>
|
||||
where
|
||||
G: Global,
|
||||
{
|
||||
self.cx.read_global(callback)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisualContext for VisualTestContext {
|
||||
|
||||
@@ -553,6 +553,7 @@ impl<'de> Deserialize<'de> for Hsla {
|
||||
pub(crate) enum BackgroundTag {
|
||||
Solid = 0,
|
||||
LinearGradient = 1,
|
||||
PatternSlash = 2,
|
||||
}
|
||||
|
||||
/// A color space for color interpolation.
|
||||
@@ -606,6 +607,15 @@ impl Default for Background {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a hash pattern background
|
||||
pub fn pattern_slash(color: Hsla) -> Background {
|
||||
Background {
|
||||
tag: BackgroundTag::PatternSlash,
|
||||
solid: color,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a LinearGradient background color.
|
||||
///
|
||||
/// The gradient line's angle of direction. A value of `0.` is equivalent to to top; increasing values rotate clockwise from there.
|
||||
@@ -683,6 +693,7 @@ impl Background {
|
||||
match self.tag {
|
||||
BackgroundTag::Solid => self.solid.is_transparent(),
|
||||
BackgroundTag::LinearGradient => self.colors.iter().all(|c| c.color.is_transparent()),
|
||||
BackgroundTag::PatternSlash => self.solid.is_transparent(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1677,6 +1677,7 @@ impl Interactivity {
|
||||
FONT_SIZE,
|
||||
&[window.text_style().to_run(str_len)],
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.ok()
|
||||
.and_then(|mut text| text.pop())
|
||||
|
||||
@@ -114,9 +114,9 @@ impl From<Arc<Image>> for ImageSource {
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
F: Fn(&mut Window, &mut App) -> Option<Result<Arc<RenderImage>, ImageCacheError>> + 'static,
|
||||
> From<F> for ImageSource
|
||||
impl<F> From<F> for ImageSource
|
||||
where
|
||||
F: Fn(&mut Window, &mut App) -> Option<Result<Arc<RenderImage>, ImageCacheError>> + 'static,
|
||||
{
|
||||
fn from(value: F) -> Self {
|
||||
Self::Custom(Arc::new(value))
|
||||
|
||||
@@ -2,7 +2,8 @@ use crate::{
|
||||
register_tooltip_mouse_handlers, set_tooltip_on_window, ActiveTooltip, AnyView, App, Bounds,
|
||||
DispatchPhase, Element, ElementId, GlobalElementId, HighlightStyle, Hitbox, IntoElement,
|
||||
LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Point, SharedString, Size,
|
||||
TextRun, TextStyle, TooltipId, Truncate, WhiteSpace, Window, WrappedLine, WrappedLineLayout,
|
||||
TextOverflow, TextRun, TextStyle, TooltipId, WhiteSpace, Window, WrappedLine,
|
||||
WrappedLineLayout,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use parking_lot::{Mutex, MutexGuard};
|
||||
@@ -255,8 +256,6 @@ struct TextLayoutInner {
|
||||
bounds: Option<Bounds<Pixels>>,
|
||||
}
|
||||
|
||||
const ELLIPSIS: &str = "…";
|
||||
|
||||
impl TextLayout {
|
||||
fn lock(&self) -> MutexGuard<Option<TextLayoutInner>> {
|
||||
self.0.lock()
|
||||
@@ -294,19 +293,22 @@ impl TextLayout {
|
||||
None
|
||||
};
|
||||
|
||||
let (truncate_width, ellipsis) = if let Some(truncate) = text_style.truncate {
|
||||
let width = known_dimensions.width.or(match available_space.width {
|
||||
crate::AvailableSpace::Definite(x) => Some(x),
|
||||
_ => None,
|
||||
});
|
||||
let (truncate_width, ellipsis) =
|
||||
if let Some(text_overflow) = text_style.text_overflow {
|
||||
let width = known_dimensions.width.or(match available_space.width {
|
||||
crate::AvailableSpace::Definite(x) => match text_style.line_clamp {
|
||||
Some(max_lines) => Some(x * max_lines),
|
||||
None => Some(x),
|
||||
},
|
||||
_ => None,
|
||||
});
|
||||
|
||||
match truncate {
|
||||
Truncate::Truncate => (width, None),
|
||||
Truncate::Ellipsis => (width, Some(ELLIPSIS)),
|
||||
}
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
match text_overflow {
|
||||
TextOverflow::Ellipsis(s) => (width, Some(s)),
|
||||
}
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
if let Some(text_layout) = element_state.0.lock().as_ref() {
|
||||
if text_layout.size.is_some()
|
||||
@@ -326,7 +328,11 @@ impl TextLayout {
|
||||
let Some(lines) = window
|
||||
.text_system()
|
||||
.shape_text(
|
||||
text, font_size, &runs, wrap_width, // Wrap if we know the width.
|
||||
text,
|
||||
font_size,
|
||||
&runs,
|
||||
wrap_width, // Wrap if we know the width.
|
||||
text_style.line_clamp, // Limit the number of lines if line_clamp is set.
|
||||
)
|
||||
.log_err()
|
||||
else {
|
||||
|
||||
@@ -82,6 +82,7 @@ mod input;
|
||||
mod interactive;
|
||||
mod key_dispatch;
|
||||
mod keymap;
|
||||
mod path_builder;
|
||||
mod platform;
|
||||
pub mod prelude;
|
||||
mod scene;
|
||||
@@ -129,12 +130,13 @@ pub use elements::*;
|
||||
pub use executor::*;
|
||||
pub use geometry::*;
|
||||
pub use global::*;
|
||||
pub use gpui_macros::{register_action, test, IntoElement, Render};
|
||||
pub use gpui_macros::{register_action, test, AppContext, IntoElement, Render, VisualContext};
|
||||
pub use http_client;
|
||||
pub use input::*;
|
||||
pub use interactive::*;
|
||||
use key_dispatch::*;
|
||||
pub use keymap::*;
|
||||
pub use path_builder::*;
|
||||
pub use platform::*;
|
||||
pub use refineable::*;
|
||||
pub use scene::*;
|
||||
@@ -153,7 +155,7 @@ pub use util::arc_cow::ArcCow;
|
||||
pub use view::*;
|
||||
pub use window::*;
|
||||
|
||||
use std::{any::Any, borrow::BorrowMut};
|
||||
use std::{any::Any, borrow::BorrowMut, future::Future};
|
||||
use taffy::TaffyLayoutEngine;
|
||||
|
||||
/// The context trait, allows the different contexts in GPUI to be used
|
||||
@@ -213,6 +215,16 @@ pub trait AppContext {
|
||||
) -> Result<R>
|
||||
where
|
||||
T: 'static;
|
||||
|
||||
/// Spawn a future on a background thread
|
||||
fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
|
||||
where
|
||||
R: Send + 'static;
|
||||
|
||||
/// Read a global from this app context
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result<R>
|
||||
where
|
||||
G: Global;
|
||||
}
|
||||
|
||||
/// Returned by [Context::reserve_entity] to later be passed to [Context::insert_model].
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user