Compare commits
1 Commits
improve-ve
...
windows-ho
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b95099056 |
70
.github/ISSUE_TEMPLATE/1.bug-report.yml
vendored
Normal file
70
.github/ISSUE_TEMPLATE/1.bug-report.yml
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
name: Report an issue
|
||||
description: Report an issue with Zed.
|
||||
type: Bug
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Feature requests should be opened in [discussions](https://github.com/zed-industries/zed/discussions/new/choose).
|
||||
|
||||
Before opening a new issue, please do a [search](https://github.com/zed-industries/zed/issues) of existing issues and :+1: upvote the existing issue instead. This will help us maintain a proper signal-to-noise ratio.
|
||||
|
||||
If you need help with your own project, you can ask a question in our [Discord Support Forums](https://discord.com/invite/zedindustries).
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Reproduction steps
|
||||
description: A step-by-step description of how to reproduce the issue from a **clean Zed install**. Any code must be sufficient to reproduce (make sure to include context!). Include code as text, not just as a screenshot. **Issues with insufficient detail may be summarily closed**.
|
||||
placeholder: |
|
||||
1. Start Zed
|
||||
2. Click X
|
||||
3. Y will happen
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Current vs. Expected behavior
|
||||
description: |
|
||||
A clear and concise description of what is the current behavior (screenshots, videos), vs. what you expected the behavior to be.
|
||||
|
||||
**Skipping this/failure to provide complete information will result in the issue being closed.**
|
||||
placeholder: "Based on my reproduction steps above, when I click X, I expect this to happen, but instead Y happens."
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: If applicable, attach your Zed log file to this issue.
|
||||
description: |
|
||||
Open the command palette in Zed, then type `zed: open log` to see the last 1000 lines. Or type `zed: reveal log in file manager` in the command palette to reveal the log file itself.
|
||||
value: |
|
||||
<details><summary>Zed.log</summary>
|
||||
|
||||
<!-- Paste your log inside the code block. -->
|
||||
```log
|
||||
|
||||
```
|
||||
|
||||
</details>
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: If applicable, provide details about your model provider
|
||||
placeholder: |
|
||||
- Provider: (Anthropic via ZedPro, Anthropic via API key, Copilot Chat, Mistral, OpenAI, etc.)
|
||||
- Model Name: (Claude Sonnet 4.5, Gemini 3 Pro, GPT-5)
|
||||
- Mode: (Agent Panel, Inline Assistant, Terminal Assistant or Text Threads)
|
||||
- Other details (ACPs, MCPs, other settings, etc.):
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Zed version and system specs
|
||||
description: |
|
||||
Open the command palette in Zed, then type “zed: copy system specs into clipboard”. **Skipping this/failure to provide complete information will result in the issue being closed**.
|
||||
placeholder: |
|
||||
Zed: v0.215.0 (Zed Nightly bfe141ea79aa4984028934067ba75c48d99136ae)
|
||||
OS: macOS 15.1
|
||||
Memory: 36 GiB
|
||||
Architecture: aarch64
|
||||
validations:
|
||||
required: true
|
||||
99
.github/ISSUE_TEMPLATE/10_bug_report.yml
vendored
99
.github/ISSUE_TEMPLATE/10_bug_report.yml
vendored
@@ -1,99 +0,0 @@
|
||||
name: Report a bug
|
||||
description: Report a problem with Zed.
|
||||
type: Bug
|
||||
labels: "state:needs triage"
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Is this bug already reported? Upvote to get it noticed faster. [Here's the search](https://github.com/zed-industries/zed/issues). Upvote means giving it a :+1: reaction.
|
||||
|
||||
Feature request? Please open in [discussions](https://github.com/zed-industries/zed/discussions/new/choose) instead.
|
||||
|
||||
Just have a question or need support? Welcome to [Discord Support Forums](https://discord.com/invite/zedindustries).
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Reproduction steps
|
||||
description: A step-by-step description of how to reproduce the bug from a **clean Zed install**. The more context you provide, the easier it is to find and fix the problem fast.
|
||||
placeholder: |
|
||||
1. Start Zed
|
||||
2. Click X
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Current vs. Expected behavior
|
||||
description: |
|
||||
Current behavior (screenshots, videos, etc. are appreciated), vs. what you expected the behavior to be.
|
||||
|
||||
placeholder: |
|
||||
Current behavior: <screenshot with an arrow> The icon is blue. Expected behavior: The icon should be red because this is what the setting is documented to do.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: environment
|
||||
attributes:
|
||||
label: Zed version and system specs
|
||||
description: |
|
||||
Open the command palette in Zed, then type “zed: copy system specs into clipboard”.
|
||||
placeholder: |
|
||||
Zed: v0.215.0 (Zed Nightly bfe141ea79aa4984028934067ba75c48d99136ae)
|
||||
OS: macOS 15.1
|
||||
Memory: 36 GiB
|
||||
Architecture: aarch64
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Attach Zed log file
|
||||
description: |
|
||||
Open the command palette in Zed, then type `zed: open log` to see the last 1000 lines. Or type `zed: reveal log in file manager` in the command palette to reveal the log file itself.
|
||||
value: |
|
||||
<details><summary>Zed.log</summary>
|
||||
|
||||
<!-- Paste your log inside the code block. -->
|
||||
```log
|
||||
|
||||
```
|
||||
|
||||
</details>
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Relevant Zed settings
|
||||
description: |
|
||||
Open the command palette in Zed, then type “zed: open settings file” and copy/paste any relevant (e.g., LSP-specific) settings.
|
||||
value: |
|
||||
<details><summary>settings.json</summary>
|
||||
|
||||
<!-- Paste your settings inside the code block. -->
|
||||
```json
|
||||
|
||||
```
|
||||
|
||||
</details>
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: (for AI issues) Model provider details
|
||||
placeholder: |
|
||||
- Provider: (Anthropic via ZedPro, Anthropic via API key, Copilot Chat, Mistral, OpenAI, etc.)
|
||||
- Model Name: (Claude Sonnet 4.5, Gemini 3 Pro, GPT-5)
|
||||
- Mode: (Agent Panel, Inline Assistant, Terminal Assistant or Text Threads)
|
||||
- Other details (ACPs, MCPs, other settings, etc.):
|
||||
validations:
|
||||
required: false
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: If you are using WSL on Windows, what flavor of Linux are you using?
|
||||
multiple: false
|
||||
options:
|
||||
- Arch Linux
|
||||
- Ubuntu
|
||||
- Fedora
|
||||
- Mint
|
||||
- Pop!_OS
|
||||
- NixOS
|
||||
- Other
|
||||
@@ -1,23 +1,32 @@
|
||||
name: Report a crash
|
||||
description: Zed is crashing or freezing or hanging.
|
||||
type: Crash
|
||||
labels: "state:needs triage"
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Reproduction steps
|
||||
description: A step-by-step description of how to reproduce the crash from a **clean Zed install**. The more context you provide, the easier it is to find and fix the problem fast.
|
||||
description: A step-by-step description of how to reproduce the crash from a **clean Zed install**. **Be verbose**. **Issues with insufficient detail may be summarily closed**.
|
||||
placeholder: |
|
||||
1. Start Zed
|
||||
2. Perform an action
|
||||
3. Zed crashes
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Current vs. Expected behavior
|
||||
description: |
|
||||
Go into depth about what actions you’re performing in Zed to trigger the crash. If Zed crashes before it loads any windows, make sure to mention that. Again, **be verbose**.
|
||||
|
||||
**Skipping this/failure to provide complete information will result in the issue being closed.**
|
||||
placeholder: "Based on my reproduction steps above, when I perform said action, I expect this to happen, but instead Zed crashes."
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Zed version and system specs
|
||||
description: |
|
||||
Open the command palette in Zed, then type “zed: copy system specs into clipboard”.
|
||||
Open the command palette in Zed, then type “zed: copy system specs into clipboard”. **Skipping this/failure to provide complete information will result in the issue being closed**.
|
||||
placeholder: |
|
||||
Zed: v0.215.0 (Zed Nightly bfe141ea79aa4984028934067ba75c48d99136ae)
|
||||
OS: macOS 15.1
|
||||
@@ -27,7 +36,7 @@ body:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Attach Zed log file
|
||||
label: If applicable, attach your Zed log file to this issue
|
||||
description: |
|
||||
Open the command palette in Zed, then type `zed: open log` to see the last 1000 lines. Or type `zed: reveal log in file manager` in the command palette to reveal the log file itself.
|
||||
value: |
|
||||
19
.github/ISSUE_TEMPLATE/99_other.yml
vendored
Normal file
19
.github/ISSUE_TEMPLATE/99_other.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: Other [Staff Only]
|
||||
description: Zed Staff Only
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Summary
|
||||
value: |
|
||||
<!-- Please insert a one line summary of the issue below -->
|
||||
SUMMARY_SENTENCE_HERE
|
||||
|
||||
### Description
|
||||
|
||||
IF YOU DO NOT WORK FOR ZED INDUSTRIES DO NOT CREATE ISSUES WITH THIS TEMPLATE.
|
||||
THEY WILL BE AUTO-CLOSED AND MAY RESULT IN YOU BEING BANNED FROM THE ZED ISSUE TRACKER.
|
||||
|
||||
FEATURE REQUESTS / SUPPORT REQUESTS SHOULD BE OPENED AS DISCUSSIONS:
|
||||
https://github.com/zed-industries/zed/discussions/new/choose
|
||||
validations:
|
||||
required: true
|
||||
@@ -23,13 +23,11 @@ jobs:
|
||||
AidanV
|
||||
akbxr
|
||||
AlvaroParker
|
||||
amtoaer
|
||||
artemevsevev
|
||||
bajrangCoder
|
||||
bcomnes
|
||||
Be-ing
|
||||
blopker
|
||||
bnjjj
|
||||
bobbymannino
|
||||
CharlesChen0823
|
||||
chbk
|
||||
@@ -37,10 +35,8 @@ jobs:
|
||||
davewa
|
||||
ddoemonn
|
||||
djsauble
|
||||
errmayank
|
||||
fantacell
|
||||
findrakecil
|
||||
FloppyDisco
|
||||
gko
|
||||
huacnlee
|
||||
imumesh18
|
||||
@@ -55,7 +51,6 @@ jobs:
|
||||
marius851000
|
||||
mikebronner
|
||||
ognevny
|
||||
playdohface
|
||||
RemcoSmitsDev
|
||||
romaninsh
|
||||
Simek
|
||||
@@ -63,14 +58,12 @@ jobs:
|
||||
sourcefrog
|
||||
suxiaoshao
|
||||
Takk8IS
|
||||
thedadams
|
||||
tidely
|
||||
timvermeulen
|
||||
valentinegb
|
||||
versecafe
|
||||
vitallium
|
||||
warrenjokinen
|
||||
WhySoBad
|
||||
ya7010
|
||||
Zertsov
|
||||
with:
|
||||
|
||||
176
.github/workflows/extension_bump.yml
vendored
176
.github/workflows/extension_bump.yml
vendored
@@ -1,176 +0,0 @@
|
||||
# Generated from xtask::workflows::extension_bump
|
||||
# Rebuild with `cargo xtask workflows`.
|
||||
name: extension_bump
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
RUST_BACKTRACE: '1'
|
||||
CARGO_INCREMENTAL: '0'
|
||||
ZED_EXTENSION_CLI_SHA: 7cfce605704d41ca247e3f84804bf323f6c6caaf
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
bump-type:
|
||||
description: bump-type
|
||||
type: string
|
||||
default: patch
|
||||
force-bump:
|
||||
description: force-bump
|
||||
required: true
|
||||
type: boolean
|
||||
secrets:
|
||||
app-id:
|
||||
description: The app ID used to create the PR
|
||||
required: true
|
||||
app-secret:
|
||||
description: The app secret for the corresponding app ID
|
||||
required: true
|
||||
jobs:
|
||||
check_extension:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
runs-on: namespace-profile-2x4-ubuntu-2404
|
||||
steps:
|
||||
- name: steps::checkout_repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
clean: false
|
||||
- id: cache-zed-extension-cli
|
||||
name: extension_tests::cache_zed_extension_cli
|
||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830
|
||||
with:
|
||||
path: zed-extension
|
||||
key: zed-extension-${{ env.ZED_EXTENSION_CLI_SHA }}
|
||||
- name: extension_tests::download_zed_extension_cli
|
||||
if: steps.cache-zed-extension-cli.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
wget --quiet "https://zed-extension-cli.nyc3.digitaloceanspaces.com/$ZED_EXTENSION_CLI_SHA/x86_64-unknown-linux-gnu/zed-extension"
|
||||
chmod +x zed-extension
|
||||
shell: bash -euxo pipefail {0}
|
||||
- name: extension_tests::check
|
||||
run: |
|
||||
mkdir -p /tmp/ext-scratch
|
||||
mkdir -p /tmp/ext-output
|
||||
./zed-extension --source-dir . --scratch-dir /tmp/ext-scratch --output-dir /tmp/ext-output
|
||||
shell: bash -euxo pipefail {0}
|
||||
timeout-minutes: 2
|
||||
check_bump_needed:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
runs-on: namespace-profile-2x4-ubuntu-2404
|
||||
steps:
|
||||
- name: steps::checkout_repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
clean: false
|
||||
fetch-depth: 0
|
||||
- id: compare-versions-check
|
||||
name: extension_bump::compare_versions
|
||||
run: |
|
||||
CURRENT_VERSION="$(sed -n 's/version = \"\(.*\)\"/\1/p' < extension.toml)"
|
||||
PR_PARENT_SHA="${{ github.event.pull_request.head.sha }}"
|
||||
|
||||
if [[ -n "$PR_PARENT_SHA" ]]; then
|
||||
git checkout "$PR_PARENT_SHA"
|
||||
elif BRANCH_PARENT_SHA="$(git merge-base origin/main origin/zed-zippy-autobump)"; then
|
||||
git checkout "$BRANCH_PARENT_SHA"
|
||||
else
|
||||
git checkout "$(git log -1 --format=%H)"~1
|
||||
fi
|
||||
|
||||
PARENT_COMMIT_VERSION="$(sed -n 's/version = \"\(.*\)\"/\1/p' < extension.toml)"
|
||||
|
||||
[[ "$CURRENT_VERSION" == "$PARENT_COMMIT_VERSION" ]] && \
|
||||
echo "needs_bump=true" >> "$GITHUB_OUTPUT" || \
|
||||
echo "needs_bump=false" >> "$GITHUB_OUTPUT"
|
||||
|
||||
echo "current_version=${CURRENT_VERSION}" >> "$GITHUB_OUTPUT"
|
||||
shell: bash -euxo pipefail {0}
|
||||
outputs:
|
||||
needs_bump: ${{ steps.compare-versions-check.outputs.needs_bump }}
|
||||
current_version: ${{ steps.compare-versions-check.outputs.current_version }}
|
||||
timeout-minutes: 1
|
||||
bump_extension_version:
|
||||
needs:
|
||||
- check_extension
|
||||
- check_bump_needed
|
||||
if: |-
|
||||
(github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') &&
|
||||
(inputs.force-bump == 'true' || needs.check_bump_needed.outputs.needs_bump == 'true')
|
||||
runs-on: namespace-profile-8x16-ubuntu-2204
|
||||
steps:
|
||||
- id: generate-token
|
||||
name: extension_bump::generate_token
|
||||
uses: actions/create-github-app-token@v2
|
||||
with:
|
||||
app-id: ${{ secrets.app-id }}
|
||||
private-key: ${{ secrets.app-secret }}
|
||||
- name: steps::checkout_repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
clean: false
|
||||
- name: extension_bump::install_bump_2_version
|
||||
run: pip install bump2version
|
||||
shell: bash -euxo pipefail {0}
|
||||
- id: bump-version
|
||||
name: extension_bump::bump_version
|
||||
run: |
|
||||
OLD_VERSION="${{ needs.check_bump_needed.outputs.current_version }}"
|
||||
|
||||
BUMP_FILES=("extension.toml")
|
||||
if [[ -f "Cargo.toml" ]]; then
|
||||
BUMP_FILES+=("Cargo.toml")
|
||||
fi
|
||||
|
||||
bump2version --verbose --current-version "$OLD_VERSION" --no-configured-files ${{ inputs.bump-type }} "${BUMP_FILES[@]}"
|
||||
|
||||
if [[ -f "Cargo.toml" ]]; then
|
||||
cargo update --workspace
|
||||
fi
|
||||
|
||||
NEW_VERSION="$(sed -n 's/version = \"\(.*\)\"/\1/p' < extension.toml)"
|
||||
|
||||
echo "new_version=${NEW_VERSION}" >> "$GITHUB_OUTPUT"
|
||||
shell: bash -euxo pipefail {0}
|
||||
- name: extension_bump::create_pull_request
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
title: Bump version to ${{ steps.bump-version.outputs.new_version }}
|
||||
body: This PR bumps the version of this extension to v${{ steps.bump-version.outputs.new_version }}
|
||||
commit-message: Bump version to v${{ steps.bump-version.outputs.new_version }}
|
||||
branch: zed-zippy-autobump
|
||||
committer: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
|
||||
base: main
|
||||
delete-branch: true
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
sign-commits: true
|
||||
timeout-minutes: 1
|
||||
create_version_label:
|
||||
needs:
|
||||
- check_extension
|
||||
- check_bump_needed
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') && github.event_name == 'push' && github.ref == 'refs/heads/main' && needs.check_bump_needed.outputs.needs_bump == 'false'
|
||||
runs-on: namespace-profile-8x16-ubuntu-2204
|
||||
steps:
|
||||
- id: generate-token
|
||||
name: extension_bump::generate_token
|
||||
uses: actions/create-github-app-token@v2
|
||||
with:
|
||||
app-id: ${{ secrets.app-id }}
|
||||
private-key: ${{ secrets.app-secret }}
|
||||
- name: steps::checkout_repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
clean: false
|
||||
- name: extension_bump::create_version_tag
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |-
|
||||
github.rest.git.createRef({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
ref: 'refs/tags/v${{ needs.check_bump_needed.outputs.current_version }}',
|
||||
sha: context.sha
|
||||
})
|
||||
github-token: ${{ steps.generate-token.outputs.token }}
|
||||
timeout-minutes: 1
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.ref_name == 'main' && github.sha || 'anysha' }}
|
||||
cancel-in-progress: true
|
||||
43
.github/workflows/extension_release.yml
vendored
43
.github/workflows/extension_release.yml
vendored
@@ -1,43 +0,0 @@
|
||||
# Generated from xtask::workflows::extension_release
|
||||
# Rebuild with `cargo xtask workflows`.
|
||||
name: extension_release
|
||||
on:
|
||||
workflow_call:
|
||||
secrets:
|
||||
app-id:
|
||||
description: The app ID used to create the PR
|
||||
required: true
|
||||
app-secret:
|
||||
description: The app secret for the corresponding app ID
|
||||
required: true
|
||||
jobs:
|
||||
create_release:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
runs-on: namespace-profile-8x16-ubuntu-2204
|
||||
steps:
|
||||
- id: generate-token
|
||||
name: extension_bump::generate_token
|
||||
uses: actions/create-github-app-token@v2
|
||||
with:
|
||||
app-id: ${{ secrets.app-id }}
|
||||
private-key: ${{ secrets.app-secret }}
|
||||
owner: zed-industries
|
||||
repositories: extensions
|
||||
- name: steps::checkout_repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
clean: false
|
||||
- id: get-extension-id
|
||||
name: extension_release::get_extension_id
|
||||
run: |
|
||||
EXTENSION_ID="$(sed -n 's/id = \"\(.*\)\"/\1/p' < extension.toml)"
|
||||
|
||||
echo "extension_id=${EXTENSION_ID}" >> "$GITHUB_OUTPUT"
|
||||
shell: bash -euxo pipefail {0}
|
||||
- name: extension_release::release_action
|
||||
uses: huacnlee/zed-extension-action@v2
|
||||
with:
|
||||
extension-name: ${{ steps.get-extension-id.outputs.extension_id }}
|
||||
push-to: zed-industries/extensions
|
||||
env:
|
||||
COMMITTER_TOKEN: ${{ steps.generate-token.outputs.token }}
|
||||
13
.github/workflows/extension_tests.yml
vendored
13
.github/workflows/extension_tests.yml
vendored
@@ -7,7 +7,12 @@ env:
|
||||
CARGO_INCREMENTAL: '0'
|
||||
ZED_EXTENSION_CLI_SHA: 7cfce605704d41ca247e3f84804bf323f6c6caaf
|
||||
on:
|
||||
workflow_call: {}
|
||||
workflow_call:
|
||||
inputs:
|
||||
run_tests:
|
||||
description: Whether the workflow should run rust tests
|
||||
required: true
|
||||
type: boolean
|
||||
jobs:
|
||||
orchestrate:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
@@ -68,12 +73,12 @@ jobs:
|
||||
run: cargo clippy --release --all-targets --all-features -- --deny warnings
|
||||
shell: bash -euxo pipefail {0}
|
||||
- name: steps::cargo_install_nextest
|
||||
if: inputs.run_tests
|
||||
uses: taiki-e/install-action@nextest
|
||||
- name: steps::cargo_nextest
|
||||
if: inputs.run_tests
|
||||
run: cargo nextest run --workspace --no-fail-fast
|
||||
shell: bash -euxo pipefail {0}
|
||||
env:
|
||||
NEXTEST_NO_TESTS: warn
|
||||
timeout-minutes: 3
|
||||
check_extension:
|
||||
needs:
|
||||
@@ -103,7 +108,7 @@ jobs:
|
||||
mkdir -p /tmp/ext-output
|
||||
./zed-extension --source-dir . --scratch-dir /tmp/ext-scratch --output-dir /tmp/ext-output
|
||||
shell: bash -euxo pipefail {0}
|
||||
timeout-minutes: 2
|
||||
timeout-minutes: 1
|
||||
tests_pass:
|
||||
needs:
|
||||
- orchestrate
|
||||
|
||||
12
.github/workflows/run_bundling.yml
vendored
12
.github/workflows/run_bundling.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
bundle_linux_aarch64:
|
||||
if: |-
|
||||
(github.event.action == 'labeled' && github.event.label.name == 'run-bundling') ||
|
||||
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
|
||||
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
|
||||
runs-on: namespace-profile-8x32-ubuntu-2004-arm-m4
|
||||
env:
|
||||
CARGO_INCREMENTAL: 0
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
bundle_linux_x86_64:
|
||||
if: |-
|
||||
(github.event.action == 'labeled' && github.event.label.name == 'run-bundling') ||
|
||||
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
|
||||
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
|
||||
runs-on: namespace-profile-32x64-ubuntu-2004
|
||||
env:
|
||||
CARGO_INCREMENTAL: 0
|
||||
@@ -99,7 +99,7 @@ jobs:
|
||||
bundle_mac_aarch64:
|
||||
if: |-
|
||||
(github.event.action == 'labeled' && github.event.label.name == 'run-bundling') ||
|
||||
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
|
||||
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
|
||||
runs-on: self-mini-macos
|
||||
env:
|
||||
CARGO_INCREMENTAL: 0
|
||||
@@ -145,7 +145,7 @@ jobs:
|
||||
bundle_mac_x86_64:
|
||||
if: |-
|
||||
(github.event.action == 'labeled' && github.event.label.name == 'run-bundling') ||
|
||||
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
|
||||
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
|
||||
runs-on: self-mini-macos
|
||||
env:
|
||||
CARGO_INCREMENTAL: 0
|
||||
@@ -191,7 +191,7 @@ jobs:
|
||||
bundle_windows_aarch64:
|
||||
if: |-
|
||||
(github.event.action == 'labeled' && github.event.label.name == 'run-bundling') ||
|
||||
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
|
||||
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
|
||||
runs-on: self-32vcpu-windows-2022
|
||||
env:
|
||||
CARGO_INCREMENTAL: 0
|
||||
@@ -229,7 +229,7 @@ jobs:
|
||||
bundle_windows_x86_64:
|
||||
if: |-
|
||||
(github.event.action == 'labeled' && github.event.label.name == 'run-bundling') ||
|
||||
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
|
||||
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
|
||||
runs-on: self-32vcpu-windows-2022
|
||||
env:
|
||||
CARGO_INCREMENTAL: 0
|
||||
|
||||
9
.github/workflows/run_cron_unit_evals.yml
vendored
9
.github/workflows/run_cron_unit_evals.yml
vendored
@@ -13,14 +13,6 @@ on:
|
||||
jobs:
|
||||
cron_unit_evals:
|
||||
runs-on: namespace-profile-16x32-ubuntu-2204
|
||||
strategy:
|
||||
matrix:
|
||||
model:
|
||||
- anthropic/claude-sonnet-4-5-latest
|
||||
- anthropic/claude-opus-4-5-latest
|
||||
- google/gemini-3-pro
|
||||
- openai/gpt-5
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: steps::checkout_repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
@@ -57,7 +49,6 @@ jobs:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
GOOGLE_AI_API_KEY: ${{ secrets.GOOGLE_AI_API_KEY }}
|
||||
GOOGLE_CLOUD_PROJECT: ${{ secrets.GOOGLE_CLOUD_PROJECT }}
|
||||
ZED_AGENT_MODEL: ${{ matrix.model }}
|
||||
- name: steps::cleanup_cargo_config
|
||||
if: always()
|
||||
run: |
|
||||
|
||||
3
.github/workflows/run_tests.yml
vendored
3
.github/workflows/run_tests.yml
vendored
@@ -84,7 +84,7 @@ jobs:
|
||||
run: ./script/check-keymaps
|
||||
shell: bash -euxo pipefail {0}
|
||||
- name: run_tests::check_style::check_for_typos
|
||||
uses: crate-ci/typos@2d0ce569feab1f8752f1dde43cc2f2aa53236e06
|
||||
uses: crate-ci/typos@80c8a4945eec0f6d464eaf9e65ed98ef085283d1
|
||||
with:
|
||||
config: ./typos.toml
|
||||
- name: steps::cargo_fmt
|
||||
@@ -520,7 +520,6 @@ jobs:
|
||||
uses: bufbuild/buf-setup-action@v1
|
||||
with:
|
||||
version: v1.29.0
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: run_tests::check_postgres_and_protobuf_migrations::bufbuild_breaking_action
|
||||
uses: bufbuild/buf-breaking-action@v1
|
||||
with:
|
||||
|
||||
97
Cargo.lock
generated
97
Cargo.lock
generated
@@ -1380,7 +1380,6 @@ dependencies = [
|
||||
"http_client",
|
||||
"markdown_preview",
|
||||
"release_channel",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smol",
|
||||
@@ -2423,7 +2422,6 @@ dependencies = [
|
||||
"rand 0.9.2",
|
||||
"rope",
|
||||
"serde_json",
|
||||
"settings",
|
||||
"sum_tree",
|
||||
"text",
|
||||
"unindent",
|
||||
@@ -5300,7 +5298,6 @@ dependencies = [
|
||||
"indoc",
|
||||
"language",
|
||||
"lsp",
|
||||
"menu",
|
||||
"paths",
|
||||
"project",
|
||||
"regex",
|
||||
@@ -5310,8 +5307,6 @@ dependencies = [
|
||||
"telemetry",
|
||||
"theme",
|
||||
"ui",
|
||||
"ui_input",
|
||||
"util",
|
||||
"workspace",
|
||||
"zed_actions",
|
||||
"zeta",
|
||||
@@ -5370,7 +5365,6 @@ dependencies = [
|
||||
"db",
|
||||
"edit_prediction",
|
||||
"emojis",
|
||||
"feature_flags",
|
||||
"file_icons",
|
||||
"fs",
|
||||
"futures 0.3.31",
|
||||
@@ -6412,7 +6406,6 @@ dependencies = [
|
||||
"git",
|
||||
"gpui",
|
||||
"ignore",
|
||||
"is_executable",
|
||||
"libc",
|
||||
"log",
|
||||
"notify 8.2.0",
|
||||
@@ -6972,7 +6965,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gh-workflow"
|
||||
version = "0.8.0"
|
||||
source = "git+https://github.com/zed-industries/gh-workflow?rev=e5f883040530b4df36437f140084ee5cc7c1c9be#e5f883040530b4df36437f140084ee5cc7c1c9be"
|
||||
source = "git+https://github.com/zed-industries/gh-workflow?rev=3eaa84abca0778eb54272f45a312cb24f9a0b435#3eaa84abca0778eb54272f45a312cb24f9a0b435"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"derive_more 2.0.1",
|
||||
@@ -6989,7 +6982,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gh-workflow-macros"
|
||||
version = "0.8.0"
|
||||
source = "git+https://github.com/zed-industries/gh-workflow?rev=e5f883040530b4df36437f140084ee5cc7c1c9be#e5f883040530b4df36437f140084ee5cc7c1c9be"
|
||||
source = "git+https://github.com/zed-industries/gh-workflow?rev=3eaa84abca0778eb54272f45a312cb24f9a0b435#3eaa84abca0778eb54272f45a312cb24f9a0b435"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"quote",
|
||||
@@ -7112,7 +7105,6 @@ dependencies = [
|
||||
"futures 0.3.31",
|
||||
"fuzzy",
|
||||
"git",
|
||||
"git_hosting_providers",
|
||||
"gpui",
|
||||
"indoc",
|
||||
"itertools 0.14.0",
|
||||
@@ -7134,7 +7126,6 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"settings",
|
||||
"smol",
|
||||
"strum 0.27.2",
|
||||
"telemetry",
|
||||
"theme",
|
||||
@@ -8445,15 +8436,6 @@ dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_executable"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baabb8b4867b26294d818bf3f651a454b6901431711abb96e296245888d6e8c4"
|
||||
dependencies = [
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
@@ -9020,7 +9002,6 @@ dependencies = [
|
||||
"chrono",
|
||||
"collections",
|
||||
"futures 0.3.31",
|
||||
"globset",
|
||||
"gpui",
|
||||
"http_client",
|
||||
"itertools 0.14.0",
|
||||
@@ -9046,9 +9027,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"serde_json_lenient",
|
||||
"settings",
|
||||
"smallvec",
|
||||
"smol",
|
||||
"snippet",
|
||||
"task",
|
||||
"terminal",
|
||||
"text",
|
||||
@@ -11551,7 +11530,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da#1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=e97b9508befa0062929da65a01054d25c4be861c#e97b9508befa0062929da65a01054d25c4be861c"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"env_logger 0.10.2",
|
||||
@@ -11576,7 +11555,6 @@ dependencies = [
|
||||
"pet-python-utils",
|
||||
"pet-reporter",
|
||||
"pet-telemetry",
|
||||
"pet-uv",
|
||||
"pet-venv",
|
||||
"pet-virtualenv",
|
||||
"pet-virtualenvwrapper",
|
||||
@@ -11589,7 +11567,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-conda"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da#1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=e97b9508befa0062929da65a01054d25c4be861c#e97b9508befa0062929da65a01054d25c4be861c"
|
||||
dependencies = [
|
||||
"env_logger 0.10.2",
|
||||
"lazy_static",
|
||||
@@ -11608,7 +11586,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-core"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da#1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=e97b9508befa0062929da65a01054d25c4be861c#e97b9508befa0062929da65a01054d25c4be861c"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"lazy_static",
|
||||
@@ -11623,7 +11601,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-env-var-path"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da#1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=e97b9508befa0062929da65a01054d25c4be861c#e97b9508befa0062929da65a01054d25c4be861c"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"log",
|
||||
@@ -11639,7 +11617,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-fs"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da#1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=e97b9508befa0062929da65a01054d25c4be861c#e97b9508befa0062929da65a01054d25c4be861c"
|
||||
dependencies = [
|
||||
"log",
|
||||
"msvc_spectre_libs",
|
||||
@@ -11648,7 +11626,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-global-virtualenvs"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da#1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=e97b9508befa0062929da65a01054d25c4be861c#e97b9508befa0062929da65a01054d25c4be861c"
|
||||
dependencies = [
|
||||
"log",
|
||||
"msvc_spectre_libs",
|
||||
@@ -11661,7 +11639,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-homebrew"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da#1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=e97b9508befa0062929da65a01054d25c4be861c#e97b9508befa0062929da65a01054d25c4be861c"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"log",
|
||||
@@ -11679,7 +11657,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-jsonrpc"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da#1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=e97b9508befa0062929da65a01054d25c4be861c#e97b9508befa0062929da65a01054d25c4be861c"
|
||||
dependencies = [
|
||||
"env_logger 0.10.2",
|
||||
"log",
|
||||
@@ -11692,7 +11670,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-linux-global-python"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da#1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=e97b9508befa0062929da65a01054d25c4be861c#e97b9508befa0062929da65a01054d25c4be861c"
|
||||
dependencies = [
|
||||
"log",
|
||||
"msvc_spectre_libs",
|
||||
@@ -11705,7 +11683,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-mac-commandlinetools"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da#1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=e97b9508befa0062929da65a01054d25c4be861c#e97b9508befa0062929da65a01054d25c4be861c"
|
||||
dependencies = [
|
||||
"log",
|
||||
"msvc_spectre_libs",
|
||||
@@ -11718,7 +11696,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-mac-python-org"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da#1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=e97b9508befa0062929da65a01054d25c4be861c#e97b9508befa0062929da65a01054d25c4be861c"
|
||||
dependencies = [
|
||||
"log",
|
||||
"msvc_spectre_libs",
|
||||
@@ -11731,7 +11709,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-mac-xcode"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da#1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=e97b9508befa0062929da65a01054d25c4be861c#e97b9508befa0062929da65a01054d25c4be861c"
|
||||
dependencies = [
|
||||
"log",
|
||||
"msvc_spectre_libs",
|
||||
@@ -11744,7 +11722,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-pipenv"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da#1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=e97b9508befa0062929da65a01054d25c4be861c#e97b9508befa0062929da65a01054d25c4be861c"
|
||||
dependencies = [
|
||||
"log",
|
||||
"msvc_spectre_libs",
|
||||
@@ -11757,7 +11735,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-pixi"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da#1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=e97b9508befa0062929da65a01054d25c4be861c#e97b9508befa0062929da65a01054d25c4be861c"
|
||||
dependencies = [
|
||||
"log",
|
||||
"msvc_spectre_libs",
|
||||
@@ -11769,7 +11747,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-poetry"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da#1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=e97b9508befa0062929da65a01054d25c4be861c#e97b9508befa0062929da65a01054d25c4be861c"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"lazy_static",
|
||||
@@ -11790,7 +11768,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-pyenv"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da#1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=e97b9508befa0062929da65a01054d25c4be861c#e97b9508befa0062929da65a01054d25c4be861c"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"log",
|
||||
@@ -11808,7 +11786,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-python-utils"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da#1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=e97b9508befa0062929da65a01054d25c4be861c#e97b9508befa0062929da65a01054d25c4be861c"
|
||||
dependencies = [
|
||||
"env_logger 0.10.2",
|
||||
"lazy_static",
|
||||
@@ -11825,7 +11803,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-reporter"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da#1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=e97b9508befa0062929da65a01054d25c4be861c#e97b9508befa0062929da65a01054d25c4be861c"
|
||||
dependencies = [
|
||||
"env_logger 0.10.2",
|
||||
"log",
|
||||
@@ -11839,7 +11817,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-telemetry"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da#1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=e97b9508befa0062929da65a01054d25c4be861c#e97b9508befa0062929da65a01054d25c4be861c"
|
||||
dependencies = [
|
||||
"env_logger 0.10.2",
|
||||
"lazy_static",
|
||||
@@ -11851,22 +11829,10 @@ dependencies = [
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pet-uv"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da#1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da"
|
||||
dependencies = [
|
||||
"log",
|
||||
"pet-core",
|
||||
"pet-python-utils",
|
||||
"serde",
|
||||
"toml 0.9.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pet-venv"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da#1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=e97b9508befa0062929da65a01054d25c4be861c#e97b9508befa0062929da65a01054d25c4be861c"
|
||||
dependencies = [
|
||||
"log",
|
||||
"msvc_spectre_libs",
|
||||
@@ -11878,7 +11844,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-virtualenv"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da#1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=e97b9508befa0062929da65a01054d25c4be861c#e97b9508befa0062929da65a01054d25c4be861c"
|
||||
dependencies = [
|
||||
"log",
|
||||
"msvc_spectre_libs",
|
||||
@@ -11890,7 +11856,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-virtualenvwrapper"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da#1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=e97b9508befa0062929da65a01054d25c4be861c#e97b9508befa0062929da65a01054d25c4be861c"
|
||||
dependencies = [
|
||||
"log",
|
||||
"msvc_spectre_libs",
|
||||
@@ -11903,7 +11869,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-windows-registry"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da#1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=e97b9508befa0062929da65a01054d25c4be861c#e97b9508befa0062929da65a01054d25c4be861c"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"log",
|
||||
@@ -11921,7 +11887,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pet-windows-store"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da#1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da"
|
||||
source = "git+https://github.com/microsoft/python-environment-tools.git?rev=e97b9508befa0062929da65a01054d25c4be861c#e97b9508befa0062929da65a01054d25c4be861c"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"log",
|
||||
@@ -15122,7 +15088,6 @@ dependencies = [
|
||||
"editor",
|
||||
"futures 0.3.31",
|
||||
"gpui",
|
||||
"itertools 0.14.0",
|
||||
"language",
|
||||
"lsp",
|
||||
"menu",
|
||||
@@ -17348,8 +17313,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tiktoken-rs"
|
||||
version = "0.9.1"
|
||||
source = "git+https://github.com/zed-industries/tiktoken-rs?rev=7249f999c5fdf9bf3cc5c288c964454e4dac0c00#7249f999c5fdf9bf3cc5c288c964454e4dac0c00"
|
||||
version = "0.8.0"
|
||||
source = "git+https://github.com/zed-industries/tiktoken-rs?rev=30c32a4522751699adeda0d5840c71c3b75ae73d#30c32a4522751699adeda0d5840c71c3b75ae73d"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
@@ -21020,7 +20985,6 @@ dependencies = [
|
||||
"indexmap",
|
||||
"indoc",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"toml 0.8.23",
|
||||
"toml_edit 0.22.27",
|
||||
]
|
||||
@@ -21205,7 +21169,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed"
|
||||
version = "0.216.0"
|
||||
version = "0.215.0"
|
||||
dependencies = [
|
||||
"acp_tools",
|
||||
"activity_indicator",
|
||||
@@ -21528,7 +21492,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed_proto"
|
||||
version = "0.2.3"
|
||||
version = "0.2.2"
|
||||
dependencies = [
|
||||
"zed_extension_api 0.1.0",
|
||||
]
|
||||
@@ -21681,7 +21645,6 @@ dependencies = [
|
||||
"collections",
|
||||
"command_palette_hooks",
|
||||
"copilot",
|
||||
"credentials_provider",
|
||||
"ctor",
|
||||
"db",
|
||||
"edit_prediction",
|
||||
|
||||
21
Cargo.toml
21
Cargo.toml
@@ -508,7 +508,7 @@ fork = "0.4.0"
|
||||
futures = "0.3"
|
||||
futures-batch = "0.6.1"
|
||||
futures-lite = "1.13"
|
||||
gh-workflow = { git = "https://github.com/zed-industries/gh-workflow", rev = "e5f883040530b4df36437f140084ee5cc7c1c9be" }
|
||||
gh-workflow = { git = "https://github.com/zed-industries/gh-workflow", rev = "3eaa84abca0778eb54272f45a312cb24f9a0b435" }
|
||||
git2 = { version = "0.20.1", default-features = false }
|
||||
globset = "0.4"
|
||||
handlebars = "4.3"
|
||||
@@ -583,14 +583,14 @@ partial-json-fixer = "0.5.3"
|
||||
parse_int = "0.9"
|
||||
pciid-parser = "0.8.0"
|
||||
pathdiff = "0.2"
|
||||
pet = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da" }
|
||||
pet-conda = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da" }
|
||||
pet-core = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da" }
|
||||
pet-fs = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da" }
|
||||
pet-pixi = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da" }
|
||||
pet-poetry = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da" }
|
||||
pet-reporter = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da" }
|
||||
pet-virtualenv = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da" }
|
||||
pet = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "e97b9508befa0062929da65a01054d25c4be861c" }
|
||||
pet-conda = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "e97b9508befa0062929da65a01054d25c4be861c" }
|
||||
pet-core = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "e97b9508befa0062929da65a01054d25c4be861c" }
|
||||
pet-fs = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "e97b9508befa0062929da65a01054d25c4be861c" }
|
||||
pet-pixi = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "e97b9508befa0062929da65a01054d25c4be861c" }
|
||||
pet-poetry = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "e97b9508befa0062929da65a01054d25c4be861c" }
|
||||
pet-reporter = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "e97b9508befa0062929da65a01054d25c4be861c" }
|
||||
pet-virtualenv = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "e97b9508befa0062929da65a01054d25c4be861c" }
|
||||
portable-pty = "0.9.0"
|
||||
postage = { version = "0.5", features = ["futures-traits"] }
|
||||
pretty_assertions = { version = "1.3.0", features = ["unstable"] }
|
||||
@@ -639,7 +639,6 @@ serde_urlencoded = "0.7"
|
||||
sha2 = "0.10"
|
||||
shellexpand = "2.1.0"
|
||||
shlex = "1.3.0"
|
||||
similar = "2.6"
|
||||
simplelog = "0.12.2"
|
||||
slotmap = "1.0.6"
|
||||
smallvec = { version = "1.6", features = ["union"] }
|
||||
@@ -656,7 +655,7 @@ sysinfo = "0.37.0"
|
||||
take-until = "0.2.0"
|
||||
tempfile = "3.20.0"
|
||||
thiserror = "2.0.12"
|
||||
tiktoken-rs = { git = "https://github.com/zed-industries/tiktoken-rs", rev = "7249f999c5fdf9bf3cc5c288c964454e4dac0c00" }
|
||||
tiktoken-rs = { git = "https://github.com/zed-industries/tiktoken-rs", rev = "30c32a4522751699adeda0d5840c71c3b75ae73d" }
|
||||
time = { version = "0.3", features = [
|
||||
"macros",
|
||||
"parsing",
|
||||
|
||||
@@ -43,9 +43,8 @@ design
|
||||
= @danilo-leal
|
||||
|
||||
docs
|
||||
= @miguelraz
|
||||
= @probably-neb
|
||||
= @yeskunall
|
||||
= @miguelraz
|
||||
|
||||
extension
|
||||
= @kubkon
|
||||
@@ -100,9 +99,6 @@ settings_ui
|
||||
= @danilo-leal
|
||||
= @probably-neb
|
||||
|
||||
sum_tree
|
||||
= @Veykril
|
||||
|
||||
support
|
||||
= @miguelraz
|
||||
|
||||
@@ -114,9 +110,6 @@ terminal
|
||||
= @kubkon
|
||||
= @Veykril
|
||||
|
||||
text
|
||||
= @Veykril
|
||||
|
||||
vim
|
||||
= @ConradIrwin
|
||||
= @dinocosta
|
||||
@@ -126,4 +119,3 @@ vim
|
||||
windows
|
||||
= @localcc
|
||||
= @reflectronic
|
||||
= @Veykril
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1_2)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.58747 12.9359C4.35741 12.778 4.17558 12.625 4.17558 12.625L10.092 2.37749C10.092 2.37749 10.3355 2.46782 10.5367 2.56426C10.7903 2.6858 11.0003 2.80429 11.0003 2.80429C13.8681 4.46005 14.8523 8.13267 13.1965 11.0005C11.5407 13.8684 7.8681 14.8525 5.00023 13.1967C5.00023 13.1967 4.79936 13.0812 4.58747 12.9359ZM10.5003 3.67032L5.50023 12.3307C7.89013 13.7105 10.9506 12.8904 12.3305 10.5006C13.7102 8.1106 12.8902 5.05015 10.5003 3.67032ZM3.07664 11.4314C2.87558 11.1403 2.804 11.0006 2.804 11.0006C1.77036 9.20524 1.69456 6.92215 2.80404 5.00046C3.91353 3.07877 5.92859 2.00291 8.0003 2.00036C8.0003 2.00036 8.28 1.99964 8.51289 2.02194C8.86375 2.05556 9.09702 2.10083 9.09702 2.10083L3.43905 11.9007C3.43905 11.9007 3.30482 11.7618 3.07664 11.4314ZM7.40178 3.03702C5.89399 3.22027 4.48727 4.08506 3.67008 5.50052C2.85288 6.9159 2.80733 8.56653 3.40252 9.96401L7.40178 3.03702Z" fill="black" stroke="black" stroke-width="0.1"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1_2">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -1279,7 +1279,6 @@
|
||||
"escape": "workspace::CloseWindow",
|
||||
"ctrl-m": "settings_editor::Minimize",
|
||||
"ctrl-f": "search::FocusSearch",
|
||||
"ctrl-,": "settings_editor::OpenCurrentFile",
|
||||
"left": "settings_editor::ToggleFocusNav",
|
||||
"ctrl-shift-e": "settings_editor::ToggleFocusNav",
|
||||
// todo(settings_ui): cut this down based on the max files and overflow UI
|
||||
@@ -1335,12 +1334,5 @@
|
||||
"alt-left": "dev::Zeta2ContextGoBack",
|
||||
"alt-right": "dev::Zeta2ContextGoForward"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "GitBranchSelector || (GitBranchSelector > Picker > Editor)",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"ctrl-shift-backspace": "branch_picker::DeleteBranch"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -49,8 +49,7 @@
|
||||
"ctrl-cmd-f": "zed::ToggleFullScreen",
|
||||
"ctrl-cmd-z": "edit_prediction::RateCompletions",
|
||||
"ctrl-cmd-i": "edit_prediction::ToggleMenu",
|
||||
"ctrl-cmd-l": "lsp_tool::ToggleMenu",
|
||||
"ctrl-cmd-c": "editor::DisplayCursorNames"
|
||||
"ctrl-cmd-l": "lsp_tool::ToggleMenu"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -590,7 +589,8 @@
|
||||
"cmd-.": "editor::ToggleCodeActions",
|
||||
"cmd-k r": "editor::RevealInFileManager",
|
||||
"cmd-k p": "editor::CopyPath",
|
||||
"cmd-\\": "pane::SplitRight"
|
||||
"cmd-\\": "pane::SplitRight",
|
||||
"ctrl-cmd-c": "editor::DisplayCursorNames"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -730,8 +730,7 @@
|
||||
"context": "Workspace && debugger_running",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"f5": "zed::NoAction",
|
||||
"f11": "debugger::StepInto"
|
||||
"f5": "zed::NoAction"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1383,7 +1382,6 @@
|
||||
"escape": "workspace::CloseWindow",
|
||||
"cmd-m": "settings_editor::Minimize",
|
||||
"cmd-f": "search::FocusSearch",
|
||||
"cmd-,": "settings_editor::OpenCurrentFile",
|
||||
"left": "settings_editor::ToggleFocusNav",
|
||||
"cmd-shift-e": "settings_editor::ToggleFocusNav",
|
||||
// todo(settings_ui): cut this down based on the max files and overflow UI
|
||||
@@ -1440,12 +1438,5 @@
|
||||
"alt-left": "dev::Zeta2ContextGoBack",
|
||||
"alt-right": "dev::Zeta2ContextGoForward"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "GitBranchSelector || (GitBranchSelector > Picker > Editor)",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"cmd-shift-backspace": "branch_picker::DeleteBranch"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -36,12 +36,12 @@
|
||||
"shift-f5": "debugger::Stop",
|
||||
"ctrl-shift-f5": "debugger::RerunSession",
|
||||
"f6": "debugger::Pause",
|
||||
"f10": "debugger::StepOver",
|
||||
"f7": "debugger::StepOver",
|
||||
"ctrl-f11": "debugger::StepInto",
|
||||
"shift-f11": "debugger::StepOut",
|
||||
"f11": "zed::ToggleFullScreen",
|
||||
"ctrl-shift-i": "edit_prediction::ToggleMenu",
|
||||
"shift-alt-l": "lsp_tool::ToggleMenu",
|
||||
"ctrl-shift-alt-c": "editor::DisplayCursorNames"
|
||||
"shift-alt-l": "lsp_tool::ToggleMenu"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -117,7 +117,7 @@
|
||||
"alt-g m": "git::OpenModifiedFiles",
|
||||
"menu": "editor::OpenContextMenu",
|
||||
"shift-f10": "editor::OpenContextMenu",
|
||||
"ctrl-alt-e": "editor::ToggleEditPrediction",
|
||||
"ctrl-shift-e": "editor::ToggleEditPrediction",
|
||||
"f9": "editor::ToggleBreakpoint",
|
||||
"shift-f9": "editor::EditLogBreakpoint"
|
||||
}
|
||||
@@ -215,7 +215,7 @@
|
||||
"context": "ContextEditor > Editor",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"ctrl-i": "assistant::Assist",
|
||||
"ctrl-enter": "assistant::Assist",
|
||||
"ctrl-s": "workspace::Save",
|
||||
"ctrl-shift-,": "assistant::InsertIntoEditor",
|
||||
"shift-enter": "assistant::Split",
|
||||
@@ -240,18 +240,18 @@
|
||||
"shift-alt-p": "agent::ManageProfiles",
|
||||
"ctrl-i": "agent::ToggleProfileSelector",
|
||||
"shift-alt-/": "agent::ToggleModelSelector",
|
||||
"shift-alt-j": "agent::ToggleNavigationMenu",
|
||||
"shift-alt-i": "agent::ToggleOptionsMenu",
|
||||
"ctrl-shift-alt-n": "agent::ToggleNewThreadMenu",
|
||||
"ctrl-shift-j": "agent::ToggleNavigationMenu",
|
||||
"ctrl-alt-i": "agent::ToggleOptionsMenu",
|
||||
// "ctrl-shift-alt-n": "agent::ToggleNewThreadMenu",
|
||||
"shift-alt-escape": "agent::ExpandMessageEditor",
|
||||
"ctrl-shift-.": "agent::AddSelectionToThread",
|
||||
"ctrl-shift-e": "project_panel::ToggleFocus",
|
||||
"ctrl-shift-enter": "agent::ContinueThread",
|
||||
"super-ctrl-b": "agent::ToggleBurnMode",
|
||||
"alt-enter": "agent::ContinueWithBurnMode",
|
||||
"shift-alt-a": "agent::AllowOnce",
|
||||
"ctrl-y": "agent::AllowOnce",
|
||||
"ctrl-alt-y": "agent::AllowAlways",
|
||||
"shift-alt-z": "agent::RejectOnce"
|
||||
"ctrl-alt-z": "agent::RejectOnce"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -500,7 +500,10 @@
|
||||
"ctrl-shift-l": "editor::SelectAllMatches", // Select all occurrences of current selection
|
||||
"ctrl-f2": "editor::SelectAllMatches", // Select all occurrences of current word
|
||||
"ctrl-d": ["editor::SelectNext", { "replace_newest": false }], // editor.action.addSelectionToNextFindMatch / find_under_expand
|
||||
"ctrl-shift-down": ["editor::SelectNext", { "replace_newest": false }], // editor.action.addSelectionToNextFindMatch
|
||||
"ctrl-shift-up": ["editor::SelectPrevious", { "replace_newest": false }], // editor.action.addSelectionToPreviousFindMatch
|
||||
"ctrl-k ctrl-d": ["editor::SelectNext", { "replace_newest": true }], // editor.action.moveSelectionToNextFindMatch / find_under_expand_skip
|
||||
"ctrl-k ctrl-shift-d": ["editor::SelectPrevious", { "replace_newest": true }], // editor.action.moveSelectionToPreviousFindMatch
|
||||
"ctrl-k ctrl-i": "editor::Hover",
|
||||
"ctrl-k ctrl-b": "editor::BlameHover",
|
||||
"ctrl-/": ["editor::ToggleComments", { "advance_downwards": false }],
|
||||
@@ -509,8 +512,12 @@
|
||||
"f2": "editor::Rename",
|
||||
"f12": "editor::GoToDefinition",
|
||||
"alt-f12": "editor::GoToDefinitionSplit",
|
||||
"ctrl-shift-f10": "editor::GoToDefinitionSplit",
|
||||
"ctrl-f12": "editor::GoToImplementation",
|
||||
"shift-f12": "editor::GoToTypeDefinition",
|
||||
"ctrl-alt-f12": "editor::GoToTypeDefinitionSplit",
|
||||
"shift-alt-f12": "editor::FindAllReferences",
|
||||
"ctrl-m": "editor::MoveToEnclosingBracket", // from jetbrains
|
||||
"ctrl-shift-\\": "editor::MoveToEnclosingBracket",
|
||||
"ctrl-shift-[": "editor::Fold",
|
||||
"ctrl-shift-]": "editor::UnfoldLines",
|
||||
@@ -534,6 +541,7 @@
|
||||
"ctrl-k r": "editor::RevealInFileManager",
|
||||
"ctrl-k p": "editor::CopyPath",
|
||||
"ctrl-\\": "pane::SplitRight",
|
||||
"ctrl-shift-alt-c": "editor::DisplayCursorNames",
|
||||
"alt-.": "editor::GoToHunk",
|
||||
"alt-,": "editor::GoToPreviousHunk"
|
||||
}
|
||||
@@ -1116,7 +1124,7 @@
|
||||
"shift-insert": "terminal::Paste",
|
||||
"ctrl-v": "terminal::Paste",
|
||||
"ctrl-shift-v": "terminal::Paste",
|
||||
"ctrl-i": "assistant::InlineAssist",
|
||||
"ctrl-enter": "assistant::InlineAssist",
|
||||
"alt-b": ["terminal::SendText", "\u001bb"],
|
||||
"alt-f": ["terminal::SendText", "\u001bf"],
|
||||
"alt-.": ["terminal::SendText", "\u001b."],
|
||||
@@ -1297,7 +1305,6 @@
|
||||
"escape": "workspace::CloseWindow",
|
||||
"ctrl-m": "settings_editor::Minimize",
|
||||
"ctrl-f": "search::FocusSearch",
|
||||
"ctrl-,": "settings_editor::OpenCurrentFile",
|
||||
"left": "settings_editor::ToggleFocusNav",
|
||||
"ctrl-shift-e": "settings_editor::ToggleFocusNav",
|
||||
// todo(settings_ui): cut this down based on the max files and overflow UI
|
||||
@@ -1354,12 +1361,5 @@
|
||||
"alt-left": "dev::Zeta2ContextGoBack",
|
||||
"alt-right": "dev::Zeta2ContextGoForward"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "GitBranchSelector || (GitBranchSelector > Picker > Editor)",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"ctrl-shift-backspace": "branch_picker::DeleteBranch"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -857,8 +857,6 @@
|
||||
"ctrl-w shift-right": "workspace::SwapPaneRight",
|
||||
"ctrl-w shift-up": "workspace::SwapPaneUp",
|
||||
"ctrl-w shift-down": "workspace::SwapPaneDown",
|
||||
"ctrl-w x": "workspace::SwapPaneAdjacent",
|
||||
"ctrl-w ctrl-x": "workspace::SwapPaneAdjacent",
|
||||
"ctrl-w shift-h": "workspace::MovePaneLeft",
|
||||
"ctrl-w shift-l": "workspace::MovePaneRight",
|
||||
"ctrl-w shift-k": "workspace::MovePaneUp",
|
||||
|
||||
@@ -1209,13 +1209,6 @@
|
||||
"tab_size": 4,
|
||||
// What debuggers are preferred by default for all languages.
|
||||
"debuggers": [],
|
||||
// Whether to enable word diff highlighting in the editor.
|
||||
//
|
||||
// When enabled, changed words within modified lines are highlighted
|
||||
// to show exactly what changed.
|
||||
//
|
||||
// Default: true
|
||||
"word_diff_enabled": true,
|
||||
// Control what info is collected by Zed.
|
||||
"telemetry": {
|
||||
// Send debug info like crash reports.
|
||||
@@ -1357,8 +1350,6 @@
|
||||
// "load_direnv": "direct"
|
||||
// 2. Load direnv configuration through the shell hook, works for POSIX shells and fish.
|
||||
// "load_direnv": "shell_hook"
|
||||
// 3. Don't load direnv configuration at all.
|
||||
// "load_direnv": "disabled"
|
||||
"load_direnv": "direct",
|
||||
"edit_predictions": {
|
||||
// A list of globs representing files that edit predictions should be disabled for.
|
||||
|
||||
@@ -96,11 +96,9 @@
|
||||
"terminal.ansi.bright_white": "#fafafaff",
|
||||
"terminal.ansi.dim_white": "#575d65ff",
|
||||
"link_text.hover": "#74ade8ff",
|
||||
"version_control.added": "#2EA04833",
|
||||
"version_control.added": "#2EA048ff",
|
||||
"version_control.modified": "#d3b020ff",
|
||||
"version_control.word_added": "#2EA0483D",
|
||||
"version_control.word_deleted": "#78081BB3",
|
||||
"version_control.deleted": "#78081B66",
|
||||
"version_control.deleted": "#78081Bff",
|
||||
"version_control.conflict_marker.ours": "#a1c1811a",
|
||||
"version_control.conflict_marker.theirs": "#74ade81a",
|
||||
"conflict": "#dec184ff",
|
||||
@@ -499,11 +497,9 @@
|
||||
"terminal.ansi.bright_white": "#ffffffff",
|
||||
"terminal.ansi.dim_white": "#aaaaaaff",
|
||||
"link_text.hover": "#5c78e2ff",
|
||||
"version_control.added": "#2EA04833",
|
||||
"version_control.added": "#2EA048ff",
|
||||
"version_control.modified": "#d3b020ff",
|
||||
"version_control.word_added": "#2EA0483D",
|
||||
"version_control.word_deleted": "#F851493D",
|
||||
"version_control.deleted": "#F8514929",
|
||||
"version_control.deleted": "#F85149ff",
|
||||
"conflict": "#a48819ff",
|
||||
"conflict.background": "#faf2e6ff",
|
||||
"conflict.border": "#f4e7d1ff",
|
||||
|
||||
@@ -528,7 +528,7 @@ impl Render for AcpTools {
|
||||
.with_sizing_behavior(gpui::ListSizingBehavior::Auto)
|
||||
.size_full(),
|
||||
)
|
||||
.vertical_scrollbar_for(&connection.list_state, window, cx)
|
||||
.vertical_scrollbar_for(connection.list_state.clone(), window, cx)
|
||||
.into_any()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,20 +424,4 @@ impl ThreadsDatabase {
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn delete_threads(&self) -> Task<Result<()>> {
|
||||
let connection = self.connection.clone();
|
||||
|
||||
self.executor.spawn(async move {
|
||||
let connection = connection.lock();
|
||||
|
||||
let mut delete = connection.exec_bound::<()>(indoc! {"
|
||||
DELETE FROM threads
|
||||
"})?;
|
||||
|
||||
delete(())?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,15 +188,6 @@ impl HistoryStore {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn delete_threads(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
|
||||
let database_future = ThreadsDatabase::connect(cx);
|
||||
cx.spawn(async move |this, cx| {
|
||||
let database = database_future.await.map_err(|err| anyhow!(err))?;
|
||||
database.delete_threads().await?;
|
||||
this.update(cx, |this, cx| this.reload(cx))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn delete_text_thread(
|
||||
&mut self,
|
||||
path: Arc<Path>,
|
||||
|
||||
@@ -66,9 +66,11 @@ pub async fn get_buffer_content_or_outline(
|
||||
let outline_text = render_outline(outline_items, None, 0, usize::MAX).await?;
|
||||
|
||||
let text = if let Some(path) = path {
|
||||
format!("# File outline for {path}\n\n{outline_text}",)
|
||||
format!(
|
||||
"# File outline for {path} (file too large to show full content)\n\n{outline_text}",
|
||||
)
|
||||
} else {
|
||||
format!("# File outline\n\n{outline_text}",)
|
||||
format!("# File outline (file too large to show full content)\n\n{outline_text}",)
|
||||
};
|
||||
Ok(BufferContent {
|
||||
text,
|
||||
|
||||
@@ -177,7 +177,7 @@ fn search_paths(glob: &str, project: Entity<Project>, cx: &mut App) -> Task<Resu
|
||||
let mut results = Vec::new();
|
||||
for snapshot in snapshots {
|
||||
for entry in snapshot.entries(false, 0) {
|
||||
if path_matcher.is_match(&snapshot.root_name().join(&entry.path)) {
|
||||
if path_matcher.is_match(snapshot.root_name().join(&entry.path).as_std_path()) {
|
||||
results.push(snapshot.absolutize(&entry.path));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,21 +32,8 @@ pub struct GrepToolInput {
|
||||
/// Do NOT specify a path here! This will only be matched against the code **content**.
|
||||
pub regex: String,
|
||||
/// A glob pattern for the paths of files to include in the search.
|
||||
/// Supports standard glob patterns like "**/*.rs" or "frontend/src/**/*.ts".
|
||||
/// Supports standard glob patterns like "**/*.rs" or "src/**/*.ts".
|
||||
/// If omitted, all files in the project will be searched.
|
||||
///
|
||||
/// The glob pattern is matched against the full path including the project root directory.
|
||||
///
|
||||
/// <example>
|
||||
/// If the project has the following root directories:
|
||||
///
|
||||
/// - /a/b/backend
|
||||
/// - /c/d/frontend
|
||||
///
|
||||
/// Use "backend/**/*.rs" to search only Rust files in the backend root directory.
|
||||
/// Use "frontend/src/**/*.ts" to search TypeScript files only in the frontend root directory (sub-directory "src").
|
||||
/// Use "**/*.rs" to search Rust files across all root directories.
|
||||
/// </example>
|
||||
pub include_pattern: Option<String>,
|
||||
/// Optional starting position for paginated results (0-based).
|
||||
/// When not provided, starts from the beginning.
|
||||
@@ -145,7 +132,8 @@ impl AgentTool for GrepTool {
|
||||
let exclude_patterns = global_settings
|
||||
.file_scan_exclusions
|
||||
.sources()
|
||||
.chain(global_settings.private_files.sources());
|
||||
.iter()
|
||||
.chain(global_settings.private_files.sources().iter());
|
||||
|
||||
match PathMatcher::new(exclude_patterns, path_style) {
|
||||
Ok(matcher) => matcher,
|
||||
|
||||
@@ -17,9 +17,6 @@ use crate::{AgentTool, Thread, ToolCallEventStream, outline};
|
||||
/// Reads the content of the given file in the project.
|
||||
///
|
||||
/// - Never attempt to read a path that hasn't been previously mentioned.
|
||||
/// - For large files, this tool returns a file outline with symbol names and line numbers instead of the full content.
|
||||
/// This outline IS a successful response - use the line numbers to read specific sections with start_line/end_line.
|
||||
/// Do NOT retry reading the same file without line numbers if you receive an outline.
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct ReadFileToolInput {
|
||||
/// The relative path of the file to read.
|
||||
@@ -257,15 +254,16 @@ impl AgentTool for ReadFileTool {
|
||||
|
||||
if buffer_content.is_outline {
|
||||
Ok(formatdoc! {"
|
||||
SUCCESS: File outline retrieved. This file is too large to read all at once, so the outline below shows the file's structure with line numbers.
|
||||
|
||||
IMPORTANT: Do NOT retry this call without line numbers - you will get the same outline.
|
||||
Instead, use the line numbers below to read specific sections by calling this tool again with start_line and end_line parameters.
|
||||
This file was too big to read all at once.
|
||||
|
||||
{}
|
||||
|
||||
NEXT STEPS: To read a specific symbol's implementation, call read_file with the same path plus start_line and end_line from the outline above.
|
||||
For example, to read a function shown as [L100-150], use start_line: 100 and end_line: 150.", buffer_content.text
|
||||
Using the line numbers in this outline, you can call this tool again
|
||||
while specifying the start_line and end_line fields to see the
|
||||
implementations of symbols in the outline.
|
||||
|
||||
Alternatively, you can fall back to the `grep` tool (if available)
|
||||
to search the file for specific content.", buffer_content.text
|
||||
}
|
||||
.into())
|
||||
} else {
|
||||
@@ -442,7 +440,7 @@ mod test {
|
||||
let content = result.to_str().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
content.lines().skip(7).take(6).collect::<Vec<_>>(),
|
||||
content.lines().skip(4).take(6).collect::<Vec<_>>(),
|
||||
vec![
|
||||
"struct Test0 [L1-4]",
|
||||
" a [L2]",
|
||||
@@ -477,7 +475,7 @@ mod test {
|
||||
pretty_assertions::assert_eq!(
|
||||
content
|
||||
.lines()
|
||||
.skip(7)
|
||||
.skip(4)
|
||||
.take(expected_content.len())
|
||||
.collect::<Vec<_>>(),
|
||||
expected_content
|
||||
|
||||
@@ -1423,7 +1423,7 @@ mod tests {
|
||||
rel_path("b/eight.txt"),
|
||||
];
|
||||
|
||||
let slash = PathStyle::local().primary_separator();
|
||||
let slash = PathStyle::local().separator();
|
||||
|
||||
let mut opened_editors = Vec::new();
|
||||
for path in paths {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::acp::AcpThreadView;
|
||||
use crate::{AgentPanel, RemoveHistory, RemoveSelectedThread};
|
||||
use crate::{AgentPanel, RemoveSelectedThread};
|
||||
use agent::{HistoryEntry, HistoryStore};
|
||||
use chrono::{Datelike as _, Local, NaiveDate, TimeDelta};
|
||||
use editor::{Editor, EditorEvent};
|
||||
@@ -12,7 +12,7 @@ use std::{fmt::Display, ops::Range};
|
||||
use text::Bias;
|
||||
use time::{OffsetDateTime, UtcOffset};
|
||||
use ui::{
|
||||
HighlightedLabel, IconButtonShape, ListItem, ListItemSpacing, Tab, Tooltip, WithScrollbar,
|
||||
HighlightedLabel, IconButtonShape, ListItem, ListItemSpacing, Tooltip, WithScrollbar,
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
@@ -25,7 +25,6 @@ pub struct AcpThreadHistory {
|
||||
search_query: SharedString,
|
||||
visible_items: Vec<ListItemType>,
|
||||
local_timezone: UtcOffset,
|
||||
confirming_delete_history: bool,
|
||||
_update_task: Task<()>,
|
||||
_subscriptions: Vec<gpui::Subscription>,
|
||||
}
|
||||
@@ -99,7 +98,6 @@ impl AcpThreadHistory {
|
||||
)
|
||||
.unwrap(),
|
||||
search_query: SharedString::default(),
|
||||
confirming_delete_history: false,
|
||||
_subscriptions: vec![search_editor_subscription, history_store_subscription],
|
||||
_update_task: Task::ready(()),
|
||||
};
|
||||
@@ -333,24 +331,6 @@ impl AcpThreadHistory {
|
||||
task.detach_and_log_err(cx);
|
||||
}
|
||||
|
||||
fn remove_history(&mut self, _window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.history_store.update(cx, |store, cx| {
|
||||
store.delete_threads(cx).detach_and_log_err(cx)
|
||||
});
|
||||
self.confirming_delete_history = false;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn prompt_delete_history(&mut self, _window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.confirming_delete_history = true;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn cancel_delete_history(&mut self, _window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.confirming_delete_history = false;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn render_list_items(
|
||||
&mut self,
|
||||
range: Range<usize>,
|
||||
@@ -446,10 +426,9 @@ impl AcpThreadHistory {
|
||||
.tooltip(move |_window, cx| {
|
||||
Tooltip::for_action("Delete", &RemoveSelectedThread, cx)
|
||||
})
|
||||
.on_click(cx.listener(move |this, _, _, cx| {
|
||||
this.remove_thread(ix, cx);
|
||||
cx.stop_propagation()
|
||||
})),
|
||||
.on_click(
|
||||
cx.listener(move |this, _, _, cx| this.remove_thread(ix, cx)),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
@@ -468,8 +447,6 @@ impl Focusable for AcpThreadHistory {
|
||||
|
||||
impl Render for AcpThreadHistory {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let has_no_history = self.history_store.read(cx).is_empty(cx);
|
||||
|
||||
v_flex()
|
||||
.key_context("ThreadHistory")
|
||||
.size_full()
|
||||
@@ -480,12 +457,9 @@ impl Render for AcpThreadHistory {
|
||||
.on_action(cx.listener(Self::select_last))
|
||||
.on_action(cx.listener(Self::confirm))
|
||||
.on_action(cx.listener(Self::remove_selected_thread))
|
||||
.on_action(cx.listener(|this, _: &RemoveHistory, window, cx| {
|
||||
this.remove_history(window, cx);
|
||||
}))
|
||||
.child(
|
||||
h_flex()
|
||||
.h(Tab::container_height(cx))
|
||||
.h(px(41.)) // Match the toolbar perfectly
|
||||
.w_full()
|
||||
.py_1()
|
||||
.px_2()
|
||||
@@ -507,7 +481,7 @@ impl Render for AcpThreadHistory {
|
||||
.overflow_hidden()
|
||||
.flex_grow();
|
||||
|
||||
if has_no_history {
|
||||
if self.history_store.read(cx).is_empty(cx) {
|
||||
view.justify_center().items_center().child(
|
||||
Label::new("You don't have any past threads yet.")
|
||||
.size(LabelSize::Small)
|
||||
@@ -528,74 +502,16 @@ impl Render for AcpThreadHistory {
|
||||
)
|
||||
.p_1()
|
||||
.pr_4()
|
||||
.track_scroll(&self.scroll_handle)
|
||||
.track_scroll(self.scroll_handle.clone())
|
||||
.flex_grow(),
|
||||
)
|
||||
.vertical_scrollbar_for(&self.scroll_handle, window, cx)
|
||||
.vertical_scrollbar_for(
|
||||
self.scroll_handle.clone(),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
})
|
||||
.when(!has_no_history, |this| {
|
||||
this.child(
|
||||
h_flex()
|
||||
.p_2()
|
||||
.border_t_1()
|
||||
.border_color(cx.theme().colors().border_variant)
|
||||
.when(!self.confirming_delete_history, |this| {
|
||||
this.child(
|
||||
Button::new("delete_history", "Delete All History")
|
||||
.full_width()
|
||||
.style(ButtonStyle::Outlined)
|
||||
.label_size(LabelSize::Small)
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
this.prompt_delete_history(window, cx);
|
||||
})),
|
||||
)
|
||||
})
|
||||
.when(self.confirming_delete_history, |this| {
|
||||
this.w_full()
|
||||
.gap_2()
|
||||
.flex_wrap()
|
||||
.justify_between()
|
||||
.child(
|
||||
h_flex()
|
||||
.flex_wrap()
|
||||
.gap_1()
|
||||
.child(
|
||||
Label::new("Delete all threads?")
|
||||
.size(LabelSize::Small),
|
||||
)
|
||||
.child(
|
||||
Label::new("You won't be able to recover them later.")
|
||||
.size(LabelSize::Small)
|
||||
.color(Color::Muted),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.child(
|
||||
Button::new("cancel_delete", "Cancel")
|
||||
.label_size(LabelSize::Small)
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
this.cancel_delete_history(window, cx);
|
||||
})),
|
||||
)
|
||||
.child(
|
||||
Button::new("confirm_delete", "Delete")
|
||||
.style(ButtonStyle::Tinted(ui::TintColor::Error))
|
||||
.color(Color::Error)
|
||||
.label_size(LabelSize::Small)
|
||||
.on_click(cx.listener(|_, _, window, cx| {
|
||||
window.dispatch_action(
|
||||
Box::new(RemoveHistory),
|
||||
cx,
|
||||
);
|
||||
})),
|
||||
),
|
||||
)
|
||||
}),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -498,17 +498,7 @@ impl AcpThreadView {
|
||||
Some(new_version_available_tx),
|
||||
);
|
||||
|
||||
let agent_name = agent.name();
|
||||
let timeout = cx.background_executor().timer(Duration::from_secs(30));
|
||||
let connect_task = smol::future::or(
|
||||
agent.connect(root_dir.as_deref(), delegate, cx),
|
||||
async move {
|
||||
timeout.await;
|
||||
Err(anyhow::Error::new(LoadError::Other(
|
||||
format!("{agent_name} is unable to initialize after 30 seconds.").into(),
|
||||
)))
|
||||
},
|
||||
);
|
||||
let connect_task = agent.connect(root_dir.as_deref(), delegate, cx);
|
||||
let load_task = cx.spawn_in(window, async move |this, cx| {
|
||||
let connection = match connect_task.await {
|
||||
Ok((connection, login)) => {
|
||||
@@ -675,8 +665,6 @@ impl AcpThreadView {
|
||||
})
|
||||
});
|
||||
|
||||
this.message_editor.focus_handle(cx).focus(window);
|
||||
|
||||
cx.notify();
|
||||
}
|
||||
Err(err) => {
|
||||
@@ -1011,10 +999,6 @@ impl AcpThreadView {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_loading(&self) -> bool {
|
||||
matches!(self.thread_state, ThreadState::Loading { .. })
|
||||
}
|
||||
|
||||
fn resume_chat(&mut self, cx: &mut Context<Self>) {
|
||||
self.thread_error.take();
|
||||
let Some(thread) = self.thread() else {
|
||||
@@ -3818,62 +3802,48 @@ impl AcpThreadView {
|
||||
}))
|
||||
}
|
||||
|
||||
fn render_plan_entries(
|
||||
&self,
|
||||
plan: &Plan,
|
||||
window: &mut Window,
|
||||
cx: &Context<Self>,
|
||||
) -> impl IntoElement {
|
||||
v_flex()
|
||||
.id("plan_items_list")
|
||||
.max_h_40()
|
||||
.overflow_y_scroll()
|
||||
.children(plan.entries.iter().enumerate().flat_map(|(index, entry)| {
|
||||
let element = h_flex()
|
||||
.py_1()
|
||||
.px_2()
|
||||
.gap_2()
|
||||
.justify_between()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.when(index < plan.entries.len() - 1, |parent| {
|
||||
parent.border_color(cx.theme().colors().border).border_b_1()
|
||||
})
|
||||
.child(
|
||||
h_flex()
|
||||
.id(("plan_entry", index))
|
||||
.gap_1p5()
|
||||
.max_w_full()
|
||||
.overflow_x_scroll()
|
||||
.text_xs()
|
||||
.text_color(cx.theme().colors().text_muted)
|
||||
.child(match entry.status {
|
||||
acp::PlanEntryStatus::Pending => Icon::new(IconName::TodoPending)
|
||||
.size(IconSize::Small)
|
||||
.color(Color::Muted)
|
||||
.into_any_element(),
|
||||
acp::PlanEntryStatus::InProgress => {
|
||||
Icon::new(IconName::TodoProgress)
|
||||
.size(IconSize::Small)
|
||||
.color(Color::Accent)
|
||||
.with_rotate_animation(2)
|
||||
.into_any_element()
|
||||
}
|
||||
acp::PlanEntryStatus::Completed => {
|
||||
Icon::new(IconName::TodoComplete)
|
||||
.size(IconSize::Small)
|
||||
.color(Color::Success)
|
||||
.into_any_element()
|
||||
}
|
||||
})
|
||||
.child(MarkdownElement::new(
|
||||
entry.content.clone(),
|
||||
plan_label_markdown_style(&entry.status, window, cx),
|
||||
)),
|
||||
);
|
||||
fn render_plan_entries(&self, plan: &Plan, window: &mut Window, cx: &Context<Self>) -> Div {
|
||||
v_flex().children(plan.entries.iter().enumerate().flat_map(|(index, entry)| {
|
||||
let element = h_flex()
|
||||
.py_1()
|
||||
.px_2()
|
||||
.gap_2()
|
||||
.justify_between()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.when(index < plan.entries.len() - 1, |parent| {
|
||||
parent.border_color(cx.theme().colors().border).border_b_1()
|
||||
})
|
||||
.child(
|
||||
h_flex()
|
||||
.id(("plan_entry", index))
|
||||
.gap_1p5()
|
||||
.max_w_full()
|
||||
.overflow_x_scroll()
|
||||
.text_xs()
|
||||
.text_color(cx.theme().colors().text_muted)
|
||||
.child(match entry.status {
|
||||
acp::PlanEntryStatus::Pending => Icon::new(IconName::TodoPending)
|
||||
.size(IconSize::Small)
|
||||
.color(Color::Muted)
|
||||
.into_any_element(),
|
||||
acp::PlanEntryStatus::InProgress => Icon::new(IconName::TodoProgress)
|
||||
.size(IconSize::Small)
|
||||
.color(Color::Accent)
|
||||
.with_rotate_animation(2)
|
||||
.into_any_element(),
|
||||
acp::PlanEntryStatus::Completed => Icon::new(IconName::TodoComplete)
|
||||
.size(IconSize::Small)
|
||||
.color(Color::Success)
|
||||
.into_any_element(),
|
||||
})
|
||||
.child(MarkdownElement::new(
|
||||
entry.content.clone(),
|
||||
plan_label_markdown_style(&entry.status, window, cx),
|
||||
)),
|
||||
);
|
||||
|
||||
Some(element)
|
||||
}))
|
||||
.into_any_element()
|
||||
Some(element)
|
||||
}))
|
||||
}
|
||||
|
||||
fn render_edits_summary(
|
||||
@@ -4011,136 +3981,126 @@ impl AcpThreadView {
|
||||
changed_buffers: &BTreeMap<Entity<Buffer>, Entity<BufferDiff>>,
|
||||
pending_edits: bool,
|
||||
cx: &Context<Self>,
|
||||
) -> impl IntoElement {
|
||||
) -> Div {
|
||||
let editor_bg_color = cx.theme().colors().editor_background;
|
||||
|
||||
v_flex()
|
||||
.id("edited_files_list")
|
||||
.max_h_40()
|
||||
.overflow_y_scroll()
|
||||
.children(
|
||||
changed_buffers
|
||||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(|(index, (buffer, _diff))| {
|
||||
let file = buffer.read(cx).file()?;
|
||||
let path = file.path();
|
||||
let path_style = file.path_style(cx);
|
||||
let separator = file.path_style(cx).primary_separator();
|
||||
v_flex().children(changed_buffers.iter().enumerate().flat_map(
|
||||
|(index, (buffer, _diff))| {
|
||||
let file = buffer.read(cx).file()?;
|
||||
let path = file.path();
|
||||
let path_style = file.path_style(cx);
|
||||
let separator = file.path_style(cx).separator();
|
||||
|
||||
let file_path = path.parent().and_then(|parent| {
|
||||
if parent.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
Label::new(format!(
|
||||
"{}{separator}",
|
||||
parent.display(path_style)
|
||||
))
|
||||
.color(Color::Muted)
|
||||
.size(LabelSize::XSmall)
|
||||
.buffer_font(cx),
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
let file_name = path.file_name().map(|name| {
|
||||
Label::new(name.to_string())
|
||||
let file_path = path.parent().and_then(|parent| {
|
||||
if parent.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
Label::new(format!("{}{separator}", parent.display(path_style)))
|
||||
.color(Color::Muted)
|
||||
.size(LabelSize::XSmall)
|
||||
.buffer_font(cx)
|
||||
.ml_1p5()
|
||||
});
|
||||
.buffer_font(cx),
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
let file_icon = FileIcons::get_icon(path.as_std_path(), cx)
|
||||
.map(Icon::from_path)
|
||||
.map(|icon| icon.color(Color::Muted).size(IconSize::Small))
|
||||
.unwrap_or_else(|| {
|
||||
Icon::new(IconName::File)
|
||||
.color(Color::Muted)
|
||||
.size(IconSize::Small)
|
||||
});
|
||||
let file_name = path.file_name().map(|name| {
|
||||
Label::new(name.to_string())
|
||||
.size(LabelSize::XSmall)
|
||||
.buffer_font(cx)
|
||||
.ml_1p5()
|
||||
});
|
||||
|
||||
let overlay_gradient = linear_gradient(
|
||||
90.,
|
||||
linear_color_stop(editor_bg_color, 1.),
|
||||
linear_color_stop(editor_bg_color.opacity(0.2), 0.),
|
||||
);
|
||||
let file_icon = FileIcons::get_icon(path.as_std_path(), cx)
|
||||
.map(Icon::from_path)
|
||||
.map(|icon| icon.color(Color::Muted).size(IconSize::Small))
|
||||
.unwrap_or_else(|| {
|
||||
Icon::new(IconName::File)
|
||||
.color(Color::Muted)
|
||||
.size(IconSize::Small)
|
||||
});
|
||||
|
||||
let element = h_flex()
|
||||
.group("edited-code")
|
||||
.id(("file-container", index))
|
||||
.py_1()
|
||||
.pl_2()
|
||||
.pr_1()
|
||||
.gap_2()
|
||||
.justify_between()
|
||||
.bg(editor_bg_color)
|
||||
.when(index < changed_buffers.len() - 1, |parent| {
|
||||
parent.border_color(cx.theme().colors().border).border_b_1()
|
||||
})
|
||||
let overlay_gradient = linear_gradient(
|
||||
90.,
|
||||
linear_color_stop(editor_bg_color, 1.),
|
||||
linear_color_stop(editor_bg_color.opacity(0.2), 0.),
|
||||
);
|
||||
|
||||
let element = h_flex()
|
||||
.group("edited-code")
|
||||
.id(("file-container", index))
|
||||
.py_1()
|
||||
.pl_2()
|
||||
.pr_1()
|
||||
.gap_2()
|
||||
.justify_between()
|
||||
.bg(editor_bg_color)
|
||||
.when(index < changed_buffers.len() - 1, |parent| {
|
||||
parent.border_color(cx.theme().colors().border).border_b_1()
|
||||
})
|
||||
.child(
|
||||
h_flex()
|
||||
.id(("file-name-row", index))
|
||||
.relative()
|
||||
.pr_8()
|
||||
.w_full()
|
||||
.overflow_x_scroll()
|
||||
.child(
|
||||
h_flex()
|
||||
.id(("file-name-row", index))
|
||||
.relative()
|
||||
.pr_8()
|
||||
.w_full()
|
||||
.overflow_x_scroll()
|
||||
.child(
|
||||
h_flex()
|
||||
.id(("file-name-path", index))
|
||||
.cursor_pointer()
|
||||
.pr_0p5()
|
||||
.gap_0p5()
|
||||
.hover(|s| s.bg(cx.theme().colors().element_hover))
|
||||
.rounded_xs()
|
||||
.child(file_icon)
|
||||
.children(file_name)
|
||||
.children(file_path)
|
||||
.tooltip(Tooltip::text("Go to File"))
|
||||
.on_click({
|
||||
let buffer = buffer.clone();
|
||||
cx.listener(move |this, _, window, cx| {
|
||||
this.open_edited_buffer(&buffer, window, cx);
|
||||
})
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.absolute()
|
||||
.h_full()
|
||||
.w_12()
|
||||
.top_0()
|
||||
.bottom_0()
|
||||
.right_0()
|
||||
.bg(overlay_gradient),
|
||||
),
|
||||
.id(("file-name-path", index))
|
||||
.cursor_pointer()
|
||||
.pr_0p5()
|
||||
.gap_0p5()
|
||||
.hover(|s| s.bg(cx.theme().colors().element_hover))
|
||||
.rounded_xs()
|
||||
.child(file_icon)
|
||||
.children(file_name)
|
||||
.children(file_path)
|
||||
.tooltip(Tooltip::text("Go to File"))
|
||||
.on_click({
|
||||
let buffer = buffer.clone();
|
||||
cx.listener(move |this, _, window, cx| {
|
||||
this.open_edited_buffer(&buffer, window, cx);
|
||||
})
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.visible_on_hover("edited-code")
|
||||
.child(
|
||||
Button::new("review", "Review")
|
||||
.label_size(LabelSize::Small)
|
||||
.on_click({
|
||||
let buffer = buffer.clone();
|
||||
cx.listener(move |this, _, window, cx| {
|
||||
this.open_edited_buffer(&buffer, window, cx);
|
||||
})
|
||||
}),
|
||||
)
|
||||
.child(Divider::vertical().color(DividerColor::BorderVariant))
|
||||
.child(
|
||||
Button::new("reject-file", "Reject")
|
||||
.label_size(LabelSize::Small)
|
||||
.disabled(pending_edits)
|
||||
.on_click({
|
||||
let buffer = buffer.clone();
|
||||
let action_log = action_log.clone();
|
||||
let telemetry = telemetry.clone();
|
||||
move |_, _, cx| {
|
||||
action_log.update(cx, |action_log, cx| {
|
||||
action_log
|
||||
div()
|
||||
.absolute()
|
||||
.h_full()
|
||||
.w_12()
|
||||
.top_0()
|
||||
.bottom_0()
|
||||
.right_0()
|
||||
.bg(overlay_gradient),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.visible_on_hover("edited-code")
|
||||
.child(
|
||||
Button::new("review", "Review")
|
||||
.label_size(LabelSize::Small)
|
||||
.on_click({
|
||||
let buffer = buffer.clone();
|
||||
cx.listener(move |this, _, window, cx| {
|
||||
this.open_edited_buffer(&buffer, window, cx);
|
||||
})
|
||||
}),
|
||||
)
|
||||
.child(Divider::vertical().color(DividerColor::BorderVariant))
|
||||
.child(
|
||||
Button::new("reject-file", "Reject")
|
||||
.label_size(LabelSize::Small)
|
||||
.disabled(pending_edits)
|
||||
.on_click({
|
||||
let buffer = buffer.clone();
|
||||
let action_log = action_log.clone();
|
||||
let telemetry = telemetry.clone();
|
||||
move |_, _, cx| {
|
||||
action_log.update(cx, |action_log, cx| {
|
||||
action_log
|
||||
.reject_edits_in_ranges(
|
||||
buffer.clone(),
|
||||
vec![Anchor::min_max_range_for_buffer(
|
||||
@@ -4150,38 +4110,37 @@ impl AcpThreadView {
|
||||
cx,
|
||||
)
|
||||
.detach_and_log_err(cx);
|
||||
})
|
||||
}
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
Button::new("keep-file", "Keep")
|
||||
.label_size(LabelSize::Small)
|
||||
.disabled(pending_edits)
|
||||
.on_click({
|
||||
let buffer = buffer.clone();
|
||||
let action_log = action_log.clone();
|
||||
let telemetry = telemetry.clone();
|
||||
move |_, _, cx| {
|
||||
action_log.update(cx, |action_log, cx| {
|
||||
action_log.keep_edits_in_range(
|
||||
buffer.clone(),
|
||||
Anchor::min_max_range_for_buffer(
|
||||
buffer.read(cx).remote_id(),
|
||||
),
|
||||
Some(telemetry.clone()),
|
||||
cx,
|
||||
);
|
||||
})
|
||||
}
|
||||
}),
|
||||
),
|
||||
);
|
||||
})
|
||||
}
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
Button::new("keep-file", "Keep")
|
||||
.label_size(LabelSize::Small)
|
||||
.disabled(pending_edits)
|
||||
.on_click({
|
||||
let buffer = buffer.clone();
|
||||
let action_log = action_log.clone();
|
||||
let telemetry = telemetry.clone();
|
||||
move |_, _, cx| {
|
||||
action_log.update(cx, |action_log, cx| {
|
||||
action_log.keep_edits_in_range(
|
||||
buffer.clone(),
|
||||
Anchor::min_max_range_for_buffer(
|
||||
buffer.read(cx).remote_id(),
|
||||
),
|
||||
Some(telemetry.clone()),
|
||||
cx,
|
||||
);
|
||||
})
|
||||
}
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
Some(element)
|
||||
}),
|
||||
)
|
||||
.into_any_element()
|
||||
Some(element)
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn render_message_editor(&mut self, window: &mut Window, cx: &mut Context<Self>) -> AnyElement {
|
||||
@@ -4202,10 +4161,8 @@ impl AcpThreadView {
|
||||
.block_mouse_except_scroll();
|
||||
|
||||
let enable_editor = match self.thread_state {
|
||||
ThreadState::Ready { .. } => true,
|
||||
ThreadState::Loading { .. }
|
||||
| ThreadState::Unauthenticated { .. }
|
||||
| ThreadState::LoadError(..) => false,
|
||||
ThreadState::Loading { .. } | ThreadState::Ready { .. } => true,
|
||||
ThreadState::Unauthenticated { .. } | ThreadState::LoadError(..) => false,
|
||||
};
|
||||
|
||||
v_flex()
|
||||
@@ -5866,10 +5823,12 @@ fn placeholder_text(agent_name: &str, has_commands: bool) -> String {
|
||||
impl Focusable for AcpThreadView {
|
||||
fn focus_handle(&self, cx: &App) -> FocusHandle {
|
||||
match self.thread_state {
|
||||
ThreadState::Ready { .. } => self.active_editor(cx).focus_handle(cx),
|
||||
ThreadState::Loading { .. }
|
||||
| ThreadState::LoadError(_)
|
||||
| ThreadState::Unauthenticated { .. } => self.focus_handle.clone(),
|
||||
ThreadState::Loading { .. } | ThreadState::Ready { .. } => {
|
||||
self.active_editor(cx).focus_handle(cx)
|
||||
}
|
||||
ThreadState::LoadError(_) | ThreadState::Unauthenticated { .. } => {
|
||||
self.focus_handle.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5937,7 +5896,7 @@ impl Render for AcpThreadView {
|
||||
.flex_grow()
|
||||
.into_any(),
|
||||
)
|
||||
.vertical_scrollbar_for(&self.list_state, window, cx)
|
||||
.vertical_scrollbar_for(self.list_state.clone(), window, cx)
|
||||
.into_any()
|
||||
} else {
|
||||
this.child(self.render_recent_history(cx)).into_any()
|
||||
@@ -7399,54 +7358,4 @@ pub(crate) mod tests {
|
||||
assert_eq!(text, expected_txt);
|
||||
})
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_initialize_timeout(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
struct InfiniteInitialize;
|
||||
|
||||
impl AgentServer for InfiniteInitialize {
|
||||
fn telemetry_id(&self) -> &'static str {
|
||||
"test"
|
||||
}
|
||||
|
||||
fn logo(&self) -> ui::IconName {
|
||||
ui::IconName::Ai
|
||||
}
|
||||
|
||||
fn name(&self) -> SharedString {
|
||||
"Test".into()
|
||||
}
|
||||
|
||||
fn connect(
|
||||
&self,
|
||||
_root_dir: Option<&Path>,
|
||||
_delegate: AgentServerDelegate,
|
||||
cx: &mut App,
|
||||
) -> Task<gpui::Result<(Rc<dyn AgentConnection>, Option<task::SpawnInTerminal>)>>
|
||||
{
|
||||
cx.spawn(async |_| futures::future::pending().await)
|
||||
}
|
||||
|
||||
fn into_any(self: Rc<Self>) -> Rc<dyn Any> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
let (thread_view, cx) = setup_thread_view(InfiniteInitialize, cx).await;
|
||||
|
||||
cx.executor().advance_clock(Duration::from_secs(31));
|
||||
cx.run_until_parked();
|
||||
|
||||
let error = thread_view.read_with(cx, |thread_view, _| match &thread_view.thread_state {
|
||||
ThreadState::LoadError(err) => err.clone(),
|
||||
_ => panic!("Incorrect thread state"),
|
||||
});
|
||||
|
||||
match error {
|
||||
LoadError::Other(str) => assert!(str.contains("initialize")),
|
||||
_ => panic!("Unexpected load error"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1209,7 +1209,7 @@ impl Render for AgentConfiguration {
|
||||
.child(self.render_context_servers_section(window, cx))
|
||||
.child(self.render_provider_configuration_section(cx)),
|
||||
)
|
||||
.vertical_scrollbar_for(&self.scroll_handle, window, cx),
|
||||
.vertical_scrollbar_for(self.scroll_handle.clone(), window, cx),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,7 +516,7 @@ impl Render for AddLlmProviderModal {
|
||||
.child(
|
||||
div()
|
||||
.size_full()
|
||||
.vertical_scrollbar_for(&self.scroll_handle, window, cx)
|
||||
.vertical_scrollbar_for(self.scroll_handle.clone(), window, cx)
|
||||
.child(
|
||||
v_flex()
|
||||
.id("modal_content")
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{
|
||||
path::PathBuf,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use anyhow::{Context as _, Result};
|
||||
use collections::HashMap;
|
||||
@@ -221,12 +224,11 @@ fn context_server_input(existing: Option<(ContextServerId, ContextServerCommand)
|
||||
Some((id, cmd)) => {
|
||||
let args = serde_json::to_string(&cmd.args).unwrap();
|
||||
let env = serde_json::to_string(&cmd.env.unwrap_or_default()).unwrap();
|
||||
let cmd_path = serde_json::to_string(&cmd.path).unwrap();
|
||||
(id.0.to_string(), cmd_path, args, env)
|
||||
(id.0.to_string(), cmd.path, args, env)
|
||||
}
|
||||
None => (
|
||||
"some-mcp-server".to_string(),
|
||||
"".to_string(),
|
||||
PathBuf::new(),
|
||||
"[]".to_string(),
|
||||
"{}".to_string(),
|
||||
),
|
||||
@@ -237,13 +239,14 @@ fn context_server_input(existing: Option<(ContextServerId, ContextServerCommand)
|
||||
/// The name of your MCP server
|
||||
"{name}": {{
|
||||
/// The command which runs the MCP server
|
||||
"command": {command},
|
||||
"command": "{}",
|
||||
/// The arguments to pass to the MCP server
|
||||
"args": {args},
|
||||
/// The environment variables to set
|
||||
"env": {env}
|
||||
}}
|
||||
}}"#
|
||||
}}"#,
|
||||
command.display()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -818,6 +821,7 @@ impl ConfigureContextServerModal {
|
||||
|
||||
impl Render for ConfigureContextServerModal {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let scroll_handle = self.scroll_handle.clone();
|
||||
div()
|
||||
.elevation_3(cx)
|
||||
.w(rems(34.))
|
||||
@@ -845,7 +849,7 @@ impl Render for ConfigureContextServerModal {
|
||||
.id("modal-content")
|
||||
.max_h(vh(0.7, window))
|
||||
.overflow_y_scroll()
|
||||
.track_scroll(&self.scroll_handle)
|
||||
.track_scroll(&scroll_handle)
|
||||
.child(self.render_modal_description(window, cx))
|
||||
.child(self.render_modal_content(cx))
|
||||
.child(match &self.state {
|
||||
@@ -858,7 +862,7 @@ impl Render for ConfigureContextServerModal {
|
||||
}
|
||||
}),
|
||||
)
|
||||
.vertical_scrollbar_for(&self.scroll_handle, window, cx),
|
||||
.vertical_scrollbar_for(scroll_handle, window, cx),
|
||||
),
|
||||
)
|
||||
.footer(self.render_modal_footer(cx)),
|
||||
|
||||
@@ -138,7 +138,7 @@ impl ConfigureContextServerToolsModal {
|
||||
items
|
||||
})),
|
||||
)
|
||||
.vertical_scrollbar_for(&self.scroll_handle, window, cx)
|
||||
.vertical_scrollbar_for(self.scroll_handle.clone(), window, cx)
|
||||
.into_any_element()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -493,7 +493,7 @@ impl Item for AgentDiffPane {
|
||||
Some("Assistant Diff Opened")
|
||||
}
|
||||
|
||||
fn as_searchable(&self, _: &Entity<Self>, _: &App) -> Option<Box<dyn SearchableItemHandle>> {
|
||||
fn as_searchable(&self, _: &Entity<Self>) -> Option<Box<dyn SearchableItemHandle>> {
|
||||
Some(Box::new(self.editor.clone()))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use std::{ops::Range, path::Path, rc::Rc, sync::Arc, time::Duration};
|
||||
use std::ops::Range;
|
||||
use std::path::Path;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use acp_thread::AcpThread;
|
||||
use agent::{ContextServerRegistry, DbThreadMetadata, HistoryEntry, HistoryStore};
|
||||
@@ -17,9 +20,10 @@ use zed_actions::agent::{OpenClaudeCodeOnboardingModal, ReauthenticateAgent};
|
||||
use crate::ManageProfiles;
|
||||
use crate::ui::{AcpOnboardingModal, ClaudeCodeOnboardingModal};
|
||||
use crate::{
|
||||
AddContextServer, AgentDiffPane, Follow, InlineAssistant, NewTextThread, NewThread,
|
||||
OpenActiveThreadAsMarkdown, OpenAgentDiff, OpenHistory, ResetTrialEndUpsell, ResetTrialUpsell,
|
||||
ToggleNavigationMenu, ToggleNewThreadMenu, ToggleOptionsMenu,
|
||||
AddContextServer, AgentDiffPane, DeleteRecentlyOpenThread, Follow, InlineAssistant,
|
||||
NewTextThread, NewThread, OpenActiveThreadAsMarkdown, OpenAgentDiff, OpenHistory,
|
||||
ResetTrialEndUpsell, ResetTrialUpsell, ToggleNavigationMenu, ToggleNewThreadMenu,
|
||||
ToggleOptionsMenu,
|
||||
acp::AcpThreadView,
|
||||
agent_configuration::{AgentConfiguration, AssistantConfigurationEvent},
|
||||
slash_command::SlashCommandCompletionProvider,
|
||||
@@ -43,9 +47,9 @@ use extension::ExtensionEvents;
|
||||
use extension_host::ExtensionStore;
|
||||
use fs::Fs;
|
||||
use gpui::{
|
||||
Action, Animation, AnimationExt, AnyElement, App, AsyncWindowContext, Corner, DismissEvent,
|
||||
Entity, EventEmitter, ExternalPaths, FocusHandle, Focusable, KeyContext, Pixels, Subscription,
|
||||
Task, UpdateGlobal, WeakEntity, prelude::*, pulsating_between,
|
||||
Action, AnyElement, App, AsyncWindowContext, Corner, DismissEvent, Entity, EventEmitter,
|
||||
ExternalPaths, FocusHandle, Focusable, KeyContext, Pixels, Subscription, Task, UpdateGlobal,
|
||||
WeakEntity, prelude::*,
|
||||
};
|
||||
use language::LanguageRegistry;
|
||||
use language_model::{ConfigurationError, LanguageModelRegistry};
|
||||
@@ -55,9 +59,10 @@ use rules_library::{RulesLibrary, open_rules_library};
|
||||
use search::{BufferSearchBar, buffer_search};
|
||||
use settings::{Settings, update_settings_file};
|
||||
use theme::ThemeSettings;
|
||||
use ui::utils::WithRemSize;
|
||||
use ui::{
|
||||
Callout, ContextMenu, ContextMenuEntry, KeyBinding, PopoverMenu, PopoverMenuHandle,
|
||||
ProgressBar, Tab, Tooltip, prelude::*, utils::WithRemSize,
|
||||
ProgressBar, Tab, Tooltip, prelude::*,
|
||||
};
|
||||
use util::ResultExt as _;
|
||||
use workspace::{
|
||||
@@ -609,14 +614,11 @@ impl AgentPanel {
|
||||
if let Some(panel) = panel.upgrade() {
|
||||
menu = Self::populate_recently_opened_menu_section(menu, panel, cx);
|
||||
}
|
||||
|
||||
menu = menu
|
||||
.action("View All", Box::new(OpenHistory))
|
||||
menu.action("View All", Box::new(OpenHistory))
|
||||
.end_slot_action(DeleteRecentlyOpenThread.boxed_clone())
|
||||
.fixed_width(px(320.).into())
|
||||
.keep_open_on_confirm(false)
|
||||
.key_context("NavigationMenu");
|
||||
|
||||
menu
|
||||
.key_context("NavigationMenu")
|
||||
});
|
||||
weak_panel
|
||||
.update(cx, |panel, cx| {
|
||||
@@ -2155,41 +2157,28 @@ impl AgentPanel {
|
||||
|
||||
let selected_agent_label = self.selected_agent.label();
|
||||
|
||||
let is_thread_loading = self
|
||||
.active_thread_view()
|
||||
.map(|thread| thread.read(cx).is_loading())
|
||||
.unwrap_or(false);
|
||||
|
||||
let has_custom_icon = selected_agent_custom_icon.is_some();
|
||||
|
||||
let selected_agent = div()
|
||||
.id("selected_agent_icon")
|
||||
.when_some(selected_agent_custom_icon, |this, icon_path| {
|
||||
let label = selected_agent_label.clone();
|
||||
this.px_1()
|
||||
.child(Icon::from_external_svg(icon_path).color(Color::Muted))
|
||||
.tooltip(move |_window, cx| {
|
||||
Tooltip::with_meta(label.clone(), None, "Selected Agent", cx)
|
||||
})
|
||||
})
|
||||
.when(!has_custom_icon, |this| {
|
||||
this.when_some(self.selected_agent.icon(), |this, icon| {
|
||||
this.px_1().child(Icon::new(icon).color(Color::Muted))
|
||||
let label = selected_agent_label.clone();
|
||||
this.px_1()
|
||||
.child(Icon::new(icon).color(Color::Muted))
|
||||
.tooltip(move |_window, cx| {
|
||||
Tooltip::with_meta(label.clone(), None, "Selected Agent", cx)
|
||||
})
|
||||
})
|
||||
})
|
||||
.tooltip(move |_, cx| {
|
||||
Tooltip::with_meta(selected_agent_label.clone(), None, "Selected Agent", cx)
|
||||
});
|
||||
|
||||
let selected_agent = if is_thread_loading {
|
||||
selected_agent
|
||||
.with_animation(
|
||||
"pulsating-icon",
|
||||
Animation::new(Duration::from_secs(1))
|
||||
.repeat()
|
||||
.with_easing(pulsating_between(0.2, 0.6)),
|
||||
|icon, delta| icon.opacity(delta),
|
||||
)
|
||||
.into_any_element()
|
||||
} else {
|
||||
selected_agent.into_any_element()
|
||||
};
|
||||
.into_any_element();
|
||||
|
||||
h_flex()
|
||||
.id("agent-panel-toolbar")
|
||||
|
||||
@@ -69,8 +69,6 @@ actions!(
|
||||
CycleModeSelector,
|
||||
/// Expands the message editor to full size.
|
||||
ExpandMessageEditor,
|
||||
/// Removes all thread history.
|
||||
RemoveHistory,
|
||||
/// Opens the conversation history view.
|
||||
OpenHistory,
|
||||
/// Adds a context server to the configuration.
|
||||
|
||||
@@ -271,7 +271,7 @@ impl CodegenAlternative {
|
||||
let mut buffer = Buffer::local_normalized(text, line_ending, cx);
|
||||
buffer.set_language(language, cx);
|
||||
if let Some(language_registry) = language_registry {
|
||||
buffer.set_language_registry(language_registry);
|
||||
buffer.set_language_registry(language_registry)
|
||||
}
|
||||
buffer
|
||||
});
|
||||
|
||||
@@ -7,9 +7,7 @@ use std::sync::atomic::AtomicBool;
|
||||
use acp_thread::MentionUri;
|
||||
use agent::{HistoryEntry, HistoryStore};
|
||||
use anyhow::Result;
|
||||
use editor::{
|
||||
CompletionProvider, Editor, ExcerptId, code_context_menus::COMPLETION_MENU_MAX_WIDTH,
|
||||
};
|
||||
use editor::{CompletionProvider, Editor, ExcerptId};
|
||||
use fuzzy::{PathMatch, StringMatch, StringMatchCandidate};
|
||||
use gpui::{App, Entity, Task, WeakEntity};
|
||||
use language::{Buffer, CodeLabel, CodeLabelBuilder, HighlightId};
|
||||
@@ -27,7 +25,6 @@ use ui::prelude::*;
|
||||
use util::ResultExt as _;
|
||||
use util::paths::PathStyle;
|
||||
use util::rel_path::RelPath;
|
||||
use util::truncate_and_remove_front;
|
||||
use workspace::Workspace;
|
||||
|
||||
use crate::AgentPanel;
|
||||
@@ -339,20 +336,14 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
|
||||
mention_set: WeakEntity<MentionSet>,
|
||||
workspace: Entity<Workspace>,
|
||||
project: Entity<Project>,
|
||||
label_max_chars: usize,
|
||||
cx: &mut App,
|
||||
) -> Option<Completion> {
|
||||
let path_style = project.read(cx).path_style(cx);
|
||||
let (file_name, directory) =
|
||||
extract_file_name_and_directory(&project_path.path, path_prefix, path_style);
|
||||
|
||||
let label = build_code_label_for_path(
|
||||
&file_name,
|
||||
directory.as_ref().map(|s| s.as_ref()),
|
||||
None,
|
||||
label_max_chars,
|
||||
cx,
|
||||
);
|
||||
let label =
|
||||
build_code_label_for_path(&file_name, directory.as_ref().map(|s| s.as_ref()), None, cx);
|
||||
|
||||
let abs_path = project.read(cx).absolute_path(&project_path, cx)?;
|
||||
|
||||
@@ -401,7 +392,6 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
|
||||
editor: WeakEntity<Editor>,
|
||||
mention_set: WeakEntity<MentionSet>,
|
||||
workspace: Entity<Workspace>,
|
||||
label_max_chars: usize,
|
||||
cx: &mut App,
|
||||
) -> Option<Completion> {
|
||||
let project = workspace.read(cx).project().clone();
|
||||
@@ -424,7 +414,6 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
|
||||
&symbol.name,
|
||||
Some(&file_name),
|
||||
Some(symbol.range.start.0.row + 1),
|
||||
label_max_chars,
|
||||
cx,
|
||||
);
|
||||
|
||||
@@ -863,7 +852,7 @@ impl<T: PromptCompletionProviderDelegate> CompletionProvider for PromptCompletio
|
||||
buffer: &Entity<Buffer>,
|
||||
buffer_position: Anchor,
|
||||
_trigger: CompletionContext,
|
||||
window: &mut Window,
|
||||
_window: &mut Window,
|
||||
cx: &mut Context<Editor>,
|
||||
) -> Task<Result<Vec<CompletionResponse>>> {
|
||||
let state = buffer.update(cx, |buffer, cx| {
|
||||
@@ -872,7 +861,7 @@ impl<T: PromptCompletionProviderDelegate> CompletionProvider for PromptCompletio
|
||||
let offset_to_line = buffer.point_to_offset(line_start);
|
||||
let mut lines = buffer.text_for_range(line_start..position).lines();
|
||||
let line = lines.next()?;
|
||||
PromptCompletion::try_parse(line, offset_to_line, &self.source.supported_modes(cx))
|
||||
ContextCompletion::try_parse(line, offset_to_line, &self.source.supported_modes(cx))
|
||||
});
|
||||
let Some(state) = state else {
|
||||
return Task::ready(Ok(Vec::new()));
|
||||
@@ -891,7 +880,7 @@ impl<T: PromptCompletionProviderDelegate> CompletionProvider for PromptCompletio
|
||||
let editor = self.editor.clone();
|
||||
let mention_set = self.mention_set.downgrade();
|
||||
match state {
|
||||
PromptCompletion::SlashCommand(SlashCommandCompletion {
|
||||
ContextCompletion::SlashCommand(SlashCommandCompletion {
|
||||
command, argument, ..
|
||||
}) => {
|
||||
let search_task = self.search_slash_commands(command.unwrap_or_default(), cx);
|
||||
@@ -954,36 +943,11 @@ impl<T: PromptCompletionProviderDelegate> CompletionProvider for PromptCompletio
|
||||
}])
|
||||
})
|
||||
}
|
||||
PromptCompletion::Mention(MentionCompletion { mode, argument, .. }) => {
|
||||
ContextCompletion::Mention(MentionCompletion { mode, argument, .. }) => {
|
||||
let query = argument.unwrap_or_default();
|
||||
let search_task =
|
||||
self.search_mentions(mode, query, Arc::<AtomicBool>::default(), cx);
|
||||
|
||||
// Calculate maximum characters available for the full label (file_name + space + directory)
|
||||
// based on maximum menu width after accounting for padding, spacing, and icon width
|
||||
let label_max_chars = {
|
||||
// Base06 left padding + Base06 gap + Base06 right padding + icon width
|
||||
let used_pixels = DynamicSpacing::Base06.px(cx) * 3.0
|
||||
+ IconSize::XSmall.rems() * window.rem_size();
|
||||
|
||||
let style = window.text_style();
|
||||
let font_id = window.text_system().resolve_font(&style.font());
|
||||
let font_size = TextSize::Small.rems(cx).to_pixels(window.rem_size());
|
||||
|
||||
// Fallback em_width of 10px matches file_finder.rs fallback for TextSize::Small
|
||||
let em_width = cx
|
||||
.text_system()
|
||||
.em_width(font_id, font_size)
|
||||
.unwrap_or(px(10.0));
|
||||
|
||||
// Calculate available pixels for text (file_name + directory)
|
||||
// Using max width since dynamic_width allows the menu to expand up to this
|
||||
let available_pixels = COMPLETION_MENU_MAX_WIDTH - used_pixels;
|
||||
|
||||
// Convert to character count (total available for file_name + directory)
|
||||
(f32::from(available_pixels) / f32::from(em_width)) as usize
|
||||
};
|
||||
|
||||
cx.spawn(async move |_, cx| {
|
||||
let matches = search_task.await;
|
||||
|
||||
@@ -1020,7 +984,6 @@ impl<T: PromptCompletionProviderDelegate> CompletionProvider for PromptCompletio
|
||||
mention_set.clone(),
|
||||
workspace.clone(),
|
||||
project.clone(),
|
||||
label_max_chars,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
@@ -1033,7 +996,6 @@ impl<T: PromptCompletionProviderDelegate> CompletionProvider for PromptCompletio
|
||||
editor.clone(),
|
||||
mention_set.clone(),
|
||||
workspace.clone(),
|
||||
label_max_chars,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
@@ -1123,12 +1085,12 @@ impl<T: PromptCompletionProviderDelegate> CompletionProvider for PromptCompletio
|
||||
let offset_to_line = buffer.point_to_offset(line_start);
|
||||
let mut lines = buffer.text_for_range(line_start..position).lines();
|
||||
if let Some(line) = lines.next() {
|
||||
PromptCompletion::try_parse(line, offset_to_line, &self.source.supported_modes(cx))
|
||||
ContextCompletion::try_parse(line, offset_to_line, &self.source.supported_modes(cx))
|
||||
.filter(|completion| {
|
||||
// Right now we don't support completing arguments of slash commands
|
||||
let is_slash_command_with_argument = matches!(
|
||||
completion,
|
||||
PromptCompletion::SlashCommand(SlashCommandCompletion {
|
||||
ContextCompletion::SlashCommand(SlashCommandCompletion {
|
||||
argument: Some(_),
|
||||
..
|
||||
})
|
||||
@@ -1198,13 +1160,12 @@ fn confirm_completion_callback<T: PromptCompletionProviderDelegate>(
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum PromptCompletion {
|
||||
enum ContextCompletion {
|
||||
SlashCommand(SlashCommandCompletion),
|
||||
Mention(MentionCompletion),
|
||||
}
|
||||
|
||||
impl PromptCompletion {
|
||||
impl ContextCompletion {
|
||||
fn source_range(&self) -> Range<usize> {
|
||||
match self {
|
||||
Self::SlashCommand(completion) => completion.source_range.clone(),
|
||||
@@ -1217,14 +1178,15 @@ impl PromptCompletion {
|
||||
offset_to_line: usize,
|
||||
supported_modes: &[PromptContextType],
|
||||
) -> Option<Self> {
|
||||
if line.contains('@') {
|
||||
if let Some(mention) =
|
||||
MentionCompletion::try_parse(line, offset_to_line, supported_modes)
|
||||
{
|
||||
return Some(Self::Mention(mention));
|
||||
}
|
||||
if let Some(command) = SlashCommandCompletion::try_parse(line, offset_to_line) {
|
||||
Some(Self::SlashCommand(command))
|
||||
} else if let Some(mention) =
|
||||
MentionCompletion::try_parse(line, offset_to_line, supported_modes)
|
||||
{
|
||||
Some(Self::Mention(mention))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
SlashCommandCompletion::try_parse(line, offset_to_line).map(Self::SlashCommand)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1633,7 +1595,6 @@ fn build_code_label_for_path(
|
||||
file: &str,
|
||||
directory: Option<&str>,
|
||||
line_number: Option<u32>,
|
||||
label_max_chars: usize,
|
||||
cx: &App,
|
||||
) -> CodeLabel {
|
||||
let variable_highlight_id = cx
|
||||
@@ -1647,13 +1608,7 @@ fn build_code_label_for_path(
|
||||
label.push_str(" ", None);
|
||||
|
||||
if let Some(directory) = directory {
|
||||
let file_name_chars = file.chars().count();
|
||||
// Account for: file_name + space (ellipsis is handled by truncate_and_remove_front)
|
||||
let directory_max_chars = label_max_chars
|
||||
.saturating_sub(file_name_chars)
|
||||
.saturating_sub(1);
|
||||
let truncated_directory = truncate_and_remove_front(directory, directory_max_chars.max(5));
|
||||
label.push_str(&truncated_directory, variable_highlight_id);
|
||||
label.push_str(directory, variable_highlight_id);
|
||||
}
|
||||
if let Some(line_number) = line_number {
|
||||
label.push_str(&format!(" L{}", line_number), variable_highlight_id);
|
||||
@@ -1698,38 +1653,6 @@ fn selection_ranges(
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_prompt_completion_parse() {
|
||||
let supported_modes = vec![PromptContextType::File, PromptContextType::Symbol];
|
||||
|
||||
assert_eq!(
|
||||
PromptCompletion::try_parse("/", 0, &supported_modes),
|
||||
Some(PromptCompletion::SlashCommand(SlashCommandCompletion {
|
||||
source_range: 0..1,
|
||||
command: None,
|
||||
argument: None,
|
||||
}))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
PromptCompletion::try_parse("@", 0, &supported_modes),
|
||||
Some(PromptCompletion::Mention(MentionCompletion {
|
||||
source_range: 0..1,
|
||||
mode: None,
|
||||
argument: None,
|
||||
}))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
PromptCompletion::try_parse("/test @file", 0, &supported_modes),
|
||||
Some(PromptCompletion::Mention(MentionCompletion {
|
||||
source_range: 6..11,
|
||||
mode: Some(PromptContextType::File),
|
||||
argument: None,
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_slash_command_completion_parse() {
|
||||
assert_eq!(
|
||||
|
||||
@@ -1456,7 +1456,6 @@ impl InlineAssistant {
|
||||
editor.set_soft_wrap_mode(language::language_settings::SoftWrap::None, cx);
|
||||
editor.set_show_wrap_guides(false, cx);
|
||||
editor.set_show_gutter(false, cx);
|
||||
editor.set_offset_content(false, cx);
|
||||
editor.scroll_manager.set_forbid_vertical_scroll(true);
|
||||
editor.set_read_only(true);
|
||||
editor.set_show_edit_predictions(Some(false), window, cx);
|
||||
|
||||
@@ -2556,11 +2556,7 @@ impl Item for TextThreadEditor {
|
||||
Some(self.title(cx).to_string().into())
|
||||
}
|
||||
|
||||
fn as_searchable(
|
||||
&self,
|
||||
handle: &Entity<Self>,
|
||||
_: &App,
|
||||
) -> Option<Box<dyn SearchableItemHandle>> {
|
||||
fn as_searchable(&self, handle: &Entity<Self>) -> Option<Box<dyn SearchableItemHandle>> {
|
||||
Some(Box::new(handle.clone()))
|
||||
}
|
||||
|
||||
|
||||
@@ -249,15 +249,10 @@ impl PasswordProxy {
|
||||
fs::write(&askpass_script_path, askpass_script)
|
||||
.await
|
||||
.with_context(|| format!("creating askpass script at {askpass_script_path:?}"))?;
|
||||
make_file_executable(&askpass_script_path)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!("marking askpass script executable at {askpass_script_path:?}")
|
||||
})?;
|
||||
// todo(shell): There might be no powershell on the system
|
||||
make_file_executable(&askpass_script_path).await?;
|
||||
#[cfg(target_os = "windows")]
|
||||
let askpass_helper = format!(
|
||||
"powershell.exe -ExecutionPolicy Bypass -File \"{}\"",
|
||||
"powershell.exe -ExecutionPolicy Bypass -File {}",
|
||||
askpass_script_path.display()
|
||||
);
|
||||
|
||||
|
||||
@@ -233,11 +233,18 @@ fn collect_diagnostics(
|
||||
options: Options,
|
||||
cx: &mut App,
|
||||
) -> Task<Result<Option<SlashCommandOutput>>> {
|
||||
let error_source = if let Some(path_matcher) = &options.path_matcher {
|
||||
debug_assert_eq!(path_matcher.sources().len(), 1);
|
||||
Some(path_matcher.sources().first().cloned().unwrap_or_default())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let path_style = project.read(cx).path_style(cx);
|
||||
let glob_is_exact_file_match = if let Some(path) = options
|
||||
.path_matcher
|
||||
.as_ref()
|
||||
.and_then(|pm| pm.sources().next())
|
||||
.and_then(|pm| pm.sources().first())
|
||||
{
|
||||
project
|
||||
.read(cx)
|
||||
@@ -259,13 +266,6 @@ fn collect_diagnostics(
|
||||
.collect();
|
||||
|
||||
cx.spawn(async move |cx| {
|
||||
let error_source = if let Some(path_matcher) = &options.path_matcher {
|
||||
debug_assert_eq!(path_matcher.sources().count(), 1);
|
||||
Some(path_matcher.sources().next().unwrap_or_default())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut output = SlashCommandOutput::default();
|
||||
|
||||
if let Some(error_source) = error_source.as_ref() {
|
||||
@@ -277,7 +277,7 @@ fn collect_diagnostics(
|
||||
let mut project_summary = DiagnosticSummary::default();
|
||||
for (project_path, path, summary) in diagnostic_summaries {
|
||||
if let Some(path_matcher) = &options.path_matcher
|
||||
&& !path_matcher.is_match(&path)
|
||||
&& !path_matcher.is_match(&path.as_std_path())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -2933,7 +2933,6 @@ impl TextThread {
|
||||
RenameOptions {
|
||||
overwrite: true,
|
||||
ignore_if_exists: true,
|
||||
create_parents: false,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -510,9 +510,7 @@ impl AutoUpdater {
|
||||
(None, None, None)
|
||||
};
|
||||
|
||||
let version = if let Some(mut version) = version {
|
||||
version.pre = semver::Prerelease::EMPTY;
|
||||
version.build = semver::BuildMetadata::EMPTY;
|
||||
let version = if let Some(version) = version {
|
||||
version.to_string()
|
||||
} else {
|
||||
"latest".to_string()
|
||||
@@ -639,11 +637,10 @@ impl AutoUpdater {
|
||||
if let AutoUpdateStatus::Updated { version, .. } = status {
|
||||
match version {
|
||||
VersionCheckType::Sha(cached_version) => {
|
||||
let should_download =
|
||||
parsed_fetched_version.as_ref().ok().is_none_or(|version| {
|
||||
version.build.as_str().rsplit('.').next()
|
||||
!= Some(&cached_version.full())
|
||||
});
|
||||
let should_download = parsed_fetched_version
|
||||
.as_ref()
|
||||
.ok()
|
||||
.is_none_or(|version| version.build.as_str() != cached_version.full());
|
||||
let newer_version = should_download
|
||||
.then(|| VersionCheckType::Sha(AppCommitSha::new(fetched_version)));
|
||||
return Ok(newer_version);
|
||||
@@ -663,9 +660,10 @@ impl AutoUpdater {
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|sha| {
|
||||
parsed_fetched_version.as_ref().ok().is_none_or(|version| {
|
||||
version.build.as_str().rsplit('.').next() != Some(&sha)
|
||||
})
|
||||
parsed_fetched_version
|
||||
.as_ref()
|
||||
.ok()
|
||||
.is_none_or(|version| version.build.as_str() != sha)
|
||||
})
|
||||
.unwrap_or(true);
|
||||
let newer_version = should_download
|
||||
@@ -719,12 +717,9 @@ impl AutoUpdater {
|
||||
}
|
||||
|
||||
fn check_if_fetched_version_is_newer_non_nightly(
|
||||
mut installed_version: Version,
|
||||
installed_version: Version,
|
||||
fetched_version: Version,
|
||||
) -> Result<Option<VersionCheckType>> {
|
||||
// For non-nightly releases, ignore build and pre-release fields as they're not provided by our endpoints right now.
|
||||
installed_version.build = semver::BuildMetadata::EMPTY;
|
||||
installed_version.pre = semver::Prerelease::EMPTY;
|
||||
let should_download = fetched_version > installed_version;
|
||||
let newer_version = should_download.then(|| VersionCheckType::Semantic(fetched_version));
|
||||
Ok(newer_version)
|
||||
|
||||
@@ -20,7 +20,6 @@ gpui.workspace = true
|
||||
http_client.workspace = true
|
||||
markdown_preview.workspace = true
|
||||
release_channel.workspace = true
|
||||
semver.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
smol.workspace = true
|
||||
|
||||
@@ -148,9 +148,7 @@ pub fn notify_if_app_was_updated(cx: &mut App) {
|
||||
let should_show_notification = should_show_notification.await?;
|
||||
if should_show_notification {
|
||||
cx.update(|cx| {
|
||||
let mut version = updater.read(cx).current_version();
|
||||
version.build = semver::BuildMetadata::EMPTY;
|
||||
version.pre = semver::Prerelease::EMPTY;
|
||||
let version = updater.read(cx).current_version();
|
||||
let app_name = ReleaseChannel::global(cx).display_name();
|
||||
show_app_notification(
|
||||
NotificationId::unique::<UpdateNotification>(),
|
||||
|
||||
@@ -12,7 +12,7 @@ workspace = true
|
||||
path = "src/buffer_diff.rs"
|
||||
|
||||
[features]
|
||||
test-support = ["settings"]
|
||||
test-support = []
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
@@ -24,7 +24,6 @@ language.workspace = true
|
||||
log.workspace = true
|
||||
pretty_assertions.workspace = true
|
||||
rope.workspace = true
|
||||
settings = { workspace = true, optional = true }
|
||||
sum_tree.workspace = true
|
||||
text.workspace = true
|
||||
util.workspace = true
|
||||
@@ -34,7 +33,6 @@ ctor.workspace = true
|
||||
gpui = { workspace = true, features = ["test-support"] }
|
||||
rand.workspace = true
|
||||
serde_json.workspace = true
|
||||
settings.workspace = true
|
||||
text = { workspace = true, features = ["test-support"] }
|
||||
unindent.workspace = true
|
||||
zlog.workspace = true
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
use futures::channel::oneshot;
|
||||
use git2::{DiffLineType as GitDiffLineType, DiffOptions as GitOptions, Patch as GitPatch};
|
||||
use gpui::{App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Task, TaskLabel};
|
||||
use language::{
|
||||
BufferRow, DiffOptions, File, Language, LanguageName, LanguageRegistry,
|
||||
language_settings::language_settings, word_diff_ranges,
|
||||
};
|
||||
use language::{Language, LanguageRegistry};
|
||||
use rope::Rope;
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
@@ -14,16 +11,14 @@ use std::{
|
||||
sync::{Arc, LazyLock},
|
||||
};
|
||||
use sum_tree::SumTree;
|
||||
use text::{Anchor, Bias, BufferId, OffsetRangeExt, Point, ToOffset as _, ToPoint as _};
|
||||
use text::{Anchor, Bias, BufferId, OffsetRangeExt, Point, ToOffset as _};
|
||||
use util::ResultExt;
|
||||
|
||||
pub static CALCULATE_DIFF_TASK: LazyLock<TaskLabel> = LazyLock::new(TaskLabel::new);
|
||||
pub const MAX_WORD_DIFF_LINE_COUNT: usize = 5;
|
||||
|
||||
pub struct BufferDiff {
|
||||
pub buffer_id: BufferId,
|
||||
inner: BufferDiffInner,
|
||||
// diff of the index vs head
|
||||
secondary_diff: Option<Entity<BufferDiff>>,
|
||||
}
|
||||
|
||||
@@ -36,7 +31,6 @@ pub struct BufferDiffSnapshot {
|
||||
#[derive(Clone)]
|
||||
struct BufferDiffInner {
|
||||
hunks: SumTree<InternalDiffHunk>,
|
||||
// Used for making staging mo
|
||||
pending_hunks: SumTree<PendingHunk>,
|
||||
base_text: language::BufferSnapshot,
|
||||
base_text_exists: bool,
|
||||
@@ -56,18 +50,11 @@ pub enum DiffHunkStatusKind {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
/// Diff of Working Copy vs Index
|
||||
/// aka 'is this hunk staged or not'
|
||||
pub enum DiffHunkSecondaryStatus {
|
||||
/// Unstaged
|
||||
HasSecondaryHunk,
|
||||
/// Partially staged
|
||||
OverlapsWithSecondaryHunk,
|
||||
/// Staged
|
||||
NoSecondaryHunk,
|
||||
/// We are unstaging
|
||||
SecondaryHunkAdditionPending,
|
||||
/// We are stagind
|
||||
SecondaryHunkRemovalPending,
|
||||
}
|
||||
|
||||
@@ -81,10 +68,6 @@ pub struct DiffHunk {
|
||||
/// The range in the buffer's diff base text to which this hunk corresponds.
|
||||
pub diff_base_byte_range: Range<usize>,
|
||||
pub secondary_status: DiffHunkSecondaryStatus,
|
||||
// Anchors representing the word diff locations in the active buffer
|
||||
pub buffer_word_diffs: Vec<Range<Anchor>>,
|
||||
// Offsets relative to the start of the deleted diff that represent word diff locations
|
||||
pub base_word_diffs: Vec<Range<usize>>,
|
||||
}
|
||||
|
||||
/// We store [`InternalDiffHunk`]s internally so we don't need to store the additional row range.
|
||||
@@ -92,8 +75,6 @@ pub struct DiffHunk {
|
||||
struct InternalDiffHunk {
|
||||
buffer_range: Range<Anchor>,
|
||||
diff_base_byte_range: Range<usize>,
|
||||
base_word_diffs: Vec<Range<usize>>,
|
||||
buffer_word_diffs: Vec<Range<Anchor>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@@ -107,7 +88,6 @@ struct PendingHunk {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DiffHunkSummary {
|
||||
buffer_range: Range<Anchor>,
|
||||
diff_base_byte_range: Range<usize>,
|
||||
}
|
||||
|
||||
impl sum_tree::Item for InternalDiffHunk {
|
||||
@@ -116,7 +96,6 @@ impl sum_tree::Item for InternalDiffHunk {
|
||||
fn summary(&self, _cx: &text::BufferSnapshot) -> Self::Summary {
|
||||
DiffHunkSummary {
|
||||
buffer_range: self.buffer_range.clone(),
|
||||
diff_base_byte_range: self.diff_base_byte_range.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,7 +106,6 @@ impl sum_tree::Item for PendingHunk {
|
||||
fn summary(&self, _cx: &text::BufferSnapshot) -> Self::Summary {
|
||||
DiffHunkSummary {
|
||||
buffer_range: self.buffer_range.clone(),
|
||||
diff_base_byte_range: self.diff_base_byte_range.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -138,7 +116,6 @@ impl sum_tree::Summary for DiffHunkSummary {
|
||||
fn zero(_cx: Self::Context<'_>) -> Self {
|
||||
DiffHunkSummary {
|
||||
buffer_range: Anchor::MIN..Anchor::MIN,
|
||||
diff_base_byte_range: 0..0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,15 +125,6 @@ impl sum_tree::Summary for DiffHunkSummary {
|
||||
.start
|
||||
.min(&other.buffer_range.start, buffer);
|
||||
self.buffer_range.end = *self.buffer_range.end.max(&other.buffer_range.end, buffer);
|
||||
|
||||
self.diff_base_byte_range.start = self
|
||||
.diff_base_byte_range
|
||||
.start
|
||||
.min(other.diff_base_byte_range.start);
|
||||
self.diff_base_byte_range.end = self
|
||||
.diff_base_byte_range
|
||||
.end
|
||||
.max(other.diff_base_byte_range.end);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,13 +195,6 @@ impl BufferDiffSnapshot {
|
||||
let base_text_pair;
|
||||
let base_text_exists;
|
||||
let base_text_snapshot;
|
||||
let diff_options = build_diff_options(
|
||||
None,
|
||||
language.as_ref().map(|l| l.name()),
|
||||
language.as_ref().map(|l| l.default_scope()),
|
||||
cx,
|
||||
);
|
||||
|
||||
if let Some(text) = &base_text {
|
||||
let base_text_rope = Rope::from(text.as_str());
|
||||
base_text_pair = Some((text.clone(), base_text_rope.clone()));
|
||||
@@ -251,7 +212,7 @@ impl BufferDiffSnapshot {
|
||||
.background_executor()
|
||||
.spawn_labeled(*CALCULATE_DIFF_TASK, {
|
||||
let buffer = buffer.clone();
|
||||
async move { compute_hunks(base_text_pair, buffer, diff_options) }
|
||||
async move { compute_hunks(base_text_pair, buffer) }
|
||||
});
|
||||
|
||||
async move {
|
||||
@@ -274,12 +235,6 @@ impl BufferDiffSnapshot {
|
||||
base_text_snapshot: language::BufferSnapshot,
|
||||
cx: &App,
|
||||
) -> impl Future<Output = Self> + use<> {
|
||||
let diff_options = build_diff_options(
|
||||
base_text_snapshot.file(),
|
||||
base_text_snapshot.language().map(|l| l.name()),
|
||||
base_text_snapshot.language().map(|l| l.default_scope()),
|
||||
cx,
|
||||
);
|
||||
let base_text_exists = base_text.is_some();
|
||||
let base_text_pair = base_text.map(|text| {
|
||||
debug_assert_eq!(&*text, &base_text_snapshot.text());
|
||||
@@ -291,7 +246,7 @@ impl BufferDiffSnapshot {
|
||||
inner: BufferDiffInner {
|
||||
base_text: base_text_snapshot,
|
||||
pending_hunks: SumTree::new(&buffer),
|
||||
hunks: compute_hunks(base_text_pair, buffer, diff_options),
|
||||
hunks: compute_hunks(base_text_pair, buffer),
|
||||
base_text_exists,
|
||||
},
|
||||
secondary_diff: None,
|
||||
@@ -350,54 +305,6 @@ impl BufferDiffSnapshot {
|
||||
let (new_id, new_empty) = (right.remote_id(), right.is_empty());
|
||||
new_id == old_id || (new_empty && old_empty)
|
||||
}
|
||||
|
||||
pub fn row_to_base_text_row(&self, row: BufferRow, buffer: &text::BufferSnapshot) -> u32 {
|
||||
// TODO(split-diff) expose a parameter to reuse a cursor to avoid repeatedly seeking from the start
|
||||
|
||||
// Find the last hunk that starts before this position.
|
||||
let mut cursor = self.inner.hunks.cursor::<DiffHunkSummary>(buffer);
|
||||
let position = buffer.anchor_before(Point::new(row, 0));
|
||||
cursor.seek(&position, Bias::Left);
|
||||
if cursor
|
||||
.item()
|
||||
.is_none_or(|hunk| hunk.buffer_range.start.cmp(&position, buffer).is_gt())
|
||||
{
|
||||
cursor.prev();
|
||||
}
|
||||
|
||||
let unclipped_point = if let Some(hunk) = cursor.item()
|
||||
&& hunk.buffer_range.start.cmp(&position, buffer).is_le()
|
||||
{
|
||||
let mut unclipped_point = cursor
|
||||
.end()
|
||||
.diff_base_byte_range
|
||||
.end
|
||||
.to_point(self.base_text());
|
||||
if position.cmp(&cursor.end().buffer_range.end, buffer).is_ge() {
|
||||
unclipped_point +=
|
||||
Point::new(row, 0) - cursor.end().buffer_range.end.to_point(buffer);
|
||||
}
|
||||
// Move the cursor so that at the next step we can clip with the start of the next hunk.
|
||||
cursor.next();
|
||||
unclipped_point
|
||||
} else {
|
||||
// Position is before the added region for the first hunk.
|
||||
debug_assert!(self.inner.hunks.first().is_none_or(|first_hunk| {
|
||||
position.cmp(&first_hunk.buffer_range.start, buffer).is_le()
|
||||
}));
|
||||
Point::new(row, 0)
|
||||
};
|
||||
|
||||
let max_point = if let Some(next_hunk) = cursor.item() {
|
||||
next_hunk
|
||||
.diff_base_byte_range
|
||||
.start
|
||||
.to_point(self.base_text())
|
||||
} else {
|
||||
self.base_text().max_point()
|
||||
};
|
||||
unclipped_point.min(max_point).row
|
||||
}
|
||||
}
|
||||
|
||||
impl BufferDiffInner {
|
||||
@@ -634,15 +541,11 @@ impl BufferDiffInner {
|
||||
[
|
||||
(
|
||||
&hunk.buffer_range.start,
|
||||
(
|
||||
hunk.buffer_range.start,
|
||||
hunk.diff_base_byte_range.start,
|
||||
hunk,
|
||||
),
|
||||
(hunk.buffer_range.start, hunk.diff_base_byte_range.start),
|
||||
),
|
||||
(
|
||||
&hunk.buffer_range.end,
|
||||
(hunk.buffer_range.end, hunk.diff_base_byte_range.end, hunk),
|
||||
(hunk.buffer_range.end, hunk.diff_base_byte_range.end),
|
||||
),
|
||||
]
|
||||
});
|
||||
@@ -661,11 +564,8 @@ impl BufferDiffInner {
|
||||
let mut summaries = buffer.summaries_for_anchors_with_payload::<Point, _, _>(anchor_iter);
|
||||
iter::from_fn(move || {
|
||||
loop {
|
||||
let (start_point, (start_anchor, start_base, hunk)) = summaries.next()?;
|
||||
let (mut end_point, (mut end_anchor, end_base, _)) = summaries.next()?;
|
||||
|
||||
let base_word_diffs = hunk.base_word_diffs.clone();
|
||||
let buffer_word_diffs = hunk.buffer_word_diffs.clone();
|
||||
let (start_point, (start_anchor, start_base)) = summaries.next()?;
|
||||
let (mut end_point, (mut end_anchor, end_base)) = summaries.next()?;
|
||||
|
||||
if !start_anchor.is_valid(buffer) {
|
||||
continue;
|
||||
@@ -735,8 +635,6 @@ impl BufferDiffInner {
|
||||
range: start_point..end_point,
|
||||
diff_base_byte_range: start_base..end_base,
|
||||
buffer_range: start_anchor..end_anchor,
|
||||
base_word_diffs,
|
||||
buffer_word_diffs,
|
||||
secondary_status,
|
||||
});
|
||||
}
|
||||
@@ -768,8 +666,6 @@ impl BufferDiffInner {
|
||||
buffer_range: hunk.buffer_range.clone(),
|
||||
// The secondary status is not used by callers of this method.
|
||||
secondary_status: DiffHunkSecondaryStatus::NoSecondaryHunk,
|
||||
base_word_diffs: hunk.base_word_diffs.clone(),
|
||||
buffer_word_diffs: hunk.buffer_word_diffs.clone(),
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -838,36 +734,9 @@ impl BufferDiffInner {
|
||||
}
|
||||
}
|
||||
|
||||
fn build_diff_options(
|
||||
file: Option<&Arc<dyn File>>,
|
||||
language: Option<LanguageName>,
|
||||
language_scope: Option<language::LanguageScope>,
|
||||
cx: &App,
|
||||
) -> Option<DiffOptions> {
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
{
|
||||
if !cx.has_global::<settings::SettingsStore>() {
|
||||
return Some(DiffOptions {
|
||||
language_scope,
|
||||
max_word_diff_line_count: MAX_WORD_DIFF_LINE_COUNT,
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
language_settings(language, file, cx)
|
||||
.word_diff_enabled
|
||||
.then_some(DiffOptions {
|
||||
language_scope,
|
||||
max_word_diff_line_count: MAX_WORD_DIFF_LINE_COUNT,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
fn compute_hunks(
|
||||
diff_base: Option<(Arc<String>, Rope)>,
|
||||
buffer: text::BufferSnapshot,
|
||||
diff_options: Option<DiffOptions>,
|
||||
) -> SumTree<InternalDiffHunk> {
|
||||
let mut tree = SumTree::new(&buffer);
|
||||
|
||||
@@ -893,8 +762,6 @@ fn compute_hunks(
|
||||
InternalDiffHunk {
|
||||
buffer_range: buffer.anchor_before(0)..buffer.anchor_before(0),
|
||||
diff_base_byte_range: 0..diff_base.len() - 1,
|
||||
base_word_diffs: Vec::default(),
|
||||
buffer_word_diffs: Vec::default(),
|
||||
},
|
||||
&buffer,
|
||||
);
|
||||
@@ -910,7 +777,6 @@ fn compute_hunks(
|
||||
&diff_base_rope,
|
||||
&buffer,
|
||||
&mut divergence,
|
||||
diff_options.as_ref(),
|
||||
);
|
||||
tree.push(hunk, &buffer);
|
||||
}
|
||||
@@ -920,8 +786,6 @@ fn compute_hunks(
|
||||
InternalDiffHunk {
|
||||
buffer_range: Anchor::min_max_range_for_buffer(buffer.remote_id()),
|
||||
diff_base_byte_range: 0..0,
|
||||
base_word_diffs: Vec::default(),
|
||||
buffer_word_diffs: Vec::default(),
|
||||
},
|
||||
&buffer,
|
||||
);
|
||||
@@ -936,7 +800,6 @@ fn process_patch_hunk(
|
||||
diff_base: &Rope,
|
||||
buffer: &text::BufferSnapshot,
|
||||
buffer_row_divergence: &mut i64,
|
||||
diff_options: Option<&DiffOptions>,
|
||||
) -> InternalDiffHunk {
|
||||
let line_item_count = patch.num_lines_in_hunk(hunk_index).unwrap();
|
||||
assert!(line_item_count > 0);
|
||||
@@ -1001,49 +864,9 @@ fn process_patch_hunk(
|
||||
let start = Point::new(buffer_row_range.start, 0);
|
||||
let end = Point::new(buffer_row_range.end, 0);
|
||||
let buffer_range = buffer.anchor_before(start)..buffer.anchor_before(end);
|
||||
|
||||
let base_line_count = line_item_count.saturating_sub(buffer_row_range.len());
|
||||
|
||||
let (base_word_diffs, buffer_word_diffs) = if let Some(diff_options) = diff_options
|
||||
&& !buffer_row_range.is_empty()
|
||||
&& base_line_count == buffer_row_range.len()
|
||||
&& diff_options.max_word_diff_line_count >= base_line_count
|
||||
{
|
||||
let base_text: String = diff_base
|
||||
.chunks_in_range(diff_base_byte_range.clone())
|
||||
.collect();
|
||||
|
||||
let buffer_text: String = buffer.text_for_range(buffer_range.clone()).collect();
|
||||
|
||||
let (base_word_diffs, buffer_word_diffs_relative) = word_diff_ranges(
|
||||
&base_text,
|
||||
&buffer_text,
|
||||
DiffOptions {
|
||||
language_scope: diff_options.language_scope.clone(),
|
||||
..*diff_options
|
||||
},
|
||||
);
|
||||
|
||||
let buffer_start_offset = buffer_range.start.to_offset(buffer);
|
||||
let buffer_word_diffs = buffer_word_diffs_relative
|
||||
.into_iter()
|
||||
.map(|range| {
|
||||
let start = buffer.anchor_after(buffer_start_offset + range.start);
|
||||
let end = buffer.anchor_after(buffer_start_offset + range.end);
|
||||
start..end
|
||||
})
|
||||
.collect();
|
||||
|
||||
(base_word_diffs, buffer_word_diffs)
|
||||
} else {
|
||||
(Vec::default(), Vec::default())
|
||||
};
|
||||
|
||||
InternalDiffHunk {
|
||||
buffer_range,
|
||||
diff_base_byte_range,
|
||||
base_word_diffs,
|
||||
buffer_word_diffs,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1123,7 +946,6 @@ impl BufferDiff {
|
||||
if self.secondary_diff.is_some() {
|
||||
self.inner.pending_hunks = SumTree::from_summary(DiffHunkSummary {
|
||||
buffer_range: Anchor::min_min_range_for_buffer(self.buffer_id),
|
||||
diff_base_byte_range: 0..0,
|
||||
});
|
||||
cx.emit(BufferDiffEvent::DiffChanged {
|
||||
changed_range: Some(Anchor::min_max_range_for_buffer(self.buffer_id)),
|
||||
@@ -1413,7 +1235,7 @@ impl DiffHunk {
|
||||
pub fn is_created_file(&self) -> bool {
|
||||
self.diff_base_byte_range == (0..0)
|
||||
&& self.buffer_range.start.is_min()
|
||||
&& self.buffer_range.end.is_max()
|
||||
&& self.buffer_range.end.is_min()
|
||||
}
|
||||
|
||||
pub fn status(&self) -> DiffHunkStatus {
|
||||
@@ -2418,62 +2240,4 @@ mod tests {
|
||||
hunks = found_hunks;
|
||||
}
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_row_to_base_text_row(cx: &mut TestAppContext) {
|
||||
let base_text = "
|
||||
zero
|
||||
one
|
||||
two
|
||||
three
|
||||
four
|
||||
five
|
||||
six
|
||||
seven
|
||||
eight
|
||||
"
|
||||
.unindent();
|
||||
let buffer_text = "
|
||||
zero
|
||||
ONE
|
||||
two
|
||||
NINE
|
||||
five
|
||||
seven
|
||||
"
|
||||
.unindent();
|
||||
|
||||
// zero
|
||||
// - one
|
||||
// + ONE
|
||||
// two
|
||||
// - three
|
||||
// - four
|
||||
// + NINE
|
||||
// five
|
||||
// - six
|
||||
// seven
|
||||
// + eight
|
||||
|
||||
let buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text);
|
||||
let buffer_snapshot = buffer.snapshot();
|
||||
let diff = BufferDiffSnapshot::new_sync(buffer_snapshot.clone(), base_text, cx);
|
||||
let expected_results = [
|
||||
// don't format me
|
||||
(0, 0),
|
||||
(1, 2),
|
||||
(2, 2),
|
||||
(3, 5),
|
||||
(4, 5),
|
||||
(5, 7),
|
||||
(6, 9),
|
||||
];
|
||||
for (buffer_row, expected) in expected_results {
|
||||
assert_eq!(
|
||||
diff.row_to_base_text_row(buffer_row, &buffer_snapshot),
|
||||
expected,
|
||||
"{buffer_row}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -524,16 +524,6 @@ impl Room {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn room_id(&self) -> impl Future<Output = Option<String>> + 'static {
|
||||
let room = self.live_kit.as_ref().map(|lk| lk.room.clone());
|
||||
async move {
|
||||
let room = room?;
|
||||
let sid = room.sid().await;
|
||||
let name = room.name();
|
||||
Some(format!("{} (sid: {sid})", name))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn status(&self) -> RoomStatus {
|
||||
self.status
|
||||
}
|
||||
|
||||
@@ -59,11 +59,3 @@ pub fn agent_server_docs(cx: &App) -> String {
|
||||
server_url = server_url(cx)
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the URL to Zed's edit prediction documentation.
|
||||
pub fn edit_prediction_docs(cx: &App) -> String {
|
||||
format!(
|
||||
"{server_url}/docs/ai/edit-prediction",
|
||||
server_url = server_url(cx)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -169,17 +169,6 @@ pub struct PredictEditsBody {
|
||||
/// Info about the git repository state, only present when can_collect_data is true.
|
||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||
pub git_info: Option<PredictEditsGitInfo>,
|
||||
/// The trigger for this request.
|
||||
#[serde(default)]
|
||||
pub trigger: PredictEditsRequestTrigger,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
pub enum PredictEditsRequestTrigger {
|
||||
Diagnostics,
|
||||
Cli,
|
||||
#[default]
|
||||
Other,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -206,41 +195,17 @@ pub struct AcceptEditPredictionBody {
|
||||
pub request_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct RejectEditPredictionsBody {
|
||||
pub rejections: Vec<EditPredictionRejection>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct RejectEditPredictionsBodyRef<'a> {
|
||||
pub rejections: &'a [EditPredictionRejection],
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct EditPredictionRejection {
|
||||
pub request_id: String,
|
||||
#[serde(default)]
|
||||
pub reason: EditPredictionRejectReason,
|
||||
pub was_shown: bool,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
|
||||
pub enum EditPredictionRejectReason {
|
||||
/// New requests were triggered before this one completed
|
||||
Canceled,
|
||||
/// No edits returned
|
||||
Empty,
|
||||
/// Edits returned, but none remained after interpolation
|
||||
InterpolatedEmpty,
|
||||
/// The new prediction was preferred over the current one
|
||||
Replaced,
|
||||
/// The current prediction was preferred over the new one
|
||||
CurrentPreferred,
|
||||
/// The current prediction was discarded
|
||||
#[default]
|
||||
Discarded,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum CompletionMode {
|
||||
|
||||
@@ -9,7 +9,7 @@ use std::{
|
||||
use strum::EnumIter;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{PredictEditsGitInfo, PredictEditsRequestTrigger};
|
||||
use crate::PredictEditsGitInfo;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PlanContextRetrievalRequest {
|
||||
@@ -53,8 +53,6 @@ pub struct PredictEditsRequest {
|
||||
pub prompt_max_bytes: Option<usize>,
|
||||
#[serde(default)]
|
||||
pub prompt_format: PromptFormat,
|
||||
#[serde(default)]
|
||||
pub trigger: PredictEditsRequestTrigger,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
||||
@@ -64,16 +64,6 @@ async fn check_is_contributor(
|
||||
}));
|
||||
}
|
||||
|
||||
if ZedZippyBot::is_zed_zippy_bot(¶ms) {
|
||||
return Ok(Json(CheckIsContributorResponse {
|
||||
signed_at: Some(
|
||||
ZedZippyBot::created_at()
|
||||
.and_utc()
|
||||
.to_rfc3339_opts(SecondsFormat::Millis, true),
|
||||
),
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(Json(CheckIsContributorResponse {
|
||||
signed_at: app
|
||||
.db
|
||||
@@ -113,36 +103,6 @@ impl RenovateBot {
|
||||
}
|
||||
}
|
||||
|
||||
/// The Zed Zippy bot GitHub user (`zed-zippy[bot]`).
|
||||
///
|
||||
/// https://api.github.com/users/zed-zippy[bot]
|
||||
struct ZedZippyBot;
|
||||
|
||||
impl ZedZippyBot {
|
||||
const LOGIN: &'static str = "zed-zippy[bot]";
|
||||
const USER_ID: i32 = 234243425;
|
||||
|
||||
/// Returns the `created_at` timestamp for the Zed Zippy bot user.
|
||||
fn created_at() -> &'static NaiveDateTime {
|
||||
static CREATED_AT: OnceLock<NaiveDateTime> = OnceLock::new();
|
||||
CREATED_AT.get_or_init(|| {
|
||||
chrono::DateTime::parse_from_rfc3339("2025-09-24T17:00:11Z")
|
||||
.expect("failed to parse 'created_at' for 'zed-zippy[bot]'")
|
||||
.naive_utc()
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns whether the given contributor selector corresponds to the Zed Zippy bot user.
|
||||
fn is_zed_zippy_bot(contributor: &ContributorSelector) -> bool {
|
||||
match contributor {
|
||||
ContributorSelector::GitHubLogin { github_login } => github_login == Self::LOGIN,
|
||||
ContributorSelector::GitHubUserId { github_user_id } => {
|
||||
github_user_id == &Self::USER_ID
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct AddContributorBody {
|
||||
github_user_id: i32,
|
||||
|
||||
@@ -541,7 +541,7 @@ impl Item for ChannelView {
|
||||
})
|
||||
}
|
||||
|
||||
fn as_searchable(&self, _: &Entity<Self>, _: &App) -> Option<Box<dyn SearchableItemHandle>> {
|
||||
fn as_searchable(&self, _: &Entity<Self>) -> Option<Box<dyn SearchableItemHandle>> {
|
||||
Some(Box::new(self.editor.clone()))
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ use ui::{
|
||||
};
|
||||
use util::{ResultExt, TryFutureExt, maybe};
|
||||
use workspace::{
|
||||
CopyRoomId, Deafen, LeaveCall, Mute, OpenChannelNotes, ScreenShare, ShareProject, Workspace,
|
||||
Deafen, LeaveCall, Mute, OpenChannelNotes, ScreenShare, ShareProject, Workspace,
|
||||
dock::{DockPosition, Panel, PanelEvent},
|
||||
notifications::{DetachAndPromptErr, NotifyResultExt},
|
||||
};
|
||||
@@ -128,32 +128,6 @@ pub fn init(cx: &mut App) {
|
||||
workspace.register_action(|_, _: &LeaveCall, window, cx| {
|
||||
CollabPanel::leave_call(window, cx);
|
||||
});
|
||||
workspace.register_action(|workspace, _: &CopyRoomId, window, cx| {
|
||||
use workspace::notifications::{NotificationId, NotifyTaskExt as _};
|
||||
|
||||
struct RoomIdCopiedToast;
|
||||
|
||||
if let Some(room) = ActiveCall::global(cx).read(cx).room() {
|
||||
let romo_id_fut = room.read(cx).room_id();
|
||||
cx.spawn(async move |workspace, cx| {
|
||||
let room_id = romo_id_fut.await.context("Failed to get livekit room")?;
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
cx.write_to_clipboard(ClipboardItem::new_string(room_id));
|
||||
workspace.show_toast(
|
||||
workspace::Toast::new(
|
||||
NotificationId::unique::<RoomIdCopiedToast>(),
|
||||
"Room ID copied to clipboard",
|
||||
)
|
||||
.autohide(),
|
||||
cx,
|
||||
);
|
||||
})
|
||||
})
|
||||
.detach_and_notify_err(window, cx);
|
||||
} else {
|
||||
workspace.show_error(&"There’s no active call; join one first.", cx);
|
||||
}
|
||||
});
|
||||
workspace.register_action(|workspace, _: &ShareProject, window, cx| {
|
||||
let project = workspace.project().clone();
|
||||
println!("{project:?}");
|
||||
|
||||
@@ -8,9 +8,6 @@ license = "GPL-3.0-or-later"
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[features]
|
||||
test-support = ["db/test-support"]
|
||||
|
||||
[lib]
|
||||
path = "src/command_palette.rs"
|
||||
doctest = false
|
||||
|
||||
@@ -366,7 +366,7 @@ impl DebugAdapter for GoDebugAdapter {
|
||||
dap::DebugRequest::Attach(attach_config) => {
|
||||
json!({
|
||||
"request": "attach",
|
||||
"mode": "local",
|
||||
"mode": "debug",
|
||||
"processId": attach_config.process_id,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -998,11 +998,7 @@ impl Item for DapLogView {
|
||||
None
|
||||
}
|
||||
|
||||
fn as_searchable(
|
||||
&self,
|
||||
handle: &Entity<Self>,
|
||||
_: &App,
|
||||
) -> Option<Box<dyn SearchableItemHandle>> {
|
||||
fn as_searchable(&self, handle: &Entity<Self>) -> Option<Box<dyn SearchableItemHandle>> {
|
||||
Some(Box::new(handle.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -575,7 +575,7 @@ impl BreakpointList {
|
||||
)
|
||||
.with_horizontal_sizing_behavior(gpui::ListHorizontalSizingBehavior::Unconstrained)
|
||||
.with_width_from_item(self.max_width_index)
|
||||
.track_scroll(&self.scroll_handle)
|
||||
.track_scroll(self.scroll_handle.clone())
|
||||
.flex_1()
|
||||
}
|
||||
|
||||
@@ -776,7 +776,7 @@ impl Render for BreakpointList {
|
||||
.child(self.render_list(cx))
|
||||
.custom_scrollbars(
|
||||
ui::Scrollbars::new(ScrollAxes::Both)
|
||||
.tracked_scroll_handle(&self.scroll_handle)
|
||||
.tracked_scroll_handle(self.scroll_handle.clone())
|
||||
.with_track_along(ScrollAxes::Both, cx.theme().colors().panel_background)
|
||||
.tracked_entity(cx.entity_id()),
|
||||
window,
|
||||
|
||||
@@ -18,14 +18,14 @@ use gpui::{
|
||||
use language::{Anchor, Buffer, CharScopeContext, CodeLabel, TextBufferSnapshot, ToOffset};
|
||||
use menu::{Confirm, SelectNext, SelectPrevious};
|
||||
use project::{
|
||||
CompletionDisplayOptions, CompletionResponse,
|
||||
Completion, CompletionDisplayOptions, CompletionResponse,
|
||||
debugger::session::{CompletionsQuery, OutputToken, Session},
|
||||
lsp_store::CompletionDocumentation,
|
||||
search_history::{SearchHistory, SearchHistoryCursor},
|
||||
};
|
||||
use settings::Settings;
|
||||
use std::fmt::Write;
|
||||
use std::{ops::Range, rc::Rc, usize};
|
||||
use std::{cell::RefCell, ops::Range, rc::Rc, usize};
|
||||
use theme::{Theme, ThemeSettings};
|
||||
use ui::{ContextMenu, Divider, PopoverMenu, SplitButton, Tooltip, prelude::*};
|
||||
use util::ResultExt;
|
||||
@@ -553,6 +553,17 @@ impl CompletionProvider for ConsoleQueryBarCompletionProvider {
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_additional_edits_for_completion(
|
||||
&self,
|
||||
_buffer: Entity<Buffer>,
|
||||
_completions: Rc<RefCell<Box<[Completion]>>>,
|
||||
_completion_index: usize,
|
||||
_push_to_history: bool,
|
||||
_cx: &mut Context<Editor>,
|
||||
) -> gpui::Task<anyhow::Result<Option<language::Transaction>>> {
|
||||
Task::ready(Ok(None))
|
||||
}
|
||||
|
||||
fn is_completion_trigger(
|
||||
&self,
|
||||
buffer: &Entity<Buffer>,
|
||||
|
||||
@@ -229,7 +229,7 @@ impl MemoryView {
|
||||
rows
|
||||
},
|
||||
)
|
||||
.track_scroll(&view_state.scroll_handle)
|
||||
.track_scroll(view_state.scroll_handle)
|
||||
.with_horizontal_sizing_behavior(ListHorizontalSizingBehavior::Unconstrained)
|
||||
.on_scroll_wheel(cx.listener(|this, evt: &ScrollWheelEvent, window, _| {
|
||||
let mut view_state = this.view_state();
|
||||
@@ -921,7 +921,7 @@ impl Render for MemoryView {
|
||||
}))
|
||||
.custom_scrollbars(
|
||||
ui::Scrollbars::new(ui::ScrollAxes::Both)
|
||||
.tracked_scroll_handle(&self.view_state_handle)
|
||||
.tracked_scroll_handle(self.view_state_handle.clone())
|
||||
.with_track_along(
|
||||
ui::ScrollAxes::Both,
|
||||
cx.theme().colors().panel_background,
|
||||
|
||||
@@ -253,7 +253,7 @@ impl ModuleList {
|
||||
range.map(|ix| this.render_entry(ix, cx)).collect()
|
||||
}),
|
||||
)
|
||||
.track_scroll(&self.scroll_handle)
|
||||
.track_scroll(self.scroll_handle.clone())
|
||||
.size_full()
|
||||
}
|
||||
}
|
||||
@@ -279,6 +279,6 @@ impl Render for ModuleList {
|
||||
.size_full()
|
||||
.p_1()
|
||||
.child(self.render_list(window, cx))
|
||||
.vertical_scrollbar_for(&self.scroll_handle, window, cx)
|
||||
.vertical_scrollbar_for(self.scroll_handle.clone(), window, cx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -913,7 +913,7 @@ impl Render for StackFrameList {
|
||||
)
|
||||
})
|
||||
.child(self.render_list(window, cx))
|
||||
.vertical_scrollbar_for(&self.list_state, window, cx)
|
||||
.vertical_scrollbar_for(self.list_state.clone(), window, cx)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1557,7 +1557,7 @@ impl Render for VariableList {
|
||||
this.render_entries(range, window, cx)
|
||||
}),
|
||||
)
|
||||
.track_scroll(&self.list_handle)
|
||||
.track_scroll(self.list_handle.clone())
|
||||
.with_width_from_item(self.max_width_index)
|
||||
.with_sizing_behavior(gpui::ListSizingBehavior::Auto)
|
||||
.with_horizontal_sizing_behavior(gpui::ListHorizontalSizingBehavior::Unconstrained)
|
||||
@@ -1574,10 +1574,10 @@ impl Render for VariableList {
|
||||
)
|
||||
.with_priority(1)
|
||||
}))
|
||||
// .vertical_scrollbar_for(&self.list_handle, window, cx)
|
||||
// .vertical_scrollbar_for(self.list_handle.clone(), window, cx)
|
||||
.custom_scrollbars(
|
||||
ui::Scrollbars::new(ScrollAxes::Both)
|
||||
.tracked_scroll_handle(&self.list_handle)
|
||||
.tracked_scroll_handle(self.list_handle.clone())
|
||||
.with_track_along(ScrollAxes::Both, cx.theme().colors().panel_background)
|
||||
.tracked_entity(cx.entity_id()),
|
||||
window,
|
||||
|
||||
@@ -428,7 +428,7 @@ impl Item for StackTraceView {
|
||||
}
|
||||
}
|
||||
|
||||
fn as_searchable(&self, _: &Entity<Self>, _: &App) -> Option<Box<dyn SearchableItemHandle>> {
|
||||
fn as_searchable(&self, _: &Entity<Self>) -> Option<Box<dyn SearchableItemHandle>> {
|
||||
Some(Box::new(self.editor.clone()))
|
||||
}
|
||||
|
||||
|
||||
@@ -890,7 +890,7 @@ impl Item for ProjectDiagnosticsEditor {
|
||||
}
|
||||
}
|
||||
|
||||
fn as_searchable(&self, _: &Entity<Self>, _: &App) -> Option<Box<dyn SearchableItemHandle>> {
|
||||
fn as_searchable(&self, _: &Entity<Self>) -> Option<Box<dyn SearchableItemHandle>> {
|
||||
Some(Box::new(self.editor.clone()))
|
||||
}
|
||||
|
||||
|
||||
@@ -32,9 +32,6 @@ settings.workspace = true
|
||||
supermaven.workspace = true
|
||||
telemetry.workspace = true
|
||||
ui.workspace = true
|
||||
ui_input.workspace = true
|
||||
menu.workspace = true
|
||||
util.workspace = true
|
||||
workspace.workspace = true
|
||||
zed_actions.workspace = true
|
||||
zeta.workspace = true
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
mod sweep_api_token_modal;
|
||||
|
||||
pub use sweep_api_token_modal::SweepApiKeyModal;
|
||||
|
||||
use anyhow::Result;
|
||||
use client::{Client, UserStore, zed_urls};
|
||||
use cloud_llm_client::UsageLimit;
|
||||
@@ -15,7 +11,7 @@ use fs::Fs;
|
||||
use gpui::{
|
||||
Action, Animation, AnimationExt, App, AsyncWindowContext, Corner, Entity, FocusHandle,
|
||||
Focusable, IntoElement, ParentElement, Render, Subscription, WeakEntity, actions, div,
|
||||
ease_in_out, pulsating_between,
|
||||
pulsating_between,
|
||||
};
|
||||
use indoc::indoc;
|
||||
use language::{
|
||||
@@ -38,13 +34,13 @@ use ui::{
|
||||
Clickable, ContextMenu, ContextMenuEntry, DocumentationEdge, DocumentationSide, IconButton,
|
||||
IconButtonShape, Indicator, PopoverMenu, PopoverMenuHandle, ProgressBar, Tooltip, prelude::*,
|
||||
};
|
||||
use util::ResultExt as _;
|
||||
use workspace::{
|
||||
StatusItemView, Toast, Workspace, create_and_open_local_file, item::ItemHandle,
|
||||
notifications::NotificationId,
|
||||
};
|
||||
use zed_actions::OpenBrowser;
|
||||
use zeta::{RateCompletions, SweepFeatureFlag, Zeta2FeatureFlag};
|
||||
use zeta::RateCompletions;
|
||||
use zeta::{SweepFeatureFlag, Zeta2FeatureFlag};
|
||||
|
||||
actions!(
|
||||
edit_prediction,
|
||||
@@ -316,10 +312,6 @@ impl Render for EditPredictionButton {
|
||||
)
|
||||
);
|
||||
|
||||
let sweep_missing_token = is_sweep
|
||||
&& !zeta::Zeta::try_global(cx)
|
||||
.map_or(false, |zeta| zeta.read(cx).has_sweep_api_token());
|
||||
|
||||
let zeta_icon = match (is_sweep, enabled) {
|
||||
(true, _) => IconName::SweepAi,
|
||||
(false, true) => IconName::ZedPredict,
|
||||
@@ -330,7 +322,7 @@ impl Render for EditPredictionButton {
|
||||
let tooltip_meta = if self.user_store.read(cx).current_user().is_some() {
|
||||
"Choose a Plan"
|
||||
} else {
|
||||
"Sign In To Use"
|
||||
"Sign In"
|
||||
};
|
||||
|
||||
return div().child(
|
||||
@@ -365,39 +357,25 @@ impl Render for EditPredictionButton {
|
||||
}
|
||||
|
||||
let show_editor_predictions = self.editor_show_predictions;
|
||||
let user = self.user_store.read(cx).current_user();
|
||||
|
||||
let indicator_color = if sweep_missing_token {
|
||||
Some(Color::Error)
|
||||
} else if enabled && (!show_editor_predictions || over_limit) {
|
||||
Some(if over_limit {
|
||||
Color::Error
|
||||
} else {
|
||||
Color::Muted
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let icon_button = IconButton::new("zed-predict-pending-button", zeta_icon)
|
||||
.shape(IconButtonShape::Square)
|
||||
.when_some(indicator_color, |this, color| {
|
||||
this.indicator(Indicator::dot().color(color))
|
||||
.when(
|
||||
enabled && (!show_editor_predictions || over_limit),
|
||||
|this| {
|
||||
this.indicator(Indicator::dot().when_else(
|
||||
over_limit,
|
||||
|dot| dot.color(Color::Error),
|
||||
|dot| dot.color(Color::Muted),
|
||||
))
|
||||
.indicator_border_color(Some(cx.theme().colors().status_bar_background))
|
||||
})
|
||||
},
|
||||
)
|
||||
.when(!self.popover_menu_handle.is_deployed(), |element| {
|
||||
let user = user.clone();
|
||||
element.tooltip(move |_window, cx| {
|
||||
if enabled {
|
||||
if show_editor_predictions {
|
||||
Tooltip::for_action("Edit Prediction", &ToggleMenu, cx)
|
||||
} else if user.is_none() {
|
||||
Tooltip::with_meta(
|
||||
"Edit Prediction",
|
||||
Some(&ToggleMenu),
|
||||
"Sign In To Use",
|
||||
cx,
|
||||
)
|
||||
} else {
|
||||
Tooltip::with_meta(
|
||||
"Edit Prediction",
|
||||
@@ -420,25 +398,11 @@ impl Render for EditPredictionButton {
|
||||
let this = cx.weak_entity();
|
||||
|
||||
let mut popover_menu = PopoverMenu::new("zeta")
|
||||
.when(user.is_some(), |popover_menu| {
|
||||
let this = this.clone();
|
||||
|
||||
popover_menu.menu(move |window, cx| {
|
||||
this.update(cx, |this, cx| {
|
||||
this.build_zeta_context_menu(provider, window, cx)
|
||||
})
|
||||
.ok()
|
||||
})
|
||||
})
|
||||
.when(user.is_none(), |popover_menu| {
|
||||
let this = this.clone();
|
||||
|
||||
popover_menu.menu(move |window, cx| {
|
||||
this.update(cx, |this, cx| {
|
||||
this.build_zeta_upsell_context_menu(window, cx)
|
||||
})
|
||||
.ok()
|
||||
.menu(move |window, cx| {
|
||||
this.update(cx, |this, cx| {
|
||||
this.build_zeta_context_menu(provider, window, cx)
|
||||
})
|
||||
.ok()
|
||||
})
|
||||
.anchor(Corner::BottomRight)
|
||||
.with_handle(self.popover_menu_handle.clone());
|
||||
@@ -549,23 +513,23 @@ impl EditPredictionButton {
|
||||
|
||||
const ZED_AI_CALLOUT: &str =
|
||||
"Zed's edit prediction is powered by Zeta, an open-source, dataset mode.";
|
||||
const USE_SWEEP_API_TOKEN_CALLOUT: &str =
|
||||
"Set the SWEEP_API_TOKEN environment variable to use Sweep";
|
||||
|
||||
let providers: Vec<_> = available_providers
|
||||
let other_providers: Vec<_> = available_providers
|
||||
.into_iter()
|
||||
.filter(|p| *p != EditPredictionProvider::None)
|
||||
.filter(|p| *p != current_provider && *p != EditPredictionProvider::None)
|
||||
.collect();
|
||||
|
||||
if !providers.is_empty() {
|
||||
menu = menu.separator().header("Providers");
|
||||
if !other_providers.is_empty() {
|
||||
menu = menu.separator().header("Switch Providers");
|
||||
|
||||
for provider in providers {
|
||||
let is_current = provider == current_provider;
|
||||
for provider in other_providers {
|
||||
let fs = self.fs.clone();
|
||||
|
||||
menu = match provider {
|
||||
EditPredictionProvider::Zed => menu.item(
|
||||
ContextMenuEntry::new("Zed AI")
|
||||
.toggleable(IconPosition::Start, is_current)
|
||||
.documentation_aside(
|
||||
DocumentationSide::Left,
|
||||
DocumentationEdge::Bottom,
|
||||
@@ -575,77 +539,46 @@ impl EditPredictionButton {
|
||||
set_completion_provider(fs.clone(), cx, provider);
|
||||
}),
|
||||
),
|
||||
EditPredictionProvider::Copilot => menu.item(
|
||||
ContextMenuEntry::new("GitHub Copilot")
|
||||
.toggleable(IconPosition::Start, is_current)
|
||||
.handler(move |_, cx| {
|
||||
set_completion_provider(fs.clone(), cx, provider);
|
||||
}),
|
||||
),
|
||||
EditPredictionProvider::Supermaven => menu.item(
|
||||
ContextMenuEntry::new("Supermaven")
|
||||
.toggleable(IconPosition::Start, is_current)
|
||||
.handler(move |_, cx| {
|
||||
set_completion_provider(fs.clone(), cx, provider);
|
||||
}),
|
||||
),
|
||||
EditPredictionProvider::Codestral => menu.item(
|
||||
ContextMenuEntry::new("Codestral")
|
||||
.toggleable(IconPosition::Start, is_current)
|
||||
.handler(move |_, cx| {
|
||||
set_completion_provider(fs.clone(), cx, provider);
|
||||
}),
|
||||
),
|
||||
EditPredictionProvider::Copilot => {
|
||||
menu.entry("GitHub Copilot", None, move |_, cx| {
|
||||
set_completion_provider(fs.clone(), cx, provider);
|
||||
})
|
||||
}
|
||||
EditPredictionProvider::Supermaven => {
|
||||
menu.entry("Supermaven", None, move |_, cx| {
|
||||
set_completion_provider(fs.clone(), cx, provider);
|
||||
})
|
||||
}
|
||||
EditPredictionProvider::Codestral => {
|
||||
menu.entry("Codestral", None, move |_, cx| {
|
||||
set_completion_provider(fs.clone(), cx, provider);
|
||||
})
|
||||
}
|
||||
EditPredictionProvider::Experimental(
|
||||
EXPERIMENTAL_SWEEP_EDIT_PREDICTION_PROVIDER_NAME,
|
||||
) => {
|
||||
let has_api_token = zeta::Zeta::try_global(cx)
|
||||
.map_or(false, |zeta| zeta.read(cx).has_sweep_api_token());
|
||||
|
||||
let should_open_modal = !has_api_token || is_current;
|
||||
|
||||
let entry = if has_api_token {
|
||||
ContextMenuEntry::new("Sweep")
|
||||
.toggleable(IconPosition::Start, is_current)
|
||||
} else {
|
||||
ContextMenuEntry::new("Sweep")
|
||||
.icon(IconName::XCircle)
|
||||
.icon_color(Color::Error)
|
||||
.documentation_aside(
|
||||
let entry = ContextMenuEntry::new("Sweep")
|
||||
.when(!has_api_token, |this| {
|
||||
this.disabled(true).documentation_aside(
|
||||
DocumentationSide::Left,
|
||||
DocumentationEdge::Bottom,
|
||||
|_| {
|
||||
Label::new("Click to configure your Sweep API token")
|
||||
.into_any_element()
|
||||
},
|
||||
|_| Label::new(USE_SWEEP_API_TOKEN_CALLOUT).into_any_element(),
|
||||
)
|
||||
};
|
||||
|
||||
let entry = entry.handler(move |window, cx| {
|
||||
if should_open_modal {
|
||||
if let Some(workspace) = window.root::<Workspace>().flatten() {
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
workspace.toggle_modal(window, cx, |window, cx| {
|
||||
SweepApiKeyModal::new(window, cx)
|
||||
});
|
||||
});
|
||||
};
|
||||
} else {
|
||||
})
|
||||
.handler(move |_, cx| {
|
||||
set_completion_provider(fs.clone(), cx, provider);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
menu.item(entry)
|
||||
}
|
||||
EditPredictionProvider::Experimental(
|
||||
EXPERIMENTAL_ZETA2_EDIT_PREDICTION_PROVIDER_NAME,
|
||||
) => menu.item(
|
||||
ContextMenuEntry::new("Zeta2")
|
||||
.toggleable(IconPosition::Start, is_current)
|
||||
.handler(move |_, cx| {
|
||||
set_completion_provider(fs.clone(), cx, provider);
|
||||
}),
|
||||
),
|
||||
) => menu.entry("Zeta2", None, move |_, cx| {
|
||||
set_completion_provider(fs.clone(), cx, provider);
|
||||
}),
|
||||
EditPredictionProvider::None | EditPredictionProvider::Experimental(_) => {
|
||||
continue;
|
||||
}
|
||||
@@ -1112,55 +1045,6 @@ impl EditPredictionButton {
|
||||
})
|
||||
}
|
||||
|
||||
fn build_zeta_upsell_context_menu(
|
||||
&self,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Entity<ContextMenu> {
|
||||
ContextMenu::build(window, cx, |mut menu, _window, cx| {
|
||||
menu = menu
|
||||
.custom_row(move |_window, cx| {
|
||||
let description = indoc! {
|
||||
"You get 2,000 accepted suggestions at every keystroke for free, \
|
||||
powered by Zeta, our open-source, open-data model"
|
||||
};
|
||||
|
||||
v_flex()
|
||||
.max_w_64()
|
||||
.h(rems_from_px(148.))
|
||||
.child(render_zeta_tab_animation(cx))
|
||||
.child(Label::new("Edit Prediction"))
|
||||
.child(
|
||||
Label::new(description)
|
||||
.color(Color::Muted)
|
||||
.size(LabelSize::Small),
|
||||
)
|
||||
.into_any_element()
|
||||
})
|
||||
.separator()
|
||||
.entry("Sign In & Start Using", None, |window, cx| {
|
||||
let client = Client::global(cx);
|
||||
window
|
||||
.spawn(cx, async move |cx| {
|
||||
client
|
||||
.sign_in_with_optional_connect(true, &cx)
|
||||
.await
|
||||
.log_err();
|
||||
})
|
||||
.detach();
|
||||
})
|
||||
.link(
|
||||
"Learn More",
|
||||
OpenBrowser {
|
||||
url: zed_urls::edit_prediction_docs(cx),
|
||||
}
|
||||
.boxed_clone(),
|
||||
);
|
||||
|
||||
menu
|
||||
})
|
||||
}
|
||||
|
||||
pub fn update_enabled(&mut self, editor: Entity<Editor>, cx: &mut Context<Self>) {
|
||||
let editor = editor.read(cx);
|
||||
let snapshot = editor.buffer().read(cx).snapshot(cx);
|
||||
@@ -1364,73 +1248,6 @@ fn toggle_edit_prediction_mode(fs: Arc<dyn Fs>, mode: EditPredictionsMode, cx: &
|
||||
}
|
||||
}
|
||||
|
||||
fn render_zeta_tab_animation(cx: &App) -> impl IntoElement {
|
||||
let tab = |n: u64, inverted: bool| {
|
||||
let text_color = cx.theme().colors().text;
|
||||
|
||||
h_flex().child(
|
||||
h_flex()
|
||||
.text_size(TextSize::XSmall.rems(cx))
|
||||
.text_color(text_color)
|
||||
.child("tab")
|
||||
.with_animation(
|
||||
ElementId::Integer(n),
|
||||
Animation::new(Duration::from_secs(3)).repeat(),
|
||||
move |tab, delta| {
|
||||
let n_f32 = n as f32;
|
||||
|
||||
let offset = if inverted {
|
||||
0.2 * (4.0 - n_f32)
|
||||
} else {
|
||||
0.2 * n_f32
|
||||
};
|
||||
|
||||
let phase = (delta - offset + 1.0) % 1.0;
|
||||
let pulse = if phase < 0.6 {
|
||||
let t = phase / 0.6;
|
||||
1.0 - (0.5 - t).abs() * 2.0
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
let eased = ease_in_out(pulse);
|
||||
let opacity = 0.1 + 0.5 * eased;
|
||||
|
||||
tab.text_color(text_color.opacity(opacity))
|
||||
},
|
||||
),
|
||||
)
|
||||
};
|
||||
|
||||
let tab_sequence = |inverted: bool| {
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.child(tab(0, inverted))
|
||||
.child(tab(1, inverted))
|
||||
.child(tab(2, inverted))
|
||||
.child(tab(3, inverted))
|
||||
.child(tab(4, inverted))
|
||||
};
|
||||
|
||||
h_flex()
|
||||
.my_1p5()
|
||||
.p_4()
|
||||
.justify_center()
|
||||
.gap_2()
|
||||
.rounded_xs()
|
||||
.border_1()
|
||||
.border_dashed()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.bg(gpui::pattern_slash(
|
||||
cx.theme().colors().border.opacity(0.5),
|
||||
1.,
|
||||
8.,
|
||||
))
|
||||
.child(tab_sequence(true))
|
||||
.child(Icon::new(IconName::ZedPredict))
|
||||
.child(tab_sequence(false))
|
||||
}
|
||||
|
||||
fn copilot_settings_url(enterprise_uri: Option<&str>) -> String {
|
||||
match enterprise_uri {
|
||||
Some(uri) => {
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
use gpui::{
|
||||
DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, IntoElement, ParentElement, Render,
|
||||
};
|
||||
use ui::{Button, ButtonStyle, Clickable, Headline, HeadlineSize, prelude::*};
|
||||
use ui_input::InputField;
|
||||
use workspace::ModalView;
|
||||
use zeta::Zeta;
|
||||
|
||||
pub struct SweepApiKeyModal {
|
||||
api_key_input: Entity<InputField>,
|
||||
focus_handle: FocusHandle,
|
||||
}
|
||||
|
||||
impl SweepApiKeyModal {
|
||||
pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let api_key_input = cx.new(|cx| InputField::new(window, cx, "Enter your Sweep API token"));
|
||||
|
||||
Self {
|
||||
api_key_input,
|
||||
focus_handle: cx.focus_handle(),
|
||||
}
|
||||
}
|
||||
|
||||
fn cancel(&mut self, _: &menu::Cancel, _window: &mut Window, cx: &mut Context<Self>) {
|
||||
cx.emit(DismissEvent);
|
||||
}
|
||||
|
||||
fn confirm(&mut self, _: &menu::Confirm, _window: &mut Window, cx: &mut Context<Self>) {
|
||||
let api_key = self.api_key_input.read(cx).text(cx);
|
||||
let api_key = (!api_key.trim().is_empty()).then_some(api_key);
|
||||
|
||||
if let Some(zeta) = Zeta::try_global(cx) {
|
||||
zeta.update(cx, |zeta, cx| {
|
||||
zeta.sweep_ai
|
||||
.set_api_token(api_key, cx)
|
||||
.detach_and_log_err(cx);
|
||||
});
|
||||
}
|
||||
|
||||
cx.emit(DismissEvent);
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<DismissEvent> for SweepApiKeyModal {}
|
||||
|
||||
impl ModalView for SweepApiKeyModal {}
|
||||
|
||||
impl Focusable for SweepApiKeyModal {
|
||||
fn focus_handle(&self, _cx: &App) -> FocusHandle {
|
||||
self.focus_handle.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for SweepApiKeyModal {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
v_flex()
|
||||
.key_context("SweepApiKeyModal")
|
||||
.on_action(cx.listener(Self::cancel))
|
||||
.on_action(cx.listener(Self::confirm))
|
||||
.elevation_2(cx)
|
||||
.w(px(400.))
|
||||
.p_4()
|
||||
.gap_3()
|
||||
.child(Headline::new("Sweep API Token").size(HeadlineSize::Small))
|
||||
.child(self.api_key_input.clone())
|
||||
.child(
|
||||
h_flex()
|
||||
.justify_end()
|
||||
.gap_2()
|
||||
.child(Button::new("cancel", "Cancel").on_click(cx.listener(
|
||||
|_, _, _window, cx| {
|
||||
cx.emit(DismissEvent);
|
||||
},
|
||||
)))
|
||||
.child(
|
||||
Button::new("save", "Save")
|
||||
.style(ButtonStyle::Filled)
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
this.confirm(&menu::Confirm, window, cx);
|
||||
})),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,6 @@ dap.workspace = true
|
||||
db.workspace = true
|
||||
buffer_diff.workspace = true
|
||||
emojis.workspace = true
|
||||
feature_flags.workspace = true
|
||||
file_icons.workspace = true
|
||||
futures.workspace = true
|
||||
fuzzy.workspace = true
|
||||
|
||||
@@ -45,7 +45,7 @@ fn to_tab_point_benchmark(c: &mut Criterion) {
|
||||
&snapshot,
|
||||
|bench, snapshot| {
|
||||
bench.iter(|| {
|
||||
snapshot.fold_point_to_tab_point(fold_point);
|
||||
snapshot.to_tab_point(fold_point);
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -79,7 +79,7 @@ fn to_fold_point_benchmark(c: &mut Criterion) {
|
||||
);
|
||||
|
||||
let (_, snapshot) = TabMap::new(fold_snapshot, NonZeroU32::new(4).unwrap());
|
||||
let tab_point = snapshot.fold_point_to_tab_point(fold_point);
|
||||
let tab_point = snapshot.to_tab_point(fold_point);
|
||||
|
||||
(length, snapshot, tab_point)
|
||||
};
|
||||
@@ -94,7 +94,7 @@ fn to_fold_point_benchmark(c: &mut Criterion) {
|
||||
&snapshot,
|
||||
|bench, snapshot| {
|
||||
bench.iter(|| {
|
||||
snapshot.tab_point_to_fold_point(tab_point, Bias::Left);
|
||||
snapshot.to_fold_point(tab_point, Bias::Left);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
@@ -7,7 +7,6 @@ use std::ops::Range;
|
||||
use crate::Editor;
|
||||
use collections::HashMap;
|
||||
use gpui::{Context, HighlightStyle};
|
||||
use itertools::Itertools;
|
||||
use language::language_settings;
|
||||
use multi_buffer::{Anchor, ExcerptId};
|
||||
use ui::{ActiveTheme, utils::ensure_minimum_contrast};
|
||||
@@ -27,23 +26,20 @@ impl Editor {
|
||||
let accents_count = cx.theme().accents().0.len();
|
||||
let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
|
||||
let all_excerpts = self.buffer().read(cx).excerpt_ids();
|
||||
let anchors_in_multi_buffer = |current_excerpt: ExcerptId,
|
||||
text_anchors: [text::Anchor; 4]|
|
||||
-> Option<[Option<_>; 4]> {
|
||||
let anchor_in_multi_buffer = |current_excerpt: ExcerptId, text_anchor: text::Anchor| {
|
||||
multi_buffer_snapshot
|
||||
.anchors_in_excerpt(current_excerpt, text_anchors)
|
||||
.anchor_in_excerpt(current_excerpt, text_anchor)
|
||||
.or_else(|| {
|
||||
all_excerpts
|
||||
.iter()
|
||||
.filter(|&&excerpt_id| excerpt_id != current_excerpt)
|
||||
.find_map(|&excerpt_id| {
|
||||
multi_buffer_snapshot.anchors_in_excerpt(excerpt_id, text_anchors)
|
||||
multi_buffer_snapshot.anchor_in_excerpt(excerpt_id, text_anchor)
|
||||
})
|
||||
})?
|
||||
.collect_array()
|
||||
})
|
||||
};
|
||||
|
||||
let bracket_matches_by_accent = self.visible_excerpts(false, cx).into_iter().fold(
|
||||
let bracket_matches_by_accent = self.visible_excerpts(cx).into_iter().fold(
|
||||
HashMap::default(),
|
||||
|mut acc, (excerpt_id, (buffer, buffer_version, buffer_range))| {
|
||||
let buffer_snapshot = buffer.read(cx).snapshot();
|
||||
@@ -81,24 +77,13 @@ impl Editor {
|
||||
let buffer_close_range = buffer_snapshot
|
||||
.anchor_before(pair.close_range.start)
|
||||
..buffer_snapshot.anchor_after(pair.close_range.end);
|
||||
let [
|
||||
buffer_open_range_start,
|
||||
buffer_open_range_end,
|
||||
buffer_close_range_start,
|
||||
buffer_close_range_end,
|
||||
] = anchors_in_multi_buffer(
|
||||
excerpt_id,
|
||||
[
|
||||
buffer_open_range.start,
|
||||
buffer_open_range.end,
|
||||
buffer_close_range.start,
|
||||
buffer_close_range.end,
|
||||
],
|
||||
)?;
|
||||
let multi_buffer_open_range =
|
||||
buffer_open_range_start.zip(buffer_open_range_end);
|
||||
anchor_in_multi_buffer(excerpt_id, buffer_open_range.start)
|
||||
.zip(anchor_in_multi_buffer(excerpt_id, buffer_open_range.end));
|
||||
let multi_buffer_close_range =
|
||||
buffer_close_range_start.zip(buffer_close_range_end);
|
||||
anchor_in_multi_buffer(excerpt_id, buffer_close_range.start).zip(
|
||||
anchor_in_multi_buffer(excerpt_id, buffer_close_range.end),
|
||||
);
|
||||
|
||||
let mut ranges = Vec::with_capacity(2);
|
||||
if let Some((open_start, open_end)) = multi_buffer_open_range {
|
||||
@@ -164,7 +149,7 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
DisplayPoint, EditorMode, EditorSnapshot, MoveToBeginning, MoveToEnd, MoveUp,
|
||||
DisplayPoint, EditorSnapshot, MoveToBeginning, MoveToEnd, MoveUp,
|
||||
display_map::{DisplayRow, ToDisplayPoint},
|
||||
editor_tests::init_test,
|
||||
test::{
|
||||
@@ -176,7 +161,7 @@ mod tests {
|
||||
use gpui::{AppContext as _, UpdateGlobal as _};
|
||||
use indoc::indoc;
|
||||
use itertools::Itertools;
|
||||
use language::{Capability, markdown_lang};
|
||||
use language::Capability;
|
||||
use languages::rust_lang;
|
||||
use multi_buffer::{ExcerptRange, MultiBuffer};
|
||||
use pretty_assertions::assert_eq;
|
||||
@@ -276,65 +261,6 @@ where
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_file_less_file_colorization(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |language_settings| {
|
||||
language_settings.defaults.colorize_brackets = Some(true);
|
||||
});
|
||||
let editor = cx.add_window(|window, cx| {
|
||||
let multi_buffer = MultiBuffer::build_simple("fn main() {}", cx);
|
||||
multi_buffer.update(cx, |multi_buffer, cx| {
|
||||
multi_buffer
|
||||
.as_singleton()
|
||||
.unwrap()
|
||||
.update(cx, |buffer, cx| {
|
||||
buffer.set_language(Some(rust_lang()), cx);
|
||||
});
|
||||
});
|
||||
Editor::new(EditorMode::full(), multi_buffer, None, window, cx)
|
||||
});
|
||||
|
||||
cx.executor().advance_clock(Duration::from_millis(100));
|
||||
cx.executor().run_until_parked();
|
||||
|
||||
assert_eq!(
|
||||
"fn main«1()1» «1{}1»
|
||||
1 hsla(207.80, 16.20%, 69.19%, 1.00)
|
||||
",
|
||||
editor
|
||||
.update(cx, |editor, window, cx| {
|
||||
editor_bracket_colors_markup(&editor.snapshot(window, cx))
|
||||
})
|
||||
.unwrap(),
|
||||
"File-less buffer should still have its brackets colorized"
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_markdown_bracket_colorization(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |language_settings| {
|
||||
language_settings.defaults.colorize_brackets = Some(true);
|
||||
});
|
||||
let mut cx = EditorLspTestContext::new(
|
||||
Arc::into_inner(markdown_lang()).unwrap(),
|
||||
lsp::ServerCapabilities::default(),
|
||||
cx,
|
||||
)
|
||||
.await;
|
||||
|
||||
cx.set_state(indoc! {r#"ˇ[LLM-powered features](./ai/overview.md), [bring and configure your own API keys](./ai/llm-providers.md#use-your-own-keys)"#});
|
||||
cx.executor().advance_clock(Duration::from_millis(100));
|
||||
cx.executor().run_until_parked();
|
||||
|
||||
assert_eq!(
|
||||
r#"«1[LLM-powered features]1»«1(./ai/overview.md)1», «1[bring and configure your own API keys]1»«1(./ai/llm-providers.md#use-your-own-keys)1»
|
||||
1 hsla(207.80, 16.20%, 69.19%, 1.00)
|
||||
"#,
|
||||
&bracket_colors_markup(&mut cx),
|
||||
"All markdown brackets should be colored based on their depth"
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_bracket_colorization_when_editing(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |language_settings| {
|
||||
|
||||
@@ -49,8 +49,6 @@ pub const MENU_GAP: Pixels = px(4.);
|
||||
pub const MENU_ASIDE_X_PADDING: Pixels = px(16.);
|
||||
pub const MENU_ASIDE_MIN_WIDTH: Pixels = px(260.);
|
||||
pub const MENU_ASIDE_MAX_WIDTH: Pixels = px(500.);
|
||||
pub const COMPLETION_MENU_MIN_WIDTH: Pixels = px(280.);
|
||||
pub const COMPLETION_MENU_MAX_WIDTH: Pixels = px(540.);
|
||||
|
||||
// Constants for the markdown cache. The purpose of this cache is to reduce flickering due to
|
||||
// documentation not yet being parsed.
|
||||
@@ -835,38 +833,36 @@ impl CompletionsMenu {
|
||||
FontWeight::BOLD.into(),
|
||||
)
|
||||
}),
|
||||
styled_runs_for_code_label(
|
||||
&completion.label,
|
||||
&style.syntax,
|
||||
&style.local_player,
|
||||
)
|
||||
.map(|(range, mut highlight)| {
|
||||
// Ignore font weight for syntax highlighting, as we'll use it
|
||||
// for fuzzy matches.
|
||||
highlight.font_weight = None;
|
||||
if completion
|
||||
.source
|
||||
.lsp_completion(false)
|
||||
.and_then(|lsp_completion| {
|
||||
match (lsp_completion.deprecated, &lsp_completion.tags) {
|
||||
(Some(true), _) => Some(true),
|
||||
(_, Some(tags)) => {
|
||||
Some(tags.contains(&CompletionItemTag::DEPRECATED))
|
||||
styled_runs_for_code_label(&completion.label, &style.syntax).map(
|
||||
|(range, mut highlight)| {
|
||||
// Ignore font weight for syntax highlighting, as we'll use it
|
||||
// for fuzzy matches.
|
||||
highlight.font_weight = None;
|
||||
if completion
|
||||
.source
|
||||
.lsp_completion(false)
|
||||
.and_then(|lsp_completion| {
|
||||
match (lsp_completion.deprecated, &lsp_completion.tags)
|
||||
{
|
||||
(Some(true), _) => Some(true),
|
||||
(_, Some(tags)) => Some(
|
||||
tags.contains(&CompletionItemTag::DEPRECATED),
|
||||
),
|
||||
_ => None,
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.unwrap_or(false)
|
||||
{
|
||||
highlight.strikethrough = Some(StrikethroughStyle {
|
||||
thickness: 1.0.into(),
|
||||
..Default::default()
|
||||
});
|
||||
highlight.color = Some(cx.theme().colors().text_muted);
|
||||
}
|
||||
})
|
||||
.unwrap_or(false)
|
||||
{
|
||||
highlight.strikethrough = Some(StrikethroughStyle {
|
||||
thickness: 1.0.into(),
|
||||
..Default::default()
|
||||
});
|
||||
highlight.color = Some(cx.theme().colors().text_muted);
|
||||
}
|
||||
|
||||
(range, highlight)
|
||||
}),
|
||||
(range, highlight)
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
let completion_label = StyledText::new(completion.label.text.clone())
|
||||
@@ -911,36 +907,33 @@ impl CompletionsMenu {
|
||||
})
|
||||
});
|
||||
|
||||
div()
|
||||
.min_w(COMPLETION_MENU_MIN_WIDTH)
|
||||
.max_w(COMPLETION_MENU_MAX_WIDTH)
|
||||
.child(
|
||||
ListItem::new(mat.candidate_id)
|
||||
.inset(true)
|
||||
.toggle_state(item_ix == selected_item)
|
||||
.on_click(cx.listener(move |editor, _event, window, cx| {
|
||||
cx.stop_propagation();
|
||||
if let Some(task) = editor.confirm_completion(
|
||||
&ConfirmCompletion {
|
||||
item_ix: Some(item_ix),
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
) {
|
||||
task.detach_and_log_err(cx)
|
||||
}
|
||||
}))
|
||||
.start_slot::<AnyElement>(start_slot)
|
||||
.child(h_flex().overflow_hidden().child(completion_label))
|
||||
.end_slot::<Label>(documentation_label),
|
||||
)
|
||||
div().min_w(px(280.)).max_w(px(540.)).child(
|
||||
ListItem::new(mat.candidate_id)
|
||||
.inset(true)
|
||||
.toggle_state(item_ix == selected_item)
|
||||
.on_click(cx.listener(move |editor, _event, window, cx| {
|
||||
cx.stop_propagation();
|
||||
if let Some(task) = editor.confirm_completion(
|
||||
&ConfirmCompletion {
|
||||
item_ix: Some(item_ix),
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
) {
|
||||
task.detach_and_log_err(cx)
|
||||
}
|
||||
}))
|
||||
.start_slot::<AnyElement>(start_slot)
|
||||
.child(h_flex().overflow_hidden().child(completion_label))
|
||||
.end_slot::<Label>(documentation_label),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}),
|
||||
)
|
||||
.occlude()
|
||||
.max_h(max_height_in_lines as f32 * window.line_height())
|
||||
.track_scroll(&self.scroll_handle)
|
||||
.track_scroll(self.scroll_handle.clone())
|
||||
.with_sizing_behavior(ListSizingBehavior::Infer)
|
||||
.map(|this| {
|
||||
if self.display_options.dynamic_width {
|
||||
@@ -955,7 +948,7 @@ impl CompletionsMenu {
|
||||
div().child(list).custom_scrollbars(
|
||||
Scrollbars::for_settings::<CompletionMenuScrollBarSetting>()
|
||||
.show_along(ScrollAxes::Vertical)
|
||||
.tracked_scroll_handle(&self.scroll_handle),
|
||||
.tracked_scroll_handle(self.scroll_handle.clone()),
|
||||
window,
|
||||
cx,
|
||||
),
|
||||
@@ -1606,7 +1599,7 @@ impl CodeActionsMenu {
|
||||
)
|
||||
.occlude()
|
||||
.max_h(max_height_in_lines as f32 * window.line_height())
|
||||
.track_scroll(&self.scroll_handle)
|
||||
.track_scroll(self.scroll_handle.clone())
|
||||
.with_width_from_item(
|
||||
self.actions
|
||||
.iter()
|
||||
|
||||
@@ -181,8 +181,6 @@ impl DisplayMap {
|
||||
.update(cx, |map, cx| map.sync(tab_snapshot, edits, cx));
|
||||
let block_snapshot = self.block_map.read(wrap_snapshot, edits).snapshot;
|
||||
|
||||
// todo word diff here?
|
||||
|
||||
DisplaySnapshot {
|
||||
block_snapshot,
|
||||
diagnostics_max_severity: self.diagnostics_max_severity,
|
||||
@@ -891,7 +889,7 @@ impl DisplaySnapshot {
|
||||
pub fn point_to_display_point(&self, point: MultiBufferPoint, bias: Bias) -> DisplayPoint {
|
||||
let inlay_point = self.inlay_snapshot().to_inlay_point(point);
|
||||
let fold_point = self.fold_snapshot().to_fold_point(inlay_point, bias);
|
||||
let tab_point = self.tab_snapshot().fold_point_to_tab_point(fold_point);
|
||||
let tab_point = self.tab_snapshot().to_tab_point(fold_point);
|
||||
let wrap_point = self.wrap_snapshot().tab_point_to_wrap_point(tab_point);
|
||||
let block_point = self.block_snapshot.to_block_point(wrap_point);
|
||||
DisplayPoint(block_point)
|
||||
@@ -921,10 +919,7 @@ impl DisplaySnapshot {
|
||||
let block_point = point.0;
|
||||
let wrap_point = self.block_snapshot.to_wrap_point(block_point, bias);
|
||||
let tab_point = self.wrap_snapshot().to_tab_point(wrap_point);
|
||||
let fold_point = self
|
||||
.tab_snapshot()
|
||||
.tab_point_to_fold_point(tab_point, bias)
|
||||
.0;
|
||||
let fold_point = self.tab_snapshot().to_fold_point(tab_point, bias).0;
|
||||
fold_point.to_inlay_point(self.fold_snapshot())
|
||||
}
|
||||
|
||||
@@ -932,13 +927,11 @@ impl DisplaySnapshot {
|
||||
let block_point = point.0;
|
||||
let wrap_point = self.block_snapshot.to_wrap_point(block_point, bias);
|
||||
let tab_point = self.wrap_snapshot().to_tab_point(wrap_point);
|
||||
self.tab_snapshot()
|
||||
.tab_point_to_fold_point(tab_point, bias)
|
||||
.0
|
||||
self.tab_snapshot().to_fold_point(tab_point, bias).0
|
||||
}
|
||||
|
||||
pub fn fold_point_to_display_point(&self, fold_point: FoldPoint) -> DisplayPoint {
|
||||
let tab_point = self.tab_snapshot().fold_point_to_tab_point(fold_point);
|
||||
let tab_point = self.tab_snapshot().to_tab_point(fold_point);
|
||||
let wrap_point = self.wrap_snapshot().tab_point_to_wrap_point(tab_point);
|
||||
let block_point = self.block_snapshot.to_block_point(wrap_point);
|
||||
DisplayPoint(block_point)
|
||||
@@ -1591,10 +1584,7 @@ impl DisplayPoint {
|
||||
pub fn to_offset(self, map: &DisplaySnapshot, bias: Bias) -> MultiBufferOffset {
|
||||
let wrap_point = map.block_snapshot.to_wrap_point(self.0, bias);
|
||||
let tab_point = map.wrap_snapshot().to_tab_point(wrap_point);
|
||||
let fold_point = map
|
||||
.tab_snapshot()
|
||||
.tab_point_to_fold_point(tab_point, bias)
|
||||
.0;
|
||||
let fold_point = map.tab_snapshot().to_fold_point(tab_point, bias).0;
|
||||
let inlay_point = fold_point.to_inlay_point(map.fold_snapshot());
|
||||
map.inlay_snapshot()
|
||||
.to_buffer_offset(map.inlay_snapshot().to_offset(inlay_point))
|
||||
|
||||
@@ -556,11 +556,6 @@ impl BlockMap {
|
||||
let mut blocks_in_edit = Vec::new();
|
||||
let mut edits = edits.into_iter().peekable();
|
||||
|
||||
let mut inlay_point_cursor = wrap_snapshot.inlay_point_cursor();
|
||||
let mut tab_point_cursor = wrap_snapshot.tab_point_cursor();
|
||||
let mut fold_point_cursor = wrap_snapshot.fold_point_cursor();
|
||||
let mut wrap_point_cursor = wrap_snapshot.wrap_point_cursor();
|
||||
|
||||
while let Some(edit) = edits.next() {
|
||||
let mut old_start = edit.old.start;
|
||||
let mut new_start = edit.new.start;
|
||||
@@ -691,9 +686,6 @@ impl BlockMap {
|
||||
last_block_ix = end_block_ix;
|
||||
|
||||
debug_assert!(blocks_in_edit.is_empty());
|
||||
// + 8 is chosen arbitrarily to cover some multibuffer headers
|
||||
blocks_in_edit
|
||||
.reserve(end_block_ix - start_block_ix + if buffer.is_singleton() { 0 } else { 8 });
|
||||
|
||||
blocks_in_edit.extend(
|
||||
self.custom_blocks[start_block_ix..end_block_ix]
|
||||
@@ -712,14 +704,7 @@ impl BlockMap {
|
||||
blocks_in_edit.extend(self.header_and_footer_blocks(
|
||||
buffer,
|
||||
(start_bound, end_bound),
|
||||
|point, bias| {
|
||||
wrap_point_cursor
|
||||
.map(
|
||||
tab_point_cursor
|
||||
.map(fold_point_cursor.map(inlay_point_cursor.map(point), bias)),
|
||||
)
|
||||
.row()
|
||||
},
|
||||
wrap_snapshot,
|
||||
));
|
||||
|
||||
BlockMap::sort_blocks(&mut blocks_in_edit);
|
||||
@@ -792,12 +777,11 @@ impl BlockMap {
|
||||
}
|
||||
}
|
||||
|
||||
/// Guarantees that `wrap_row_for` is called with points in increasing order.
|
||||
fn header_and_footer_blocks<'a, R, T>(
|
||||
&'a self,
|
||||
buffer: &'a multi_buffer::MultiBufferSnapshot,
|
||||
range: R,
|
||||
mut wrap_row_for: impl 'a + FnMut(Point, Bias) -> WrapRow,
|
||||
wrap_snapshot: &'a WrapSnapshot,
|
||||
) -> impl Iterator<Item = (BlockPlacement<WrapRow>, Block)> + 'a
|
||||
where
|
||||
R: RangeBounds<T>,
|
||||
@@ -808,7 +792,9 @@ impl BlockMap {
|
||||
std::iter::from_fn(move || {
|
||||
loop {
|
||||
let excerpt_boundary = boundaries.next()?;
|
||||
let wrap_row = wrap_row_for(Point::new(excerpt_boundary.row.0, 0), Bias::Left);
|
||||
let wrap_row = wrap_snapshot
|
||||
.make_wrap_point(Point::new(excerpt_boundary.row.0, 0), Bias::Left)
|
||||
.row();
|
||||
|
||||
let new_buffer_id = match (&excerpt_boundary.prev, &excerpt_boundary.next) {
|
||||
(None, next) => Some(next.buffer_id),
|
||||
@@ -840,13 +826,16 @@ impl BlockMap {
|
||||
|
||||
boundaries.next();
|
||||
}
|
||||
let wrap_end_row = wrap_row_for(
|
||||
Point::new(
|
||||
last_excerpt_end_row.0,
|
||||
buffer.line_len(last_excerpt_end_row),
|
||||
),
|
||||
Bias::Right,
|
||||
);
|
||||
|
||||
let wrap_end_row = wrap_snapshot
|
||||
.make_wrap_point(
|
||||
Point::new(
|
||||
last_excerpt_end_row.0,
|
||||
buffer.line_len(last_excerpt_end_row),
|
||||
),
|
||||
Bias::Right,
|
||||
)
|
||||
.row();
|
||||
|
||||
return Some((
|
||||
BlockPlacement::Replace(wrap_row..=wrap_end_row),
|
||||
@@ -3254,23 +3243,11 @@ mod tests {
|
||||
))
|
||||
}));
|
||||
|
||||
let mut inlay_point_cursor = wraps_snapshot.inlay_point_cursor();
|
||||
let mut tab_point_cursor = wraps_snapshot.tab_point_cursor();
|
||||
let mut fold_point_cursor = wraps_snapshot.fold_point_cursor();
|
||||
let mut wrap_point_cursor = wraps_snapshot.wrap_point_cursor();
|
||||
|
||||
// Note that this needs to be synced with the related section in BlockMap::sync
|
||||
expected_blocks.extend(block_map.header_and_footer_blocks(
|
||||
&buffer_snapshot,
|
||||
MultiBufferOffset(0)..,
|
||||
|point, bias| {
|
||||
wrap_point_cursor
|
||||
.map(
|
||||
tab_point_cursor
|
||||
.map(fold_point_cursor.map(inlay_point_cursor.map(point), bias)),
|
||||
)
|
||||
.row()
|
||||
},
|
||||
&wraps_snapshot,
|
||||
));
|
||||
|
||||
BlockMap::sort_blocks(&mut expected_blocks);
|
||||
|
||||
@@ -734,13 +734,6 @@ impl FoldSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fold_point_cursor(&self) -> FoldPointCursor<'_> {
|
||||
let cursor = self
|
||||
.transforms
|
||||
.cursor::<Dimensions<InlayPoint, FoldPoint>>(());
|
||||
FoldPointCursor { cursor }
|
||||
}
|
||||
|
||||
pub fn len(&self) -> FoldOffset {
|
||||
FoldOffset(self.transforms.summary().output.len)
|
||||
}
|
||||
@@ -934,31 +927,6 @@ impl FoldSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FoldPointCursor<'transforms> {
|
||||
cursor: Cursor<'transforms, 'static, Transform, Dimensions<InlayPoint, FoldPoint>>,
|
||||
}
|
||||
|
||||
impl FoldPointCursor<'_> {
|
||||
pub fn map(&mut self, point: InlayPoint, bias: Bias) -> FoldPoint {
|
||||
let cursor = &mut self.cursor;
|
||||
if cursor.did_seek() {
|
||||
cursor.seek_forward(&point, Bias::Right);
|
||||
} else {
|
||||
cursor.seek(&point, Bias::Right);
|
||||
}
|
||||
if cursor.item().is_some_and(|t| t.is_fold()) {
|
||||
if bias == Bias::Left || point == cursor.start().0 {
|
||||
cursor.start().1
|
||||
} else {
|
||||
cursor.end().1
|
||||
}
|
||||
} else {
|
||||
let overshoot = point.0 - cursor.start().0.0;
|
||||
FoldPoint(cmp::min(cursor.start().1.0 + overshoot, cursor.end().1.0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn push_isomorphic(transforms: &mut SumTree<Transform>, summary: MBTextSummary) {
|
||||
let mut did_merge = false;
|
||||
transforms.update_last(
|
||||
|
||||
@@ -879,16 +879,37 @@ impl InlaySnapshot {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_inlay_point(&self, point: Point) -> InlayPoint {
|
||||
self.inlay_point_cursor().map(point)
|
||||
}
|
||||
|
||||
pub fn inlay_point_cursor(&self) -> InlayPointCursor<'_> {
|
||||
let cursor = self.transforms.cursor::<Dimensions<Point, InlayPoint>>(());
|
||||
InlayPointCursor {
|
||||
cursor,
|
||||
transforms: &self.transforms,
|
||||
let mut cursor = self.transforms.cursor::<Dimensions<Point, InlayPoint>>(());
|
||||
cursor.seek(&point, Bias::Left);
|
||||
loop {
|
||||
match cursor.item() {
|
||||
Some(Transform::Isomorphic(_)) => {
|
||||
if point == cursor.end().0 {
|
||||
while let Some(Transform::Inlay(inlay)) = cursor.next_item() {
|
||||
if inlay.position.bias() == Bias::Right {
|
||||
break;
|
||||
} else {
|
||||
cursor.next();
|
||||
}
|
||||
}
|
||||
return cursor.end().1;
|
||||
} else {
|
||||
let overshoot = point - cursor.start().0;
|
||||
return InlayPoint(cursor.start().1.0 + overshoot);
|
||||
}
|
||||
}
|
||||
Some(Transform::Inlay(inlay)) => {
|
||||
if inlay.position.bias() == Bias::Left {
|
||||
cursor.next();
|
||||
} else {
|
||||
return cursor.start().1;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return self.max_point();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1141,51 +1162,6 @@ impl InlaySnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InlayPointCursor<'transforms> {
|
||||
cursor: Cursor<'transforms, 'static, Transform, Dimensions<Point, InlayPoint>>,
|
||||
transforms: &'transforms SumTree<Transform>,
|
||||
}
|
||||
|
||||
impl InlayPointCursor<'_> {
|
||||
pub fn map(&mut self, point: Point) -> InlayPoint {
|
||||
let cursor = &mut self.cursor;
|
||||
if cursor.did_seek() {
|
||||
cursor.seek_forward(&point, Bias::Left);
|
||||
} else {
|
||||
cursor.seek(&point, Bias::Left);
|
||||
}
|
||||
loop {
|
||||
match cursor.item() {
|
||||
Some(Transform::Isomorphic(_)) => {
|
||||
if point == cursor.end().0 {
|
||||
while let Some(Transform::Inlay(inlay)) = cursor.next_item() {
|
||||
if inlay.position.bias() == Bias::Right {
|
||||
break;
|
||||
} else {
|
||||
cursor.next();
|
||||
}
|
||||
}
|
||||
return cursor.end().1;
|
||||
} else {
|
||||
let overshoot = point - cursor.start().0;
|
||||
return InlayPoint(cursor.start().1.0 + overshoot);
|
||||
}
|
||||
}
|
||||
Some(Transform::Inlay(inlay)) => {
|
||||
if inlay.position.bias() == Bias::Left {
|
||||
cursor.next();
|
||||
} else {
|
||||
return cursor.start().1;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return InlayPoint(self.transforms.summary().output.lines);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn push_isomorphic(sum_tree: &mut SumTree<Transform>, summary: MBTextSummary) {
|
||||
if summary.len == MultiBufferOffset(0) {
|
||||
return;
|
||||
|
||||
@@ -137,10 +137,10 @@ impl TabMap {
|
||||
let new_start = fold_edit.new.start.to_point(&new_snapshot.fold_snapshot);
|
||||
let new_end = fold_edit.new.end.to_point(&new_snapshot.fold_snapshot);
|
||||
TabEdit {
|
||||
old: old_snapshot.fold_point_to_tab_point(old_start)
|
||||
..old_snapshot.fold_point_to_tab_point(old_end),
|
||||
new: new_snapshot.fold_point_to_tab_point(new_start)
|
||||
..new_snapshot.fold_point_to_tab_point(new_end),
|
||||
old: old_snapshot.to_tab_point(old_start)
|
||||
..old_snapshot.to_tab_point(old_end),
|
||||
new: new_snapshot.to_tab_point(new_start)
|
||||
..new_snapshot.to_tab_point(new_end),
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
@@ -183,7 +183,7 @@ impl TabSnapshot {
|
||||
pub fn line_len(&self, row: u32) -> u32 {
|
||||
let max_point = self.max_point();
|
||||
if row < max_point.row() {
|
||||
self.fold_point_to_tab_point(FoldPoint::new(row, self.fold_snapshot.line_len(row)))
|
||||
self.to_tab_point(FoldPoint::new(row, self.fold_snapshot.line_len(row)))
|
||||
.0
|
||||
.column
|
||||
} else {
|
||||
@@ -196,8 +196,8 @@ impl TabSnapshot {
|
||||
}
|
||||
|
||||
pub fn text_summary_for_range(&self, range: Range<TabPoint>) -> TextSummary {
|
||||
let input_start = self.tab_point_to_fold_point(range.start, Bias::Left).0;
|
||||
let input_end = self.tab_point_to_fold_point(range.end, Bias::Right).0;
|
||||
let input_start = self.to_fold_point(range.start, Bias::Left).0;
|
||||
let input_end = self.to_fold_point(range.end, Bias::Right).0;
|
||||
let input_summary = self
|
||||
.fold_snapshot
|
||||
.text_summary_for_range(input_start..input_end);
|
||||
@@ -241,11 +241,11 @@ impl TabSnapshot {
|
||||
highlights: Highlights<'a>,
|
||||
) -> TabChunks<'a> {
|
||||
let (input_start, expanded_char_column, to_next_stop) =
|
||||
self.tab_point_to_fold_point(range.start, Bias::Left);
|
||||
self.to_fold_point(range.start, Bias::Left);
|
||||
let input_column = input_start.column();
|
||||
let input_start = input_start.to_offset(&self.fold_snapshot);
|
||||
let input_end = self
|
||||
.tab_point_to_fold_point(range.end, Bias::Right)
|
||||
.to_fold_point(range.end, Bias::Right)
|
||||
.0
|
||||
.to_offset(&self.fold_snapshot);
|
||||
let to_next_stop = if range.start.0 + Point::new(0, to_next_stop) > range.end.0 {
|
||||
@@ -292,28 +292,24 @@ impl TabSnapshot {
|
||||
}
|
||||
|
||||
pub fn max_point(&self) -> TabPoint {
|
||||
self.fold_point_to_tab_point(self.fold_snapshot.max_point())
|
||||
self.to_tab_point(self.fold_snapshot.max_point())
|
||||
}
|
||||
|
||||
pub fn clip_point(&self, point: TabPoint, bias: Bias) -> TabPoint {
|
||||
self.fold_point_to_tab_point(
|
||||
self.to_tab_point(
|
||||
self.fold_snapshot
|
||||
.clip_point(self.tab_point_to_fold_point(point, bias).0, bias),
|
||||
.clip_point(self.to_fold_point(point, bias).0, bias),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn fold_point_to_tab_point(&self, input: FoldPoint) -> TabPoint {
|
||||
pub fn to_tab_point(&self, input: FoldPoint) -> TabPoint {
|
||||
let chunks = self.fold_snapshot.chunks_at(FoldPoint::new(input.row(), 0));
|
||||
let tab_cursor = TabStopCursor::new(chunks);
|
||||
let expanded = self.expand_tabs(tab_cursor, input.column());
|
||||
TabPoint::new(input.row(), expanded)
|
||||
}
|
||||
|
||||
pub fn tab_point_cursor(&self) -> TabPointCursor<'_> {
|
||||
TabPointCursor { this: self }
|
||||
}
|
||||
|
||||
pub fn tab_point_to_fold_point(&self, output: TabPoint, bias: Bias) -> (FoldPoint, u32, u32) {
|
||||
pub fn to_fold_point(&self, output: TabPoint, bias: Bias) -> (FoldPoint, u32, u32) {
|
||||
let chunks = self
|
||||
.fold_snapshot
|
||||
.chunks_at(FoldPoint::new(output.row(), 0));
|
||||
@@ -330,14 +326,14 @@ impl TabSnapshot {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn point_to_tab_point(&self, point: Point, bias: Bias) -> TabPoint {
|
||||
pub fn make_tab_point(&self, point: Point, bias: Bias) -> TabPoint {
|
||||
let inlay_point = self.fold_snapshot.inlay_snapshot.to_inlay_point(point);
|
||||
let fold_point = self.fold_snapshot.to_fold_point(inlay_point, bias);
|
||||
self.fold_point_to_tab_point(fold_point)
|
||||
self.to_tab_point(fold_point)
|
||||
}
|
||||
|
||||
pub fn tab_point_to_point(&self, point: TabPoint, bias: Bias) -> Point {
|
||||
let fold_point = self.tab_point_to_fold_point(point, bias).0;
|
||||
pub fn to_point(&self, point: TabPoint, bias: Bias) -> Point {
|
||||
let fold_point = self.to_fold_point(point, bias).0;
|
||||
let inlay_point = fold_point.to_inlay_point(&self.fold_snapshot);
|
||||
self.fold_snapshot
|
||||
.inlay_snapshot
|
||||
@@ -436,17 +432,6 @@ impl TabSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
// todo(lw): Implement TabPointCursor properly
|
||||
pub struct TabPointCursor<'this> {
|
||||
this: &'this TabSnapshot,
|
||||
}
|
||||
|
||||
impl TabPointCursor<'_> {
|
||||
pub fn map(&mut self, point: FoldPoint) -> TabPoint {
|
||||
self.this.fold_point_to_tab_point(point)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
|
||||
pub struct TabPoint(pub Point);
|
||||
|
||||
@@ -542,14 +527,13 @@ pub struct TabChunks<'a> {
|
||||
|
||||
impl TabChunks<'_> {
|
||||
pub(crate) fn seek(&mut self, range: Range<TabPoint>) {
|
||||
let (input_start, expanded_char_column, to_next_stop) = self
|
||||
.snapshot
|
||||
.tab_point_to_fold_point(range.start, Bias::Left);
|
||||
let (input_start, expanded_char_column, to_next_stop) =
|
||||
self.snapshot.to_fold_point(range.start, Bias::Left);
|
||||
let input_column = input_start.column();
|
||||
let input_start = input_start.to_offset(&self.snapshot.fold_snapshot);
|
||||
let input_end = self
|
||||
.snapshot
|
||||
.tab_point_to_fold_point(range.end, Bias::Right)
|
||||
.to_fold_point(range.end, Bias::Right)
|
||||
.0
|
||||
.to_offset(&self.snapshot.fold_snapshot);
|
||||
let to_next_stop = if range.start.0 + Point::new(0, to_next_stop) > range.end.0 {
|
||||
@@ -820,23 +804,23 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
tab_snapshot.expected_to_fold_point(range.start, Bias::Left),
|
||||
tab_snapshot.tab_point_to_fold_point(range.start, Bias::Left),
|
||||
tab_snapshot.to_fold_point(range.start, Bias::Left),
|
||||
"Failed with tab_point at column {ix}"
|
||||
);
|
||||
assert_eq!(
|
||||
tab_snapshot.expected_to_fold_point(range.start, Bias::Right),
|
||||
tab_snapshot.tab_point_to_fold_point(range.start, Bias::Right),
|
||||
tab_snapshot.to_fold_point(range.start, Bias::Right),
|
||||
"Failed with tab_point at column {ix}"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tab_snapshot.expected_to_fold_point(range.end, Bias::Left),
|
||||
tab_snapshot.tab_point_to_fold_point(range.end, Bias::Left),
|
||||
tab_snapshot.to_fold_point(range.end, Bias::Left),
|
||||
"Failed with tab_point at column {ix}"
|
||||
);
|
||||
assert_eq!(
|
||||
tab_snapshot.expected_to_fold_point(range.end, Bias::Right),
|
||||
tab_snapshot.tab_point_to_fold_point(range.end, Bias::Right),
|
||||
tab_snapshot.to_fold_point(range.end, Bias::Right),
|
||||
"Failed with tab_point at column {ix}"
|
||||
);
|
||||
}
|
||||
@@ -856,7 +840,7 @@ mod tests {
|
||||
|
||||
// This should panic with the expected vs actual mismatch
|
||||
let tab_point = TabPoint::new(0, 9);
|
||||
let result = tab_snapshot.tab_point_to_fold_point(tab_point, Bias::Left);
|
||||
let result = tab_snapshot.to_fold_point(tab_point, Bias::Left);
|
||||
let expected = tab_snapshot.expected_to_fold_point(tab_point, Bias::Left);
|
||||
|
||||
assert_eq!(result, expected);
|
||||
@@ -900,26 +884,26 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
tab_snapshot.expected_to_fold_point(range.start, Bias::Left),
|
||||
tab_snapshot.tab_point_to_fold_point(range.start, Bias::Left),
|
||||
tab_snapshot.to_fold_point(range.start, Bias::Left),
|
||||
"Failed with input: {}, with idx: {ix}",
|
||||
input
|
||||
);
|
||||
assert_eq!(
|
||||
tab_snapshot.expected_to_fold_point(range.start, Bias::Right),
|
||||
tab_snapshot.tab_point_to_fold_point(range.start, Bias::Right),
|
||||
tab_snapshot.to_fold_point(range.start, Bias::Right),
|
||||
"Failed with input: {}, with idx: {ix}",
|
||||
input
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tab_snapshot.expected_to_fold_point(range.end, Bias::Left),
|
||||
tab_snapshot.tab_point_to_fold_point(range.end, Bias::Left),
|
||||
tab_snapshot.to_fold_point(range.end, Bias::Left),
|
||||
"Failed with input: {}, with idx: {ix}",
|
||||
input
|
||||
);
|
||||
assert_eq!(
|
||||
tab_snapshot.expected_to_fold_point(range.end, Bias::Right),
|
||||
tab_snapshot.tab_point_to_fold_point(range.end, Bias::Right),
|
||||
tab_snapshot.to_fold_point(range.end, Bias::Right),
|
||||
"Failed with input: {}, with idx: {ix}",
|
||||
input
|
||||
);
|
||||
@@ -959,13 +943,13 @@ mod tests {
|
||||
let input_point = Point::new(0, ix as u32);
|
||||
let output_point = Point::new(0, output.find(c).unwrap() as u32);
|
||||
assert_eq!(
|
||||
tab_snapshot.fold_point_to_tab_point(FoldPoint(input_point)),
|
||||
tab_snapshot.to_tab_point(FoldPoint(input_point)),
|
||||
TabPoint(output_point),
|
||||
"to_tab_point({input_point:?})"
|
||||
);
|
||||
assert_eq!(
|
||||
tab_snapshot
|
||||
.tab_point_to_fold_point(TabPoint(output_point), Bias::Left)
|
||||
.to_fold_point(TabPoint(output_point), Bias::Left)
|
||||
.0,
|
||||
FoldPoint(input_point),
|
||||
"to_fold_point({output_point:?})"
|
||||
@@ -1154,7 +1138,7 @@ mod tests {
|
||||
let column = rng.random_range(0..=max_column + 10);
|
||||
let fold_point = FoldPoint::new(row, column);
|
||||
|
||||
let actual = tab_snapshot.fold_point_to_tab_point(fold_point);
|
||||
let actual = tab_snapshot.to_tab_point(fold_point);
|
||||
let expected = tab_snapshot.expected_to_tab_point(fold_point);
|
||||
|
||||
assert_eq!(
|
||||
|
||||
@@ -622,10 +622,9 @@ impl WrapSnapshot {
|
||||
if transforms.item().is_some_and(|t| t.is_isomorphic()) {
|
||||
input_start.0 += output_start.0 - transforms.start().0.0;
|
||||
}
|
||||
let input_end = self.to_tab_point(output_end);
|
||||
let max_point = self.tab_snapshot.max_point();
|
||||
let input_start = input_start.min(max_point);
|
||||
let input_end = input_end.min(max_point);
|
||||
let input_end = self
|
||||
.to_tab_point(output_end)
|
||||
.min(self.tab_snapshot.max_point());
|
||||
WrapChunks {
|
||||
input_chunks: self.tab_snapshot.chunks(
|
||||
input_start..input_end,
|
||||
@@ -778,12 +777,11 @@ impl WrapSnapshot {
|
||||
}
|
||||
|
||||
pub fn to_point(&self, point: WrapPoint, bias: Bias) -> Point {
|
||||
self.tab_snapshot
|
||||
.tab_point_to_point(self.to_tab_point(point), bias)
|
||||
self.tab_snapshot.to_point(self.to_tab_point(point), bias)
|
||||
}
|
||||
|
||||
pub fn make_wrap_point(&self, point: Point, bias: Bias) -> WrapPoint {
|
||||
self.tab_point_to_wrap_point(self.tab_snapshot.point_to_tab_point(point, bias))
|
||||
self.tab_point_to_wrap_point(self.tab_snapshot.make_tab_point(point, bias))
|
||||
}
|
||||
|
||||
pub fn tab_point_to_wrap_point(&self, point: TabPoint) -> WrapPoint {
|
||||
@@ -793,14 +791,6 @@ impl WrapSnapshot {
|
||||
WrapPoint(start.1.0 + (point.0 - start.0.0))
|
||||
}
|
||||
|
||||
pub fn wrap_point_cursor(&self) -> WrapPointCursor<'_> {
|
||||
WrapPointCursor {
|
||||
cursor: self
|
||||
.transforms
|
||||
.cursor::<Dimensions<TabPoint, WrapPoint>>(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clip_point(&self, mut point: WrapPoint, bias: Bias) -> WrapPoint {
|
||||
if bias == Bias::Left {
|
||||
let (start, _, item) = self
|
||||
@@ -922,22 +912,6 @@ impl WrapSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WrapPointCursor<'transforms> {
|
||||
cursor: Cursor<'transforms, 'static, Transform, Dimensions<TabPoint, WrapPoint>>,
|
||||
}
|
||||
|
||||
impl WrapPointCursor<'_> {
|
||||
pub fn map(&mut self, point: TabPoint) -> WrapPoint {
|
||||
let cursor = &mut self.cursor;
|
||||
if cursor.did_seek() {
|
||||
cursor.seek_forward(&point, Bias::Right);
|
||||
} else {
|
||||
cursor.seek(&point, Bias::Right);
|
||||
}
|
||||
WrapPoint(cursor.start().1.0 + (point.0 - cursor.start().0.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl WrapChunks<'_> {
|
||||
pub(crate) fn seek(&mut self, rows: Range<WrapRow>) {
|
||||
let output_start = WrapPoint::new(rows.start, 0);
|
||||
@@ -947,10 +921,10 @@ impl WrapChunks<'_> {
|
||||
if self.transforms.item().is_some_and(|t| t.is_isomorphic()) {
|
||||
input_start.0 += output_start.0 - self.transforms.start().0.0;
|
||||
}
|
||||
let input_end = self.snapshot.to_tab_point(output_end);
|
||||
let max_point = self.snapshot.tab_snapshot.max_point();
|
||||
let input_start = input_start.min(max_point);
|
||||
let input_end = input_end.min(max_point);
|
||||
let input_end = self
|
||||
.snapshot
|
||||
.to_tab_point(output_end)
|
||||
.min(self.snapshot.tab_snapshot.max_point());
|
||||
self.input_chunks.seek(input_start..input_end);
|
||||
self.input_chunk = Chunk::default();
|
||||
self.output_position = output_start;
|
||||
@@ -1056,7 +1030,6 @@ impl Iterator for WrapRows<'_> {
|
||||
RowInfo {
|
||||
buffer_id: None,
|
||||
buffer_row: None,
|
||||
base_text_row: None,
|
||||
multibuffer_row: None,
|
||||
diff_status,
|
||||
expand_info: None,
|
||||
|
||||
@@ -36,7 +36,6 @@ mod persistence;
|
||||
mod rust_analyzer_ext;
|
||||
pub mod scroll;
|
||||
mod selections_collection;
|
||||
mod split;
|
||||
pub mod tasks;
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -70,7 +69,6 @@ pub use multi_buffer::{
|
||||
MultiBufferOffset, MultiBufferOffsetUtf16, MultiBufferSnapshot, PathKey, RowInfo, ToOffset,
|
||||
ToPoint,
|
||||
};
|
||||
pub use split::SplittableEditor;
|
||||
pub use text::Bias;
|
||||
|
||||
use ::git::{
|
||||
@@ -146,8 +144,8 @@ use persistence::DB;
|
||||
use project::{
|
||||
BreakpointWithPosition, CodeAction, Completion, CompletionDisplayOptions, CompletionIntent,
|
||||
CompletionResponse, CompletionSource, DisableAiSettings, DocumentHighlight, InlayHint, InlayId,
|
||||
InvalidationStrategy, Location, LocationLink, LspAction, PrepareRenameResponse, Project,
|
||||
ProjectItem, ProjectPath, ProjectTransaction, TaskSourceKind,
|
||||
InvalidationStrategy, Location, LocationLink, PrepareRenameResponse, Project, ProjectItem,
|
||||
ProjectPath, ProjectTransaction, TaskSourceKind,
|
||||
debugger::{
|
||||
breakpoint_store::{
|
||||
Breakpoint, BreakpointEditAction, BreakpointSessionState, BreakpointState,
|
||||
@@ -284,9 +282,6 @@ pub enum ConflictsTheirs {}
|
||||
pub enum ConflictsOursMarker {}
|
||||
pub enum ConflictsTheirsMarker {}
|
||||
|
||||
pub struct HunkAddedColor;
|
||||
pub struct HunkRemovedColor;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum Navigated {
|
||||
Yes,
|
||||
@@ -310,7 +305,6 @@ enum DisplayDiffHunk {
|
||||
display_row_range: Range<DisplayRow>,
|
||||
multi_buffer_range: Range<Anchor>,
|
||||
status: DiffHunkStatus,
|
||||
word_diffs: Vec<Range<MultiBufferOffset>>,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1204,7 +1198,6 @@ pub struct Editor {
|
||||
applicable_language_settings: HashMap<Option<LanguageName>, LanguageSettings>,
|
||||
accent_overrides: Vec<SharedString>,
|
||||
fetched_tree_sitter_chunks: HashMap<ExcerptId, HashSet<Range<BufferRow>>>,
|
||||
use_base_text_line_numbers: bool,
|
||||
}
|
||||
|
||||
fn debounce_value(debounce_ms: u64) -> Option<Duration> {
|
||||
@@ -1644,7 +1637,7 @@ pub(crate) struct FocusedBlock {
|
||||
focus_handle: WeakFocusHandle,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone)]
|
||||
enum JumpData {
|
||||
MultiBufferRow {
|
||||
row: MultiBufferRow,
|
||||
@@ -2351,7 +2344,6 @@ impl Editor {
|
||||
applicable_language_settings: HashMap::default(),
|
||||
accent_overrides: Vec::new(),
|
||||
fetched_tree_sitter_chunks: HashMap::default(),
|
||||
use_base_text_line_numbers: false,
|
||||
};
|
||||
|
||||
if is_minimap {
|
||||
@@ -5312,10 +5304,12 @@ impl Editor {
|
||||
|
||||
pub fn visible_excerpts(
|
||||
&self,
|
||||
lsp_related_only: bool,
|
||||
cx: &mut Context<Editor>,
|
||||
) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
|
||||
let project = self.project().cloned();
|
||||
let Some(project) = self.project() else {
|
||||
return HashMap::default();
|
||||
};
|
||||
let project = project.read(cx);
|
||||
let multi_buffer = self.buffer().read(cx);
|
||||
let multi_buffer_snapshot = multi_buffer.snapshot(cx);
|
||||
let multi_buffer_visible_start = self
|
||||
@@ -5333,18 +5327,6 @@ impl Editor {
|
||||
.into_iter()
|
||||
.filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
|
||||
.filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
|
||||
if !lsp_related_only {
|
||||
return Some((
|
||||
excerpt_id,
|
||||
(
|
||||
multi_buffer.buffer(buffer.remote_id()).unwrap(),
|
||||
buffer.version().clone(),
|
||||
excerpt_visible_range.start.0..excerpt_visible_range.end.0,
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
let project = project.as_ref()?.read(cx);
|
||||
let buffer_file = project::File::from_dyn(buffer.file())?;
|
||||
let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
|
||||
let worktree_entry = buffer_worktree
|
||||
@@ -6151,43 +6133,9 @@ impl Editor {
|
||||
}
|
||||
|
||||
let provider = self.completion_provider.as_ref()?;
|
||||
|
||||
let lsp_store = self.project().map(|project| project.read(cx).lsp_store());
|
||||
let command = lsp_store.as_ref().and_then(|lsp_store| {
|
||||
let CompletionSource::Lsp {
|
||||
lsp_completion,
|
||||
server_id,
|
||||
..
|
||||
} = &completion.source
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
let lsp_command = lsp_completion.command.as_ref()?;
|
||||
let available_commands = lsp_store
|
||||
.read(cx)
|
||||
.lsp_server_capabilities
|
||||
.get(server_id)
|
||||
.and_then(|server_capabilities| {
|
||||
server_capabilities
|
||||
.execute_command_provider
|
||||
.as_ref()
|
||||
.map(|options| options.commands.as_slice())
|
||||
})?;
|
||||
if available_commands.contains(&lsp_command.command) {
|
||||
Some(CodeAction {
|
||||
server_id: *server_id,
|
||||
range: language::Anchor::MIN..language::Anchor::MIN,
|
||||
lsp_action: LspAction::Command(lsp_command.clone()),
|
||||
resolved: false,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
drop(completion);
|
||||
let apply_edits = provider.apply_additional_edits_for_completion(
|
||||
buffer_handle.clone(),
|
||||
buffer_handle,
|
||||
completions_menu.completions.clone(),
|
||||
candidate_id,
|
||||
true,
|
||||
@@ -6201,29 +6149,8 @@ impl Editor {
|
||||
self.show_signature_help(&ShowSignatureHelp, window, cx);
|
||||
}
|
||||
|
||||
Some(cx.spawn_in(window, async move |editor, cx| {
|
||||
Some(cx.foreground_executor().spawn(async move {
|
||||
apply_edits.await?;
|
||||
|
||||
if let Some((lsp_store, command)) = lsp_store.zip(command) {
|
||||
let title = command.lsp_action.title().to_owned();
|
||||
let project_transaction = lsp_store
|
||||
.update(cx, |lsp_store, cx| {
|
||||
lsp_store.apply_code_action(buffer_handle, command, false, cx)
|
||||
})?
|
||||
.await
|
||||
.context("applying post-completion command")?;
|
||||
if let Some(workspace) = editor.read_with(cx, |editor, _| editor.workspace())? {
|
||||
Self::open_project_transaction(
|
||||
&editor,
|
||||
workspace.downgrade(),
|
||||
project_transaction,
|
||||
title,
|
||||
cx,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}))
|
||||
}
|
||||
@@ -8493,14 +8420,8 @@ impl Editor {
|
||||
(true, true) => ui::IconName::DebugDisabledLogBreakpoint,
|
||||
};
|
||||
|
||||
let color = cx.theme().colors();
|
||||
|
||||
let color = if is_phantom {
|
||||
if collides_with_existing {
|
||||
Color::Custom(color.debugger_accent.blend(color.text.opacity(0.6)))
|
||||
} else {
|
||||
Color::Hint
|
||||
}
|
||||
Color::Hint
|
||||
} else if is_rejected {
|
||||
Color::Disabled
|
||||
} else {
|
||||
@@ -19277,10 +19198,6 @@ impl Editor {
|
||||
self.display_map.read(cx).fold_placeholder.clone()
|
||||
}
|
||||
|
||||
pub fn set_use_base_text_line_numbers(&mut self, show: bool, _cx: &mut Context<Self>) {
|
||||
self.use_base_text_line_numbers = show;
|
||||
}
|
||||
|
||||
pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
|
||||
self.buffer.update(cx, |buffer, cx| {
|
||||
buffer.set_all_diff_hunks_expanded(cx);
|
||||
@@ -19516,10 +19433,6 @@ impl Editor {
|
||||
&hunks
|
||||
.map(|hunk| buffer_diff::DiffHunk {
|
||||
buffer_range: hunk.buffer_range,
|
||||
// We don't need to pass in word diffs here because they're only used for rendering and
|
||||
// this function changes internal state
|
||||
base_word_diffs: Vec::default(),
|
||||
buffer_word_diffs: Vec::default(),
|
||||
diff_base_byte_range: hunk.diff_base_byte_range.start.0
|
||||
..hunk.diff_base_byte_range.end.0,
|
||||
secondary_status: hunk.secondary_status,
|
||||
@@ -21652,22 +21565,12 @@ impl Editor {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let theme_settings = theme::ThemeSettings::get_global(cx);
|
||||
|
||||
theme_settings
|
||||
theme::ThemeSettings::get_global(cx)
|
||||
.theme_overrides
|
||||
.get(cx.theme().name.as_ref())
|
||||
.map(|theme_style| &theme_style.accents)
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.chain(
|
||||
theme_settings
|
||||
.experimental_theme_overrides
|
||||
.as_ref()
|
||||
.map(|overrides| &overrides.accents)
|
||||
.into_iter()
|
||||
.flatten(),
|
||||
)
|
||||
.flat_map(|accent| accent.0.clone())
|
||||
.collect()
|
||||
}
|
||||
@@ -22665,7 +22568,7 @@ impl Editor {
|
||||
if self.ignore_lsp_data() {
|
||||
return;
|
||||
}
|
||||
for (_, (visible_buffer, _, _)) in self.visible_excerpts(true, cx) {
|
||||
for (_, (visible_buffer, _, _)) in self.visible_excerpts(cx) {
|
||||
self.register_buffer(visible_buffer.read(cx).remote_id(), cx);
|
||||
}
|
||||
}
|
||||
@@ -24189,12 +24092,10 @@ impl EditorSnapshot {
|
||||
end_row.0 += 1;
|
||||
}
|
||||
let is_created_file = hunk.is_created_file();
|
||||
|
||||
DisplayDiffHunk::Unfolded {
|
||||
status: hunk.status(),
|
||||
diff_base_byte_range: hunk.diff_base_byte_range.start.0
|
||||
..hunk.diff_base_byte_range.end.0,
|
||||
word_diffs: hunk.word_diffs,
|
||||
display_row_range: hunk_display_start.row()..end_row,
|
||||
multi_buffer_range: Anchor::range_in_buffer(
|
||||
hunk.excerpt_id,
|
||||
@@ -24997,7 +24898,6 @@ pub fn diagnostic_style(severity: lsp::DiagnosticSeverity, colors: &StatusColors
|
||||
pub fn styled_runs_for_code_label<'a>(
|
||||
label: &'a CodeLabel,
|
||||
syntax_theme: &'a theme::SyntaxTheme,
|
||||
local_player: &'a theme::PlayerColor,
|
||||
) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
|
||||
let fade_out = HighlightStyle {
|
||||
fade_out: Some(0.35),
|
||||
@@ -25010,17 +24910,7 @@ pub fn styled_runs_for_code_label<'a>(
|
||||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(move |(ix, (range, highlight_id))| {
|
||||
let style = if *highlight_id == language::HighlightId::TABSTOP_INSERT_ID {
|
||||
HighlightStyle {
|
||||
color: Some(local_player.cursor),
|
||||
..Default::default()
|
||||
}
|
||||
} else if *highlight_id == language::HighlightId::TABSTOP_REPLACE_ID {
|
||||
HighlightStyle {
|
||||
background_color: Some(local_player.selection),
|
||||
..Default::default()
|
||||
}
|
||||
} else if let Some(style) = highlight_id.style(syntax_theme) {
|
||||
let style = if let Some(style) = highlight_id.style(syntax_theme) {
|
||||
style
|
||||
} else {
|
||||
return Default::default();
|
||||
|
||||
@@ -35,9 +35,7 @@ use language_settings::Formatter;
|
||||
use languages::markdown_lang;
|
||||
use languages::rust_lang;
|
||||
use lsp::CompletionParams;
|
||||
use multi_buffer::{
|
||||
IndentGuide, MultiBufferFilterMode, MultiBufferOffset, MultiBufferOffsetUtf16, PathKey,
|
||||
};
|
||||
use multi_buffer::{IndentGuide, MultiBufferOffset, MultiBufferOffsetUtf16, PathKey};
|
||||
use parking_lot::Mutex;
|
||||
use pretty_assertions::{assert_eq, assert_ne};
|
||||
use project::{
|
||||
@@ -14755,180 +14753,6 @@ async fn test_completion(cx: &mut TestAppContext) {
|
||||
apply_additional_edits.await.unwrap();
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_completion_can_run_commands(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
path!("/a"),
|
||||
json!({
|
||||
"main.rs": "",
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
|
||||
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
|
||||
language_registry.add(rust_lang());
|
||||
let command_calls = Arc::new(AtomicUsize::new(0));
|
||||
let registered_command = "_the/command";
|
||||
|
||||
let closure_command_calls = command_calls.clone();
|
||||
let mut fake_servers = language_registry.register_fake_lsp(
|
||||
"Rust",
|
||||
FakeLspAdapter {
|
||||
capabilities: lsp::ServerCapabilities {
|
||||
completion_provider: Some(lsp::CompletionOptions {
|
||||
trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
|
||||
..lsp::CompletionOptions::default()
|
||||
}),
|
||||
execute_command_provider: Some(lsp::ExecuteCommandOptions {
|
||||
commands: vec![registered_command.to_owned()],
|
||||
..lsp::ExecuteCommandOptions::default()
|
||||
}),
|
||||
..lsp::ServerCapabilities::default()
|
||||
},
|
||||
initializer: Some(Box::new(move |fake_server| {
|
||||
fake_server.set_request_handler::<lsp::request::Completion, _, _>(
|
||||
move |params, _| async move {
|
||||
Ok(Some(lsp::CompletionResponse::Array(vec![
|
||||
lsp::CompletionItem {
|
||||
label: "registered_command".to_owned(),
|
||||
text_edit: gen_text_edit(¶ms, ""),
|
||||
command: Some(lsp::Command {
|
||||
title: registered_command.to_owned(),
|
||||
command: "_the/command".to_owned(),
|
||||
arguments: Some(vec![serde_json::Value::Bool(true)]),
|
||||
}),
|
||||
..lsp::CompletionItem::default()
|
||||
},
|
||||
lsp::CompletionItem {
|
||||
label: "unregistered_command".to_owned(),
|
||||
text_edit: gen_text_edit(¶ms, ""),
|
||||
command: Some(lsp::Command {
|
||||
title: "????????????".to_owned(),
|
||||
command: "????????????".to_owned(),
|
||||
arguments: Some(vec![serde_json::Value::Null]),
|
||||
}),
|
||||
..lsp::CompletionItem::default()
|
||||
},
|
||||
])))
|
||||
},
|
||||
);
|
||||
fake_server.set_request_handler::<lsp::request::ExecuteCommand, _, _>({
|
||||
let command_calls = closure_command_calls.clone();
|
||||
move |params, _| {
|
||||
assert_eq!(params.command, registered_command);
|
||||
let command_calls = command_calls.clone();
|
||||
async move {
|
||||
command_calls.fetch_add(1, atomic::Ordering::Release);
|
||||
Ok(Some(json!(null)))
|
||||
}
|
||||
}
|
||||
});
|
||||
})),
|
||||
..FakeLspAdapter::default()
|
||||
},
|
||||
);
|
||||
let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
|
||||
let cx = &mut VisualTestContext::from_window(*workspace, cx);
|
||||
let editor = workspace
|
||||
.update(cx, |workspace, window, cx| {
|
||||
workspace.open_abs_path(
|
||||
PathBuf::from(path!("/a/main.rs")),
|
||||
OpenOptions::default(),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.unwrap()
|
||||
.await
|
||||
.unwrap()
|
||||
.downcast::<Editor>()
|
||||
.unwrap();
|
||||
let _fake_server = fake_servers.next().await.unwrap();
|
||||
|
||||
editor.update_in(cx, |editor, window, cx| {
|
||||
cx.focus_self(window);
|
||||
editor.move_to_end(&MoveToEnd, window, cx);
|
||||
editor.handle_input(".", window, cx);
|
||||
});
|
||||
cx.run_until_parked();
|
||||
editor.update(cx, |editor, _| {
|
||||
assert!(editor.context_menu_visible());
|
||||
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
|
||||
{
|
||||
let completion_labels = menu
|
||||
.completions
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|c| c.label.text.clone())
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
completion_labels,
|
||||
&["registered_command", "unregistered_command",],
|
||||
);
|
||||
} else {
|
||||
panic!("expected completion menu to be open");
|
||||
}
|
||||
});
|
||||
|
||||
editor
|
||||
.update_in(cx, |editor, window, cx| {
|
||||
editor
|
||||
.confirm_completion(&ConfirmCompletion::default(), window, cx)
|
||||
.unwrap()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
cx.run_until_parked();
|
||||
assert_eq!(
|
||||
command_calls.load(atomic::Ordering::Acquire),
|
||||
1,
|
||||
"For completion with a registered command, Zed should send a command execution request",
|
||||
);
|
||||
|
||||
editor.update_in(cx, |editor, window, cx| {
|
||||
cx.focus_self(window);
|
||||
editor.handle_input(".", window, cx);
|
||||
});
|
||||
cx.run_until_parked();
|
||||
editor.update(cx, |editor, _| {
|
||||
assert!(editor.context_menu_visible());
|
||||
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
|
||||
{
|
||||
let completion_labels = menu
|
||||
.completions
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|c| c.label.text.clone())
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
completion_labels,
|
||||
&["registered_command", "unregistered_command",],
|
||||
);
|
||||
} else {
|
||||
panic!("expected completion menu to be open");
|
||||
}
|
||||
});
|
||||
editor
|
||||
.update_in(cx, |editor, window, cx| {
|
||||
editor.context_menu_next(&Default::default(), window, cx);
|
||||
editor
|
||||
.confirm_completion(&ConfirmCompletion::default(), window, cx)
|
||||
.unwrap()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
cx.run_until_parked();
|
||||
assert_eq!(
|
||||
command_calls.load(atomic::Ordering::Acquire),
|
||||
1,
|
||||
"For completion with an unregistered command, Zed should not send a command execution request",
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_completion_reuse(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
@@ -28374,289 +28198,3 @@ async fn test_multibuffer_selections_with_folding(cx: &mut TestAppContext) {
|
||||
3
|
||||
"});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_filtered_editor_pair(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
let mut leader_cx = EditorTestContext::new(cx).await;
|
||||
|
||||
let diff_base = indoc!(
|
||||
r#"
|
||||
one
|
||||
two
|
||||
three
|
||||
four
|
||||
five
|
||||
six
|
||||
"#
|
||||
);
|
||||
|
||||
let initial_state = indoc!(
|
||||
r#"
|
||||
ˇone
|
||||
two
|
||||
THREE
|
||||
four
|
||||
five
|
||||
six
|
||||
"#
|
||||
);
|
||||
|
||||
leader_cx.set_state(initial_state);
|
||||
|
||||
leader_cx.set_head_text(&diff_base);
|
||||
leader_cx.run_until_parked();
|
||||
|
||||
let follower = leader_cx.update_multibuffer(|leader, cx| {
|
||||
leader.set_filter_mode(Some(MultiBufferFilterMode::KeepInsertions));
|
||||
leader.set_all_diff_hunks_expanded(cx);
|
||||
leader.get_or_create_follower(cx)
|
||||
});
|
||||
follower.update(cx, |follower, cx| {
|
||||
follower.set_filter_mode(Some(MultiBufferFilterMode::KeepDeletions));
|
||||
follower.set_all_diff_hunks_expanded(cx);
|
||||
});
|
||||
|
||||
let follower_editor =
|
||||
leader_cx.new_window_entity(|window, cx| build_editor(follower, window, cx));
|
||||
// leader_cx.window.focus(&follower_editor.focus_handle(cx));
|
||||
|
||||
let mut follower_cx = EditorTestContext::for_editor_in(follower_editor, &mut leader_cx).await;
|
||||
cx.run_until_parked();
|
||||
|
||||
leader_cx.assert_editor_state(initial_state);
|
||||
follower_cx.assert_editor_state(indoc! {
|
||||
r#"
|
||||
ˇone
|
||||
two
|
||||
three
|
||||
four
|
||||
five
|
||||
six
|
||||
"#
|
||||
});
|
||||
|
||||
follower_cx.editor(|editor, _window, cx| {
|
||||
assert!(editor.read_only(cx));
|
||||
});
|
||||
|
||||
leader_cx.update_editor(|editor, _window, cx| {
|
||||
editor.edit([(Point::new(4, 0)..Point::new(5, 0), "FIVE\n")], cx);
|
||||
});
|
||||
cx.run_until_parked();
|
||||
|
||||
leader_cx.assert_editor_state(indoc! {
|
||||
r#"
|
||||
ˇone
|
||||
two
|
||||
THREE
|
||||
four
|
||||
FIVE
|
||||
six
|
||||
"#
|
||||
});
|
||||
|
||||
follower_cx.assert_editor_state(indoc! {
|
||||
r#"
|
||||
ˇone
|
||||
two
|
||||
three
|
||||
four
|
||||
five
|
||||
six
|
||||
"#
|
||||
});
|
||||
|
||||
leader_cx.update_editor(|editor, _window, cx| {
|
||||
editor.edit([(Point::new(6, 0)..Point::new(6, 0), "SEVEN")], cx);
|
||||
});
|
||||
cx.run_until_parked();
|
||||
|
||||
leader_cx.assert_editor_state(indoc! {
|
||||
r#"
|
||||
ˇone
|
||||
two
|
||||
THREE
|
||||
four
|
||||
FIVE
|
||||
six
|
||||
SEVEN"#
|
||||
});
|
||||
|
||||
follower_cx.assert_editor_state(indoc! {
|
||||
r#"
|
||||
ˇone
|
||||
two
|
||||
three
|
||||
four
|
||||
five
|
||||
six
|
||||
"#
|
||||
});
|
||||
|
||||
leader_cx.update_editor(|editor, window, cx| {
|
||||
editor.move_down(&MoveDown, window, cx);
|
||||
editor.refresh_selected_text_highlights(true, window, cx);
|
||||
});
|
||||
leader_cx.run_until_parked();
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_filtered_editor_pair_complex(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
let base_text = "base\n";
|
||||
let buffer_text = "buffer\n";
|
||||
|
||||
let buffer1 = cx.new(|cx| Buffer::local(buffer_text, cx));
|
||||
let diff1 = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer1, cx));
|
||||
|
||||
let extra_buffer_1 = cx.new(|cx| Buffer::local("dummy text 1\n", cx));
|
||||
let extra_diff_1 = cx.new(|cx| BufferDiff::new_with_base_text("", &extra_buffer_1, cx));
|
||||
let extra_buffer_2 = cx.new(|cx| Buffer::local("dummy text 2\n", cx));
|
||||
let extra_diff_2 = cx.new(|cx| BufferDiff::new_with_base_text("", &extra_buffer_2, cx));
|
||||
|
||||
let leader = cx.new(|cx| {
|
||||
let mut leader = MultiBuffer::new(Capability::ReadWrite);
|
||||
leader.set_all_diff_hunks_expanded(cx);
|
||||
leader.set_filter_mode(Some(MultiBufferFilterMode::KeepInsertions));
|
||||
leader
|
||||
});
|
||||
let follower = leader.update(cx, |leader, cx| leader.get_or_create_follower(cx));
|
||||
follower.update(cx, |follower, _| {
|
||||
follower.set_filter_mode(Some(MultiBufferFilterMode::KeepDeletions));
|
||||
});
|
||||
|
||||
leader.update(cx, |leader, cx| {
|
||||
leader.insert_excerpts_after(
|
||||
ExcerptId::min(),
|
||||
extra_buffer_2.clone(),
|
||||
vec![ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
|
||||
cx,
|
||||
);
|
||||
leader.add_diff(extra_diff_2.clone(), cx);
|
||||
|
||||
leader.insert_excerpts_after(
|
||||
ExcerptId::min(),
|
||||
extra_buffer_1.clone(),
|
||||
vec![ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
|
||||
cx,
|
||||
);
|
||||
leader.add_diff(extra_diff_1.clone(), cx);
|
||||
|
||||
leader.insert_excerpts_after(
|
||||
ExcerptId::min(),
|
||||
buffer1.clone(),
|
||||
vec![ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
|
||||
cx,
|
||||
);
|
||||
leader.add_diff(diff1.clone(), cx);
|
||||
});
|
||||
|
||||
cx.run_until_parked();
|
||||
let mut cx = cx.add_empty_window();
|
||||
|
||||
let leader_editor = cx
|
||||
.new_window_entity(|window, cx| Editor::for_multibuffer(leader.clone(), None, window, cx));
|
||||
let follower_editor = cx.new_window_entity(|window, cx| {
|
||||
Editor::for_multibuffer(follower.clone(), None, window, cx)
|
||||
});
|
||||
|
||||
let mut leader_cx = EditorTestContext::for_editor_in(leader_editor.clone(), &mut cx).await;
|
||||
leader_cx.assert_editor_state(indoc! {"
|
||||
ˇbuffer
|
||||
|
||||
dummy text 1
|
||||
|
||||
dummy text 2
|
||||
"});
|
||||
let mut follower_cx = EditorTestContext::for_editor_in(follower_editor.clone(), &mut cx).await;
|
||||
follower_cx.assert_editor_state(indoc! {"
|
||||
ˇbase
|
||||
|
||||
|
||||
"});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_multibuffer_scroll_cursor_top_margin(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (editor, cx) = cx.add_window_view(|window, cx| {
|
||||
let multi_buffer = MultiBuffer::build_multi(
|
||||
[
|
||||
("1\n2\n3\n", vec![Point::row_range(0..3)]),
|
||||
("1\n2\n3\n4\n5\n6\n7\n8\n9\n", vec![Point::row_range(0..9)]),
|
||||
],
|
||||
cx,
|
||||
);
|
||||
Editor::new(EditorMode::full(), multi_buffer, None, window, cx)
|
||||
});
|
||||
|
||||
let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
|
||||
|
||||
cx.assert_excerpts_with_selections(indoc! {"
|
||||
[EXCERPT]
|
||||
ˇ1
|
||||
2
|
||||
3
|
||||
[EXCERPT]
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
"});
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.change_selections(None.into(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(19)..MultiBufferOffset(19)]);
|
||||
});
|
||||
});
|
||||
|
||||
cx.assert_excerpts_with_selections(indoc! {"
|
||||
[EXCERPT]
|
||||
1
|
||||
2
|
||||
3
|
||||
[EXCERPT]
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
ˇ7
|
||||
8
|
||||
9
|
||||
"});
|
||||
|
||||
cx.update_editor(|editor, _window, cx| {
|
||||
editor.set_vertical_scroll_margin(0, cx);
|
||||
});
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
assert_eq!(editor.vertical_scroll_margin(), 0);
|
||||
editor.scroll_cursor_top(&ScrollCursorTop, window, cx);
|
||||
assert_eq!(
|
||||
editor.snapshot(window, cx).scroll_position(),
|
||||
gpui::Point::new(0., 12.0)
|
||||
);
|
||||
});
|
||||
|
||||
cx.update_editor(|editor, _window, cx| {
|
||||
editor.set_vertical_scroll_margin(3, cx);
|
||||
});
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
assert_eq!(editor.vertical_scroll_margin(), 3);
|
||||
editor.scroll_cursor_top(&ScrollCursorTop, window, cx);
|
||||
assert_eq!(
|
||||
editor.snapshot(window, cx).scroll_position(),
|
||||
gpui::Point::new(0., 9.0)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ use text::{BufferId, SelectionGoal};
|
||||
use theme::{ActiveTheme, Appearance, BufferLineHeight, PlayerColor};
|
||||
use ui::utils::ensure_minimum_contrast;
|
||||
use ui::{
|
||||
ButtonLike, ContextMenu, Indicator, KeyBinding, POPOVER_Y_PADDING, Tooltip, prelude::*,
|
||||
ButtonLike, ContextMenu, Indicator, KeyBinding, POPOVER_Y_PADDING, Tooltip, h_flex, prelude::*,
|
||||
right_click_menu, scrollbars::ShowScrollbar, text_for_keystroke,
|
||||
};
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
@@ -3274,8 +3274,6 @@ impl EditorElement {
|
||||
line_number.clear();
|
||||
let non_relative_number = if relative.wrapped() {
|
||||
row_info.buffer_row.or(row_info.wrapped_buffer_row)? + 1
|
||||
} else if self.editor.read(cx).use_base_text_line_numbers {
|
||||
row_info.base_text_row?.0 + 1
|
||||
} else {
|
||||
row_info.buffer_row? + 1
|
||||
};
|
||||
@@ -3284,7 +3282,6 @@ impl EditorElement {
|
||||
&& row_info
|
||||
.diff_status
|
||||
.is_some_and(|status| status.is_deleted())
|
||||
&& !self.editor.read(cx).use_base_text_line_numbers
|
||||
{
|
||||
return None;
|
||||
}
|
||||
@@ -3972,14 +3969,9 @@ impl EditorElement {
|
||||
.children(toggle_chevron_icon)
|
||||
.tooltip({
|
||||
let focus_handle = focus_handle.clone();
|
||||
let is_folded_for_tooltip = is_folded;
|
||||
move |_window, cx| {
|
||||
Tooltip::with_meta_in(
|
||||
if is_folded_for_tooltip {
|
||||
"Unfold Excerpt"
|
||||
} else {
|
||||
"Fold Excerpt"
|
||||
},
|
||||
"Toggle Excerpt Fold",
|
||||
Some(&ToggleFold),
|
||||
format!(
|
||||
"{} to toggle all",
|
||||
@@ -4031,7 +4023,7 @@ impl EditorElement {
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.size_3()
|
||||
.size(rems_from_px(12.0))
|
||||
.justify_center()
|
||||
.flex_shrink_0()
|
||||
.children(indicator),
|
||||
@@ -4039,12 +4031,11 @@ impl EditorElement {
|
||||
.child(
|
||||
h_flex()
|
||||
.cursor_pointer()
|
||||
.id("path_header_block")
|
||||
.min_w_0()
|
||||
.id("path header block")
|
||||
.size_full()
|
||||
.justify_between()
|
||||
.overflow_hidden()
|
||||
.child(h_flex().min_w_0().flex_1().gap_0p5().map(|path_header| {
|
||||
.child(h_flex().gap_0p5().map(|path_header| {
|
||||
let filename = filename
|
||||
.map(SharedString::from)
|
||||
.unwrap_or_else(|| "untitled".into());
|
||||
@@ -4054,19 +4045,28 @@ impl EditorElement {
|
||||
let path = path::Path::new(filename.as_str());
|
||||
let icon =
|
||||
FileIcons::get_icon(path, cx).unwrap_or_default();
|
||||
|
||||
el.child(Icon::from_path(icon).color(Color::Muted))
|
||||
let icon = Icon::from_path(icon).color(Color::Muted);
|
||||
el.child(icon)
|
||||
})
|
||||
.child(
|
||||
ButtonLike::new("filename-button")
|
||||
.style(ButtonStyle::Subtle)
|
||||
.child(
|
||||
Label::new(filename)
|
||||
.single_line()
|
||||
.color(file_status_label_color(file_status))
|
||||
.when(
|
||||
file_status.is_some_and(|s| s.is_deleted()),
|
||||
|label| label.strikethrough(),
|
||||
),
|
||||
div()
|
||||
.child(
|
||||
Label::new(filename)
|
||||
.single_line()
|
||||
.color(file_status_label_color(
|
||||
file_status,
|
||||
))
|
||||
.when(
|
||||
file_status.is_some_and(|s| {
|
||||
s.is_deleted()
|
||||
}),
|
||||
|label| label.strikethrough(),
|
||||
),
|
||||
)
|
||||
.group_hover("", |div| div.underline()),
|
||||
)
|
||||
.on_click(window.listener_for(&self.editor, {
|
||||
let jump_data = jump_data.clone();
|
||||
@@ -4081,11 +4081,11 @@ impl EditorElement {
|
||||
})),
|
||||
)
|
||||
.when_some(parent_path, |then, path| {
|
||||
then.child(Label::new(path).truncate().color(
|
||||
then.child(div().child(path).text_color(
|
||||
if file_status.is_some_and(FileStatus::is_deleted) {
|
||||
Color::Custom(colors.text_disabled)
|
||||
colors.text_disabled
|
||||
} else {
|
||||
Color::Custom(colors.text_muted)
|
||||
colors.text_muted
|
||||
},
|
||||
))
|
||||
})
|
||||
@@ -4094,13 +4094,18 @@ impl EditorElement {
|
||||
can_open_excerpts && is_selected && relative_path.is_some(),
|
||||
|el| {
|
||||
el.child(
|
||||
Button::new("open-file-button", "Open File")
|
||||
ButtonLike::new("open-file-button")
|
||||
.style(ButtonStyle::OutlinedGhost)
|
||||
.key_binding(KeyBinding::for_action_in(
|
||||
&OpenExcerpts,
|
||||
&focus_handle,
|
||||
cx,
|
||||
))
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_2p5()
|
||||
.child(Label::new("Open file"))
|
||||
.child(KeyBinding::for_action_in(
|
||||
&OpenExcerpts,
|
||||
&focus_handle,
|
||||
cx,
|
||||
)),
|
||||
)
|
||||
.on_click(window.listener_for(&self.editor, {
|
||||
let jump_data = jump_data.clone();
|
||||
move |editor, e: &ClickEvent, window, cx| {
|
||||
@@ -5572,50 +5577,6 @@ impl EditorElement {
|
||||
}
|
||||
}
|
||||
|
||||
fn layout_word_diff_highlights(
|
||||
display_hunks: &[(DisplayDiffHunk, Option<Hitbox>)],
|
||||
row_infos: &[RowInfo],
|
||||
start_row: DisplayRow,
|
||||
snapshot: &EditorSnapshot,
|
||||
highlighted_ranges: &mut Vec<(Range<DisplayPoint>, Hsla)>,
|
||||
cx: &mut App,
|
||||
) {
|
||||
let colors = cx.theme().colors();
|
||||
|
||||
let word_highlights = display_hunks
|
||||
.into_iter()
|
||||
.filter_map(|(hunk, _)| match hunk {
|
||||
DisplayDiffHunk::Unfolded {
|
||||
word_diffs, status, ..
|
||||
} => Some((word_diffs, status)),
|
||||
_ => None,
|
||||
})
|
||||
.filter(|(_, status)| status.is_modified())
|
||||
.flat_map(|(word_diffs, _)| word_diffs)
|
||||
.filter_map(|word_diff| {
|
||||
let start_point = word_diff.start.to_display_point(&snapshot.display_snapshot);
|
||||
let end_point = word_diff.end.to_display_point(&snapshot.display_snapshot);
|
||||
let start_row_offset = start_point.row().0.saturating_sub(start_row.0) as usize;
|
||||
|
||||
row_infos
|
||||
.get(start_row_offset)
|
||||
.and_then(|row_info| row_info.diff_status)
|
||||
.and_then(|diff_status| {
|
||||
let background_color = match diff_status.kind {
|
||||
DiffHunkStatusKind::Added => colors.version_control_word_added,
|
||||
DiffHunkStatusKind::Deleted => colors.version_control_word_deleted,
|
||||
DiffHunkStatusKind::Modified => {
|
||||
debug_panic!("modified diff status for row info");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
Some((start_point..end_point, background_color))
|
||||
})
|
||||
});
|
||||
|
||||
highlighted_ranges.extend(word_highlights);
|
||||
}
|
||||
|
||||
fn layout_diff_hunk_controls(
|
||||
&self,
|
||||
row_range: Range<DisplayRow>,
|
||||
@@ -9166,7 +9127,7 @@ impl Element for EditorElement {
|
||||
);
|
||||
let end_row = DisplayRow(end_row);
|
||||
|
||||
let row_infos = snapshot // note we only get the visual range
|
||||
let row_infos = snapshot
|
||||
.row_infos(start_row)
|
||||
.take((start_row..end_row).len())
|
||||
.collect::<Vec<RowInfo>>();
|
||||
@@ -9197,50 +9158,23 @@ impl Element for EditorElement {
|
||||
|
||||
let is_light = cx.theme().appearance().is_light();
|
||||
|
||||
let mut highlighted_ranges = self
|
||||
.editor_with_selections(cx)
|
||||
.map(|editor| {
|
||||
editor.read(cx).background_highlights_in_range(
|
||||
start_anchor..end_anchor,
|
||||
&snapshot.display_snapshot,
|
||||
cx.theme(),
|
||||
)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
for (ix, row_info) in row_infos.iter().enumerate() {
|
||||
let Some(diff_status) = row_info.diff_status else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let background_color = dbg!(match diff_status.kind {
|
||||
DiffHunkStatusKind::Added =>
|
||||
cx.theme().colors().version_control_added,
|
||||
DiffHunkStatusKind::Deleted =>
|
||||
cx.theme().colors().version_control_deleted,
|
||||
DiffHunkStatusKind::Modified => {
|
||||
debug_panic!("modified diff status for row info");
|
||||
continue;
|
||||
}
|
||||
});
|
||||
let background_color = match diff_status.kind {
|
||||
DiffHunkStatusKind::Added => cx.theme().colors().version_control_added,
|
||||
DiffHunkStatusKind::Deleted => {
|
||||
cx.theme().colors().version_control_deleted
|
||||
}
|
||||
DiffHunkStatusKind::Modified => {
|
||||
debug_panic!("modified diff status for row info");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
// Potential fix with new tokens:
|
||||
// diff.added = "#2EA048"
|
||||
// diff.added_word = @diff.added/24%
|
||||
// diff.added_background = @diff.added/20%
|
||||
//
|
||||
//
|
||||
// Ideal State:
|
||||
// version_control.added = "#2EA048"
|
||||
// version_control.added_word = @version_control.added/24%
|
||||
// version_control.added_background = @version_control.added/20%
|
||||
// version_control.added_icon = "something"
|
||||
|
||||
// let hunk_opacity = if background_color.a < 1.0 {
|
||||
// background_color.a // Use existing alpha
|
||||
// } else {
|
||||
// if is_light { 0.16 } else { 0.20 }
|
||||
// };
|
||||
let hunk_opacity = if is_light { 0.16 } else { 0.12 };
|
||||
|
||||
let hollow_highlight = LineHighlight {
|
||||
background: (background_color.opacity(if is_light {
|
||||
@@ -9259,7 +9193,7 @@ impl Element for EditorElement {
|
||||
};
|
||||
|
||||
let filled_highlight = LineHighlight {
|
||||
background: solid_background(background_color),
|
||||
background: solid_background(background_color.opacity(hunk_opacity)),
|
||||
border: None,
|
||||
include_gutter: true,
|
||||
type_id: None,
|
||||
@@ -9271,14 +9205,21 @@ impl Element for EditorElement {
|
||||
filled_highlight
|
||||
};
|
||||
|
||||
let base_display_point =
|
||||
DisplayPoint::new(start_row + DisplayRow(ix as u32), 0);
|
||||
|
||||
highlighted_rows
|
||||
.entry(base_display_point.row())
|
||||
.entry(start_row + DisplayRow(ix as u32))
|
||||
.or_insert(background);
|
||||
}
|
||||
|
||||
let highlighted_ranges = self
|
||||
.editor_with_selections(cx)
|
||||
.map(|editor| {
|
||||
editor.read(cx).background_highlights_in_range(
|
||||
start_anchor..end_anchor,
|
||||
&snapshot.display_snapshot,
|
||||
cx.theme(),
|
||||
)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let highlighted_gutter_ranges =
|
||||
self.editor.read(cx).gutter_highlights_in_range(
|
||||
start_anchor..end_anchor,
|
||||
@@ -9451,7 +9392,7 @@ impl Element for EditorElement {
|
||||
let crease_trailers =
|
||||
window.with_element_namespace("crease_trailers", |window| {
|
||||
self.layout_crease_trailers(
|
||||
row_infos.iter().cloned(),
|
||||
row_infos.iter().copied(),
|
||||
&snapshot,
|
||||
window,
|
||||
cx,
|
||||
@@ -9467,15 +9408,6 @@ impl Element for EditorElement {
|
||||
cx,
|
||||
);
|
||||
|
||||
Self::layout_word_diff_highlights(
|
||||
&display_hunks,
|
||||
&row_infos,
|
||||
start_row,
|
||||
&snapshot,
|
||||
&mut highlighted_ranges,
|
||||
cx,
|
||||
);
|
||||
|
||||
let merged_highlighted_ranges =
|
||||
if let Some((_, colors)) = document_colors.as_ref() {
|
||||
&highlighted_ranges
|
||||
@@ -10780,9 +10712,9 @@ impl ScrollbarLayout {
|
||||
show_thumb: bool,
|
||||
axis: ScrollbarAxis,
|
||||
) -> Self {
|
||||
let text_units_per_page = viewport_size.to_f64() / glyph_space.to_f64();
|
||||
let text_units_per_page = f64::from(viewport_size / glyph_space);
|
||||
let visible_range = scroll_position..scroll_position + text_units_per_page;
|
||||
let total_text_units = scroll_range / glyph_space.to_f64();
|
||||
let total_text_units = scroll_range / f64::from(glyph_space);
|
||||
|
||||
let thumb_percentage = text_units_per_page / total_text_units;
|
||||
let thumb_size = Pixels::from(ScrollOffset::from(track_length) * thumb_percentage)
|
||||
|
||||
@@ -914,7 +914,7 @@ impl InfoPopover {
|
||||
)
|
||||
.custom_scrollbars(
|
||||
Scrollbars::for_settings::<EditorSettings>()
|
||||
.tracked_scroll_handle(&self.scroll_handle),
|
||||
.tracked_scroll_handle(self.scroll_handle.clone()),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
@@ -1012,7 +1012,7 @@ impl DiagnosticPopover {
|
||||
)
|
||||
.custom_scrollbars(
|
||||
Scrollbars::for_settings::<EditorSettings>()
|
||||
.tracked_scroll_handle(&self.scroll_handle),
|
||||
.tracked_scroll_handle(self.scroll_handle.clone()),
|
||||
window,
|
||||
cx,
|
||||
),
|
||||
|
||||
@@ -291,7 +291,7 @@ impl Editor {
|
||||
}),
|
||||
};
|
||||
|
||||
let mut visible_excerpts = self.visible_excerpts(true, cx);
|
||||
let mut visible_excerpts = self.visible_excerpts(cx);
|
||||
let mut invalidate_hints_for_buffers = HashSet::default();
|
||||
let ignore_previous_fetches = match reason {
|
||||
InlayHintRefreshReason::ModifiersChanged(_)
|
||||
@@ -2211,7 +2211,7 @@ pub mod tests {
|
||||
cx: &mut gpui::TestAppContext,
|
||||
) -> Range<Point> {
|
||||
let ranges = editor
|
||||
.update(cx, |editor, _window, cx| editor.visible_excerpts(true, cx))
|
||||
.update(cx, |editor, _window, cx| editor.visible_excerpts(cx))
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
ranges.len(),
|
||||
|
||||
@@ -929,11 +929,7 @@ impl Item for Editor {
|
||||
})
|
||||
}
|
||||
|
||||
fn as_searchable(
|
||||
&self,
|
||||
handle: &Entity<Self>,
|
||||
_: &App,
|
||||
) -> Option<Box<dyn SearchableItemHandle>> {
|
||||
fn as_searchable(&self, handle: &Entity<Self>) -> Option<Box<dyn SearchableItemHandle>> {
|
||||
Some(Box::new(handle.clone()))
|
||||
}
|
||||
|
||||
|
||||
@@ -164,7 +164,7 @@ impl Editor {
|
||||
}
|
||||
|
||||
let visible_buffers = self
|
||||
.visible_excerpts(true, cx)
|
||||
.visible_excerpts(cx)
|
||||
.into_values()
|
||||
.map(|(buffer, ..)| buffer)
|
||||
.filter(|editor_buffer| {
|
||||
|
||||
@@ -276,8 +276,7 @@ pub fn deploy_context_menu(
|
||||
!has_git_repo,
|
||||
"Copy Permalink",
|
||||
Box::new(CopyPermalinkToLine),
|
||||
)
|
||||
.action_disabled_when(!has_git_repo, "File History", Box::new(git::FileHistory));
|
||||
);
|
||||
match focus {
|
||||
Some(focus) => builder.context(focus),
|
||||
None => builder,
|
||||
|
||||
@@ -71,20 +71,14 @@ impl Editor {
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Editor>,
|
||||
) {
|
||||
let display_snapshot = self.display_snapshot(cx);
|
||||
let scroll_margin_rows = self.vertical_scroll_margin() as u32;
|
||||
let new_screen_top = self
|
||||
.selections
|
||||
.newest_display(&display_snapshot)
|
||||
.newest_display(&self.display_snapshot(cx))
|
||||
.head()
|
||||
.row()
|
||||
.0;
|
||||
let header_offset = display_snapshot
|
||||
.buffer_snapshot()
|
||||
.show_headers()
|
||||
.then(|| display_snapshot.buffer_header_height())
|
||||
.unwrap_or(0);
|
||||
let new_screen_top = new_screen_top.saturating_sub(scroll_margin_rows + header_offset);
|
||||
let new_screen_top = new_screen_top.saturating_sub(scroll_margin_rows);
|
||||
self.set_scroll_top_row(DisplayRow(new_screen_top), window, cx);
|
||||
}
|
||||
|
||||
|
||||
@@ -391,7 +391,7 @@ impl SignatureHelpPopover {
|
||||
)
|
||||
}),
|
||||
)
|
||||
.vertical_scrollbar_for(&self.scroll_handle, window, cx);
|
||||
.vertical_scrollbar_for(self.scroll_handle.clone(), window, cx);
|
||||
|
||||
let controls = if self.signatures.len() > 1 {
|
||||
let prev_button = IconButton::new("signature_help_prev", IconName::ChevronUp)
|
||||
|
||||
@@ -1,267 +0,0 @@
|
||||
use feature_flags::{FeatureFlag, FeatureFlagAppExt as _};
|
||||
use gpui::{
|
||||
Action, AppContext as _, Entity, EventEmitter, Focusable, NoAction, Subscription, WeakEntity,
|
||||
};
|
||||
use multi_buffer::{MultiBuffer, MultiBufferFilterMode};
|
||||
use project::Project;
|
||||
use ui::{
|
||||
App, Context, InteractiveElement as _, IntoElement as _, ParentElement as _, Render,
|
||||
Styled as _, Window, div,
|
||||
};
|
||||
use workspace::{
|
||||
ActivePaneDecorator, Item, ItemHandle, Pane, PaneGroup, SplitDirection, Workspace,
|
||||
};
|
||||
|
||||
use crate::{Editor, EditorEvent};
|
||||
|
||||
struct SplitDiffFeatureFlag;
|
||||
|
||||
impl FeatureFlag for SplitDiffFeatureFlag {
|
||||
const NAME: &'static str = "split-diff";
|
||||
|
||||
fn enabled_for_staff() -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Action, Default)]
|
||||
#[action(namespace = editor)]
|
||||
struct SplitDiff;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Action, Default)]
|
||||
#[action(namespace = editor)]
|
||||
struct UnsplitDiff;
|
||||
|
||||
pub struct SplittableEditor {
|
||||
primary_editor: Entity<Editor>,
|
||||
secondary: Option<SecondaryEditor>,
|
||||
panes: PaneGroup,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
_subscriptions: Vec<Subscription>,
|
||||
}
|
||||
|
||||
struct SecondaryEditor {
|
||||
editor: Entity<Editor>,
|
||||
pane: Entity<Pane>,
|
||||
has_latest_selection: bool,
|
||||
_subscriptions: Vec<Subscription>,
|
||||
}
|
||||
|
||||
impl SplittableEditor {
|
||||
pub fn primary_editor(&self) -> &Entity<Editor> {
|
||||
&self.primary_editor
|
||||
}
|
||||
|
||||
pub fn last_selected_editor(&self) -> &Entity<Editor> {
|
||||
if let Some(secondary) = &self.secondary
|
||||
&& secondary.has_latest_selection
|
||||
{
|
||||
&secondary.editor
|
||||
} else {
|
||||
&self.primary_editor
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_unsplit(
|
||||
buffer: Entity<MultiBuffer>,
|
||||
project: Entity<Project>,
|
||||
workspace: Entity<Workspace>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let primary_editor =
|
||||
cx.new(|cx| Editor::for_multibuffer(buffer, Some(project.clone()), window, cx));
|
||||
let pane = cx.new(|cx| {
|
||||
let mut pane = Pane::new(
|
||||
workspace.downgrade(),
|
||||
project,
|
||||
Default::default(),
|
||||
None,
|
||||
NoAction.boxed_clone(),
|
||||
true,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
pane.set_should_display_tab_bar(|_, _| false);
|
||||
pane.add_item(primary_editor.boxed_clone(), true, true, None, window, cx);
|
||||
pane
|
||||
});
|
||||
let panes = PaneGroup::new(pane);
|
||||
// TODO(split-diff) we might want to tag editor events with whether they came from primary/secondary
|
||||
let subscriptions =
|
||||
vec![
|
||||
cx.subscribe(&primary_editor, |this, _, event: &EditorEvent, cx| {
|
||||
if let EditorEvent::SelectionsChanged { .. } = event
|
||||
&& let Some(secondary) = &mut this.secondary
|
||||
{
|
||||
secondary.has_latest_selection = false;
|
||||
}
|
||||
cx.emit(event.clone())
|
||||
}),
|
||||
];
|
||||
|
||||
window.defer(cx, {
|
||||
let workspace = workspace.downgrade();
|
||||
let primary_editor = primary_editor.downgrade();
|
||||
move |window, cx| {
|
||||
workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
primary_editor.update(cx, |editor, cx| {
|
||||
editor.added_to_workspace(workspace, window, cx);
|
||||
})
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
});
|
||||
Self {
|
||||
primary_editor,
|
||||
secondary: None,
|
||||
panes,
|
||||
workspace: workspace.downgrade(),
|
||||
_subscriptions: subscriptions,
|
||||
}
|
||||
}
|
||||
|
||||
fn split(&mut self, _: &SplitDiff, window: &mut Window, cx: &mut Context<Self>) {
|
||||
if !cx.has_flag::<SplitDiffFeatureFlag>() {
|
||||
return;
|
||||
}
|
||||
if self.secondary.is_some() {
|
||||
return;
|
||||
}
|
||||
let Some(workspace) = self.workspace.upgrade() else {
|
||||
return;
|
||||
};
|
||||
let project = workspace.read(cx).project().clone();
|
||||
let follower = self.primary_editor.update(cx, |primary, cx| {
|
||||
primary.buffer().update(cx, |buffer, cx| {
|
||||
let follower = buffer.get_or_create_follower(cx);
|
||||
buffer.set_filter_mode(Some(MultiBufferFilterMode::KeepInsertions));
|
||||
follower
|
||||
})
|
||||
});
|
||||
follower.update(cx, |follower, _| {
|
||||
follower.set_filter_mode(Some(MultiBufferFilterMode::KeepDeletions));
|
||||
});
|
||||
let secondary_editor = workspace.update(cx, |workspace, cx| {
|
||||
cx.new(|cx| {
|
||||
let mut editor = Editor::for_multibuffer(follower, Some(project), window, cx);
|
||||
// TODO(split-diff) this should be at the multibuffer level
|
||||
editor.set_use_base_text_line_numbers(true, cx);
|
||||
editor.added_to_workspace(workspace, window, cx);
|
||||
editor
|
||||
})
|
||||
});
|
||||
let secondary_pane = cx.new(|cx| {
|
||||
let mut pane = Pane::new(
|
||||
workspace.downgrade(),
|
||||
workspace.read(cx).project().clone(),
|
||||
Default::default(),
|
||||
None,
|
||||
NoAction.boxed_clone(),
|
||||
true,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
pane.set_should_display_tab_bar(|_, _| false);
|
||||
pane.add_item(
|
||||
ItemHandle::boxed_clone(&secondary_editor),
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
pane
|
||||
});
|
||||
|
||||
let subscriptions =
|
||||
vec![
|
||||
cx.subscribe(&secondary_editor, |this, _, event: &EditorEvent, cx| {
|
||||
if let EditorEvent::SelectionsChanged { .. } = event
|
||||
&& let Some(secondary) = &mut this.secondary
|
||||
{
|
||||
secondary.has_latest_selection = true;
|
||||
}
|
||||
cx.emit(event.clone())
|
||||
}),
|
||||
];
|
||||
self.secondary = Some(SecondaryEditor {
|
||||
editor: secondary_editor,
|
||||
pane: secondary_pane.clone(),
|
||||
has_latest_selection: false,
|
||||
_subscriptions: subscriptions,
|
||||
});
|
||||
let primary_pane = self.panes.first_pane();
|
||||
self.panes
|
||||
.split(&primary_pane, &secondary_pane, SplitDirection::Left)
|
||||
.unwrap();
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn unsplit(&mut self, _: &UnsplitDiff, _: &mut Window, cx: &mut Context<Self>) {
|
||||
let Some(secondary) = self.secondary.take() else {
|
||||
return;
|
||||
};
|
||||
self.panes.remove(&secondary.pane).unwrap();
|
||||
self.primary_editor.update(cx, |primary, cx| {
|
||||
primary.buffer().update(cx, |buffer, _| {
|
||||
buffer.set_filter_mode(None);
|
||||
});
|
||||
});
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn added_to_workspace(
|
||||
&mut self,
|
||||
workspace: &mut Workspace,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.workspace = workspace.weak_handle();
|
||||
self.primary_editor.update(cx, |primary_editor, cx| {
|
||||
primary_editor.added_to_workspace(workspace, window, cx);
|
||||
});
|
||||
if let Some(secondary) = &self.secondary {
|
||||
secondary.editor.update(cx, |secondary_editor, cx| {
|
||||
secondary_editor.added_to_workspace(workspace, window, cx);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<EditorEvent> for SplittableEditor {}
|
||||
impl Focusable for SplittableEditor {
|
||||
fn focus_handle(&self, cx: &App) -> gpui::FocusHandle {
|
||||
self.primary_editor.read(cx).focus_handle(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for SplittableEditor {
|
||||
fn render(
|
||||
&mut self,
|
||||
window: &mut ui::Window,
|
||||
cx: &mut ui::Context<Self>,
|
||||
) -> impl ui::IntoElement {
|
||||
let inner = if self.secondary.is_none() {
|
||||
self.primary_editor.clone().into_any_element()
|
||||
} else if let Some(active) = self.panes.panes().into_iter().next() {
|
||||
self.panes
|
||||
.render(
|
||||
None,
|
||||
&ActivePaneDecorator::new(active, &self.workspace),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
.into_any_element()
|
||||
} else {
|
||||
div().into_any_element()
|
||||
};
|
||||
div()
|
||||
.id("splittable-editor")
|
||||
.on_action(cx.listener(Self::split))
|
||||
.on_action(cx.listener(Self::unsplit))
|
||||
.size_full()
|
||||
.child(inner)
|
||||
}
|
||||
}
|
||||
@@ -1376,11 +1376,7 @@ impl ExtensionStore {
|
||||
wasm_extensions.push((extension.manifest.clone(), wasm_extension))
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!(
|
||||
"Failed to load extension: {}, {:#}",
|
||||
extension.manifest.id,
|
||||
e
|
||||
);
|
||||
log::error!("Failed to load extension: {e:#}");
|
||||
this.update(cx, |_, cx| {
|
||||
cx.emit(Event::ExtensionFailedToLoad(extension.manifest.id.clone()))
|
||||
})
|
||||
|
||||
@@ -96,7 +96,7 @@ impl HeadlessExtensionStore {
|
||||
|
||||
for extension in to_load {
|
||||
if let Err(e) = Self::load_extension(this.clone(), extension.clone(), cx).await {
|
||||
log::info!("failed to load extension: {}, {:#}", extension.id, e);
|
||||
log::info!("failed to load extension: {}, {:?}", extension.id, e);
|
||||
missing.push(extension)
|
||||
} else if extension.dev {
|
||||
missing.push(extension)
|
||||
|
||||
@@ -1704,12 +1704,12 @@ impl Render for ExtensionsPage {
|
||||
if count == 0 {
|
||||
this.child(self.render_empty_state(cx)).into_any_element()
|
||||
} else {
|
||||
let scroll_handle = &self.list;
|
||||
let scroll_handle = self.list.clone();
|
||||
this.child(
|
||||
uniform_list("entries", count, cx.processor(Self::render_extensions))
|
||||
.flex_grow()
|
||||
.pb_4()
|
||||
.track_scroll(scroll_handle),
|
||||
.track_scroll(scroll_handle.clone()),
|
||||
)
|
||||
.vertical_scrollbar_for(scroll_handle, window, cx)
|
||||
.into_any_element()
|
||||
|
||||
@@ -1060,7 +1060,7 @@ impl FileFinderDelegate {
|
||||
(
|
||||
filename.to_string(),
|
||||
Vec::new(),
|
||||
prefix.display(path_style).to_string() + path_style.primary_separator(),
|
||||
prefix.display(path_style).to_string() + path_style.separator(),
|
||||
Vec::new(),
|
||||
)
|
||||
} else {
|
||||
@@ -1071,7 +1071,7 @@ impl FileFinderDelegate {
|
||||
.map_or(String::new(), |f| f.to_string_lossy().into_owned()),
|
||||
Vec::new(),
|
||||
entry_path.absolute.parent().map_or(String::new(), |path| {
|
||||
path.to_string_lossy().into_owned() + path_style.primary_separator()
|
||||
path.to_string_lossy().into_owned() + path_style.separator()
|
||||
}),
|
||||
Vec::new(),
|
||||
)
|
||||
|
||||
@@ -1598,7 +1598,7 @@ async fn test_history_match_positions(cx: &mut gpui::TestAppContext) {
|
||||
assert_eq!(file_label.highlight_indices(), &[0, 1, 2]);
|
||||
assert_eq!(
|
||||
path_label.text(),
|
||||
format!("test{}", PathStyle::local().primary_separator())
|
||||
format!("test{}", PathStyle::local().separator())
|
||||
);
|
||||
assert_eq!(path_label.highlight_indices(), &[] as &[usize]);
|
||||
});
|
||||
|
||||
@@ -559,7 +559,7 @@ impl PickerDelegate for OpenPathDelegate {
|
||||
parent_path,
|
||||
candidate.path.string,
|
||||
if candidate.is_dir {
|
||||
path_style.primary_separator()
|
||||
path_style.separator()
|
||||
} else {
|
||||
""
|
||||
}
|
||||
@@ -569,7 +569,7 @@ impl PickerDelegate for OpenPathDelegate {
|
||||
parent_path,
|
||||
candidate.path.string,
|
||||
if candidate.is_dir {
|
||||
path_style.primary_separator()
|
||||
path_style.separator()
|
||||
} else {
|
||||
""
|
||||
}
|
||||
@@ -826,13 +826,7 @@ impl PickerDelegate for OpenPathDelegate {
|
||||
}
|
||||
|
||||
fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
|
||||
Arc::from(
|
||||
format!(
|
||||
"[directory{}]filename.ext",
|
||||
self.path_style.primary_separator()
|
||||
)
|
||||
.as_str(),
|
||||
)
|
||||
Arc::from(format!("[directory{}]filename.ext", self.path_style.separator()).as_str())
|
||||
}
|
||||
|
||||
fn separators_after_indices(&self) -> Vec<usize> {
|
||||
|
||||
@@ -33,7 +33,6 @@ tempfile.workspace = true
|
||||
text.workspace = true
|
||||
time.workspace = true
|
||||
util.workspace = true
|
||||
is_executable = "1.0.5"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
fsevent.workspace = true
|
||||
|
||||
@@ -138,7 +138,6 @@ impl GitRepository for FakeGitRepository {
|
||||
path: RepoPath,
|
||||
content: Option<String>,
|
||||
_env: Arc<HashMap<String, String>>,
|
||||
_is_executable: bool,
|
||||
) -> BoxFuture<'_, anyhow::Result<()>> {
|
||||
self.with_state_async(true, move |state| {
|
||||
if let Some(message) = &state.simulated_index_write_error_message {
|
||||
@@ -432,10 +431,6 @@ impl GitRepository for FakeGitRepository {
|
||||
})
|
||||
}
|
||||
|
||||
fn delete_branch(&self, _name: String) -> BoxFuture<'_, Result<()>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn blame(&self, path: RepoPath, _content: Rope) -> BoxFuture<'_, Result<git::blame::Blame>> {
|
||||
self.with_state_async(false, move |state| {
|
||||
state
|
||||
@@ -446,25 +441,6 @@ impl GitRepository for FakeGitRepository {
|
||||
})
|
||||
}
|
||||
|
||||
fn file_history(&self, path: RepoPath) -> BoxFuture<'_, Result<git::repository::FileHistory>> {
|
||||
self.file_history_paginated(path, 0, None)
|
||||
}
|
||||
|
||||
fn file_history_paginated(
|
||||
&self,
|
||||
path: RepoPath,
|
||||
_skip: usize,
|
||||
_limit: Option<usize>,
|
||||
) -> BoxFuture<'_, Result<git::repository::FileHistory>> {
|
||||
async move {
|
||||
Ok(git::repository::FileHistory {
|
||||
entries: Vec::new(),
|
||||
path,
|
||||
})
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn stage_paths(
|
||||
&self,
|
||||
paths: Vec<RepoPath>,
|
||||
@@ -598,15 +574,7 @@ impl GitRepository for FakeGitRepository {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_push_remote(&self, _branch: String) -> BoxFuture<'_, Result<Option<Remote>>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_branch_remote(&self, _branch: String) -> BoxFuture<'_, Result<Option<Remote>>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_all_remotes(&self) -> BoxFuture<'_, Result<Vec<Remote>>> {
|
||||
fn get_remotes(&self, _branch: Option<String>) -> BoxFuture<'_, Result<Vec<Remote>>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user