Compare commits
203 Commits
fix-git-ht
...
v0.177.11
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2182449e56 | ||
|
|
33d0340729 | ||
|
|
157b78dc92 | ||
|
|
88f5cdb4e8 | ||
|
|
e8dab9c9af | ||
|
|
af6da2d641 | ||
|
|
8185c47594 | ||
|
|
a829e03648 | ||
|
|
f64820a1e1 | ||
|
|
32b9ca125d | ||
|
|
4da6dfa89a | ||
|
|
d6e24d3fe9 | ||
|
|
3f9c2bf81a | ||
|
|
140856c6f8 | ||
|
|
7039018de1 | ||
|
|
7634056f9a | ||
|
|
e3487e7829 | ||
|
|
9df953dd1b | ||
|
|
2bd2ad89df | ||
|
|
fe79166931 | ||
|
|
99d1bcbe78 | ||
|
|
da7da16723 | ||
|
|
17aa999693 | ||
|
|
e37aa89984 | ||
|
|
81e0367d31 | ||
|
|
8979aa8440 | ||
|
|
3f5bee3e23 | ||
|
|
ab6b368698 | ||
|
|
6d6998f0c8 | ||
|
|
f0bc0bf007 | ||
|
|
3a681ecb5c | ||
|
|
d0bd74be78 | ||
|
|
645d8ac58c | ||
|
|
7f1ec5c221 | ||
|
|
38cd1b853a | ||
|
|
438d4c7979 | ||
|
|
b56b4723d4 | ||
|
|
ab9b3f1ba3 | ||
|
|
c20682336b | ||
|
|
ca1ec68399 | ||
|
|
cf2a11ccde | ||
|
|
06d9fbe675 | ||
|
|
f143d1b86e | ||
|
|
7728a8bffb | ||
|
|
d8bc10ec58 | ||
|
|
896276fc4a | ||
|
|
828dd3cf39 | ||
|
|
0bea86d065 | ||
|
|
6a3204cf1d | ||
|
|
6247405396 | ||
|
|
4d521a6f71 | ||
|
|
a34e00e511 | ||
|
|
6ef3870d5f | ||
|
|
70247843db | ||
|
|
c5c3c79a70 | ||
|
|
bbea043d42 | ||
|
|
b7a242b22e | ||
|
|
ac209f11f6 | ||
|
|
44a43c72d0 | ||
|
|
74c1ec5bac | ||
|
|
61187a974e | ||
|
|
2da767d545 | ||
|
|
58bb9f344f | ||
|
|
8c3585d9b6 | ||
|
|
1365247ff6 | ||
|
|
f4028ef9ad | ||
|
|
7fa916a488 | ||
|
|
198ecff6ad | ||
|
|
a4b9b5df8b | ||
|
|
633df53388 | ||
|
|
790fb9e5cb | ||
|
|
17c0113390 | ||
|
|
cb8d667cdb | ||
|
|
6d7943419b | ||
|
|
d474438c9d | ||
|
|
c21ab404e5 | ||
|
|
4b24ef3d7e | ||
|
|
1d7ffff974 | ||
|
|
44c93b087f | ||
|
|
4144a2e37c | ||
|
|
1eff87c2f6 | ||
|
|
25997137fc | ||
|
|
ea2df99770 | ||
|
|
6c574fa94c | ||
|
|
783e239d52 | ||
|
|
4c455ca96e | ||
|
|
270e47cc79 | ||
|
|
c075dd5bd6 | ||
|
|
3129e3dcf5 | ||
|
|
dfbfcdfa2c | ||
|
|
53ff7ca27d | ||
|
|
0a391abaa4 | ||
|
|
e29fb616ed | ||
|
|
9db97b887c | ||
|
|
871bca5b4d | ||
|
|
6dde040b97 | ||
|
|
d6190dbe4f | ||
|
|
7365cac495 | ||
|
|
84f8b86fb9 | ||
|
|
e53389fd53 | ||
|
|
12fb23bf62 | ||
|
|
75f7def31c | ||
|
|
9195cd5233 | ||
|
|
f0be14277a | ||
|
|
0dec6fbfab | ||
|
|
4f8a094c11 | ||
|
|
43c81067c7 | ||
|
|
b7b700e4f7 | ||
|
|
f3fb7b776d | ||
|
|
decfd9877a | ||
|
|
661e36f736 | ||
|
|
c80d25174f | ||
|
|
420bb84152 | ||
|
|
b8d1c3c866 | ||
|
|
090c38d872 | ||
|
|
8a0fb9100e | ||
|
|
664ccc48c8 | ||
|
|
b575bc9a9d | ||
|
|
da895a6fd8 | ||
|
|
2309721274 | ||
|
|
fa2f982848 | ||
|
|
78b460f701 | ||
|
|
2d5063b5f5 | ||
|
|
a87929c5fd | ||
|
|
535c949a1a | ||
|
|
4b6fcef379 | ||
|
|
dc374713d8 | ||
|
|
c084706377 | ||
|
|
578c9f826b | ||
|
|
f06cee40df | ||
|
|
1f936eccc7 | ||
|
|
1516ee3e46 | ||
|
|
53af68aa82 | ||
|
|
e897f191f6 | ||
|
|
aba10b73d2 | ||
|
|
46190bd087 | ||
|
|
0b360febad | ||
|
|
11d75c42f1 | ||
|
|
b3de2bf740 | ||
|
|
b2f174a622 | ||
|
|
a3b7c1d9e3 | ||
|
|
f4b83d1fba | ||
|
|
5de7f1bcd5 | ||
|
|
375885e6ec | ||
|
|
7ab9ec904e | ||
|
|
94425051a1 | ||
|
|
7bd4a85a29 | ||
|
|
7d1b50ea85 | ||
|
|
96ce87d2dd | ||
|
|
1c6bf1f9b1 | ||
|
|
8d9d14c2b9 | ||
|
|
46944b679f | ||
|
|
b1386bff7b | ||
|
|
02204dee06 | ||
|
|
a1e613805a | ||
|
|
5852f2e0a4 | ||
|
|
3130b46515 | ||
|
|
669082dbe0 | ||
|
|
d5bc7b9a79 | ||
|
|
8bb2739e28 | ||
|
|
466be14b56 | ||
|
|
95446195af | ||
|
|
b34c0fd71b | ||
|
|
e0060b92cc | ||
|
|
06bcc42652 | ||
|
|
f24c226af8 | ||
|
|
593f3dc1d5 | ||
|
|
61d584db45 | ||
|
|
c37f616c3b | ||
|
|
73ac19958a | ||
|
|
508b9d3b5d | ||
|
|
0a4ff2f475 | ||
|
|
ae6d350334 | ||
|
|
0e44f93178 | ||
|
|
65d92d7278 | ||
|
|
8b5ef2558b | ||
|
|
fec228bb23 | ||
|
|
e00d737196 | ||
|
|
b0dee94126 | ||
|
|
e65471c7a1 | ||
|
|
6713ec8cdf | ||
|
|
48e09c0026 | ||
|
|
3f03d7b023 | ||
|
|
fa96e2259b | ||
|
|
a8a05f208b | ||
|
|
aa1ab50656 | ||
|
|
d115cb1944 | ||
|
|
42571e405f | ||
|
|
2d61a51ded | ||
|
|
a2876f5d3e | ||
|
|
13deaa3f69 | ||
|
|
694afd15c9 | ||
|
|
eb648dd096 | ||
|
|
1c4c568068 | ||
|
|
ec88a6886f | ||
|
|
7fb16977ce | ||
|
|
53b2792844 | ||
|
|
760d08711c | ||
|
|
71866d6314 | ||
|
|
0c2bbb3aa9 | ||
|
|
9d8a163f5b | ||
|
|
8a22a07d14 | ||
|
|
fad4df5e70 |
51
.github/ISSUE_TEMPLATE/0_git_beta_bug_report.yml
vendored
Normal file
51
.github/ISSUE_TEMPLATE/0_git_beta_bug_report.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
name: Git Beta
|
||||
description: There is a bug related to new Git features in Zed
|
||||
type: "Bug"
|
||||
labels: [git]
|
||||
title: "Git Beta: <a short description of the Git bug>"
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Summary
|
||||
description: Describe the bug with a one line summary, and provide detailed reproduction steps
|
||||
value: |
|
||||
<!-- Please insert a one line summary of the issue below -->
|
||||
|
||||
<!-- Include all steps necessary to reproduce from a clean Zed installation. Be verbose -->
|
||||
Steps to trigger the problem:
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
Actual Behavior:
|
||||
|
||||
Expected Behavior:
|
||||
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: environment
|
||||
attributes:
|
||||
label: Zed Version and System Specs
|
||||
description: 'Open Zed, and in the command palette select "zed: Copy System Specs Into Clipboard"'
|
||||
placeholder: |
|
||||
Output of "zed: Copy System Specs Into Clipboard"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: If applicable, attach your `~/Library/Logs/Zed/Zed.log` file to this issue.
|
||||
description: |
|
||||
macOS: `~/Library/Logs/Zed/Zed.log`
|
||||
Linux: `~/.local/share/zed/logs/Zed.log` or $XDG_DATA_HOME
|
||||
If you only need the most recent lines, you can run the `zed: open log` command palette action to see the last 1000.
|
||||
value: |
|
||||
<details><summary>Zed.log</summary>
|
||||
|
||||
<!-- Click below this line and paste or drag-and-drop your log-->
|
||||
```
|
||||
|
||||
```
|
||||
<!-- Click above this line and paste or drag-and-drop your log--></details>
|
||||
validations:
|
||||
required: false
|
||||
229
.github/workflows/ci.yml
vendored
229
.github/workflows/ci.yml
vendored
@@ -23,9 +23,47 @@ env:
|
||||
RUST_BACKTRACE: 1
|
||||
|
||||
jobs:
|
||||
job_spec:
|
||||
name: Decide which jobs to run
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
outputs:
|
||||
run_tests: ${{ steps.filter.outputs.run_tests }}
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
with:
|
||||
# 350 is arbitrary; ~10days of history on main (5secs); full history is ~25secs
|
||||
fetch-depth: ${{ github.ref == 'refs/heads/main' && 2 || 350 }}
|
||||
- name: Fetch git history and generate output filters
|
||||
id: filter
|
||||
run: |
|
||||
if [ -z "$GITHUB_BASE_REF" ]; then
|
||||
echo "Not in a PR context (i.e., push to main/stable/preview)"
|
||||
COMPARE_REV=$(git rev-parse HEAD~1)
|
||||
else
|
||||
echo "In a PR context comparing to pull_request.base.ref"
|
||||
git fetch origin "$GITHUB_BASE_REF" --depth=350
|
||||
COMPARE_REV=$(git merge-base "origin/${GITHUB_BASE_REF}" HEAD)
|
||||
fi
|
||||
if [[ $(git diff --name-only $COMPARE_REV ${{ github.sha }} | grep -v "^docs/") ]]; then
|
||||
echo "run_tests=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "run_tests=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
if [[ $(git diff --name-only $COMPARE_REV ${{ github.sha }} | grep '^Cargo.lock') ]]; then
|
||||
echo "run_license=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "run_license=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
migration_checks:
|
||||
name: Check Postgres and Protobuf migrations, mergability
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
needs: [job_spec]
|
||||
if: |
|
||||
github.repository_owner == 'zed-industries' &&
|
||||
needs.job_spec.outputs.run_tests == 'true'
|
||||
timeout-minutes: 60
|
||||
runs-on:
|
||||
- self-hosted
|
||||
@@ -69,6 +107,7 @@ jobs:
|
||||
style:
|
||||
timeout-minutes: 60
|
||||
name: Check formatting and spelling
|
||||
needs: [job_spec]
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on:
|
||||
- buildjet-8vcpu-ubuntu-2204
|
||||
@@ -76,6 +115,21 @@ jobs:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
|
||||
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
|
||||
with:
|
||||
version: 9
|
||||
|
||||
- name: Prettier Check on /docs
|
||||
working-directory: ./docs
|
||||
run: |
|
||||
pnpm dlx prettier@${PRETTIER_VERSION} . --check || {
|
||||
echo "To fix, run from the root of the zed repo:"
|
||||
echo " cd docs && pnpm dlx prettier@${PRETTIER_VERSION} . --write && cd .."
|
||||
false
|
||||
}
|
||||
env:
|
||||
PRETTIER_VERSION: 3.5.0
|
||||
|
||||
# To support writing comments that they will certainly be revisited.
|
||||
- name: Check for todo! and FIXME comments
|
||||
run: script/check-todos
|
||||
@@ -91,7 +145,10 @@ jobs:
|
||||
macos_tests:
|
||||
timeout-minutes: 60
|
||||
name: (macOS) Run Clippy and tests
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
needs: [job_spec]
|
||||
if: |
|
||||
github.repository_owner == 'zed-industries' &&
|
||||
needs.job_spec.outputs.run_tests == 'true'
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- test
|
||||
@@ -123,7 +180,9 @@ jobs:
|
||||
- name: Check licenses
|
||||
run: |
|
||||
script/check-licenses
|
||||
script/generate-licenses /tmp/zed_licenses_output
|
||||
if [[ "${{ needs.job_spec.outputs.run_license }}" == "true" ]]; then
|
||||
script/generate-licenses /tmp/zed_licenses_output
|
||||
fi
|
||||
|
||||
- name: Check for new vulnerable dependencies
|
||||
if: github.event_name == 'pull_request'
|
||||
@@ -154,7 +213,10 @@ jobs:
|
||||
linux_tests:
|
||||
timeout-minutes: 60
|
||||
name: (Linux) Run Clippy and tests
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
needs: [job_spec]
|
||||
if: |
|
||||
github.repository_owner == 'zed-industries' &&
|
||||
needs.job_spec.outputs.run_tests == 'true'
|
||||
runs-on:
|
||||
- buildjet-16vcpu-ubuntu-2204
|
||||
steps:
|
||||
@@ -203,9 +265,12 @@ jobs:
|
||||
build_remote_server:
|
||||
timeout-minutes: 60
|
||||
name: (Linux) Build Remote Server
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
needs: [job_spec]
|
||||
if: |
|
||||
github.repository_owner == 'zed-industries' &&
|
||||
needs.job_spec.outputs.run_tests == 'true'
|
||||
runs-on:
|
||||
- buildjet-16vcpu-ubuntu-2204
|
||||
- buildjet-8vcpu-ubuntu-2204
|
||||
steps:
|
||||
- name: Add Rust to the PATH
|
||||
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
|
||||
@@ -236,11 +301,14 @@ jobs:
|
||||
if: always()
|
||||
run: rm -rf ./../.cargo
|
||||
|
||||
windows_tests:
|
||||
windows_clippy:
|
||||
timeout-minutes: 60
|
||||
name: (Windows) Run Clippy and tests
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on: hosted-windows-2
|
||||
name: (Windows) Run Clippy
|
||||
needs: [job_spec]
|
||||
if: |
|
||||
github.repository_owner == 'zed-industries' &&
|
||||
needs.job_spec.outputs.run_tests == 'true'
|
||||
runs-on: windows-2025-16
|
||||
steps:
|
||||
# more info here:- https://github.com/rust-lang/cargo/issues/13020
|
||||
- name: Enable longer pathnames for git
|
||||
@@ -275,6 +343,61 @@ jobs:
|
||||
working-directory: ${{ env.ZED_WORKSPACE }}
|
||||
run: ./script/clippy.ps1
|
||||
|
||||
- name: Check dev drive space
|
||||
working-directory: ${{ env.ZED_WORKSPACE }}
|
||||
# `setup-dev-driver.ps1` creates a 100GB drive, with CI taking up ~45GB of the drive.
|
||||
run: ./script/exit-ci-if-dev-drive-is-full.ps1 95
|
||||
|
||||
# Since the Windows runners are stateful, so we need to remove the config file to prevent potential bug.
|
||||
- name: Clean CI config file
|
||||
if: always()
|
||||
run: |
|
||||
if (Test-Path "${{ env.CARGO_HOME }}/config.toml") {
|
||||
Remove-Item -Path "${{ env.CARGO_HOME }}/config.toml" -Force
|
||||
}
|
||||
|
||||
# Windows CI takes twice as long as our other platforms and fast github hosted runners are expensive.
|
||||
# But we still want to do CI, so let's only run tests on main and come back to this when we're
|
||||
# ready to self host our Windows CI (e.g. during the push for full Windows support)
|
||||
windows_tests:
|
||||
timeout-minutes: 60
|
||||
name: (Windows) Run Tests
|
||||
needs: [job_spec]
|
||||
if: |
|
||||
github.repository_owner == 'zed-industries' &&
|
||||
needs.job_spec.outputs.run_tests == 'true'
|
||||
# Use bigger runners for PRs (speed); smaller for async (cost)
|
||||
runs-on: ${{ github.event_name == 'pull_request' && 'windows-2025-32' || 'windows-2025-16' }}
|
||||
steps:
|
||||
# more info here:- https://github.com/rust-lang/cargo/issues/13020
|
||||
- name: Enable longer pathnames for git
|
||||
run: git config --system core.longpaths true
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
with:
|
||||
clean: false
|
||||
|
||||
- name: Create Dev Drive using ReFS
|
||||
run: ./script/setup-dev-driver.ps1
|
||||
|
||||
# actions/checkout does not let us clone into anywhere outside ${{ github.workspace }}, so we have to copy the clone...
|
||||
- name: Copy Git Repo to Dev Drive
|
||||
run: |
|
||||
Copy-Item -Path "${{ github.workspace }}" -Destination "${{ env.ZED_WORKSPACE }}" -Recurse
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 # v2
|
||||
with:
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
workspaces: ${{ env.ZED_WORKSPACE }}
|
||||
cache-provider: "github"
|
||||
|
||||
- name: Configure CI
|
||||
run: |
|
||||
mkdir -p ${{ env.CARGO_HOME }} -ErrorAction Ignore
|
||||
cp ./.cargo/ci-config.toml ${{ env.CARGO_HOME }}/config.toml
|
||||
|
||||
- name: Run tests
|
||||
uses: ./.github/actions/run_tests_windows
|
||||
with:
|
||||
@@ -292,7 +415,45 @@ jobs:
|
||||
# Since the Windows runners are stateful, so we need to remove the config file to prevent potential bug.
|
||||
- name: Clean CI config file
|
||||
if: always()
|
||||
run: Remove-Item -Path "${{ env.CARGO_HOME }}/config.toml" -Force
|
||||
run: |
|
||||
if (Test-Path "${{ env.CARGO_HOME }}/config.toml") {
|
||||
Remove-Item -Path "${{ env.CARGO_HOME }}/config.toml" -Force
|
||||
}
|
||||
|
||||
tests_pass:
|
||||
name: Tests Pass
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- job_spec
|
||||
- style
|
||||
- migration_checks
|
||||
- linux_tests
|
||||
- build_remote_server
|
||||
- macos_tests
|
||||
- windows_clippy
|
||||
- windows_tests
|
||||
if: always()
|
||||
steps:
|
||||
- name: Check all tests passed
|
||||
run: |
|
||||
# Check dependent jobs...
|
||||
RET_CODE=0
|
||||
# Always check style
|
||||
[[ "${{ needs.style.result }}" != 'success' ]] && { RET_CODE=1; echo "style tests failed"; }
|
||||
|
||||
# Only check test jobs if they were supposed to run
|
||||
if [[ "${{ needs.job_spec.outputs.run_tests }}" == "true" ]]; then
|
||||
[[ "${{ needs.macos_tests.result }}" != 'success' ]] && { RET_CODE=1; echo "macOS tests failed"; }
|
||||
[[ "${{ needs.linux_tests.result }}" != 'success' ]] && { RET_CODE=1; echo "Linux tests failed"; }
|
||||
[[ "${{ needs.windows_tests.result }}" != 'success' ]] && { RET_CODE=1; echo "Windows tests failed"; }
|
||||
[[ "${{ needs.windows_clippy.result }}" != 'success' ]] && { RET_CODE=1; echo "Windows clippy failed"; }
|
||||
[[ "${{ needs.migration_checks.result }}" != 'success' ]] && { RET_CODE=1; echo "Migration checks failed"; }
|
||||
[[ "${{ needs.build_remote_server.result }}" != 'success' ]] && { RET_CODE=1; echo "Remote server build failed"; }
|
||||
fi
|
||||
if [[ "$RET_CODE" -eq 0 ]]; then
|
||||
echo "All tests passed successfully!"
|
||||
fi
|
||||
exit $RET_CODE
|
||||
|
||||
bundle-mac:
|
||||
timeout-minutes: 120
|
||||
@@ -300,7 +461,9 @@ jobs:
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- bundle
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
|
||||
if: |
|
||||
startsWith(github.ref, 'refs/tags/v')
|
||||
|| contains(github.event.pull_request.labels.*.name, 'run-bundling')
|
||||
needs: [macos_tests]
|
||||
env:
|
||||
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
|
||||
@@ -390,7 +553,9 @@ jobs:
|
||||
name: Linux x86_x64 release bundle
|
||||
runs-on:
|
||||
- buildjet-16vcpu-ubuntu-2004
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
|
||||
if: |
|
||||
startsWith(github.ref, 'refs/tags/v')
|
||||
|| contains(github.event.pull_request.labels.*.name, 'run-bundling')
|
||||
needs: [linux_tests]
|
||||
env:
|
||||
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
|
||||
@@ -407,7 +572,7 @@ jobs:
|
||||
run: ./script/linux && ./script/install-mold 2.34.0
|
||||
|
||||
- name: Determine version and release channel
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
run: |
|
||||
# This exports RELEASE_CHANNEL into env (GITHUB_ENV)
|
||||
script/determine-release-channel
|
||||
@@ -417,11 +582,22 @@ jobs:
|
||||
|
||||
- name: Upload Linux bundle to workflow run if main branch or specific label
|
||||
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4
|
||||
if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
|
||||
if: |
|
||||
github.ref == 'refs/heads/main'
|
||||
|| contains(github.event.pull_request.labels.*.name, 'run-bundling')
|
||||
with:
|
||||
name: zed-${{ github.event.pull_request.head.sha || github.sha }}-x86_64-unknown-linux-gnu.tar.gz
|
||||
path: target/release/zed-*.tar.gz
|
||||
|
||||
- name: Upload Linux remote server to workflow run if main branch or specific label
|
||||
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4
|
||||
if: |
|
||||
github.ref == 'refs/heads/main'
|
||||
|| contains(github.event.pull_request.labels.*.name, 'run-bundling')
|
||||
with:
|
||||
name: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-x86_64-unknown-linux-gnu.gz
|
||||
path: target/zed-remote-server-linux-x86_64.gz
|
||||
|
||||
- name: Upload app bundle to release
|
||||
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
|
||||
with:
|
||||
@@ -438,7 +614,9 @@ jobs:
|
||||
name: Linux arm64 release bundle
|
||||
runs-on:
|
||||
- buildjet-16vcpu-ubuntu-2204-arm
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
|
||||
if: |
|
||||
startsWith(github.ref, 'refs/tags/v')
|
||||
|| contains(github.event.pull_request.labels.*.name, 'run-bundling')
|
||||
needs: [linux_tests]
|
||||
env:
|
||||
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
|
||||
@@ -455,7 +633,7 @@ jobs:
|
||||
run: ./script/linux
|
||||
|
||||
- name: Determine version and release channel
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
run: |
|
||||
# This exports RELEASE_CHANNEL into env (GITHUB_ENV)
|
||||
script/determine-release-channel
|
||||
@@ -465,11 +643,22 @@ jobs:
|
||||
|
||||
- name: Upload Linux bundle to workflow run if main branch or specific label
|
||||
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4
|
||||
if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
|
||||
if: |
|
||||
github.ref == 'refs/heads/main'
|
||||
|| contains(github.event.pull_request.labels.*.name, 'run-bundling')
|
||||
with:
|
||||
name: zed-${{ github.event.pull_request.head.sha || github.sha }}-aarch64-unknown-linux-gnu.tar.gz
|
||||
path: target/release/zed-*.tar.gz
|
||||
|
||||
- name: Upload Linux remote server to workflow run if main branch or specific label
|
||||
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4
|
||||
if: |
|
||||
github.ref == 'refs/heads/main'
|
||||
|| contains(github.event.pull_request.labels.*.name, 'run-bundling')
|
||||
with:
|
||||
name: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-aarch64-unknown-linux-gnu.gz
|
||||
path: target/zed-remote-server-linux-aarch64.gz
|
||||
|
||||
- name: Upload app bundle to release
|
||||
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
|
||||
with:
|
||||
@@ -483,7 +672,9 @@ jobs:
|
||||
|
||||
auto-release-preview:
|
||||
name: Auto release preview
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/v') && endsWith(github.ref, '-pre') && !endsWith(github.ref, '.0-pre') }}
|
||||
if: |
|
||||
startsWith(github.ref, 'refs/tags/v')
|
||||
&& endsWith(github.ref, '-pre') && !endsWith(github.ref, '.0-pre')
|
||||
needs: [bundle-mac, bundle-linux-x86_x64, bundle-linux-aarch64]
|
||||
runs-on:
|
||||
- self-hosted
|
||||
|
||||
32
.github/workflows/community_release_actions.yml
vendored
32
.github/workflows/community_release_actions.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
else
|
||||
URL="https://zed.dev/releases/stable/latest"
|
||||
fi
|
||||
echo "::set-output name=URL::$URL"
|
||||
echo "URL=$URL" >> $GITHUB_OUTPUT
|
||||
- name: Get content
|
||||
uses: 2428392/gh-truncate-string-action@b3ff790d21cf42af3ca7579146eedb93c8fb0757 # v1.4.1
|
||||
id: get-content
|
||||
@@ -33,3 +33,33 @@ jobs:
|
||||
with:
|
||||
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||
content: ${{ steps.get-content.outputs.string }}
|
||||
|
||||
send_release_notes_email:
|
||||
if: github.repository_owner == 'zed-industries' && !github.event.release.prerelease
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check if release was promoted from preview
|
||||
id: check-promotion-from-preview
|
||||
run: |
|
||||
VERSION="${{ github.event.release.tag_name }}"
|
||||
PREVIEW_TAG="${VERSION}-pre"
|
||||
if git rev-parse "$PREVIEW_TAG" >/dev/null 2>&1; then
|
||||
echo "was_preview=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "was_preview=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Send release notes email
|
||||
if: steps.check-promotion-from-preview.outputs.was_preview == 'true'
|
||||
run: |
|
||||
curl -X POST "https://zed.dev/api/send_release_notes_email" \
|
||||
-H "Authorization: Bearer ${{ secrets.RELEASE_NOTES_API_TOKEN }}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"version": "${{ github.event.release.tag_name }}",
|
||||
"markdown_body": ${{ toJSON(github.event.release.body) }}
|
||||
}'
|
||||
|
||||
39
.github/workflows/docs.yml
vendored
39
.github/workflows/docs.yml
vendored
@@ -1,39 +0,0 @@
|
||||
name: Docs
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "docs/**"
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
check_formatting:
|
||||
name: "Check formatting"
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
|
||||
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
|
||||
with:
|
||||
version: 9
|
||||
|
||||
- name: Prettier Check on /docs
|
||||
working-directory: ./docs
|
||||
run: |
|
||||
pnpm dlx prettier@${PRETTIER_VERSION} . --check || {
|
||||
echo "To fix, run from the root of the zed repo:"
|
||||
echo " cd docs && pnpm dlx prettier@${PRETTIER_VERSION} . --write && cd .."
|
||||
false
|
||||
}
|
||||
env:
|
||||
PRETTIER_VERSION: 3.5.0
|
||||
|
||||
- name: Check for Typos with Typos-CLI
|
||||
uses: crate-ci/typos@8e6a4285bcbde632c5d79900a7779746e8b7ea3f # v1.24.6
|
||||
with:
|
||||
config: ./typos.toml
|
||||
files: ./docs/
|
||||
224
Cargo.lock
generated
224
Cargo.lock
generated
@@ -358,6 +358,19 @@ dependencies = [
|
||||
"zbus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "askpass"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures 0.3.31",
|
||||
"gpui",
|
||||
"smol",
|
||||
"tempfile",
|
||||
"util",
|
||||
"which 6.0.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "assets"
|
||||
version = "0.1.0"
|
||||
@@ -405,6 +418,7 @@ dependencies = [
|
||||
"pretty_assertions",
|
||||
"project",
|
||||
"prompt_library",
|
||||
"prompt_store",
|
||||
"proto",
|
||||
"rand 0.8.5",
|
||||
"rope",
|
||||
@@ -472,6 +486,7 @@ dependencies = [
|
||||
"picker",
|
||||
"project",
|
||||
"prompt_library",
|
||||
"prompt_store",
|
||||
"proto",
|
||||
"rand 0.8.5",
|
||||
"rope",
|
||||
@@ -526,7 +541,7 @@ dependencies = [
|
||||
"picker",
|
||||
"pretty_assertions",
|
||||
"project",
|
||||
"prompt_library",
|
||||
"prompt_store",
|
||||
"rand 0.8.5",
|
||||
"regex",
|
||||
"rope",
|
||||
@@ -617,7 +632,7 @@ dependencies = [
|
||||
"log",
|
||||
"pretty_assertions",
|
||||
"project",
|
||||
"prompt_library",
|
||||
"prompt_store",
|
||||
"rope",
|
||||
"schemars",
|
||||
"semantic_index",
|
||||
@@ -1176,9 +1191,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-config"
|
||||
version = "1.5.17"
|
||||
version = "1.5.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "490aa7465ee685b2ced076bb87ef654a47724a7844e2c7d3af4e749ce5b875dd"
|
||||
checksum = "50236e4d60fe8458de90a71c0922c761e41755adf091b1b03de1cef537179915"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -1269,9 +1284,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-bedrockruntime"
|
||||
version = "1.75.0"
|
||||
version = "1.74.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ddf7475b6f50a1a5be8edb1bcdf6e4ae00feed5b890d14a3f1f0e14d76f5a16"
|
||||
checksum = "6938541d1948a543bca23303fec4cff9c36bf0e63b8fa3ae1b337bcb9d5b81af"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -1293,9 +1308,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-kinesis"
|
||||
version = "1.62.0"
|
||||
version = "1.61.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e31622345afd0c35d33c1cbba73ccf9fb88e09857413d8963dea2c493e00704d"
|
||||
checksum = "89f2163d8704e8fdcd51ec6c2e0441c418471e422ee9690451b17a1c46344e1a"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -1315,9 +1330,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-s3"
|
||||
version = "1.77.0"
|
||||
version = "1.76.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34e87342432a3de0e94e82c99a7cbd9042f99de029ae1f4e368160f9e9929264"
|
||||
checksum = "66e83401ad7287ad15244d557e35502c2a94105ca5b41d656c391f1a4fc04ca2"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -1349,9 +1364,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-sso"
|
||||
version = "1.60.0"
|
||||
version = "1.58.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60186fab60b24376d3e33b9ff0a43485f99efd470e3b75a9160c849741d63d56"
|
||||
checksum = "16ff718c9ee45cc1ebd4774a0e086bb80a6ab752b4902edf1c9f56b86ee1f770"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -1371,9 +1386,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-ssooidc"
|
||||
version = "1.61.0"
|
||||
version = "1.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7033130ce1ee13e6018905b7b976c915963755aef299c1521897679d6cd4f8ef"
|
||||
checksum = "5183e088715cc135d8d396fdd3bc02f018f0da4c511f53cb8d795b6a31c55809"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -1393,9 +1408,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-sts"
|
||||
version = "1.61.0"
|
||||
version = "1.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5c1cac7677179d622b4448b0d31bcb359185295dc6fca891920cfb17e2b5156"
|
||||
checksum = "c9f944ef032717596639cea4a2118a3a457268ef51bbb5fde9637e54c465da00"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -1456,9 +1471,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-checksums"
|
||||
version = "0.63.0"
|
||||
version = "0.62.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db2dc8d842d872529355c72632de49ef8c5a2949a4472f10e802f28cf925770c"
|
||||
checksum = "f2f45a1c384d7a393026bc5f5c177105aa9fa68e4749653b985707ac27d77295"
|
||||
dependencies = [
|
||||
"aws-smithy-http",
|
||||
"aws-smithy-types",
|
||||
@@ -1808,7 +1823,7 @@ dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"itertools 0.10.5",
|
||||
"itertools 0.12.1",
|
||||
"lazy_static",
|
||||
"lazycell",
|
||||
"log",
|
||||
@@ -1831,7 +1846,7 @@ dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"itertools 0.10.5",
|
||||
"itertools 0.12.1",
|
||||
"log",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
@@ -2076,6 +2091,7 @@ name = "buffer_diff"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clock",
|
||||
"ctor",
|
||||
"env_logger 0.11.6",
|
||||
"futures 0.3.31",
|
||||
@@ -2401,6 +2417,25 @@ dependencies = [
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cbindgen"
|
||||
version = "0.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fce8dd7fcfcbf3a0a87d8f515194b49d6135acab73e18bd380d1d93bb1a15eb"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"heck 0.4.1",
|
||||
"indexmap",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"syn 2.0.90",
|
||||
"tempfile",
|
||||
"toml 0.8.20",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cbindgen"
|
||||
version = "0.28.0"
|
||||
@@ -2498,9 +2533,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.40"
|
||||
version = "0.4.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c"
|
||||
checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
@@ -2508,7 +2543,7 @@ dependencies = [
|
||||
"num-traits",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
"windows-link",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2841,7 +2876,7 @@ dependencies = [
|
||||
"pretty_assertions",
|
||||
"project",
|
||||
"prometheus",
|
||||
"prompt_library",
|
||||
"prompt_store",
|
||||
"prost 0.9.0",
|
||||
"rand 0.8.5",
|
||||
"recent_projects",
|
||||
@@ -3011,8 +3046,12 @@ dependencies = [
|
||||
name = "component_preview"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"client",
|
||||
"component",
|
||||
"gpui",
|
||||
"languages",
|
||||
"notifications",
|
||||
"project",
|
||||
"ui",
|
||||
"workspace",
|
||||
]
|
||||
@@ -3505,10 +3544,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crc64fast-nvme"
|
||||
version = "1.2.0"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4955638f00a809894c947f85a024020a20815b65a5eea633798ea7924edab2b3"
|
||||
checksum = "d5e2ee08013e3f228d6d2394116c4549a6df77708442c62d887d83f68ef2ee37"
|
||||
dependencies = [
|
||||
"cbindgen 0.27.0",
|
||||
"crc",
|
||||
]
|
||||
|
||||
@@ -4411,7 +4451,6 @@ dependencies = [
|
||||
"env_logger 0.11.6",
|
||||
"feature_flags",
|
||||
"fs",
|
||||
"git",
|
||||
"gpui",
|
||||
"http_client",
|
||||
"language",
|
||||
@@ -5334,9 +5373,11 @@ name = "git"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"askpass",
|
||||
"async-trait",
|
||||
"collections",
|
||||
"derive_more",
|
||||
"futures 0.3.31",
|
||||
"git2",
|
||||
"gpui",
|
||||
"http_client",
|
||||
@@ -5393,31 +5434,46 @@ name = "git_ui"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"askpass",
|
||||
"assistant_settings",
|
||||
"buffer_diff",
|
||||
"chrono",
|
||||
"collections",
|
||||
"command_palette_hooks",
|
||||
"component",
|
||||
"ctor",
|
||||
"db",
|
||||
"editor",
|
||||
"feature_flags",
|
||||
"env_logger 0.11.6",
|
||||
"futures 0.3.31",
|
||||
"fuzzy",
|
||||
"git",
|
||||
"gpui",
|
||||
"itertools 0.14.0",
|
||||
"language",
|
||||
"language_model",
|
||||
"linkify",
|
||||
"linkme",
|
||||
"log",
|
||||
"menu",
|
||||
"multi_buffer",
|
||||
"notifications",
|
||||
"panel",
|
||||
"picker",
|
||||
"postage",
|
||||
"pretty_assertions",
|
||||
"project",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"settings",
|
||||
"smallvec",
|
||||
"strum",
|
||||
"telemetry",
|
||||
"theme",
|
||||
"time",
|
||||
"time_format",
|
||||
"ui",
|
||||
"unindent",
|
||||
"util",
|
||||
@@ -5554,7 +5610,7 @@ dependencies = [
|
||||
"bytemuck",
|
||||
"calloop",
|
||||
"calloop-wayland-source",
|
||||
"cbindgen",
|
||||
"cbindgen 0.28.0",
|
||||
"cocoa 0.26.0",
|
||||
"collections",
|
||||
"core-foundation 0.9.4",
|
||||
@@ -7227,9 +7283,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.170"
|
||||
version = "0.2.169"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828"
|
||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||
|
||||
[[package]]
|
||||
name = "libdbus-sys"
|
||||
@@ -8277,13 +8333,17 @@ dependencies = [
|
||||
"channel",
|
||||
"client",
|
||||
"collections",
|
||||
"component",
|
||||
"db",
|
||||
"gpui",
|
||||
"linkme",
|
||||
"rpc",
|
||||
"settings",
|
||||
"sum_tree",
|
||||
"time",
|
||||
"ui",
|
||||
"util",
|
||||
"workspace",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9756,15 +9816,6 @@ dependencies = [
|
||||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pgvector"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0e8871b6d7ca78348c6cd29b911b94851f3429f0cd403130ca17f26c1fb91a6"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.11.2"
|
||||
@@ -10182,6 +10233,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"anyhow",
|
||||
"askpass",
|
||||
"async-trait",
|
||||
"buffer_diff",
|
||||
"client",
|
||||
@@ -10309,12 +10361,36 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "prompt_library"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collections",
|
||||
"editor",
|
||||
"gpui",
|
||||
"language",
|
||||
"language_model",
|
||||
"log",
|
||||
"menu",
|
||||
"picker",
|
||||
"prompt_store",
|
||||
"release_channel",
|
||||
"rope",
|
||||
"serde",
|
||||
"settings",
|
||||
"theme",
|
||||
"ui",
|
||||
"util",
|
||||
"workspace",
|
||||
"zed_actions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prompt_store"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assets",
|
||||
"chrono",
|
||||
"collections",
|
||||
"editor",
|
||||
"fs",
|
||||
"futures 0.3.31",
|
||||
"fuzzy",
|
||||
@@ -10322,23 +10398,14 @@ dependencies = [
|
||||
"handlebars 4.5.0",
|
||||
"heed",
|
||||
"language",
|
||||
"language_model",
|
||||
"log",
|
||||
"menu",
|
||||
"parking_lot",
|
||||
"paths",
|
||||
"picker",
|
||||
"release_channel",
|
||||
"rope",
|
||||
"serde",
|
||||
"settings",
|
||||
"text",
|
||||
"theme",
|
||||
"ui",
|
||||
"util",
|
||||
"uuid",
|
||||
"workspace",
|
||||
"zed_actions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10389,7 +10456,7 @@ checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4"
|
||||
dependencies = [
|
||||
"bytes 1.10.0",
|
||||
"heck 0.5.0",
|
||||
"itertools 0.10.5",
|
||||
"itertools 0.12.1",
|
||||
"log",
|
||||
"multimap 0.10.0",
|
||||
"once_cell",
|
||||
@@ -10422,7 +10489,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.10.5",
|
||||
"itertools 0.12.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
@@ -10988,6 +11055,7 @@ name = "remote"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"askpass",
|
||||
"async-trait",
|
||||
"collections",
|
||||
"fs",
|
||||
@@ -11008,7 +11076,6 @@ dependencies = [
|
||||
"tempfile",
|
||||
"thiserror 1.0.69",
|
||||
"util",
|
||||
"which 6.0.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11450,9 +11517,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed"
|
||||
version = "8.6.0"
|
||||
version = "8.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b3aba5104622db5c9fc61098de54708feb732e7763d7faa2fa625899f00bf6f"
|
||||
checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0"
|
||||
dependencies = [
|
||||
"rust-embed-impl",
|
||||
"rust-embed-utils",
|
||||
@@ -11461,9 +11528,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-impl"
|
||||
version = "8.6.0"
|
||||
version = "8.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f198c73be048d2c5aa8e12f7960ad08443e56fd39cc26336719fdb4ea0ebaae"
|
||||
checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -11474,9 +11541,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-utils"
|
||||
version = "8.6.0"
|
||||
version = "8.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a2fcdc9f40c8dc2922842ca9add611ad19f332227fc651d015881ad1552bd9a"
|
||||
checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d"
|
||||
dependencies = [
|
||||
"globset",
|
||||
"sha2",
|
||||
@@ -11751,9 +11818,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "schemars"
|
||||
version = "0.8.22"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615"
|
||||
checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92"
|
||||
dependencies = [
|
||||
"dyn-clone",
|
||||
"indexmap",
|
||||
@@ -11764,9 +11831,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "schemars_derive"
|
||||
version = "0.8.22"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d"
|
||||
checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -11829,18 +11896,17 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sea-orm"
|
||||
version = "1.1.6"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13fba7b2c749b2d0a00303d5cb13e6761e39a4172554bdf930852cac4e7aeabd"
|
||||
checksum = "00733e5418e8ae3758cdb988c3654174e716230cc53ee2cb884207cf86a23029"
|
||||
dependencies = [
|
||||
"async-stream",
|
||||
"async-trait",
|
||||
"bigdecimal",
|
||||
"chrono",
|
||||
"futures-util",
|
||||
"futures 0.3.31",
|
||||
"log",
|
||||
"ouroboros",
|
||||
"pgvector",
|
||||
"rust_decimal",
|
||||
"sea-orm-macros",
|
||||
"sea-query",
|
||||
@@ -11858,9 +11924,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sea-orm-macros"
|
||||
version = "1.1.6"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2568cff8d35d5150b4276cc0dd766192a587f64b6ece60ae3706e0872c4eb209"
|
||||
checksum = "a98408f82fb4875d41ef469a79944a7da29767c7b3e4028e22188a3dd613b10f"
|
||||
dependencies = [
|
||||
"heck 0.4.1",
|
||||
"proc-macro2",
|
||||
@@ -13835,6 +13901,7 @@ dependencies = [
|
||||
"client",
|
||||
"collections",
|
||||
"feature_flags",
|
||||
"git_ui",
|
||||
"gpui",
|
||||
"http_client",
|
||||
"notifications",
|
||||
@@ -14210,9 +14277,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter"
|
||||
version = "0.25.2"
|
||||
version = "0.25.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5168a515fe492af54c5cc8800ff8c840be09fa5168de45838afaecd3e008bce4"
|
||||
checksum = "b9ac5ea5e7f2f1700842ec071401010b9c59bf735295f6e9fa079c3dc035b167"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"regex",
|
||||
@@ -14780,9 +14847,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.15.1"
|
||||
version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587"
|
||||
checksum = "8c1f41ffb7cf259f1ecc2876861a17e7142e63ead296f671f81f6ae85903e0d6"
|
||||
dependencies = [
|
||||
"getrandom 0.3.1",
|
||||
"serde",
|
||||
@@ -14884,6 +14951,7 @@ dependencies = [
|
||||
"multi_buffer",
|
||||
"nvim-rs",
|
||||
"parking_lot",
|
||||
"project",
|
||||
"project_panel",
|
||||
"regex",
|
||||
"release_channel",
|
||||
@@ -15905,12 +15973,6 @@ dependencies = [
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3"
|
||||
|
||||
[[package]]
|
||||
name = "windows-registry"
|
||||
version = "0.2.0"
|
||||
@@ -16746,7 +16808,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed"
|
||||
version = "0.177.0"
|
||||
version = "0.177.11"
|
||||
dependencies = [
|
||||
"activity_indicator",
|
||||
"anyhow",
|
||||
@@ -16824,7 +16886,7 @@ dependencies = [
|
||||
"project",
|
||||
"project_panel",
|
||||
"project_symbols",
|
||||
"prompt_library",
|
||||
"prompt_store",
|
||||
"proto",
|
||||
"recent_projects",
|
||||
"release_channel",
|
||||
|
||||
@@ -3,6 +3,7 @@ resolver = "2"
|
||||
members = [
|
||||
"crates/activity_indicator",
|
||||
"crates/anthropic",
|
||||
"crates/askpass",
|
||||
"crates/assets",
|
||||
"crates/assistant",
|
||||
"crates/assistant2",
|
||||
@@ -103,6 +104,7 @@ members = [
|
||||
"crates/project_panel",
|
||||
"crates/project_symbols",
|
||||
"crates/prompt_library",
|
||||
"crates/prompt_store",
|
||||
"crates/proto",
|
||||
"crates/recent_projects",
|
||||
"crates/refineable",
|
||||
@@ -208,6 +210,7 @@ edition = "2021"
|
||||
activity_indicator = { path = "crates/activity_indicator" }
|
||||
ai = { path = "crates/ai" }
|
||||
anthropic = { path = "crates/anthropic" }
|
||||
askpass = { path = "crates/askpass" }
|
||||
assets = { path = "crates/assets" }
|
||||
assistant = { path = "crates/assistant" }
|
||||
assistant2 = { path = "crates/assistant2" }
|
||||
@@ -308,6 +311,7 @@ project = { path = "crates/project" }
|
||||
project_panel = { path = "crates/project_panel" }
|
||||
project_symbols = { path = "crates/project_symbols" }
|
||||
prompt_library = { path = "crates/prompt_library" }
|
||||
prompt_store = { path = "crates/prompt_store" }
|
||||
proto = { path = "crates/proto" }
|
||||
recent_projects = { path = "crates/recent_projects" }
|
||||
refineable = { path = "crates/refineable" }
|
||||
@@ -536,7 +540,7 @@ tiny_http = "0.8"
|
||||
toml = "0.8"
|
||||
tokio = { version = "1" }
|
||||
tower-http = "0.4.4"
|
||||
tree-sitter = { version = "0.25.2", features = ["wasm"] }
|
||||
tree-sitter = { version = "0.25.3", features = ["wasm"] }
|
||||
tree-sitter-bash = "0.23"
|
||||
tree-sitter-c = "0.23"
|
||||
tree-sitter-cpp = "0.23"
|
||||
@@ -749,4 +753,4 @@ should_implement_trait = { level = "allow" }
|
||||
let_underscore_future = "allow"
|
||||
|
||||
[workspace.metadata.cargo-machete]
|
||||
ignored = ["bindgen", "cbindgen", "prost_build", "serde"]
|
||||
ignored = ["bindgen", "cbindgen", "prost_build", "serde", "component", "linkme"]
|
||||
|
||||
10
assets/icons/ai_edit.svg
Normal file
10
assets/icons/ai_edit.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.5871 5.40624C12.8514 5.14195 13 4.78346 13 4.40965C13 4.03583 12.8516 3.67731 12.5873 3.41295C12.323 3.14859 11.9645 3.00005 11.5907 3C11.2169 2.99995 10.8584 3.14841 10.594 3.4127L3.92098 10.0874C3.80488 10.2031 3.71903 10.3456 3.67097 10.5024L3.01047 12.6784C2.99754 12.7217 2.99657 12.7676 3.00764 12.8113C3.01872 12.8551 3.04143 12.895 3.07337 12.9269C3.1053 12.9588 3.14528 12.9815 3.18905 12.9925C3.23282 13.0035 3.27875 13.0024 3.32197 12.9894L5.49849 12.3294C5.65508 12.2818 5.79758 12.1964 5.91349 12.0809L12.5871 5.40624Z" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10 4L12 6" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6.38818 3.53598V2.53598" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M9.56982 12.6995L9.56982 13.6995" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M2.38818 6.53598H3.38818" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M13.5698 9.69949L12.5698 9.69949" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4.38818 4.53598L3.38818 3.53598" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M11.5698 11.6995L12.5698 12.6995" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
1
assets/icons/cloud.svg
Normal file
1
assets/icons/cloud.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-cloud"><path d="M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z"/></svg>
|
||||
|
After Width: | Height: | Size: 279 B |
6
assets/icons/git_branch_small.svg
Normal file
6
assets/icons/git_branch_small.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.75 3.25C4.02614 3.25 4.25 3.02614 4.25 2.75C4.25 2.47386 4.02614 2.25 3.75 2.25C3.47386 2.25 3.25 2.47386 3.25 2.75C3.25 3.02614 3.47386 3.25 3.75 3.25ZM3.75 4.25C4.57843 4.25 5.25 3.57843 5.25 2.75C5.25 1.92157 4.57843 1.25 3.75 1.25C2.92157 1.25 2.25 1.92157 2.25 2.75C2.25 3.57843 2.92157 4.25 3.75 4.25Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.25 3.25C8.52614 3.25 8.75 3.02614 8.75 2.75C8.75 2.47386 8.52614 2.25 8.25 2.25C7.97386 2.25 7.75 2.47386 7.75 2.75C7.75 3.02614 7.97386 3.25 8.25 3.25ZM8.25 4.25C9.07843 4.25 9.75 3.57843 9.75 2.75C9.75 1.92157 9.07843 1.25 8.25 1.25C7.42157 1.25 6.75 1.92157 6.75 2.75C6.75 3.57843 7.42157 4.25 8.25 4.25Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.75 9.75C4.02614 9.75 4.25 9.52614 4.25 9.25C4.25 8.97386 4.02614 8.75 3.75 8.75C3.47386 8.75 3.25 8.97386 3.25 9.25C3.25 9.52614 3.47386 9.75 3.75 9.75ZM3.75 10.75C4.57843 10.75 5.25 10.0784 5.25 9.25C5.25 8.42157 4.57843 7.75 3.75 7.75C2.92157 7.75 2.25 8.42157 2.25 9.25C2.25 10.0784 2.92157 10.75 3.75 10.75Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.25 3.75H4.25V5.59609C4.67823 5.35824 5.24991 5.25 6 5.25H7.25017C7.5262 5.25 7.75 5.02625 7.75 4.75V3.75H8.75V4.75C8.75 5.57832 8.07871 6.25 7.25017 6.25H6C5.14559 6.25 4.77639 6.41132 4.59684 6.56615C4.42571 6.71373 4.33877 6.92604 4.25 7.30651V8.25H3.25V3.75Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
40
assets/icons/git_onboarding_bg.svg
Normal file
40
assets/icons/git_onboarding_bg.svg
Normal file
@@ -0,0 +1,40 @@
|
||||
<svg width="400" height="120" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<pattern id="tilePattern" width="124" height="24" patternUnits="userSpaceOnUse">
|
||||
<svg width="124" height="24" viewBox="0 0 124 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g opacity="0.2">
|
||||
<path d="M16.666 12.0013L11.9993 16.668L7.33268 12.0013" stroke="white" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 7.33464L12 16.668" stroke="white" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M29 8.33464C29.3682 8.33464 29.6667 8.03616 29.6667 7.66797C29.6667 7.29978 29.3682 7.0013 29 7.0013C28.6318 7.0013 28.3333 7.29978 28.3333 7.66797C28.3333 8.03616 28.6318 8.33464 29 8.33464ZM29 9.66797C30.1046 9.66797 31 8.77254 31 7.66797C31 6.5634 30.1046 5.66797 29 5.66797C27.8954 5.66797 27 6.5634 27 7.66797C27 8.77254 27.8954 9.66797 29 9.66797Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M35 8.33464C35.3682 8.33464 35.6667 8.03616 35.6667 7.66797C35.6667 7.29978 35.3682 7.0013 35 7.0013C34.6318 7.0013 34.3333 7.29978 34.3333 7.66797C34.3333 8.03616 34.6318 8.33464 35 8.33464ZM35 9.66797C36.1046 9.66797 37 8.77254 37 7.66797C37 6.5634 36.1046 5.66797 35 5.66797C33.8954 5.66797 33 6.5634 33 7.66797C33 8.77254 33.8954 9.66797 35 9.66797Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M29 16.9987C29.3682 16.9987 29.6667 16.7002 29.6667 16.332C29.6667 15.9638 29.3682 15.6654 29 15.6654C28.6318 15.6654 28.3333 15.9638 28.3333 16.332C28.3333 16.7002 28.6318 16.9987 29 16.9987ZM29 18.332C30.1046 18.332 31 17.4366 31 16.332C31 15.2275 30.1046 14.332 29 14.332C27.8954 14.332 27 15.2275 27 16.332C27 17.4366 27.8954 18.332 29 18.332Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M28.334 9H29.6673V11.4615C30.2383 11.1443 31.0005 11 32.0007 11H33.6675C34.0356 11 34.334 10.7017 34.334 10.3333V9H35.6673V10.3333C35.6673 11.4378 34.7723 12.3333 33.6675 12.3333H32.0007C30.8614 12.3333 30.3692 12.5484 30.1298 12.7549C29.9016 12.9516 29.7857 13.2347 29.6673 13.742V15H28.334V9Z" fill="white"/>
|
||||
<path d="M48.668 8.66406H55.3346V15.3307" stroke="white" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M48.668 15.3307L55.3346 8.66406" stroke="white" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M76.5871 9.40624C76.8514 9.14195 77 8.78346 77 8.40965C77 8.03583 76.8516 7.67731 76.5873 7.41295C76.323 7.14859 75.9645 7.00005 75.5907 7C75.2169 6.99995 74.8584 7.14841 74.594 7.4127L67.921 14.0874C67.8049 14.2031 67.719 14.3456 67.671 14.5024L67.0105 16.6784C66.9975 16.7217 66.9966 16.7676 67.0076 16.8113C67.0187 16.8551 67.0414 16.895 67.0734 16.9269C67.1053 16.9588 67.1453 16.9815 67.1891 16.9925C67.2328 17.0035 67.2788 17.0024 67.322 16.9894L69.4985 16.3294C69.6551 16.2818 69.7976 16.1964 69.9135 16.0809L76.5871 9.40624Z" stroke="white" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M74 8L76 10" stroke="white" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M70.3877 7.53516V6.53516" stroke="white" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M73.5693 16.6992V17.6992" stroke="white" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M66.3877 10.5352H67.3877" stroke="white" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M77.5693 13.6992H76.5693" stroke="white" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M68.3877 8.53516L67.3877 7.53516" stroke="white" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M75.5693 15.6992L76.5693 16.6992" stroke="white" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M87.334 11.9987L92.0007 7.33203L96.6673 11.9987" stroke="white" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M92 16.6654V7.33203" stroke="white" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M117 12C117 10.6739 116.473 9.40215 115.536 8.46447C114.598 7.52678 113.326 7 112 7C110.602 7.00526 109.261 7.55068 108.256 8.52222L107 9.77778" stroke="white" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M107 7V9.77778H109.778" stroke="white" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M107 12C107 13.3261 107.527 14.5979 108.464 15.5355C109.402 16.4732 110.674 17 112 17C113.398 16.9947 114.739 16.4493 115.744 15.4778L117 14.2222" stroke="white" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M114.223 14.2188H117V16.9965" stroke="white" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
</pattern>
|
||||
<linearGradient id="fade" y2="1" x2="0">
|
||||
<stop offset="0" stop-color="white" stop-opacity=".52"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<mask id="fadeMask" maskContentUnits="objectBoundingBox">
|
||||
<rect width="1" height="1" fill="url(#fade)"/>
|
||||
</mask>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" fill="url(#tilePattern)" mask="url(#fadeMask)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.4 KiB |
@@ -10,8 +10,8 @@
|
||||
"pagedown": "menu::SelectLast",
|
||||
"ctrl-n": "menu::SelectNext",
|
||||
"tab": "menu::SelectNext",
|
||||
"ctrl-p": "menu::SelectPrev",
|
||||
"shift-tab": "menu::SelectPrev",
|
||||
"ctrl-p": "menu::SelectPrevious",
|
||||
"shift-tab": "menu::SelectPrevious",
|
||||
"enter": "menu::Confirm",
|
||||
"ctrl-enter": "menu::SecondaryConfirm",
|
||||
"ctrl-escape": "menu::Cancel",
|
||||
@@ -38,14 +38,14 @@
|
||||
{
|
||||
"context": "Picker || menu",
|
||||
"bindings": {
|
||||
"up": "menu::SelectPrev",
|
||||
"up": "menu::SelectPrevious",
|
||||
"down": "menu::SelectNext"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Prompt",
|
||||
"bindings": {
|
||||
"left": "menu::SelectPrev",
|
||||
"left": "menu::SelectPrevious",
|
||||
"right": "menu::SelectNext"
|
||||
}
|
||||
},
|
||||
@@ -57,7 +57,7 @@
|
||||
"backspace": "editor::Backspace",
|
||||
"delete": "editor::Delete",
|
||||
"tab": "editor::Tab",
|
||||
"shift-tab": "editor::TabPrev",
|
||||
"shift-tab": "editor::Backtab",
|
||||
"ctrl-k": "editor::CutToEndOfLine",
|
||||
// "ctrl-t": "editor::Transpose",
|
||||
"ctrl-k ctrl-q": "editor::Rewrap",
|
||||
@@ -180,7 +180,7 @@
|
||||
"ctrl-k c": "assistant::CopyCode",
|
||||
"ctrl-shift-e": "project_panel::ToggleFocus",
|
||||
"ctrl-g": "search::SelectNextMatch",
|
||||
"ctrl-shift-g": "search::SelectPrevMatch",
|
||||
"ctrl-shift-g": "search::SelectPreviousMatch",
|
||||
"ctrl-alt-/": "assistant::ToggleModelSelector",
|
||||
"ctrl-k h": "assistant::DeployHistory",
|
||||
"ctrl-k l": "assistant::DeployPromptLibrary",
|
||||
@@ -203,7 +203,7 @@
|
||||
"escape": "buffer_search::Dismiss",
|
||||
"tab": "buffer_search::FocusEditor",
|
||||
"enter": "search::SelectNextMatch",
|
||||
"shift-enter": "search::SelectPrevMatch",
|
||||
"shift-enter": "search::SelectPreviousMatch",
|
||||
"alt-enter": "search::SelectAllMatches",
|
||||
"find": "search::FocusSearch",
|
||||
"ctrl-f": "search::FocusSearch",
|
||||
@@ -272,7 +272,7 @@
|
||||
"alt-8": ["pane::ActivateItem", 7],
|
||||
"alt-9": ["pane::ActivateItem", 8],
|
||||
"alt-0": "pane::ActivateLastItem",
|
||||
"ctrl-pageup": "pane::ActivatePrevItem",
|
||||
"ctrl-pageup": "pane::ActivatePreviousItem",
|
||||
"ctrl-pagedown": "pane::ActivateNextItem",
|
||||
"ctrl-shift-pageup": "pane::SwapItemLeft",
|
||||
"ctrl-shift-pagedown": "pane::SwapItemRight",
|
||||
@@ -290,8 +290,8 @@
|
||||
"forward": "pane::GoForward",
|
||||
"ctrl-alt-g": "search::SelectNextMatch",
|
||||
"f3": "search::SelectNextMatch",
|
||||
"ctrl-alt-shift-g": "search::SelectPrevMatch",
|
||||
"shift-f3": "search::SelectPrevMatch",
|
||||
"ctrl-alt-shift-g": "search::SelectPreviousMatch",
|
||||
"shift-f3": "search::SelectPreviousMatch",
|
||||
"shift-find": "project_search::ToggleFocus",
|
||||
"ctrl-shift-f": "project_search::ToggleFocus",
|
||||
"ctrl-alt-shift-h": "search::ToggleReplace",
|
||||
@@ -334,7 +334,7 @@
|
||||
"ctrl-u": "editor::UndoSelection",
|
||||
"ctrl-shift-u": "editor::RedoSelection",
|
||||
"f8": "editor::GoToDiagnostic",
|
||||
"shift-f8": "editor::GoToPrevDiagnostic",
|
||||
"shift-f8": "editor::GoToPreviousDiagnostic",
|
||||
"f2": "editor::Rename",
|
||||
"f12": "editor::GoToDefinition",
|
||||
"alt-f12": "editor::GoToDefinitionSplit",
|
||||
@@ -373,7 +373,7 @@
|
||||
"alt-y": "git::StageAndNext",
|
||||
"alt-shift-y": "git::UnstageAndNext",
|
||||
"alt-.": "editor::GoToHunk",
|
||||
"alt-,": "editor::GoToPrevHunk"
|
||||
"alt-,": "editor::GoToPreviousHunk"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -393,6 +393,7 @@
|
||||
"alt-shift-open": "projects::OpenRemote",
|
||||
"alt-ctrl-shift-o": "projects::OpenRemote",
|
||||
"alt-ctrl-shift-b": "branches::OpenRecent",
|
||||
"alt-shift-enter": "toast::RunAction",
|
||||
"ctrl-~": "workspace::NewTerminal",
|
||||
"save": "workspace::Save",
|
||||
"ctrl-s": "workspace::Save",
|
||||
@@ -536,8 +537,8 @@
|
||||
{
|
||||
"context": "Editor && (showing_code_actions || showing_completions)",
|
||||
"bindings": {
|
||||
"ctrl-p": "editor::ContextMenuPrev",
|
||||
"up": "editor::ContextMenuPrev",
|
||||
"ctrl-p": "editor::ContextMenuPrevious",
|
||||
"up": "editor::ContextMenuPrevious",
|
||||
"ctrl-n": "editor::ContextMenuNext",
|
||||
"down": "editor::ContextMenuNext",
|
||||
"pageup": "editor::ContextMenuFirst",
|
||||
@@ -565,7 +566,7 @@
|
||||
"ctrl-alt-enter": "editor::OpenExcerptsSplit",
|
||||
"ctrl-shift-e": "pane::RevealInProjectPanel",
|
||||
"ctrl-f8": "editor::GoToHunk",
|
||||
"ctrl-shift-f8": "editor::GoToPrevHunk",
|
||||
"ctrl-shift-f8": "editor::GoToPreviousHunk",
|
||||
"ctrl-enter": "assistant::InlineAssist",
|
||||
"ctrl-:": "editor::ToggleInlayHints"
|
||||
}
|
||||
@@ -662,7 +663,7 @@
|
||||
"alt-ctrl-r": "outline_panel::RevealInFileManager",
|
||||
"space": "outline_panel::Open",
|
||||
"shift-down": "menu::SelectNext",
|
||||
"shift-up": "menu::SelectPrev",
|
||||
"shift-up": "menu::SelectPrevious",
|
||||
"alt-enter": "editor::OpenExcerpts",
|
||||
"ctrl-alt-enter": "editor::OpenExcerptsSplit"
|
||||
}
|
||||
@@ -700,7 +701,7 @@
|
||||
"shift-find": "project_panel::NewSearchInDirectory",
|
||||
"ctrl-shift-f": "project_panel::NewSearchInDirectory",
|
||||
"shift-down": "menu::SelectNext",
|
||||
"shift-up": "menu::SelectPrev",
|
||||
"shift-up": "menu::SelectPrevious",
|
||||
"escape": "menu::Cancel"
|
||||
}
|
||||
},
|
||||
@@ -713,30 +714,57 @@
|
||||
{
|
||||
"context": "GitPanel && ChangesList",
|
||||
"bindings": {
|
||||
"up": "menu::SelectPrev",
|
||||
"up": "menu::SelectPrevious",
|
||||
"down": "menu::SelectNext",
|
||||
"enter": "menu::Confirm",
|
||||
"alt-y": "git::StageFile",
|
||||
"alt-shift-y": "git::UnstageFile",
|
||||
"ctrl-alt-y": "git::ToggleStaged",
|
||||
"space": "git::ToggleStaged",
|
||||
"ctrl-space": "git::StageAll",
|
||||
"ctrl-shift-space": "git::UnstageAll",
|
||||
"tab": "git_panel::FocusEditor",
|
||||
"shift-tab": "git_panel::FocusEditor",
|
||||
"escape": "git_panel::ToggleFocus",
|
||||
"ctrl-enter": "git::Commit",
|
||||
"alt-enter": "menu::SecondaryConfirm"
|
||||
"alt-enter": "menu::SecondaryConfirm",
|
||||
"backspace": "git::RestoreFile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "GitCommit > Editor",
|
||||
"bindings": {
|
||||
"escape": "menu::Cancel",
|
||||
"enter": "editor::Newline",
|
||||
"ctrl-enter": "git::Commit"
|
||||
"ctrl-enter": "git::Commit",
|
||||
"alt-l": "git::GenerateCommitMessage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "GitPanel",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"ctrl-g ctrl-g": "git::Fetch",
|
||||
"ctrl-g up": "git::Push",
|
||||
"ctrl-g down": "git::Pull",
|
||||
"ctrl-g shift-up": "git::ForcePush",
|
||||
"ctrl-g d": "git::Diff",
|
||||
"ctrl-g backspace": "git::RestoreTrackedFiles",
|
||||
"ctrl-g shift-backspace": "git::TrashUntrackedFiles",
|
||||
"ctrl-space": "git::StageAll",
|
||||
"ctrl-shift-space": "git::UnstageAll"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "GitDiff > Editor",
|
||||
"bindings": {
|
||||
"ctrl-enter": "git::Commit"
|
||||
"ctrl-enter": "git::Commit",
|
||||
"ctrl-space": "git::StageAll",
|
||||
"ctrl-shift-space": "git::UnstageAll"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "AskPass > Editor",
|
||||
"bindings": {
|
||||
"enter": "menu::Confirm"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -745,16 +773,10 @@
|
||||
"escape": "git_panel::FocusChanges",
|
||||
"tab": "git_panel::FocusChanges",
|
||||
"shift-tab": "git_panel::FocusChanges",
|
||||
"ctrl-enter": "git::Commit",
|
||||
"alt-up": "git_panel::FocusChanges"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "GitCommit > Editor",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"enter": "editor::Newline",
|
||||
"ctrl-enter": "git::Commit"
|
||||
"ctrl-enter": "git::Commit",
|
||||
"alt-up": "git_panel::FocusChanges",
|
||||
"alt-l": "git::GenerateCommitMessage"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -779,6 +801,9 @@
|
||||
{
|
||||
"context": "Picker > Editor",
|
||||
"bindings": {
|
||||
"escape": "menu::Cancel",
|
||||
"up": "menu::SelectPrevious",
|
||||
"down": "menu::SelectNext",
|
||||
"tab": "picker::ConfirmCompletion",
|
||||
"alt-enter": ["picker::ConfirmInput", { "secondary": false }]
|
||||
}
|
||||
@@ -792,7 +817,7 @@
|
||||
{
|
||||
"context": "FileFinder || (FileFinder > Picker > Editor) || (FileFinder > Picker > menu)",
|
||||
"bindings": {
|
||||
"ctrl-shift-p": "file_finder::SelectPrev",
|
||||
"ctrl-shift-p": "file_finder::SelectPrevious",
|
||||
"ctrl-j": "pane::SplitDown",
|
||||
"ctrl-k": "pane::SplitUp",
|
||||
"ctrl-h": "pane::SplitLeft",
|
||||
@@ -802,8 +827,8 @@
|
||||
{
|
||||
"context": "TabSwitcher",
|
||||
"bindings": {
|
||||
"ctrl-shift-tab": "menu::SelectPrev",
|
||||
"ctrl-up": "menu::SelectPrev",
|
||||
"ctrl-shift-tab": "menu::SelectPrevious",
|
||||
"ctrl-up": "menu::SelectPrevious",
|
||||
"ctrl-down": "menu::SelectNext",
|
||||
"ctrl-backspace": "tab_switcher::CloseSelectedItem"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,15 @@
|
||||
[
|
||||
// Moved before Standard macOS bindings so that `cmd-w` is not the last binding for
|
||||
// `workspace::CloseWindow` and displayed/intercepted by macOS
|
||||
{
|
||||
"context": "PromptLibrary",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"cmd-n": "prompt_library::NewPrompt",
|
||||
"cmd-shift-s": "prompt_library::ToggleDefaultPrompt",
|
||||
"cmd-w": "workspace::CloseWindow"
|
||||
}
|
||||
},
|
||||
// Standard macOS bindings
|
||||
{
|
||||
"use_key_equivalents": true,
|
||||
@@ -14,14 +25,14 @@
|
||||
"tab": "menu::SelectNext",
|
||||
"ctrl-n": "menu::SelectNext",
|
||||
"down": "menu::SelectNext",
|
||||
"shift-tab": "menu::SelectPrev",
|
||||
"ctrl-p": "menu::SelectPrev",
|
||||
"up": "menu::SelectPrev",
|
||||
"shift-tab": "menu::SelectPrevious",
|
||||
"ctrl-p": "menu::SelectPrevious",
|
||||
"up": "menu::SelectPrevious",
|
||||
"enter": "menu::Confirm",
|
||||
"ctrl-enter": "menu::SecondaryConfirm",
|
||||
"cmd-enter": "menu::SecondaryConfirm",
|
||||
"ctrl-escape": "menu::Cancel",
|
||||
"cmd-escape": "menu::Cancel",
|
||||
"ctrl-escape": "menu::Cancel",
|
||||
"ctrl-c": "menu::Cancel",
|
||||
"escape": "menu::Cancel",
|
||||
"alt-shift-enter": "menu::Restart",
|
||||
@@ -54,7 +65,7 @@
|
||||
"ctrl-d": "editor::Delete",
|
||||
"delete": "editor::Delete",
|
||||
"tab": "editor::Tab",
|
||||
"shift-tab": "editor::TabPrev",
|
||||
"shift-tab": "editor::Backtab",
|
||||
"ctrl-t": "editor::Transpose",
|
||||
"ctrl-k": "editor::KillRingCut",
|
||||
"ctrl-y": "editor::KillRingYank",
|
||||
@@ -97,8 +108,8 @@
|
||||
"cmd-right": ["editor::MoveToEndOfLine", { "stop_at_soft_wraps": true }],
|
||||
"ctrl-e": ["editor::MoveToEndOfLine", { "stop_at_soft_wraps": false }],
|
||||
"end": ["editor::MoveToEndOfLine", { "stop_at_soft_wraps": true }],
|
||||
"cmd-up": "editor::MoveToStartOfExcerpt",
|
||||
"cmd-down": "editor::MoveToEndOfExcerpt",
|
||||
"cmd-up": "editor::MoveToBeginning",
|
||||
"cmd-down": "editor::MoveToEnd",
|
||||
"cmd-home": "editor::MoveToBeginning", // Typed via `cmd-fn-left`
|
||||
"cmd-end": "editor::MoveToEnd", // Typed via `cmd-fn-right`
|
||||
"shift-up": "editor::SelectUp",
|
||||
@@ -113,11 +124,12 @@
|
||||
"alt-shift-right": "editor::SelectToNextWordEnd", // cursorWordRightSelect
|
||||
"ctrl-shift-up": "editor::SelectToStartOfParagraph",
|
||||
"ctrl-shift-down": "editor::SelectToEndOfParagraph",
|
||||
"cmd-shift-up": "editor::SelectToStartOfExcerpt",
|
||||
"cmd-shift-down": "editor::SelectToEndOfExcerpt",
|
||||
"cmd-shift-up": "editor::SelectToBeginning",
|
||||
"cmd-shift-down": "editor::SelectToEnd",
|
||||
"cmd-a": "editor::SelectAll",
|
||||
"cmd-l": "editor::SelectLine",
|
||||
"cmd-shift-i": "editor::Format",
|
||||
"alt-shift-o": "editor::OrganizeImports",
|
||||
"cmd-shift-left": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true, "stop_at_indent": true }],
|
||||
"shift-home": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true, "stop_at_indent": true }],
|
||||
"ctrl-shift-a": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true, "stop_at_indent": true }],
|
||||
@@ -160,6 +172,16 @@
|
||||
"alt-enter": "editor::OpenSelectionsInMultibuffer"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Editor && multibuffer",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"cmd-up": "editor::MoveToStartOfExcerpt",
|
||||
"cmd-down": "editor::MoveToStartOfNextExcerpt",
|
||||
"cmd-shift-up": "editor::SelectToStartOfExcerpt",
|
||||
"cmd-shift-down": "editor::SelectToStartOfNextExcerpt"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Editor && mode == full && edit_prediction",
|
||||
"use_key_equivalents": true,
|
||||
@@ -207,7 +229,7 @@
|
||||
"cmd-k c": "assistant::CopyCode",
|
||||
"cmd-shift-e": "project_panel::ToggleFocus",
|
||||
"cmd-g": "search::SelectNextMatch",
|
||||
"cmd-shift-g": "search::SelectPrevMatch",
|
||||
"cmd-shift-g": "search::SelectPreviousMatch",
|
||||
"cmd-alt-/": "assistant::ToggleModelSelector",
|
||||
"cmd-k h": "assistant::DeployHistory",
|
||||
"cmd-k l": "assistant::DeployPromptLibrary",
|
||||
@@ -269,15 +291,6 @@
|
||||
"backspace": "assistant2::RemoveSelectedThread"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "PromptLibrary",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"cmd-n": "prompt_library::NewPrompt",
|
||||
"cmd-shift-s": "prompt_library::ToggleDefaultPrompt",
|
||||
"cmd-w": "workspace::CloseWindow"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "BufferSearchBar",
|
||||
"use_key_equivalents": true,
|
||||
@@ -285,7 +298,7 @@
|
||||
"escape": "buffer_search::Dismiss",
|
||||
"tab": "buffer_search::FocusEditor",
|
||||
"enter": "search::SelectNextMatch",
|
||||
"shift-enter": "search::SelectPrevMatch",
|
||||
"shift-enter": "search::SelectPreviousMatch",
|
||||
"alt-enter": "search::SelectAllMatches",
|
||||
"cmd-f": "search::FocusSearch",
|
||||
"cmd-alt-f": "search::ToggleReplace",
|
||||
@@ -352,8 +365,8 @@
|
||||
"context": "Pane",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"alt-cmd-left": "pane::ActivatePrevItem",
|
||||
"cmd-{": "pane::ActivatePrevItem",
|
||||
"alt-cmd-left": "pane::ActivatePreviousItem",
|
||||
"cmd-{": "pane::ActivatePreviousItem",
|
||||
"alt-cmd-right": "pane::ActivateNextItem",
|
||||
"cmd-}": "pane::ActivateNextItem",
|
||||
"ctrl-shift-pageup": "pane::SwapItemLeft",
|
||||
@@ -367,7 +380,7 @@
|
||||
"cmd-k cmd-w": ["pane::CloseAllItems", { "close_pinned": false }],
|
||||
"cmd-f": "project_search::ToggleFocus",
|
||||
"cmd-g": "search::SelectNextMatch",
|
||||
"cmd-shift-g": "search::SelectPrevMatch",
|
||||
"cmd-shift-g": "search::SelectPreviousMatch",
|
||||
"cmd-shift-h": "search::ToggleReplace",
|
||||
"cmd-alt-l": "search::ToggleSelection",
|
||||
"alt-enter": "search::SelectAllMatches",
|
||||
@@ -407,7 +420,7 @@
|
||||
"cmd-u": "editor::UndoSelection",
|
||||
"cmd-shift-u": "editor::RedoSelection",
|
||||
"f8": "editor::GoToDiagnostic",
|
||||
"shift-f8": "editor::GoToPrevDiagnostic",
|
||||
"shift-f8": "editor::GoToPreviousDiagnostic",
|
||||
"f2": "editor::Rename",
|
||||
"f12": "editor::GoToDefinition",
|
||||
"alt-f12": "editor::GoToDefinitionSplit",
|
||||
@@ -484,6 +497,7 @@
|
||||
"ctrl-~": "workspace::NewTerminal",
|
||||
"cmd-s": "workspace::Save",
|
||||
"cmd-k s": "workspace::SaveWithoutFormat",
|
||||
"alt-shift-enter": "toast::RunAction",
|
||||
"cmd-shift-s": "workspace::SaveAs",
|
||||
"cmd-shift-n": "workspace::NewWindow",
|
||||
"ctrl-`": "terminal_panel::ToggleFocus",
|
||||
@@ -613,8 +627,8 @@
|
||||
"context": "Editor && (showing_code_actions || showing_completions)",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"up": "editor::ContextMenuPrev",
|
||||
"ctrl-p": "editor::ContextMenuPrev",
|
||||
"up": "editor::ContextMenuPrevious",
|
||||
"ctrl-p": "editor::ContextMenuPrevious",
|
||||
"down": "editor::ContextMenuNext",
|
||||
"ctrl-n": "editor::ContextMenuNext",
|
||||
"pageup": "editor::ContextMenuFirst",
|
||||
@@ -640,7 +654,7 @@
|
||||
"cmd-alt-enter": "editor::OpenExcerptsSplit",
|
||||
"cmd-shift-e": "pane::RevealInProjectPanel",
|
||||
"cmd-f8": "editor::GoToHunk",
|
||||
"cmd-shift-f8": "editor::GoToPrevHunk",
|
||||
"cmd-shift-f8": "editor::GoToPreviousHunk",
|
||||
"ctrl-enter": "assistant::InlineAssist",
|
||||
"ctrl-:": "editor::ToggleInlayHints"
|
||||
}
|
||||
@@ -683,7 +697,7 @@
|
||||
"alt-cmd-r": "outline_panel::RevealInFileManager",
|
||||
"space": "outline_panel::Open",
|
||||
"shift-down": "menu::SelectNext",
|
||||
"shift-up": "menu::SelectPrev",
|
||||
"shift-up": "menu::SelectPrevious",
|
||||
"alt-enter": "editor::OpenExcerpts",
|
||||
"cmd-alt-enter": "editor::OpenExcerptsSplit"
|
||||
}
|
||||
@@ -713,7 +727,7 @@
|
||||
"cmd-alt-backspace": ["project_panel::Delete", { "skip_prompt": false }],
|
||||
"cmd-shift-f": "project_panel::NewSearchInDirectory",
|
||||
"shift-down": "menu::SelectNext",
|
||||
"shift-up": "menu::SelectPrev",
|
||||
"shift-up": "menu::SelectPrevious",
|
||||
"escape": "menu::Cancel"
|
||||
}
|
||||
},
|
||||
@@ -728,26 +742,30 @@
|
||||
"context": "GitPanel && ChangesList",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"up": "menu::SelectPrev",
|
||||
"up": "menu::SelectPrevious",
|
||||
"down": "menu::SelectNext",
|
||||
"cmd-up": "menu::SelectFirst",
|
||||
"cmd-down": "menu::SelectLast",
|
||||
"enter": "menu::Confirm",
|
||||
"cmd-alt-y": "git::ToggleStaged",
|
||||
"space": "git::ToggleStaged",
|
||||
"cmd-shift-space": "git::StageAll",
|
||||
"ctrl-shift-space": "git::UnstageAll",
|
||||
"cmd-y": "git::StageFile",
|
||||
"cmd-shift-y": "git::UnstageFile",
|
||||
"alt-down": "git_panel::FocusEditor",
|
||||
"tab": "git_panel::FocusEditor",
|
||||
"shift-tab": "git_panel::FocusEditor",
|
||||
"escape": "git_panel::ToggleFocus",
|
||||
"cmd-enter": "git::Commit"
|
||||
"cmd-enter": "git::Commit",
|
||||
"backspace": "git::RestoreFile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "GitDiff > Editor",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"cmd-enter": "git::Commit"
|
||||
"cmd-enter": "git::Commit",
|
||||
"cmd-ctrl-y": "git::StageAll",
|
||||
"cmd-ctrl-shift-y": "git::UnstageAll"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -758,7 +776,24 @@
|
||||
"cmd-enter": "git::Commit",
|
||||
"tab": "git_panel::FocusChanges",
|
||||
"shift-tab": "git_panel::FocusChanges",
|
||||
"alt-up": "git_panel::FocusChanges"
|
||||
"alt-up": "git_panel::FocusChanges",
|
||||
"shift-escape": "git::ExpandCommitEditor",
|
||||
"alt-tab": "git::GenerateCommitMessage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "GitPanel",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"ctrl-g ctrl-g": "git::Fetch",
|
||||
"ctrl-g up": "git::Push",
|
||||
"ctrl-g down": "git::Pull",
|
||||
"ctrl-g shift-up": "git::ForcePush",
|
||||
"ctrl-g d": "git::Diff",
|
||||
"ctrl-g backspace": "git::RestoreTrackedFiles",
|
||||
"ctrl-g shift-backspace": "git::TrashUntrackedFiles",
|
||||
"cmd-ctrl-y": "git::StageAll",
|
||||
"cmd-ctrl-shift-y": "git::UnstageAll"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -766,7 +801,9 @@
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"enter": "editor::Newline",
|
||||
"cmd-enter": "git::Commit"
|
||||
"escape": "menu::Cancel",
|
||||
"cmd-enter": "git::Commit",
|
||||
"alt-tab": "git::GenerateCommitMessage"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -795,6 +832,9 @@
|
||||
"context": "Picker > Editor",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"escape": "menu::Cancel",
|
||||
"up": "menu::SelectPrevious",
|
||||
"down": "menu::SelectNext",
|
||||
"tab": "picker::ConfirmCompletion",
|
||||
"alt-enter": ["picker::ConfirmInput", { "secondary": false }],
|
||||
"cmd-alt-enter": ["picker::ConfirmInput", { "secondary": true }]
|
||||
@@ -811,7 +851,7 @@
|
||||
"context": "FileFinder || (FileFinder > Picker > Editor) || (FileFinder > Picker > menu)",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"cmd-shift-p": "file_finder::SelectPrev",
|
||||
"cmd-shift-p": "file_finder::SelectPrevious",
|
||||
"cmd-j": "pane::SplitDown",
|
||||
"cmd-k": "pane::SplitUp",
|
||||
"cmd-h": "pane::SplitLeft",
|
||||
@@ -822,8 +862,8 @@
|
||||
"context": "TabSwitcher",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"ctrl-shift-tab": "menu::SelectPrev",
|
||||
"ctrl-up": "menu::SelectPrev",
|
||||
"ctrl-shift-tab": "menu::SelectPrevious",
|
||||
"ctrl-up": "menu::SelectPrevious",
|
||||
"ctrl-down": "menu::SelectNext",
|
||||
"ctrl-backspace": "tab_switcher::CloseSelectedItem"
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"context": "BufferSearchBar",
|
||||
"bindings": {
|
||||
"ctrl-f3": "search::SelectNextMatch", // find-and-replace:find-next-selected
|
||||
"ctrl-shift-f3": "search::SelectPrevMatch" // find-and-replace:find-previous-selected
|
||||
"ctrl-shift-f3": "search::SelectPreviousMatch" // find-and-replace:find-previous-selected
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
"context": "BufferSearchBar > Editor",
|
||||
"bindings": {
|
||||
"ctrl-s": "search::SelectNextMatch",
|
||||
"ctrl-r": "search::SelectPrevMatch",
|
||||
"ctrl-r": "search::SelectPreviousMatch",
|
||||
"ctrl-g": "buffer_search::Dismiss"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
{
|
||||
"bindings": {
|
||||
"ctrl-alt-s": "zed::OpenSettings",
|
||||
"ctrl-{": "pane::ActivatePrevItem",
|
||||
"ctrl-{": "pane::ActivatePreviousItem",
|
||||
"ctrl-}": "pane::ActivateNextItem"
|
||||
}
|
||||
},
|
||||
@@ -41,9 +41,9 @@
|
||||
"ctrl-shift-b": "editor::GoToTypeDefinition",
|
||||
"ctrl-alt-shift-b": "editor::GoToTypeDefinitionSplit",
|
||||
"f2": "editor::GoToDiagnostic",
|
||||
"shift-f2": "editor::GoToPrevDiagnostic",
|
||||
"shift-f2": "editor::GoToPreviousDiagnostic",
|
||||
"ctrl-alt-shift-down": "editor::GoToHunk",
|
||||
"ctrl-alt-shift-up": "editor::GoToPrevHunk",
|
||||
"ctrl-alt-shift-up": "editor::GoToPreviousHunk",
|
||||
"ctrl-alt-z": "git::Restore",
|
||||
"ctrl-home": "editor::MoveToBeginning",
|
||||
"ctrl-end": "editor::MoveToEnd",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
[
|
||||
{
|
||||
"bindings": {
|
||||
"ctrl-{": "pane::ActivatePrevItem",
|
||||
"ctrl-{": "pane::ActivatePreviousItem",
|
||||
"ctrl-}": "pane::ActivateNextItem",
|
||||
"ctrl-pageup": "pane::ActivatePrevItem",
|
||||
"ctrl-pageup": "pane::ActivatePreviousItem",
|
||||
"ctrl-pagedown": "pane::ActivateNextItem",
|
||||
"ctrl-1": ["workspace::ActivatePane", 0],
|
||||
"ctrl-2": ["workspace::ActivatePane", 1],
|
||||
@@ -44,7 +44,7 @@
|
||||
"shift-f12": "editor::FindAllReferences",
|
||||
"ctrl-shift-f12": "editor::FindAllReferences",
|
||||
"ctrl-.": "editor::GoToHunk",
|
||||
"ctrl-,": "editor::GoToPrevHunk",
|
||||
"ctrl-,": "editor::GoToPreviousHunk",
|
||||
"ctrl-k ctrl-u": "editor::ConvertToUpperCase",
|
||||
"ctrl-k ctrl-l": "editor::ConvertToLowerCase",
|
||||
"shift-alt-m": "markdown::OpenPreviewToTheSide",
|
||||
@@ -62,7 +62,7 @@
|
||||
"context": "Pane",
|
||||
"bindings": {
|
||||
"f4": "search::SelectNextMatch",
|
||||
"shift-f4": "search::SelectPrevMatch",
|
||||
"shift-f4": "search::SelectPreviousMatch",
|
||||
"alt-1": ["pane::ActivateItem", 0],
|
||||
"alt-2": ["pane::ActivateItem", 1],
|
||||
"alt-3": ["pane::ActivateItem", 2],
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
"context": "BufferSearchBar",
|
||||
"bindings": {
|
||||
"cmd-f3": "search::SelectNextMatch",
|
||||
"cmd-shift-f3": "search::SelectPrevMatch"
|
||||
"cmd-shift-f3": "search::SelectPreviousMatch"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
"context": "BufferSearchBar > Editor",
|
||||
"bindings": {
|
||||
"ctrl-s": "search::SelectNextMatch",
|
||||
"ctrl-r": "search::SelectPrevMatch",
|
||||
"ctrl-r": "search::SelectPreviousMatch",
|
||||
"ctrl-g": "buffer_search::Dismiss"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[
|
||||
{
|
||||
"bindings": {
|
||||
"cmd-{": "pane::ActivatePrevItem",
|
||||
"cmd-{": "pane::ActivatePreviousItem",
|
||||
"cmd-}": "pane::ActivateNextItem"
|
||||
}
|
||||
},
|
||||
@@ -39,9 +39,9 @@
|
||||
"cmd-shift-b": "editor::GoToTypeDefinition",
|
||||
"cmd-alt-shift-b": "editor::GoToTypeDefinitionSplit",
|
||||
"f2": "editor::GoToDiagnostic",
|
||||
"shift-f2": "editor::GoToPrevDiagnostic",
|
||||
"shift-f2": "editor::GoToPreviousDiagnostic",
|
||||
"ctrl-alt-shift-down": "editor::GoToHunk",
|
||||
"ctrl-alt-shift-up": "editor::GoToPrevHunk",
|
||||
"ctrl-alt-shift-up": "editor::GoToPreviousHunk",
|
||||
"cmd-home": "editor::MoveToBeginning",
|
||||
"cmd-end": "editor::MoveToEnd",
|
||||
"cmd-shift-home": "editor::SelectToBeginning",
|
||||
@@ -61,7 +61,7 @@
|
||||
{
|
||||
"context": "BufferSearchBar > Editor",
|
||||
"bindings": {
|
||||
"shift-enter": "search::SelectPrevMatch"
|
||||
"shift-enter": "search::SelectPreviousMatch"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
[
|
||||
{
|
||||
"bindings": {
|
||||
"cmd-{": "pane::ActivatePrevItem",
|
||||
"cmd-{": "pane::ActivatePreviousItem",
|
||||
"cmd-}": "pane::ActivateNextItem",
|
||||
"ctrl-pageup": "pane::ActivatePrevItem",
|
||||
"ctrl-pageup": "pane::ActivatePreviousItem",
|
||||
"ctrl-pagedown": "pane::ActivateNextItem",
|
||||
"ctrl-1": ["workspace::ActivatePane", 0],
|
||||
"ctrl-2": ["workspace::ActivatePane", 1],
|
||||
@@ -45,7 +45,7 @@
|
||||
"ctrl-alt-cmd-down": "editor::GoToDefinitionSplit",
|
||||
"alt-shift-cmd-down": "editor::FindAllReferences",
|
||||
"ctrl-.": "editor::GoToHunk",
|
||||
"ctrl-,": "editor::GoToPrevHunk",
|
||||
"ctrl-,": "editor::GoToPreviousHunk",
|
||||
"cmd-k cmd-u": "editor::ConvertToUpperCase",
|
||||
"cmd-k cmd-l": "editor::ConvertToLowerCase",
|
||||
"cmd-shift-j": "editor::JoinLines",
|
||||
@@ -64,7 +64,7 @@
|
||||
"context": "Pane",
|
||||
"bindings": {
|
||||
"f4": "search::SelectNextMatch",
|
||||
"shift-f4": "search::SelectPrevMatch",
|
||||
"shift-f4": "search::SelectPreviousMatch",
|
||||
"cmd-1": ["pane::ActivateItem", 0],
|
||||
"cmd-2": ["pane::ActivateItem", 1],
|
||||
"cmd-3": ["pane::ActivateItem", 2],
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
"context": "BufferSearchBar",
|
||||
"bindings": {
|
||||
"ctrl-s": "search::SelectNextMatch",
|
||||
"ctrl-shift-s": "search::SelectPrevMatch"
|
||||
"ctrl-shift-s": "search::SelectPreviousMatch"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
"tab": "menu::SelectNext",
|
||||
"ctrl-n": "menu::SelectNext",
|
||||
"down": "menu::SelectNext",
|
||||
"shift-tab": "menu::SelectPrev",
|
||||
"ctrl-p": "menu::SelectPrev",
|
||||
"up": "menu::SelectPrev",
|
||||
"shift-tab": "menu::SelectPrevious",
|
||||
"ctrl-p": "menu::SelectPrevious",
|
||||
"up": "menu::SelectPrevious",
|
||||
"enter": "menu::Confirm",
|
||||
"ctrl-enter": "menu::SecondaryConfirm",
|
||||
"cmd-enter": "menu::SecondaryConfirm",
|
||||
|
||||
@@ -62,9 +62,9 @@
|
||||
"g /": "pane::DeploySearch",
|
||||
"?": ["vim::Search", { "backwards": true }],
|
||||
"*": "vim::MoveToNext",
|
||||
"#": "vim::MoveToPrev",
|
||||
"#": "vim::MoveToPrevious",
|
||||
"n": "vim::MoveToNextMatch",
|
||||
"shift-n": "vim::MoveToPrevMatch",
|
||||
"shift-n": "vim::MoveToPreviousMatch",
|
||||
"%": "vim::Matching",
|
||||
"] }": ["vim::UnmatchedForward", { "char": "}" }],
|
||||
"[ {": ["vim::UnmatchedBackward", { "char": "{" }],
|
||||
@@ -106,7 +106,7 @@
|
||||
"g g": "vim::StartOfDocument",
|
||||
"g h": "editor::Hover",
|
||||
"g t": "pane::ActivateNextItem",
|
||||
"g shift-t": "pane::ActivatePrevItem",
|
||||
"g shift-t": "pane::ActivatePreviousItem",
|
||||
"g d": "editor::GoToDefinition",
|
||||
"g shift-d": "editor::GoToDeclaration",
|
||||
"g y": "editor::GoToTypeDefinition",
|
||||
@@ -126,7 +126,7 @@
|
||||
"g shift-a": "editor::FindAllReferences", // zed specific
|
||||
"g space": "editor::OpenExcerpts", // zed specific
|
||||
"g *": ["vim::MoveToNext", { "partial_word": true }],
|
||||
"g #": ["vim::MoveToPrev", { "partial_word": true }],
|
||||
"g #": ["vim::MoveToPrevious", { "partial_word": true }],
|
||||
"g j": ["vim::Down", { "display_lines": true }],
|
||||
"g down": ["vim::Down", { "display_lines": true }],
|
||||
"g k": ["vim::Up", { "display_lines": true }],
|
||||
@@ -138,7 +138,7 @@
|
||||
"g ^": ["vim::FirstNonWhitespace", { "display_lines": true }],
|
||||
"g v": "vim::RestoreVisualSelection",
|
||||
"g ]": "editor::GoToDiagnostic",
|
||||
"g [": "editor::GoToPrevDiagnostic",
|
||||
"g [": "editor::GoToPreviousDiagnostic",
|
||||
"g i": "vim::InsertAtPrevious",
|
||||
"g ,": "vim::ChangeListNewer",
|
||||
"g ;": "vim::ChangeListOlder",
|
||||
@@ -231,15 +231,15 @@
|
||||
"g w": "vim::PushRewrap",
|
||||
"g q": "vim::PushRewrap",
|
||||
"ctrl-pagedown": "pane::ActivateNextItem",
|
||||
"ctrl-pageup": "pane::ActivatePrevItem",
|
||||
"ctrl-pageup": "pane::ActivatePreviousItem",
|
||||
"insert": "vim::InsertBefore",
|
||||
// tree-sitter related commands
|
||||
"[ x": "vim::SelectLargerSyntaxNode",
|
||||
"] x": "vim::SelectSmallerSyntaxNode",
|
||||
"] d": "editor::GoToDiagnostic",
|
||||
"[ d": "editor::GoToPrevDiagnostic",
|
||||
"[ d": "editor::GoToPreviousDiagnostic",
|
||||
"] c": "editor::GoToHunk",
|
||||
"[ c": "editor::GoToPrevHunk",
|
||||
"[ c": "editor::GoToPreviousHunk",
|
||||
"g c": "vim::PushToggleComments"
|
||||
}
|
||||
},
|
||||
@@ -272,7 +272,7 @@
|
||||
"shift-s": "vim::SubstituteLine",
|
||||
"~": "vim::ChangeCase",
|
||||
"*": ["vim::MoveToNext", { "partial_word": true }],
|
||||
"#": ["vim::MoveToPrev", { "partial_word": true }],
|
||||
"#": ["vim::MoveToPrevious", { "partial_word": true }],
|
||||
"ctrl-a": "vim::Increment",
|
||||
"ctrl-x": "vim::Decrement",
|
||||
"g ctrl-a": ["vim::Increment", { "step": true }],
|
||||
@@ -448,7 +448,10 @@
|
||||
"d": "vim::CurrentLine",
|
||||
"s": "vim::PushDeleteSurrounds",
|
||||
"o": "editor::ToggleSelectedDiffHunks", // "d o"
|
||||
"p": "git::Restore" // "d p"
|
||||
"shift-o": "git::ToggleStaged",
|
||||
"p": "git::Restore", // "d p"
|
||||
"u": "git::StageAndNext", // "d u"
|
||||
"shift-u": "git::UnstageAndNext" // "d shift-u"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -620,8 +623,8 @@
|
||||
"ctrl-w =": "vim::ResetPaneSizes",
|
||||
"ctrl-w g t": "pane::ActivateNextItem",
|
||||
"ctrl-w ctrl-g t": "pane::ActivateNextItem",
|
||||
"ctrl-w g shift-t": "pane::ActivatePrevItem",
|
||||
"ctrl-w ctrl-g shift-t": "pane::ActivatePrevItem",
|
||||
"ctrl-w g shift-t": "pane::ActivatePreviousItem",
|
||||
"ctrl-w ctrl-g shift-t": "pane::ActivatePreviousItem",
|
||||
"ctrl-w w": "workspace::ActivateNextPane",
|
||||
"ctrl-w ctrl-w": "workspace::ActivateNextPane",
|
||||
"ctrl-w p": "workspace::ActivatePreviousPane",
|
||||
@@ -664,7 +667,7 @@
|
||||
"escape": "project_panel::ToggleFocus",
|
||||
"h": "project_panel::CollapseSelectedEntry",
|
||||
"j": "menu::SelectNext",
|
||||
"k": "menu::SelectPrev",
|
||||
"k": "menu::SelectPrevious",
|
||||
"l": "project_panel::ExpandSelectedEntry",
|
||||
"o": "project_panel::OpenPermanent",
|
||||
"shift-d": "project_panel::Delete",
|
||||
@@ -690,7 +693,7 @@
|
||||
"context": "OutlinePanel && not_editing",
|
||||
"bindings": {
|
||||
"j": "menu::SelectNext",
|
||||
"k": "menu::SelectPrev",
|
||||
"k": "menu::SelectPrevious",
|
||||
"shift-g": "menu::SelectLast",
|
||||
"g g": "menu::SelectFirst"
|
||||
}
|
||||
@@ -699,7 +702,7 @@
|
||||
"context": "GitPanel && ChangesList",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"k": "menu::SelectPrev",
|
||||
"k": "menu::SelectPrevious",
|
||||
"j": "menu::SelectNext",
|
||||
"g g": "menu::SelectFirst",
|
||||
"shift-g": "menu::SelectLast",
|
||||
|
||||
@@ -555,6 +555,12 @@
|
||||
//
|
||||
// Default: icon
|
||||
"status_style": "icon",
|
||||
// What branch name to use if init.defaultBranch
|
||||
// is not set
|
||||
//
|
||||
// Default: main
|
||||
"fallback_branch_name": "main",
|
||||
|
||||
"scrollbar": {
|
||||
// When to show the scrollbar in the git panel.
|
||||
//
|
||||
@@ -648,11 +654,19 @@
|
||||
// Show git status colors in the editor tabs.
|
||||
"git_status": false,
|
||||
// Position of the close button on the editor tabs.
|
||||
// One of: ["right", "left", "hidden"]
|
||||
"close_position": "right",
|
||||
// Whether to show the file icon for a tab.
|
||||
"file_icons": false,
|
||||
// Whether to always show the close button on tabs.
|
||||
"always_show_close_button": false,
|
||||
// Controls the appearance behavior of the tab's close button.
|
||||
//
|
||||
// 1. Show it just upon hovering the tab. (default)
|
||||
// "hover"
|
||||
// 2. Show it persistently.
|
||||
// "always"
|
||||
// 3. Never show it, even if hovering it.
|
||||
// "hidden"
|
||||
"show_close_button": "hover",
|
||||
// What to do after closing the current tab.
|
||||
//
|
||||
// 1. Activate the tab that was open previously (default)
|
||||
@@ -843,15 +857,7 @@
|
||||
// Any addition to this list will be merged with the default list.
|
||||
// Globs are matched relative to the worktree root,
|
||||
// except when starting with a slash (/) or equivalent in Windows.
|
||||
"disabled_globs": [
|
||||
"**/.env*",
|
||||
"**/*.pem",
|
||||
"**/*.key",
|
||||
"**/*.cert",
|
||||
"**/*.crt",
|
||||
"**/.dev.vars",
|
||||
"**/secrets.yml"
|
||||
],
|
||||
"disabled_globs": ["**/.env*", "**/*.pem", "**/*.key", "**/*.cert", "**/*.crt", "**/.dev.vars", "**/secrets.yml"],
|
||||
// When to show edit predictions previews in buffer.
|
||||
// This setting takes two possible values:
|
||||
// 1. Display predictions inline when there are no language server completions available.
|
||||
|
||||
@@ -6,15 +6,7 @@
|
||||
{
|
||||
"name": "Gruvbox Dark",
|
||||
"appearance": "dark",
|
||||
"accents": [
|
||||
"#cc241dff",
|
||||
"#98971aff",
|
||||
"#d79921ff",
|
||||
"#458588ff",
|
||||
"#b16286ff",
|
||||
"#689d6aff",
|
||||
"#d65d0eff"
|
||||
],
|
||||
"accents": ["#cc241dff", "#98971aff", "#d79921ff", "#458588ff", "#b16286ff", "#689d6aff", "#d65d0eff"],
|
||||
"style": {
|
||||
"border": "#5b534dff",
|
||||
"border.variant": "#494340ff",
|
||||
@@ -105,9 +97,9 @@
|
||||
"terminal.ansi.bright_white": "#fbf1c7ff",
|
||||
"terminal.ansi.dim_white": "#b0a189ff",
|
||||
"link_text.hover": "#83a598ff",
|
||||
"version_control_added": "#b7bb26ff",
|
||||
"version_control_modified": "#f9bd2fff",
|
||||
"version_control_deleted": "#fb4a35ff",
|
||||
"version_control.added": "#b7bb26ff",
|
||||
"version_control.modified": "#f9bd2fff",
|
||||
"version_control.deleted": "#fb4a35ff",
|
||||
"conflict": "#f9bd2fff",
|
||||
"conflict.background": "#572e10ff",
|
||||
"conflict.border": "#754916ff",
|
||||
@@ -394,15 +386,7 @@
|
||||
{
|
||||
"name": "Gruvbox Dark Hard",
|
||||
"appearance": "dark",
|
||||
"accents": [
|
||||
"#cc241dff",
|
||||
"#98971aff",
|
||||
"#d79921ff",
|
||||
"#458588ff",
|
||||
"#b16286ff",
|
||||
"#689d6aff",
|
||||
"#d65d0eff"
|
||||
],
|
||||
"accents": ["#cc241dff", "#98971aff", "#d79921ff", "#458588ff", "#b16286ff", "#689d6aff", "#d65d0eff"],
|
||||
"style": {
|
||||
"border": "#5b534dff",
|
||||
"border.variant": "#494340ff",
|
||||
@@ -493,9 +477,9 @@
|
||||
"terminal.ansi.bright_white": "#fbf1c7ff",
|
||||
"terminal.ansi.dim_white": "#b0a189ff",
|
||||
"link_text.hover": "#83a598ff",
|
||||
"version_control_added": "#b7bb26ff",
|
||||
"version_control_modified": "#f9bd2fff",
|
||||
"version_control_deleted": "#fb4a35ff",
|
||||
"version_control.added": "#b7bb26ff",
|
||||
"version_control.modified": "#f9bd2fff",
|
||||
"version_control.deleted": "#fb4a35ff",
|
||||
"conflict": "#f9bd2fff",
|
||||
"conflict.background": "#572e10ff",
|
||||
"conflict.border": "#754916ff",
|
||||
@@ -782,15 +766,7 @@
|
||||
{
|
||||
"name": "Gruvbox Dark Soft",
|
||||
"appearance": "dark",
|
||||
"accents": [
|
||||
"#cc241dff",
|
||||
"#98971aff",
|
||||
"#d79921ff",
|
||||
"#458588ff",
|
||||
"#b16286ff",
|
||||
"#689d6aff",
|
||||
"#d65d0eff"
|
||||
],
|
||||
"accents": ["#cc241dff", "#98971aff", "#d79921ff", "#458588ff", "#b16286ff", "#689d6aff", "#d65d0eff"],
|
||||
"style": {
|
||||
"border": "#5b534dff",
|
||||
"border.variant": "#494340ff",
|
||||
@@ -881,9 +857,9 @@
|
||||
"terminal.ansi.bright_white": "#fbf1c7ff",
|
||||
"terminal.ansi.dim_white": "#b0a189ff",
|
||||
"link_text.hover": "#83a598ff",
|
||||
"version_control_added": "#b7bb26ff",
|
||||
"version_control_modified": "#f9bd2fff",
|
||||
"version_control_deleted": "#fb4a35ff",
|
||||
"version_control.added": "#b7bb26ff",
|
||||
"version_control.modified": "#f9bd2fff",
|
||||
"version_control.deleted": "#fb4a35ff",
|
||||
"conflict": "#f9bd2fff",
|
||||
"conflict.background": "#572e10ff",
|
||||
"conflict.border": "#754916ff",
|
||||
@@ -1170,15 +1146,7 @@
|
||||
{
|
||||
"name": "Gruvbox Light",
|
||||
"appearance": "light",
|
||||
"accents": [
|
||||
"#cc241dff",
|
||||
"#98971aff",
|
||||
"#d79921ff",
|
||||
"#458588ff",
|
||||
"#b16286ff",
|
||||
"#689d6aff",
|
||||
"#d65d0eff"
|
||||
],
|
||||
"accents": ["#cc241dff", "#98971aff", "#d79921ff", "#458588ff", "#b16286ff", "#689d6aff", "#d65d0eff"],
|
||||
"style": {
|
||||
"border": "#c8b899ff",
|
||||
"border.variant": "#ddcca7ff",
|
||||
@@ -1269,9 +1237,9 @@
|
||||
"terminal.ansi.bright_white": "#282828ff",
|
||||
"terminal.ansi.dim_white": "#73675eff",
|
||||
"link_text.hover": "#0b6678ff",
|
||||
"version_control_added": "#797410ff",
|
||||
"version_control_modified": "#b57615ff",
|
||||
"version_control_deleted": "#9d0308ff",
|
||||
"version_control.added": "#797410ff",
|
||||
"version_control.modified": "#b57615ff",
|
||||
"version_control.deleted": "#9d0308ff",
|
||||
"conflict": "#b57615ff",
|
||||
"conflict.background": "#f5e2d0ff",
|
||||
"conflict.border": "#ebccabff",
|
||||
@@ -1558,15 +1526,7 @@
|
||||
{
|
||||
"name": "Gruvbox Light Hard",
|
||||
"appearance": "light",
|
||||
"accents": [
|
||||
"#cc241dff",
|
||||
"#98971aff",
|
||||
"#d79921ff",
|
||||
"#458588ff",
|
||||
"#b16286ff",
|
||||
"#689d6aff",
|
||||
"#d65d0eff"
|
||||
],
|
||||
"accents": ["#cc241dff", "#98971aff", "#d79921ff", "#458588ff", "#b16286ff", "#689d6aff", "#d65d0eff"],
|
||||
"style": {
|
||||
"border": "#c8b899ff",
|
||||
"border.variant": "#ddcca7ff",
|
||||
@@ -1657,9 +1617,9 @@
|
||||
"terminal.ansi.bright_white": "#282828ff",
|
||||
"terminal.ansi.dim_white": "#73675eff",
|
||||
"link_text.hover": "#0b6678ff",
|
||||
"version_control_added": "#797410ff",
|
||||
"version_control_modified": "#b57615ff",
|
||||
"version_control_deleted": "#9d0308ff",
|
||||
"version_control.added": "#797410ff",
|
||||
"version_control.modified": "#b57615ff",
|
||||
"version_control.deleted": "#9d0308ff",
|
||||
"conflict": "#b57615ff",
|
||||
"conflict.background": "#f5e2d0ff",
|
||||
"conflict.border": "#ebccabff",
|
||||
@@ -1946,15 +1906,7 @@
|
||||
{
|
||||
"name": "Gruvbox Light Soft",
|
||||
"appearance": "light",
|
||||
"accents": [
|
||||
"#cc241dff",
|
||||
"#98971aff",
|
||||
"#d79921ff",
|
||||
"#458588ff",
|
||||
"#b16286ff",
|
||||
"#689d6aff",
|
||||
"#d65d0eff"
|
||||
],
|
||||
"accents": ["#cc241dff", "#98971aff", "#d79921ff", "#458588ff", "#b16286ff", "#689d6aff", "#d65d0eff"],
|
||||
"style": {
|
||||
"border": "#c8b899ff",
|
||||
"border.variant": "#ddcca7ff",
|
||||
@@ -2045,9 +1997,9 @@
|
||||
"terminal.ansi.bright_white": "#282828ff",
|
||||
"terminal.ansi.dim_white": "#73675eff",
|
||||
"link_text.hover": "#0b6678ff",
|
||||
"version_control_added": "#797410ff",
|
||||
"version_control_modified": "#b57615ff",
|
||||
"version_control_deleted": "#9d0308ff",
|
||||
"version_control.added": "#797410ff",
|
||||
"version_control.modified": "#b57615ff",
|
||||
"version_control.deleted": "#9d0308ff",
|
||||
"conflict": "#b57615ff",
|
||||
"conflict.background": "#f5e2d0ff",
|
||||
"conflict.border": "#ebccabff",
|
||||
|
||||
@@ -96,9 +96,9 @@
|
||||
"terminal.ansi.bright_white": "#dce0e5ff",
|
||||
"terminal.ansi.dim_white": "#575d65ff",
|
||||
"link_text.hover": "#74ade8ff",
|
||||
"version_control_added": "#a7c088ff",
|
||||
"version_control_modified": "#dec184ff",
|
||||
"version_control_deleted": "#d07277ff",
|
||||
"version_control.added": "#27a657ff",
|
||||
"version_control.modified": "#d3b020ff",
|
||||
"version_control.deleted": "#e06c76ff",
|
||||
"conflict": "#dec184ff",
|
||||
"conflict.background": "#dec1841a",
|
||||
"conflict.border": "#5d4c2fff",
|
||||
@@ -475,9 +475,9 @@
|
||||
"terminal.ansi.bright_white": "#242529ff",
|
||||
"terminal.ansi.dim_white": "#97979aff",
|
||||
"link_text.hover": "#5c78e2ff",
|
||||
"version_control_added": "#669f59ff",
|
||||
"version_control_modified": "#a48819ff",
|
||||
"version_control_deleted": "#d36151ff",
|
||||
"version_control.added": "#27a657ff",
|
||||
"version_control.modified": "#d3b020ff",
|
||||
"version_control.deleted": "#e06c76ff",
|
||||
"conflict": "#a48819ff",
|
||||
"conflict.background": "#faf2e6ff",
|
||||
"conflict.border": "#f4e7d1ff",
|
||||
|
||||
@@ -9,7 +9,10 @@ use gpui::{
|
||||
};
|
||||
use language::{LanguageRegistry, LanguageServerBinaryStatus, LanguageServerId};
|
||||
use lsp::LanguageServerName;
|
||||
use project::{EnvironmentErrorMessage, LanguageServerProgress, Project, WorktreeId};
|
||||
use project::{
|
||||
EnvironmentErrorMessage, LanguageServerProgress, LspStoreEvent, Project,
|
||||
ProjectEnvironmentEvent, WorktreeId,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::{cmp::Reverse, fmt::Write, sync::Arc, time::Duration};
|
||||
use ui::{prelude::*, ButtonLike, ContextMenu, PopoverMenu, PopoverMenuHandle, Tooltip};
|
||||
@@ -73,7 +76,22 @@ impl ActivityIndicator {
|
||||
})
|
||||
.detach();
|
||||
|
||||
cx.observe(&project, |_, _, cx| cx.notify()).detach();
|
||||
cx.subscribe(
|
||||
&project.read(cx).lsp_store(),
|
||||
|_, _, event, cx| match event {
|
||||
LspStoreEvent::LanguageServerUpdate { .. } => cx.notify(),
|
||||
_ => {}
|
||||
},
|
||||
)
|
||||
.detach();
|
||||
|
||||
cx.subscribe(
|
||||
&project.read(cx).environment().clone(),
|
||||
|_, _, event, cx| match event {
|
||||
ProjectEnvironmentEvent::ErrorsUpdated => cx.notify(),
|
||||
},
|
||||
)
|
||||
.detach();
|
||||
|
||||
if let Some(auto_updater) = auto_updater.as_ref() {
|
||||
cx.observe(auto_updater, |_, _, cx| cx.notify()).detach();
|
||||
@@ -204,7 +222,7 @@ impl ActivityIndicator {
|
||||
message: error.0.clone(),
|
||||
on_click: Some(Arc::new(move |this, window, cx| {
|
||||
this.project.update(cx, |project, cx| {
|
||||
project.remove_environment_error(cx, worktree_id);
|
||||
project.remove_environment_error(worktree_id, cx);
|
||||
});
|
||||
window.dispatch_action(Box::new(workspace::OpenLog), cx);
|
||||
})),
|
||||
|
||||
21
crates/askpass/Cargo.toml
Normal file
21
crates/askpass/Cargo.toml
Normal file
@@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "askpass"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
publish.workspace = true
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[lib]
|
||||
path = "src/askpass.rs"
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
smol.workspace = true
|
||||
tempfile.workspace = true
|
||||
util.workspace = true
|
||||
which.workspace = true
|
||||
1
crates/askpass/LICENSE-APACHE
Symbolic link
1
crates/askpass/LICENSE-APACHE
Symbolic link
@@ -0,0 +1 @@
|
||||
../../LICENSE-APACHE
|
||||
194
crates/askpass/src/askpass.rs
Normal file
194
crates/askpass/src/askpass.rs
Normal file
@@ -0,0 +1,194 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
|
||||
#[cfg(unix)]
|
||||
use anyhow::Context as _;
|
||||
use futures::channel::{mpsc, oneshot};
|
||||
#[cfg(unix)]
|
||||
use futures::{io::BufReader, AsyncBufReadExt as _};
|
||||
#[cfg(unix)]
|
||||
use futures::{select_biased, AsyncWriteExt as _, FutureExt as _};
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use gpui::{AsyncApp, BackgroundExecutor, Task};
|
||||
#[cfg(unix)]
|
||||
use smol::fs;
|
||||
#[cfg(unix)]
|
||||
use smol::{fs::unix::PermissionsExt as _, net::unix::UnixListener};
|
||||
#[cfg(unix)]
|
||||
use util::ResultExt as _;
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub enum AskPassResult {
|
||||
CancelledByUser,
|
||||
Timedout,
|
||||
}
|
||||
|
||||
pub struct AskPassDelegate {
|
||||
tx: mpsc::UnboundedSender<(String, oneshot::Sender<String>)>,
|
||||
_task: Task<()>,
|
||||
}
|
||||
|
||||
impl AskPassDelegate {
|
||||
pub fn new(
|
||||
cx: &mut AsyncApp,
|
||||
password_prompt: impl Fn(String, oneshot::Sender<String>, &mut AsyncApp) + Send + Sync + 'static,
|
||||
) -> Self {
|
||||
let (tx, mut rx) = mpsc::unbounded::<(String, oneshot::Sender<String>)>();
|
||||
let task = cx.spawn(|mut cx| async move {
|
||||
while let Some((prompt, channel)) = rx.next().await {
|
||||
password_prompt(prompt, channel, &mut cx);
|
||||
}
|
||||
});
|
||||
Self { tx, _task: task }
|
||||
}
|
||||
|
||||
pub async fn ask_password(&mut self, prompt: String) -> anyhow::Result<String> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
self.tx.send((prompt, tx)).await?;
|
||||
Ok(rx.await?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub struct AskPassSession {
|
||||
script_path: PathBuf,
|
||||
_askpass_task: Task<()>,
|
||||
askpass_opened_rx: Option<oneshot::Receiver<()>>,
|
||||
askpass_kill_master_rx: Option<oneshot::Receiver<()>>,
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl AskPassSession {
|
||||
/// This will create a new AskPassSession.
|
||||
/// You must retain this session until the master process exits.
|
||||
#[must_use]
|
||||
pub async fn new(
|
||||
executor: &BackgroundExecutor,
|
||||
mut delegate: AskPassDelegate,
|
||||
) -> anyhow::Result<Self> {
|
||||
let temp_dir = tempfile::Builder::new().prefix("zed-askpass").tempdir()?;
|
||||
let askpass_socket = temp_dir.path().join("askpass.sock");
|
||||
let askpass_script_path = temp_dir.path().join("askpass.sh");
|
||||
let (askpass_opened_tx, askpass_opened_rx) = oneshot::channel::<()>();
|
||||
let listener =
|
||||
UnixListener::bind(&askpass_socket).context("failed to create askpass socket")?;
|
||||
|
||||
let (askpass_kill_master_tx, askpass_kill_master_rx) = oneshot::channel::<()>();
|
||||
let mut kill_tx = Some(askpass_kill_master_tx);
|
||||
|
||||
let askpass_task = executor.spawn(async move {
|
||||
let mut askpass_opened_tx = Some(askpass_opened_tx);
|
||||
|
||||
while let Ok((mut stream, _)) = listener.accept().await {
|
||||
if let Some(askpass_opened_tx) = askpass_opened_tx.take() {
|
||||
askpass_opened_tx.send(()).ok();
|
||||
}
|
||||
let mut buffer = Vec::new();
|
||||
let mut reader = BufReader::new(&mut stream);
|
||||
if reader.read_until(b'\0', &mut buffer).await.is_err() {
|
||||
buffer.clear();
|
||||
}
|
||||
let prompt = String::from_utf8_lossy(&buffer);
|
||||
if let Some(password) = delegate
|
||||
.ask_password(prompt.to_string())
|
||||
.await
|
||||
.context("failed to get askpass password")
|
||||
.log_err()
|
||||
{
|
||||
stream.write_all(password.as_bytes()).await.log_err();
|
||||
} else {
|
||||
if let Some(kill_tx) = kill_tx.take() {
|
||||
kill_tx.send(()).log_err();
|
||||
}
|
||||
// note: we expect the caller to drop this task when it's done.
|
||||
// We need to keep the stream open until the caller is done to avoid
|
||||
// spurious errors from ssh.
|
||||
std::future::pending::<()>().await;
|
||||
drop(stream);
|
||||
}
|
||||
}
|
||||
drop(temp_dir)
|
||||
});
|
||||
|
||||
anyhow::ensure!(
|
||||
which::which("nc").is_ok(),
|
||||
"Cannot find `nc` command (netcat), which is required to connect over SSH."
|
||||
);
|
||||
|
||||
// Create an askpass script that communicates back to this process.
|
||||
let askpass_script = format!(
|
||||
"{shebang}\n{print_args} | {nc} -U {askpass_socket} 2> /dev/null \n",
|
||||
// on macOS `brew install netcat` provides the GNU netcat implementation
|
||||
// which does not support -U.
|
||||
nc = if cfg!(target_os = "macos") {
|
||||
"/usr/bin/nc"
|
||||
} else {
|
||||
"nc"
|
||||
},
|
||||
askpass_socket = askpass_socket.display(),
|
||||
print_args = "printf '%s\\0' \"$@\"",
|
||||
shebang = "#!/bin/sh",
|
||||
);
|
||||
fs::write(&askpass_script_path, askpass_script).await?;
|
||||
fs::set_permissions(&askpass_script_path, std::fs::Permissions::from_mode(0o755)).await?;
|
||||
|
||||
Ok(Self {
|
||||
script_path: askpass_script_path,
|
||||
_askpass_task: askpass_task,
|
||||
askpass_kill_master_rx: Some(askpass_kill_master_rx),
|
||||
askpass_opened_rx: Some(askpass_opened_rx),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn script_path(&self) -> &Path {
|
||||
&self.script_path
|
||||
}
|
||||
|
||||
// This will run the askpass task forever, resolving as many authentication requests as needed.
|
||||
// The caller is responsible for examining the result of their own commands and cancelling this
|
||||
// future when this is no longer needed. Note that this can only be called once, but due to the
|
||||
// drop order this takes an &mut, so you can `drop()` it after you're done with the master process.
|
||||
pub async fn run(&mut self) -> AskPassResult {
|
||||
let connection_timeout = Duration::from_secs(10);
|
||||
let askpass_opened_rx = self.askpass_opened_rx.take().expect("Only call run once");
|
||||
let askpass_kill_master_rx = self
|
||||
.askpass_kill_master_rx
|
||||
.take()
|
||||
.expect("Only call run once");
|
||||
|
||||
select_biased! {
|
||||
_ = askpass_opened_rx.fuse() => {
|
||||
// Note: this await can only resolve after we are dropped.
|
||||
askpass_kill_master_rx.await.ok();
|
||||
return AskPassResult::CancelledByUser
|
||||
}
|
||||
|
||||
_ = futures::FutureExt::fuse(smol::Timer::after(connection_timeout)) => {
|
||||
return AskPassResult::Timedout
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
pub struct AskPassSession {
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
impl AskPassSession {
|
||||
pub async fn new(_: &BackgroundExecutor, _: AskPassDelegate) -> anyhow::Result<Self> {
|
||||
Ok(Self {
|
||||
path: PathBuf::new(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn script_path(&self) -> &Path {
|
||||
&self.path
|
||||
}
|
||||
|
||||
pub async fn run(&mut self) -> AskPassResult {
|
||||
futures::FutureExt::fuse(smol::Timer::after(Duration::from_secs(10))).await;
|
||||
AskPassResult::Timedout
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,7 @@ parking_lot.workspace = true
|
||||
paths.workspace = true
|
||||
project.workspace = true
|
||||
prompt_library.workspace = true
|
||||
prompt_store.workspace = true
|
||||
proto.workspace = true
|
||||
rope.workspace = true
|
||||
schemars.workspace = true
|
||||
|
||||
@@ -19,7 +19,7 @@ use gpui::{actions, App, Global, UpdateGlobal};
|
||||
use language_model::{
|
||||
LanguageModelId, LanguageModelProviderId, LanguageModelRegistry, LanguageModelResponseMessage,
|
||||
};
|
||||
use prompt_library::PromptBuilder;
|
||||
use prompt_store::PromptBuilder;
|
||||
use semantic_index::{CloudEmbeddingProvider, SemanticDb};
|
||||
use serde::Deserialize;
|
||||
use settings::{Settings, SettingsStore};
|
||||
|
||||
@@ -24,7 +24,8 @@ use language_model::{
|
||||
AuthenticateError, LanguageModelProviderId, LanguageModelRegistry, ZED_CLOUD_PROVIDER_ID,
|
||||
};
|
||||
use project::Project;
|
||||
use prompt_library::{open_prompt_library, PromptBuilder, PromptLibrary};
|
||||
use prompt_library::{open_prompt_library, PromptLibrary};
|
||||
use prompt_store::PromptBuilder;
|
||||
use search::{buffer_search::DivRegistrar, BufferSearchBar};
|
||||
use settings::{update_settings_file, Settings};
|
||||
use smol::stream::StreamExt;
|
||||
|
||||
@@ -35,11 +35,11 @@ use language_model::{
|
||||
report_assistant_event, LanguageModel, LanguageModelRegistry, LanguageModelRequest,
|
||||
LanguageModelRequestMessage, LanguageModelTextStream, Role,
|
||||
};
|
||||
use language_model_selector::{InlineLanguageModelSelector, LanguageModelSelector};
|
||||
use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};
|
||||
use multi_buffer::MultiBufferRow;
|
||||
use parking_lot::Mutex;
|
||||
use project::{CodeAction, ProjectTransaction};
|
||||
use prompt_library::PromptBuilder;
|
||||
use prompt_store::PromptBuilder;
|
||||
use rope::Rope;
|
||||
use settings::{update_settings_file, Settings, SettingsStore};
|
||||
use smol::future::FutureExt;
|
||||
@@ -1589,10 +1589,29 @@ impl Render for PromptEditor {
|
||||
.w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0))
|
||||
.justify_center()
|
||||
.gap_2()
|
||||
.child(
|
||||
InlineLanguageModelSelector::new(self.language_model_selector.clone())
|
||||
.render(window, cx),
|
||||
)
|
||||
.child(LanguageModelSelectorPopoverMenu::new(
|
||||
self.language_model_selector.clone(),
|
||||
IconButton::new("context", IconName::SettingsAlt)
|
||||
.shape(IconButtonShape::Square)
|
||||
.icon_size(IconSize::Small)
|
||||
.icon_color(Color::Muted),
|
||||
move |window, cx| {
|
||||
Tooltip::with_meta(
|
||||
format!(
|
||||
"Using {}",
|
||||
LanguageModelRegistry::read_global(cx)
|
||||
.active_model()
|
||||
.map(|model| model.name().0)
|
||||
.unwrap_or_else(|| "No model selected".into()),
|
||||
),
|
||||
None,
|
||||
"Change Model",
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
},
|
||||
gpui::Corner::TopRight,
|
||||
))
|
||||
.map(|el| {
|
||||
let CodegenStatus::Error(error) = self.codegen.read(cx).status(cx) else {
|
||||
return el;
|
||||
|
||||
@@ -19,8 +19,8 @@ use language_model::{
|
||||
report_assistant_event, LanguageModelRegistry, LanguageModelRequest,
|
||||
LanguageModelRequestMessage, Role,
|
||||
};
|
||||
use language_model_selector::{InlineLanguageModelSelector, LanguageModelSelector};
|
||||
use prompt_library::PromptBuilder;
|
||||
use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};
|
||||
use prompt_store::PromptBuilder;
|
||||
use settings::{update_settings_file, Settings};
|
||||
use std::{
|
||||
cmp,
|
||||
@@ -506,7 +506,7 @@ struct PromptEditor {
|
||||
impl EventEmitter<PromptEditorEvent> for PromptEditor {}
|
||||
|
||||
impl Render for PromptEditor {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let status = &self.codegen.read(cx).status;
|
||||
let buttons = match status {
|
||||
CodegenStatus::Idle => {
|
||||
@@ -641,10 +641,29 @@ impl Render for PromptEditor {
|
||||
.w_12()
|
||||
.justify_center()
|
||||
.gap_2()
|
||||
.child(
|
||||
InlineLanguageModelSelector::new(self.language_model_selector.clone())
|
||||
.render(window, cx),
|
||||
)
|
||||
.child(LanguageModelSelectorPopoverMenu::new(
|
||||
self.language_model_selector.clone(),
|
||||
IconButton::new("change-model", IconName::SettingsAlt)
|
||||
.shape(IconButtonShape::Square)
|
||||
.icon_size(IconSize::Small)
|
||||
.icon_color(Color::Muted),
|
||||
move |window, cx| {
|
||||
Tooltip::with_meta(
|
||||
format!(
|
||||
"Using {}",
|
||||
LanguageModelRegistry::read_global(cx)
|
||||
.active_model()
|
||||
.map(|model| model.name().0)
|
||||
.unwrap_or_else(|| "No model selected".into()),
|
||||
),
|
||||
None,
|
||||
"Change Model",
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
},
|
||||
gpui::Corner::TopRight,
|
||||
))
|
||||
.children(
|
||||
if let CodegenStatus::Error(error) = &self.codegen.read(cx).status {
|
||||
let error_message = SharedString::from(error.to_string());
|
||||
|
||||
@@ -56,6 +56,7 @@ paths.workspace = true
|
||||
picker.workspace = true
|
||||
project.workspace = true
|
||||
prompt_library.workspace = true
|
||||
prompt_store.workspace = true
|
||||
proto.workspace = true
|
||||
rope.workspace = true
|
||||
serde.workspace = true
|
||||
|
||||
@@ -270,8 +270,15 @@ impl ActiveThread {
|
||||
let model_registry = LanguageModelRegistry::read_global(cx);
|
||||
if let Some(model) = model_registry.active_model() {
|
||||
self.thread.update(cx, |thread, cx| {
|
||||
// Insert an empty user message to contain the tool results.
|
||||
thread.insert_user_message("", Vec::new(), cx);
|
||||
// Insert a user message to contain the tool results.
|
||||
thread.insert_user_message(
|
||||
// TODO: Sending up a user message without any content results in the model sending back
|
||||
// responses that also don't have any content. We currently don't handle this case well,
|
||||
// so for now we provide some text to keep the model on track.
|
||||
"Here are the tool results.",
|
||||
Vec::new(),
|
||||
cx,
|
||||
);
|
||||
thread.send_to_model(model, RequestKind::Chat, true, cx);
|
||||
});
|
||||
}
|
||||
@@ -295,10 +302,7 @@ impl ActiveThread {
|
||||
let colors = cx.theme().colors();
|
||||
|
||||
// Don't render user messages that are just there for returning tool results.
|
||||
if message.role == Role::User
|
||||
&& message.text.is_empty()
|
||||
&& self.thread.read(cx).message_has_tool_results(message_id)
|
||||
{
|
||||
if message.role == Role::User && self.thread.read(cx).message_has_tool_results(message_id) {
|
||||
return Empty.into_any();
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ use command_palette_hooks::CommandPaletteFilter;
|
||||
use feature_flags::{Assistant2FeatureFlag, FeatureFlagAppExt};
|
||||
use fs::Fs;
|
||||
use gpui::{actions, App};
|
||||
use prompt_library::PromptBuilder;
|
||||
use prompt_store::PromptBuilder;
|
||||
use settings::Settings as _;
|
||||
|
||||
pub use crate::assistant_panel::{AssistantPanel, ConcreteAssistantPanelDelegate};
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
use assistant_settings::AssistantSettings;
|
||||
use fs::Fs;
|
||||
use gpui::{Entity, FocusHandle};
|
||||
use language_model_selector::{AssistantLanguageModelSelector, LanguageModelSelector};
|
||||
use gpui::{Entity, FocusHandle, SharedString};
|
||||
use language_model::LanguageModelRegistry;
|
||||
use language_model_selector::{
|
||||
LanguageModelSelector, LanguageModelSelectorPopoverMenu, ToggleModelSelector,
|
||||
};
|
||||
use settings::update_settings_file;
|
||||
use std::sync::Arc;
|
||||
use ui::prelude::*;
|
||||
use ui::{prelude::*, ButtonLike, PopoverMenuHandle, Tooltip};
|
||||
|
||||
pub struct AssistantModelSelector {
|
||||
pub selector: Entity<LanguageModelSelector>,
|
||||
selector: Entity<LanguageModelSelector>,
|
||||
menu_handle: PopoverMenuHandle<LanguageModelSelector>,
|
||||
focus_handle: FocusHandle,
|
||||
}
|
||||
|
||||
impl AssistantModelSelector {
|
||||
pub(crate) fn new(
|
||||
fs: Arc<dyn Fs>,
|
||||
menu_handle: PopoverMenuHandle<LanguageModelSelector>,
|
||||
focus_handle: FocusHandle,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
@@ -33,14 +38,54 @@ impl AssistantModelSelector {
|
||||
cx,
|
||||
)
|
||||
}),
|
||||
menu_handle,
|
||||
focus_handle,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toggle(&self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.menu_handle.toggle(window, cx);
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for AssistantModelSelector {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
AssistantLanguageModelSelector::new(self.focus_handle.clone(), self.selector.clone())
|
||||
.render(window, cx)
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let active_model = LanguageModelRegistry::read_global(cx).active_model();
|
||||
let focus_handle = self.focus_handle.clone();
|
||||
let model_name = match active_model {
|
||||
Some(model) => model.name().0,
|
||||
_ => SharedString::from("No model selected"),
|
||||
};
|
||||
|
||||
LanguageModelSelectorPopoverMenu::new(
|
||||
self.selector.clone(),
|
||||
ButtonLike::new("active-model")
|
||||
.style(ButtonStyle::Subtle)
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_0p5()
|
||||
.child(
|
||||
Label::new(model_name)
|
||||
.size(LabelSize::Small)
|
||||
.color(Color::Muted),
|
||||
)
|
||||
.child(
|
||||
Icon::new(IconName::ChevronDown)
|
||||
.color(Color::Muted)
|
||||
.size(IconSize::XSmall),
|
||||
),
|
||||
),
|
||||
move |window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
"Change Model",
|
||||
&ToggleModelSelector,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
},
|
||||
gpui::Corner::BottomRight,
|
||||
)
|
||||
.with_handle(self.menu_handle.clone())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@ use gpui::{
|
||||
use language::LanguageRegistry;
|
||||
use language_model::{LanguageModelProviderTosView, LanguageModelRegistry};
|
||||
use project::Project;
|
||||
use prompt_library::{open_prompt_library, PromptBuilder, PromptLibrary};
|
||||
use prompt_library::{open_prompt_library, PromptLibrary};
|
||||
use prompt_store::PromptBuilder;
|
||||
use settings::{update_settings_file, Settings};
|
||||
use time::UtcOffset;
|
||||
use ui::{prelude::*, ContextMenu, KeyBinding, PopoverMenu, PopoverMenuHandle, Tab, Tooltip};
|
||||
@@ -608,7 +609,7 @@ impl AssistantPanel {
|
||||
.id("title")
|
||||
.overflow_x_scroll()
|
||||
.px(DynamicSpacing::Base08.rems(cx))
|
||||
.child(Label::new(title).text_ellipsis()),
|
||||
.child(Label::new(title).truncate()),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
|
||||
@@ -14,7 +14,7 @@ use language_model::{
|
||||
};
|
||||
use multi_buffer::MultiBufferRow;
|
||||
use parking_lot::Mutex;
|
||||
use prompt_library::PromptBuilder;
|
||||
use prompt_store::PromptBuilder;
|
||||
use rope::Rope;
|
||||
use smol::future::FutureExt;
|
||||
use std::{
|
||||
|
||||
@@ -167,8 +167,8 @@ impl PickerDelegate for FetchContextPickerDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
fn no_matches_text(&self, _window: &mut Window, _cx: &mut App) -> SharedString {
|
||||
"Enter the URL that you would like to fetch".into()
|
||||
fn no_matches_text(&self, _window: &mut Window, _cx: &mut App) -> Option<SharedString> {
|
||||
Some("Enter the URL that you would like to fetch".into())
|
||||
}
|
||||
|
||||
fn selected_index(&self) -> usize {
|
||||
|
||||
@@ -28,7 +28,7 @@ use language_model::{report_assistant_event, LanguageModelRegistry};
|
||||
use multi_buffer::MultiBufferRow;
|
||||
use parking_lot::Mutex;
|
||||
use project::{CodeAction, ProjectTransaction};
|
||||
use prompt_library::PromptBuilder;
|
||||
use prompt_store::PromptBuilder;
|
||||
use settings::{Settings, SettingsStore};
|
||||
use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase};
|
||||
use terminal_view::{terminal_panel::TerminalPanel, TerminalView};
|
||||
|
||||
@@ -20,6 +20,7 @@ use gpui::{
|
||||
EventEmitter, FocusHandle, Focusable, FontWeight, Subscription, TextStyle, WeakEntity, Window,
|
||||
};
|
||||
use language_model::{LanguageModel, LanguageModelRegistry};
|
||||
use language_model_selector::ToggleModelSelector;
|
||||
use parking_lot::Mutex;
|
||||
use settings::Settings;
|
||||
use std::cmp;
|
||||
@@ -102,11 +103,9 @@ impl<T: 'static> Render for PromptEditor<T> {
|
||||
.items_start()
|
||||
.cursor(CursorStyle::Arrow)
|
||||
.on_action(cx.listener(Self::toggle_context_picker))
|
||||
.on_action(cx.listener(|this, action, window, cx| {
|
||||
let selector = this.model_selector.read(cx).selector.clone();
|
||||
selector.update(cx, |selector, cx| {
|
||||
selector.toggle_model_selector(action, window, cx);
|
||||
})
|
||||
.on_action(cx.listener(|this, _: &ToggleModelSelector, window, cx| {
|
||||
this.model_selector
|
||||
.update(cx, |model_selector, cx| model_selector.toggle(window, cx));
|
||||
}))
|
||||
.on_action(cx.listener(Self::confirm))
|
||||
.on_action(cx.listener(Self::cancel))
|
||||
@@ -858,6 +857,7 @@ impl PromptEditor<BufferCodegen> {
|
||||
editor
|
||||
});
|
||||
let context_picker_menu_handle = PopoverMenuHandle::default();
|
||||
let model_selector_menu_handle = PopoverMenuHandle::default();
|
||||
|
||||
let context_strip = cx.new(|cx| {
|
||||
ContextStrip::new(
|
||||
@@ -881,7 +881,13 @@ impl PromptEditor<BufferCodegen> {
|
||||
context_strip,
|
||||
context_picker_menu_handle,
|
||||
model_selector: cx.new(|cx| {
|
||||
AssistantModelSelector::new(fs, prompt_editor.focus_handle(cx), window, cx)
|
||||
AssistantModelSelector::new(
|
||||
fs,
|
||||
model_selector_menu_handle,
|
||||
prompt_editor.focus_handle(cx),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}),
|
||||
edited_since_done: false,
|
||||
prompt_history,
|
||||
@@ -1006,6 +1012,7 @@ impl PromptEditor<TerminalCodegen> {
|
||||
editor
|
||||
});
|
||||
let context_picker_menu_handle = PopoverMenuHandle::default();
|
||||
let model_selector_menu_handle = PopoverMenuHandle::default();
|
||||
|
||||
let context_strip = cx.new(|cx| {
|
||||
ContextStrip::new(
|
||||
@@ -1029,7 +1036,13 @@ impl PromptEditor<TerminalCodegen> {
|
||||
context_strip,
|
||||
context_picker_menu_handle,
|
||||
model_selector: cx.new(|cx| {
|
||||
AssistantModelSelector::new(fs, prompt_editor.focus_handle(cx), window, cx)
|
||||
AssistantModelSelector::new(
|
||||
fs,
|
||||
model_selector_menu_handle.clone(),
|
||||
prompt_editor.focus_handle(cx),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}),
|
||||
edited_since_done: false,
|
||||
prompt_history,
|
||||
|
||||
@@ -8,6 +8,7 @@ use gpui::{
|
||||
TextStyle, WeakEntity,
|
||||
};
|
||||
use language_model::LanguageModelRegistry;
|
||||
use language_model_selector::ToggleModelSelector;
|
||||
use rope::Point;
|
||||
use settings::Settings;
|
||||
use std::time::Duration;
|
||||
@@ -53,6 +54,7 @@ impl MessageEditor {
|
||||
let context_store = cx.new(|_cx| ContextStore::new(workspace.clone()));
|
||||
let context_picker_menu_handle = PopoverMenuHandle::default();
|
||||
let inline_context_picker_menu_handle = PopoverMenuHandle::default();
|
||||
let model_selector_menu_handle = PopoverMenuHandle::default();
|
||||
|
||||
let editor = cx.new(|cx| {
|
||||
let mut editor = Editor::auto_height(10, window, cx);
|
||||
@@ -105,8 +107,15 @@ impl MessageEditor {
|
||||
context_picker_menu_handle,
|
||||
inline_context_picker,
|
||||
inline_context_picker_menu_handle,
|
||||
model_selector: cx
|
||||
.new(|cx| AssistantModelSelector::new(fs, editor.focus_handle(cx), window, cx)),
|
||||
model_selector: cx.new(|cx| {
|
||||
AssistantModelSelector::new(
|
||||
fs,
|
||||
model_selector_menu_handle,
|
||||
editor.focus_handle(cx),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}),
|
||||
use_tools: false,
|
||||
_subscriptions: subscriptions,
|
||||
}
|
||||
@@ -297,11 +306,9 @@ impl Render for MessageEditor {
|
||||
v_flex()
|
||||
.key_context("MessageEditor")
|
||||
.on_action(cx.listener(Self::chat))
|
||||
.on_action(cx.listener(|this, action, window, cx| {
|
||||
let selector = this.model_selector.read(cx).selector.clone();
|
||||
selector.update(cx, |this, cx| {
|
||||
this.toggle_model_selector(action, window, cx);
|
||||
})
|
||||
.on_action(cx.listener(|this, _: &ToggleModelSelector, window, cx| {
|
||||
this.model_selector
|
||||
.update(cx, |model_selector, cx| model_selector.toggle(window, cx));
|
||||
}))
|
||||
.on_action(cx.listener(Self::toggle_context_picker))
|
||||
.on_action(cx.listener(Self::remove_all_context))
|
||||
|
||||
@@ -16,7 +16,7 @@ use language_model::{
|
||||
report_assistant_event, LanguageModelRegistry, LanguageModelRequest,
|
||||
LanguageModelRequestMessage, Role,
|
||||
};
|
||||
use prompt_library::PromptBuilder;
|
||||
use prompt_store::PromptBuilder;
|
||||
use std::sync::Arc;
|
||||
use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase};
|
||||
use terminal_view::TerminalView;
|
||||
|
||||
@@ -33,9 +33,9 @@ impl ThreadHistory {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select_prev(
|
||||
pub fn select_previous(
|
||||
&mut self,
|
||||
_: &menu::SelectPrev,
|
||||
_: &menu::SelectPrevious,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
@@ -166,7 +166,7 @@ impl Render for ThreadHistory {
|
||||
.overflow_y_scroll()
|
||||
.size_full()
|
||||
.p_1()
|
||||
.on_action(cx.listener(Self::select_prev))
|
||||
.on_action(cx.listener(Self::select_previous))
|
||||
.on_action(cx.listener(Self::select_next))
|
||||
.on_action(cx.listener(Self::select_first))
|
||||
.on_action(cx.listener(Self::select_last))
|
||||
@@ -260,7 +260,7 @@ impl RenderOnce for PastThread {
|
||||
.start_slot(
|
||||
div()
|
||||
.max_w_4_5()
|
||||
.child(Label::new(summary).size(LabelSize::Small).text_ellipsis()),
|
||||
.child(Label::new(summary).size(LabelSize::Small).truncate()),
|
||||
)
|
||||
.end_slot(
|
||||
h_flex()
|
||||
@@ -356,7 +356,7 @@ impl RenderOnce for PastContext {
|
||||
.start_slot(
|
||||
div()
|
||||
.max_w_4_5()
|
||||
.child(Label::new(summary).size(LabelSize::Small).text_ellipsis()),
|
||||
.child(Label::new(summary).size(LabelSize::Small).truncate()),
|
||||
)
|
||||
.end_slot(
|
||||
h_flex()
|
||||
|
||||
@@ -37,7 +37,7 @@ parking_lot.workspace = true
|
||||
paths.workspace = true
|
||||
picker.workspace = true
|
||||
project.workspace = true
|
||||
prompt_library.workspace = true
|
||||
prompt_store.workspace = true
|
||||
regex.workspace = true
|
||||
rope.workspace = true
|
||||
rpc.workspace = true
|
||||
|
||||
@@ -27,7 +27,7 @@ use language_model::{
|
||||
use open_ai::Model as OpenAiModel;
|
||||
use paths::contexts_dir;
|
||||
use project::Project;
|
||||
use prompt_library::PromptBuilder;
|
||||
use prompt_store::PromptBuilder;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
|
||||
@@ -20,7 +20,7 @@ use language_model::{LanguageModelCacheConfiguration, LanguageModelRegistry, Rol
|
||||
use parking_lot::Mutex;
|
||||
use pretty_assertions::assert_eq;
|
||||
use project::Project;
|
||||
use prompt_library::PromptBuilder;
|
||||
use prompt_store::PromptBuilder;
|
||||
use rand::prelude::*;
|
||||
use serde_json::json;
|
||||
use settings::SettingsStore;
|
||||
@@ -671,7 +671,7 @@ async fn test_slash_commands(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_workflow_step_parsing(cx: &mut TestAppContext) {
|
||||
cx.update(prompt_library::init);
|
||||
cx.update(prompt_store::init);
|
||||
let mut settings_store = cx.update(SettingsStore::test);
|
||||
cx.update(|cx| {
|
||||
settings_store
|
||||
|
||||
@@ -37,7 +37,9 @@ use language_model::{
|
||||
LanguageModelImage, LanguageModelProvider, LanguageModelProviderTosView, LanguageModelRegistry,
|
||||
Role,
|
||||
};
|
||||
use language_model_selector::{AssistantLanguageModelSelector, LanguageModelSelector};
|
||||
use language_model_selector::{
|
||||
LanguageModelSelector, LanguageModelSelectorPopoverMenu, ToggleModelSelector,
|
||||
};
|
||||
use multi_buffer::MultiBufferRow;
|
||||
use picker::Picker;
|
||||
use project::lsp_store::LocalLspAdapterDelegate;
|
||||
@@ -196,6 +198,7 @@ pub struct ContextEditor {
|
||||
// context editor, we keep a reference here.
|
||||
dragged_file_worktrees: Vec<Entity<Worktree>>,
|
||||
language_model_selector: Entity<LanguageModelSelector>,
|
||||
language_model_selector_menu_handle: PopoverMenuHandle<LanguageModelSelector>,
|
||||
}
|
||||
|
||||
pub const DEFAULT_TAB_TITLE: &str = "New Chat";
|
||||
@@ -249,21 +252,6 @@ impl ContextEditor {
|
||||
cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
|
||||
];
|
||||
|
||||
let fs_clone = fs.clone();
|
||||
let language_model_selector = cx.new(|cx| {
|
||||
LanguageModelSelector::new(
|
||||
move |model, cx| {
|
||||
update_settings_file::<AssistantSettings>(
|
||||
fs_clone.clone(),
|
||||
cx,
|
||||
move |settings, _| settings.set_model(model.clone()),
|
||||
);
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
let sections = context.read(cx).slash_command_output_sections().to_vec();
|
||||
let patch_ranges = context.read(cx).patch_ranges().collect::<Vec<_>>();
|
||||
let slash_commands = context.read(cx).slash_commands().clone();
|
||||
@@ -276,7 +264,7 @@ impl ContextEditor {
|
||||
image_blocks: Default::default(),
|
||||
scroll_position: None,
|
||||
remote_id: None,
|
||||
fs,
|
||||
fs: fs.clone(),
|
||||
workspace,
|
||||
project,
|
||||
pending_slash_command_creases: HashMap::default(),
|
||||
@@ -288,7 +276,20 @@ impl ContextEditor {
|
||||
show_accept_terms: false,
|
||||
slash_menu_handle: Default::default(),
|
||||
dragged_file_worktrees: Vec::new(),
|
||||
language_model_selector,
|
||||
language_model_selector: cx.new(|cx| {
|
||||
LanguageModelSelector::new(
|
||||
move |model, cx| {
|
||||
update_settings_file::<AssistantSettings>(
|
||||
fs.clone(),
|
||||
cx,
|
||||
move |settings, _| settings.set_model(model.clone()),
|
||||
);
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}),
|
||||
language_model_selector_menu_handle: PopoverMenuHandle::default(),
|
||||
};
|
||||
this.update_message_headers(cx);
|
||||
this.update_image_blocks(cx);
|
||||
@@ -2388,6 +2389,46 @@ impl ContextEditor {
|
||||
)
|
||||
}
|
||||
|
||||
fn render_language_model_selector(&self, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let active_model = LanguageModelRegistry::read_global(cx).active_model();
|
||||
let focus_handle = self.editor().focus_handle(cx).clone();
|
||||
let model_name = match active_model {
|
||||
Some(model) => model.name().0,
|
||||
None => SharedString::from("No model selected"),
|
||||
};
|
||||
|
||||
LanguageModelSelectorPopoverMenu::new(
|
||||
self.language_model_selector.clone(),
|
||||
ButtonLike::new("active-model")
|
||||
.style(ButtonStyle::Subtle)
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_0p5()
|
||||
.child(
|
||||
Label::new(model_name)
|
||||
.size(LabelSize::Small)
|
||||
.color(Color::Muted),
|
||||
)
|
||||
.child(
|
||||
Icon::new(IconName::ChevronDown)
|
||||
.color(Color::Muted)
|
||||
.size(IconSize::XSmall),
|
||||
),
|
||||
),
|
||||
move |window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
"Change Model",
|
||||
&ToggleModelSelector,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
},
|
||||
gpui::Corner::BottomLeft,
|
||||
)
|
||||
.with_handle(self.language_model_selector_menu_handle.clone())
|
||||
}
|
||||
|
||||
fn render_last_error(&self, cx: &mut Context<Self>) -> Option<AnyElement> {
|
||||
let last_error = self.last_error.as_ref()?;
|
||||
|
||||
@@ -2832,7 +2873,7 @@ impl Render for ContextEditor {
|
||||
None
|
||||
};
|
||||
|
||||
let language_model_selector = self.language_model_selector.clone();
|
||||
let language_model_selector = self.language_model_selector_menu_handle.clone();
|
||||
v_flex()
|
||||
.key_context("ContextEditor")
|
||||
.capture_action(cx.listener(ContextEditor::cancel))
|
||||
@@ -2845,10 +2886,8 @@ impl Render for ContextEditor {
|
||||
.on_action(cx.listener(ContextEditor::edit))
|
||||
.on_action(cx.listener(ContextEditor::assist))
|
||||
.on_action(cx.listener(ContextEditor::split))
|
||||
.on_action(move |action, window, cx| {
|
||||
language_model_selector.update(cx, |this, cx| {
|
||||
this.toggle_model_selector(action, window, cx);
|
||||
})
|
||||
.on_action(move |_: &ToggleModelSelector, window, cx| {
|
||||
language_model_selector.toggle(window, cx);
|
||||
})
|
||||
.size_full()
|
||||
.children(self.render_notice(cx))
|
||||
@@ -2887,14 +2926,11 @@ impl Render for ContextEditor {
|
||||
.gap_1()
|
||||
.child(self.render_inject_context_menu(cx))
|
||||
.child(ui::Divider::vertical())
|
||||
.child(div().pl_0p5().child({
|
||||
let focus_handle = self.editor().focus_handle(cx).clone();
|
||||
AssistantLanguageModelSelector::new(
|
||||
focus_handle,
|
||||
self.language_model_selector.clone(),
|
||||
)
|
||||
.render(window, cx)
|
||||
})),
|
||||
.child(
|
||||
div()
|
||||
.pl_0p5()
|
||||
.child(self.render_language_model_selector(cx)),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
|
||||
@@ -16,7 +16,7 @@ use gpui::{App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Task,
|
||||
use language::LanguageRegistry;
|
||||
use paths::contexts_dir;
|
||||
use project::Project;
|
||||
use prompt_library::PromptBuilder;
|
||||
use prompt_store::PromptBuilder;
|
||||
use regex::Regex;
|
||||
use rpc::AnyProtoClient;
|
||||
use std::sync::LazyLock;
|
||||
@@ -104,49 +104,53 @@ impl ContextStore {
|
||||
const CONTEXT_WATCH_DURATION: Duration = Duration::from_millis(100);
|
||||
let (mut events, _) = fs.watch(contexts_dir(), CONTEXT_WATCH_DURATION).await;
|
||||
|
||||
let this = cx.new(|cx: &mut Context<Self>| {
|
||||
let context_server_factory_registry =
|
||||
ContextServerFactoryRegistry::default_global(cx);
|
||||
let context_server_manager = cx.new(|cx| {
|
||||
ContextServerManager::new(context_server_factory_registry, project.clone(), cx)
|
||||
});
|
||||
let mut this = Self {
|
||||
contexts: Vec::new(),
|
||||
contexts_metadata: Vec::new(),
|
||||
context_server_manager,
|
||||
context_server_slash_command_ids: HashMap::default(),
|
||||
host_contexts: Vec::new(),
|
||||
fs,
|
||||
languages,
|
||||
slash_commands,
|
||||
telemetry,
|
||||
_watch_updates: cx.spawn(|this, mut cx| {
|
||||
async move {
|
||||
while events.next().await.is_some() {
|
||||
this.update(&mut cx, |this, cx| this.reload(cx))?
|
||||
.await
|
||||
.log_err();
|
||||
let this =
|
||||
cx.new(|cx: &mut Context<Self>| {
|
||||
let context_server_factory_registry =
|
||||
ContextServerFactoryRegistry::default_global(cx);
|
||||
let context_server_manager = cx.new(|cx| {
|
||||
ContextServerManager::new(
|
||||
context_server_factory_registry,
|
||||
project.clone(),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
let mut this = Self {
|
||||
contexts: Vec::new(),
|
||||
contexts_metadata: Vec::new(),
|
||||
context_server_manager,
|
||||
context_server_slash_command_ids: HashMap::default(),
|
||||
host_contexts: Vec::new(),
|
||||
fs,
|
||||
languages,
|
||||
slash_commands,
|
||||
telemetry,
|
||||
_watch_updates: cx.spawn(|this, mut cx| {
|
||||
async move {
|
||||
while events.next().await.is_some() {
|
||||
this.update(&mut cx, |this, cx| this.reload(cx))?
|
||||
.await
|
||||
.log_err();
|
||||
}
|
||||
anyhow::Ok(())
|
||||
}
|
||||
anyhow::Ok(())
|
||||
}
|
||||
.log_err()
|
||||
}),
|
||||
client_subscription: None,
|
||||
_project_subscriptions: vec![
|
||||
cx.observe(&project, Self::handle_project_changed),
|
||||
cx.subscribe(&project, Self::handle_project_event),
|
||||
],
|
||||
project_is_shared: false,
|
||||
client: project.read(cx).client(),
|
||||
project: project.clone(),
|
||||
prompt_builder,
|
||||
};
|
||||
this.handle_project_changed(project.clone(), cx);
|
||||
this.synchronize_contexts(cx);
|
||||
this.register_context_server_handlers(cx);
|
||||
this.reload(cx).detach_and_log_err(cx);
|
||||
this
|
||||
})?;
|
||||
.log_err()
|
||||
}),
|
||||
client_subscription: None,
|
||||
_project_subscriptions: vec![
|
||||
cx.subscribe(&project, Self::handle_project_event)
|
||||
],
|
||||
project_is_shared: false,
|
||||
client: project.read(cx).client(),
|
||||
project: project.clone(),
|
||||
prompt_builder,
|
||||
};
|
||||
this.handle_project_shared(project.clone(), cx);
|
||||
this.synchronize_contexts(cx);
|
||||
this.register_context_server_handlers(cx);
|
||||
this.reload(cx).detach_and_log_err(cx);
|
||||
this
|
||||
})?;
|
||||
|
||||
Ok(this)
|
||||
})
|
||||
@@ -288,7 +292,7 @@ impl ContextStore {
|
||||
})?
|
||||
}
|
||||
|
||||
fn handle_project_changed(&mut self, _: Entity<Project>, cx: &mut Context<Self>) {
|
||||
fn handle_project_shared(&mut self, _: Entity<Project>, cx: &mut Context<Self>) {
|
||||
let is_shared = self.project.read(cx).is_shared();
|
||||
let was_shared = mem::replace(&mut self.project_is_shared, is_shared);
|
||||
if is_shared == was_shared {
|
||||
@@ -318,11 +322,14 @@ impl ContextStore {
|
||||
|
||||
fn handle_project_event(
|
||||
&mut self,
|
||||
_: Entity<Project>,
|
||||
project: Entity<Project>,
|
||||
event: &project::Event,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
match event {
|
||||
project::Event::RemoteIdChanged(_) => {
|
||||
self.handle_project_shared(project, cx);
|
||||
}
|
||||
project::Event::Reshared => {
|
||||
self.advertise_contexts(cx);
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ impl ResolvedPatch {
|
||||
buffer.edit(
|
||||
edits,
|
||||
Some(AutoindentMode::Block {
|
||||
original_start_columns: Vec::new(),
|
||||
original_indent_columns: Vec::new(),
|
||||
}),
|
||||
cx,
|
||||
);
|
||||
|
||||
@@ -207,24 +207,31 @@ impl PickerDelegate for SlashCommandDelegate {
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1p5()
|
||||
.child(Icon::new(info.icon).size(IconSize::XSmall))
|
||||
.child(div().font_buffer(cx).child({
|
||||
.child(
|
||||
Icon::new(info.icon)
|
||||
.size(IconSize::XSmall)
|
||||
.color(Color::Muted),
|
||||
)
|
||||
.child({
|
||||
let mut label = format!("{}", info.name);
|
||||
if let Some(args) = info.args.as_ref().filter(|_| selected)
|
||||
{
|
||||
label.push_str(&args);
|
||||
}
|
||||
Label::new(label).single_line().size(LabelSize::Small)
|
||||
}))
|
||||
Label::new(label)
|
||||
.single_line()
|
||||
.size(LabelSize::Small)
|
||||
.buffer_font(cx)
|
||||
})
|
||||
.children(info.args.clone().filter(|_| !selected).map(
|
||||
|args| {
|
||||
div()
|
||||
.font_buffer(cx)
|
||||
.child(
|
||||
Label::new(args)
|
||||
.single_line()
|
||||
.size(LabelSize::Small)
|
||||
.color(Color::Muted),
|
||||
.color(Color::Muted)
|
||||
.buffer_font(cx),
|
||||
)
|
||||
.visible_on_hover(format!(
|
||||
"command-entry-label-{ix}"
|
||||
@@ -236,7 +243,7 @@ impl PickerDelegate for SlashCommandDelegate {
|
||||
Label::new(info.description.clone())
|
||||
.size(LabelSize::Small)
|
||||
.color(Color::Muted)
|
||||
.text_ellipsis(),
|
||||
.truncate(),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -294,10 +301,9 @@ where
|
||||
.gap_1p5()
|
||||
.child(Icon::new(IconName::Plus).size(IconSize::XSmall))
|
||||
.child(
|
||||
div().font_buffer(cx).child(
|
||||
Label::new("create-your-command")
|
||||
.size(LabelSize::Small),
|
||||
),
|
||||
Label::new("create-your-command")
|
||||
.size(LabelSize::Small)
|
||||
.buffer_font(cx),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
@@ -341,7 +347,7 @@ where
|
||||
.anchor(gpui::Corner::BottomLeft)
|
||||
.offset(gpui::Point {
|
||||
x: px(0.0),
|
||||
y: px(-16.0),
|
||||
y: px(-2.0),
|
||||
})
|
||||
.when_some(handle, |this, handle| this.with_handle(handle))
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ language.workspace = true
|
||||
language_model.workspace = true
|
||||
log.workspace = true
|
||||
project.workspace = true
|
||||
prompt_library.workspace = true
|
||||
prompt_store.workspace = true
|
||||
rope.workspace = true
|
||||
schemars.workspace = true
|
||||
semantic_index.workspace = true
|
||||
|
||||
@@ -5,7 +5,7 @@ use assistant_slash_command::{
|
||||
};
|
||||
use gpui::{Task, WeakEntity};
|
||||
use language::{BufferSnapshot, LspAdapterDelegate};
|
||||
use prompt_library::PromptStore;
|
||||
use prompt_store::PromptStore;
|
||||
use std::{
|
||||
fmt::Write,
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
|
||||
@@ -13,7 +13,7 @@ use feature_flags::FeatureFlag;
|
||||
use gpui::{App, Task, WeakEntity};
|
||||
use language::{Anchor, CodeLabel, LspAdapterDelegate};
|
||||
use language_model::{LanguageModelRegistry, LanguageModelTool};
|
||||
use prompt_library::PromptBuilder;
|
||||
use prompt_store::PromptBuilder;
|
||||
use schemars::JsonSchema;
|
||||
use semantic_index::SemanticDb;
|
||||
use serde::Deserialize;
|
||||
|
||||
@@ -5,7 +5,7 @@ use assistant_slash_command::{
|
||||
};
|
||||
use gpui::{Task, WeakEntity};
|
||||
use language::{BufferSnapshot, LspAdapterDelegate};
|
||||
use prompt_library::PromptStore;
|
||||
use prompt_store::PromptStore;
|
||||
use std::sync::{atomic::AtomicBool, Arc};
|
||||
use ui::prelude::*;
|
||||
use workspace::Workspace;
|
||||
|
||||
@@ -141,19 +141,20 @@ pub fn notify_if_app_was_updated(cx: &mut App) {
|
||||
cx,
|
||||
move |cx| {
|
||||
let workspace_handle = cx.entity().downgrade();
|
||||
cx.new(|_cx| {
|
||||
MessageNotification::new(format!("Updated to {app_name} {}", version))
|
||||
.primary_message("View Release Notes")
|
||||
.primary_on_click(move |window, cx| {
|
||||
if let Some(workspace) = workspace_handle.upgrade() {
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
crate::view_release_notes_locally(
|
||||
workspace, window, cx,
|
||||
);
|
||||
})
|
||||
}
|
||||
cx.emit(DismissEvent);
|
||||
})
|
||||
cx.new(|cx| {
|
||||
MessageNotification::new(
|
||||
format!("Updated to {app_name} {}", version),
|
||||
cx,
|
||||
)
|
||||
.primary_message("View Release Notes")
|
||||
.primary_on_click(move |window, cx| {
|
||||
if let Some(workspace) = workspace_handle.upgrade() {
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
crate::view_release_notes_locally(workspace, window, cx);
|
||||
})
|
||||
}
|
||||
cx.emit(DismissEvent);
|
||||
})
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
@@ -82,7 +82,7 @@ impl Render for Breadcrumbs {
|
||||
text_style.color = Color::Muted.color(cx);
|
||||
|
||||
StyledText::new(segment.text.replace('\n', "⏎"))
|
||||
.with_highlights(&text_style, segment.highlights.unwrap_or_default())
|
||||
.with_default_highlights(&text_style, segment.highlights.unwrap_or_default())
|
||||
.into_any()
|
||||
});
|
||||
let breadcrumbs = Itertools::intersperse_with(highlighted_segments, || {
|
||||
|
||||
@@ -16,11 +16,13 @@ test-support = []
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
clock.workspace = true
|
||||
futures.workspace = true
|
||||
git2.workspace = true
|
||||
gpui.workspace = true
|
||||
language.workspace = true
|
||||
log.workspace = true
|
||||
pretty_assertions.workspace = true
|
||||
rope.workspace = true
|
||||
sum_tree.workspace = true
|
||||
text.workspace = true
|
||||
@@ -30,7 +32,6 @@ util.workspace = true
|
||||
ctor.workspace = true
|
||||
env_logger.workspace = true
|
||||
gpui = { workspace = true, features = ["test-support"] }
|
||||
pretty_assertions.workspace = true
|
||||
rand.workspace = true
|
||||
serde_json.workspace = true
|
||||
text = { workspace = true, features = ["test-support"] }
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -614,12 +614,19 @@ mod windows {
|
||||
let path = if let Some(path) = path {
|
||||
path.to_path_buf().canonicalize()?
|
||||
} else {
|
||||
std::env::current_exe()?
|
||||
.parent()
|
||||
.context("no parent path for cli")?
|
||||
.parent()
|
||||
.context("no parent path for cli folder")?
|
||||
.join("Zed.exe")
|
||||
let cli = std::env::current_exe()?;
|
||||
let dir = cli.parent().context("no parent path for cli")?;
|
||||
|
||||
// ../Zed.exe is the standard, lib/zed is for MSYS2, ./zed.exe is for the target
|
||||
// directory in development builds.
|
||||
let possible_locations = ["../Zed.exe", "../lib/zed/zed-editor.exe", "./zed.exe"];
|
||||
possible_locations
|
||||
.iter()
|
||||
.find_map(|p| dir.join(p).canonicalize().ok().filter(|path| path != &cli))
|
||||
.context(format!(
|
||||
"could not find any of: {}",
|
||||
possible_locations.join(", ")
|
||||
))?
|
||||
};
|
||||
|
||||
Ok(App(path))
|
||||
|
||||
@@ -418,6 +418,8 @@ impl Telemetry {
|
||||
|
||||
fn report_event(self: &Arc<Self>, event: Event) {
|
||||
let mut state = self.state.lock();
|
||||
// RUST_LOG=telemetry=trace to debug telemetry events
|
||||
log::trace!(target: "telemetry", "{:?}", event);
|
||||
|
||||
if !state.settings.metrics {
|
||||
return;
|
||||
|
||||
@@ -111,7 +111,7 @@ node_runtime.workspace = true
|
||||
notifications = { workspace = true, features = ["test-support"] }
|
||||
pretty_assertions.workspace = true
|
||||
project = { workspace = true, features = ["test-support"] }
|
||||
prompt_library.workspace = true
|
||||
prompt_store.workspace = true
|
||||
recent_projects = { workspace = true }
|
||||
release_channel.workspace = true
|
||||
remote = { workspace = true, features = ["test-support"] }
|
||||
|
||||
@@ -308,7 +308,7 @@ impl Server {
|
||||
.add_request_handler(forward_read_only_project_request::<proto::InlayHints>)
|
||||
.add_request_handler(forward_read_only_project_request::<proto::ResolveInlayHint>)
|
||||
.add_request_handler(forward_read_only_project_request::<proto::OpenBufferByPath>)
|
||||
.add_request_handler(forward_read_only_project_request::<proto::GitBranches>)
|
||||
.add_request_handler(forward_read_only_project_request::<proto::GitGetBranches>)
|
||||
.add_request_handler(forward_read_only_project_request::<proto::OpenUnstagedDiff>)
|
||||
.add_request_handler(forward_read_only_project_request::<proto::OpenUncommittedDiff>)
|
||||
.add_request_handler(
|
||||
@@ -328,6 +328,7 @@ impl Server {
|
||||
.add_request_handler(forward_mutating_project_request::<proto::PrepareRename>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::PerformRename>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::ReloadBuffers>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::ApplyCodeActionKind>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::FormatBuffers>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::CreateProjectEntry>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::RenameProjectEntry>)
|
||||
@@ -392,18 +393,20 @@ impl Server {
|
||||
.add_request_handler(forward_mutating_project_request::<proto::OpenContext>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::CreateContext>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::SynchronizeContexts>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::Push>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::Pull>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::Fetch>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::Stage>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::Unstage>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::Commit>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::GitInit>)
|
||||
.add_request_handler(forward_read_only_project_request::<proto::GetRemotes>)
|
||||
.add_request_handler(forward_read_only_project_request::<proto::GitShow>)
|
||||
.add_request_handler(forward_read_only_project_request::<proto::GitReset>)
|
||||
.add_request_handler(forward_read_only_project_request::<proto::GitCheckoutFiles>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::SetIndexText>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::OpenCommitMessageBuffer>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::GitDiff>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::GitCreateBranch>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::GitChangeBranch>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::CheckForPushedCommits>)
|
||||
.add_message_handler(broadcast_project_message_from_host::<proto::AdvertiseContexts>)
|
||||
.add_message_handler(update_context)
|
||||
.add_request_handler({
|
||||
|
||||
@@ -3,7 +3,6 @@ use crate::{
|
||||
tests::{rust_lang, TestServer},
|
||||
};
|
||||
use call::ActiveCall;
|
||||
use collections::HashMap;
|
||||
use editor::{
|
||||
actions::{
|
||||
ConfirmCodeAction, ConfirmCompletion, ConfirmRename, ContextMenuFirst, Redo, Rename,
|
||||
@@ -1983,7 +1982,6 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA
|
||||
blame_entry("3a3a3a", 2..3),
|
||||
blame_entry("4c4c4c", 3..4),
|
||||
],
|
||||
permalinks: HashMap::default(), // This field is deprecrated
|
||||
messages: [
|
||||
("1b1b1b", "message for idx-0"),
|
||||
("0d0d0d", "message for idx-1"),
|
||||
@@ -2027,11 +2025,20 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA
|
||||
.unwrap()
|
||||
.downcast::<Editor>()
|
||||
.unwrap();
|
||||
let buffer_id_b = editor_b.update(cx_b, |editor_b, cx| {
|
||||
editor_b
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.as_singleton()
|
||||
.unwrap()
|
||||
.read(cx)
|
||||
.remote_id()
|
||||
});
|
||||
|
||||
// client_b now requests git blame for the open buffer
|
||||
editor_b.update_in(cx_b, |editor_b, window, cx| {
|
||||
assert!(editor_b.blame().is_none());
|
||||
editor_b.toggle_git_blame(&editor::actions::ToggleGitBlame {}, window, cx);
|
||||
editor_b.toggle_git_blame(&git::Blame {}, window, cx);
|
||||
});
|
||||
|
||||
cx_a.executor().run_until_parked();
|
||||
@@ -2045,6 +2052,7 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA
|
||||
&(0..4)
|
||||
.map(|row| RowInfo {
|
||||
buffer_row: Some(row),
|
||||
buffer_id: Some(buffer_id_b),
|
||||
..Default::default()
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
@@ -2092,6 +2100,7 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA
|
||||
&(0..4)
|
||||
.map(|row| RowInfo {
|
||||
buffer_row: Some(row),
|
||||
buffer_id: Some(buffer_id_b),
|
||||
..Default::default()
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
@@ -2127,6 +2136,7 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA
|
||||
&(0..4)
|
||||
.map(|row| RowInfo {
|
||||
buffer_row: Some(row),
|
||||
buffer_id: Some(buffer_id_b),
|
||||
..Default::default()
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
|
||||
@@ -14,7 +14,7 @@ use client::{User, RECEIVE_TIMEOUT};
|
||||
use collections::{HashMap, HashSet};
|
||||
use fs::{FakeFs, Fs as _, RemoveOptions};
|
||||
use futures::{channel::mpsc, StreamExt as _};
|
||||
use prompt_library::PromptBuilder;
|
||||
use prompt_store::PromptBuilder;
|
||||
|
||||
use git::status::{FileStatus, StatusCode, TrackedStatus, UnmergedStatus, UnmergedStatusCode};
|
||||
use gpui::{
|
||||
@@ -6741,19 +6741,24 @@ async fn test_remote_git_branches(
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
let (project_a, worktree_id) = client_a.build_local_project("/project", cx_a).await;
|
||||
|
||||
let project_id = active_call_a
|
||||
.update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
|
||||
.await
|
||||
.unwrap();
|
||||
let project_b = client_b.join_remote_project(project_id, cx_b).await;
|
||||
|
||||
let root_path = ProjectPath::root_path(worktree_id);
|
||||
// Client A sees that a guest has joined.
|
||||
// Client A sees that a guest has joined and the repo has been populated
|
||||
executor.run_until_parked();
|
||||
|
||||
let repo_b = cx_b.update(|cx| project_b.read(cx).active_repository(cx).unwrap());
|
||||
|
||||
let root_path = ProjectPath::root_path(worktree_id);
|
||||
|
||||
let branches_b = cx_b
|
||||
.update(|cx| project_b.update(cx, |project, cx| project.branches(root_path.clone(), cx)))
|
||||
.update(|cx| repo_b.update(cx, |repository, _| repository.branches()))
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
let new_branch = branches[2];
|
||||
@@ -6765,13 +6770,10 @@ async fn test_remote_git_branches(
|
||||
|
||||
assert_eq!(branches_b, branches_set);
|
||||
|
||||
cx_b.update(|cx| {
|
||||
project_b.update(cx, |project, cx| {
|
||||
project.update_or_create_branch(root_path.clone(), new_branch.to_string(), cx)
|
||||
})
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
cx_b.update(|cx| repo_b.read(cx).change_branch(new_branch.to_string()))
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
executor.run_until_parked();
|
||||
|
||||
@@ -6789,11 +6791,21 @@ async fn test_remote_git_branches(
|
||||
|
||||
// Also try creating a new branch
|
||||
cx_b.update(|cx| {
|
||||
project_b.update(cx, |project, cx| {
|
||||
project.update_or_create_branch(root_path.clone(), "totally-new-branch".to_string(), cx)
|
||||
})
|
||||
repo_b
|
||||
.read(cx)
|
||||
.create_branch("totally-new-branch".to_string())
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
cx_b.update(|cx| {
|
||||
repo_b
|
||||
.read(cx)
|
||||
.change_branch("totally-new-branch".to_string())
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
executor.run_until_parked();
|
||||
|
||||
@@ -276,11 +276,13 @@ async fn test_ssh_collaboration_git_branches(
|
||||
// has some git repositories
|
||||
executor.run_until_parked();
|
||||
|
||||
let repo_b = cx_b.update(|cx| project_b.read(cx).active_repository(cx).unwrap());
|
||||
let root_path = ProjectPath::root_path(worktree_id);
|
||||
|
||||
let branches_b = cx_b
|
||||
.update(|cx| project_b.update(cx, |project, cx| project.branches(root_path.clone(), cx)))
|
||||
.update(|cx| repo_b.read(cx).branches())
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
let new_branch = branches[2];
|
||||
@@ -292,13 +294,10 @@ async fn test_ssh_collaboration_git_branches(
|
||||
|
||||
assert_eq!(&branches_b, &branches_set);
|
||||
|
||||
cx_b.update(|cx| {
|
||||
project_b.update(cx, |project, cx| {
|
||||
project.update_or_create_branch(root_path.clone(), new_branch.to_string(), cx)
|
||||
})
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
cx_b.update(|cx| repo_b.read(cx).change_branch(new_branch.to_string()))
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
executor.run_until_parked();
|
||||
|
||||
@@ -318,11 +317,21 @@ async fn test_ssh_collaboration_git_branches(
|
||||
|
||||
// Also try creating a new branch
|
||||
cx_b.update(|cx| {
|
||||
project_b.update(cx, |project, cx| {
|
||||
project.update_or_create_branch(root_path.clone(), "totally-new-branch".to_string(), cx)
|
||||
})
|
||||
repo_b
|
||||
.read(cx)
|
||||
.create_branch("totally-new-branch".to_string())
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
cx_b.update(|cx| {
|
||||
repo_b
|
||||
.read(cx)
|
||||
.change_branch("totally-new-branch".to_string())
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
executor.run_until_parked();
|
||||
|
||||
@@ -271,7 +271,7 @@ impl TestServer {
|
||||
|
||||
let git_hosting_provider_registry = cx.update(GitHostingProviderRegistry::default_global);
|
||||
git_hosting_provider_registry
|
||||
.register_hosting_provider(Arc::new(git_hosting_providers::Github));
|
||||
.register_hosting_provider(Arc::new(git_hosting_providers::Github::new()));
|
||||
|
||||
let user_store = cx.new(|cx| UserStore::new(client.clone(), cx));
|
||||
let workspace_store = cx.new(|cx| WorkspaceStore::new(client.clone(), cx));
|
||||
|
||||
@@ -17,7 +17,7 @@ use gpui::{
|
||||
ListState, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Render, SharedString,
|
||||
Styled, Subscription, Task, TextStyle, WeakEntity, Window,
|
||||
};
|
||||
use menu::{Cancel, Confirm, SecondaryConfirm, SelectNext, SelectPrev};
|
||||
use menu::{Cancel, Confirm, SecondaryConfirm, SelectNext, SelectPrevious};
|
||||
use project::{Fs, Project};
|
||||
use rpc::{
|
||||
proto::{self, ChannelVisibility, PeerId},
|
||||
@@ -1430,7 +1430,7 @@ impl CollabPanel {
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn select_prev(&mut self, _: &SelectPrev, _: &mut Window, cx: &mut Context<Self>) {
|
||||
fn select_previous(&mut self, _: &SelectPrevious, _: &mut Window, cx: &mut Context<Self>) {
|
||||
let ix = self.selection.take().unwrap_or(0);
|
||||
if ix > 0 {
|
||||
self.selection = Some(ix - 1);
|
||||
@@ -2878,7 +2878,7 @@ impl Render for CollabPanel {
|
||||
.key_context("CollabPanel")
|
||||
.on_action(cx.listener(CollabPanel::cancel))
|
||||
.on_action(cx.listener(CollabPanel::select_next))
|
||||
.on_action(cx.listener(CollabPanel::select_prev))
|
||||
.on_action(cx.listener(CollabPanel::select_previous))
|
||||
.on_action(cx.listener(CollabPanel::confirm))
|
||||
.on_action(cx.listener(CollabPanel::insert_space))
|
||||
.on_action(cx.listener(CollabPanel::remove_selected_channel))
|
||||
|
||||
@@ -22,7 +22,7 @@ use ui::{
|
||||
h_flex, prelude::*, v_flex, Avatar, Button, Icon, IconButton, IconName, Label, Tab, Tooltip,
|
||||
};
|
||||
use util::{ResultExt, TryFutureExt};
|
||||
use workspace::notifications::NotificationId;
|
||||
use workspace::notifications::{Notification as WorkspaceNotification, NotificationId};
|
||||
use workspace::{
|
||||
dock::{DockPosition, Panel, PanelEvent},
|
||||
Workspace,
|
||||
@@ -570,11 +570,12 @@ impl NotificationPanel {
|
||||
workspace.dismiss_notification(&id, cx);
|
||||
workspace.show_notification(id, cx, |cx| {
|
||||
let workspace = cx.entity().downgrade();
|
||||
cx.new(|_| NotificationToast {
|
||||
cx.new(|cx| NotificationToast {
|
||||
notification_id,
|
||||
actor,
|
||||
text,
|
||||
workspace,
|
||||
focus_handle: cx.focus_handle(),
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -771,8 +772,17 @@ pub struct NotificationToast {
|
||||
actor: Option<Arc<User>>,
|
||||
text: String,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
focus_handle: FocusHandle,
|
||||
}
|
||||
|
||||
impl Focusable for NotificationToast {
|
||||
fn focus_handle(&self, _cx: &App) -> FocusHandle {
|
||||
self.focus_handle.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl WorkspaceNotification for NotificationToast {}
|
||||
|
||||
impl NotificationToast {
|
||||
fn focus_notification_panel(&self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let workspace = self.workspace.clone();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::fmt::Display;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::LazyLock;
|
||||
|
||||
@@ -8,7 +9,7 @@ use parking_lot::RwLock;
|
||||
use theme::ActiveTheme;
|
||||
|
||||
pub trait Component {
|
||||
fn scope() -> Option<&'static str>;
|
||||
fn scope() -> Option<ComponentScope>;
|
||||
fn name() -> &'static str {
|
||||
std::any::type_name::<Self>()
|
||||
}
|
||||
@@ -18,7 +19,7 @@ pub trait Component {
|
||||
}
|
||||
|
||||
pub trait ComponentPreview: Component {
|
||||
fn preview(_window: &mut Window, _cx: &App) -> AnyElement;
|
||||
fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement;
|
||||
}
|
||||
|
||||
#[distributed_slice]
|
||||
@@ -31,8 +32,8 @@ pub static COMPONENT_DATA: LazyLock<RwLock<ComponentRegistry>> =
|
||||
LazyLock::new(|| RwLock::new(ComponentRegistry::new()));
|
||||
|
||||
pub struct ComponentRegistry {
|
||||
components: Vec<(Option<&'static str>, &'static str, Option<&'static str>)>,
|
||||
previews: HashMap<&'static str, fn(&mut Window, &App) -> AnyElement>,
|
||||
components: Vec<(Option<ComponentScope>, &'static str, Option<&'static str>)>,
|
||||
previews: HashMap<&'static str, fn(&mut Window, &mut App) -> AnyElement>,
|
||||
}
|
||||
|
||||
impl ComponentRegistry {
|
||||
@@ -62,7 +63,10 @@ pub fn register_component<T: Component>() {
|
||||
}
|
||||
|
||||
pub fn register_preview<T: ComponentPreview>() {
|
||||
let preview_data = (T::name(), T::preview as fn(&mut Window, &App) -> AnyElement);
|
||||
let preview_data = (
|
||||
T::name(),
|
||||
T::preview as fn(&mut Window, &mut App) -> AnyElement,
|
||||
);
|
||||
COMPONENT_DATA
|
||||
.write()
|
||||
.previews
|
||||
@@ -75,9 +79,9 @@ pub struct ComponentId(pub &'static str);
|
||||
#[derive(Clone)]
|
||||
pub struct ComponentMetadata {
|
||||
name: SharedString,
|
||||
scope: Option<SharedString>,
|
||||
scope: Option<ComponentScope>,
|
||||
description: Option<SharedString>,
|
||||
preview: Option<fn(&mut Window, &App) -> AnyElement>,
|
||||
preview: Option<fn(&mut Window, &mut App) -> AnyElement>,
|
||||
}
|
||||
|
||||
impl ComponentMetadata {
|
||||
@@ -85,7 +89,7 @@ impl ComponentMetadata {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
pub fn scope(&self) -> Option<SharedString> {
|
||||
pub fn scope(&self) -> Option<ComponentScope> {
|
||||
self.scope.clone()
|
||||
}
|
||||
|
||||
@@ -93,7 +97,7 @@ impl ComponentMetadata {
|
||||
self.description.clone()
|
||||
}
|
||||
|
||||
pub fn preview(&self) -> Option<fn(&mut Window, &App) -> AnyElement> {
|
||||
pub fn preview(&self) -> Option<fn(&mut Window, &mut App) -> AnyElement> {
|
||||
self.preview
|
||||
}
|
||||
}
|
||||
@@ -149,14 +153,14 @@ pub fn components() -> AllComponents {
|
||||
let data = COMPONENT_DATA.read();
|
||||
let mut all_components = AllComponents::new();
|
||||
|
||||
for &(scope, name, description) in &data.components {
|
||||
let scope = scope.map(Into::into);
|
||||
for (ref scope, name, description) in &data.components {
|
||||
let preview = data.previews.get(name).cloned();
|
||||
let component_name = SharedString::new_static(name);
|
||||
all_components.insert(
|
||||
ComponentId(name),
|
||||
ComponentMetadata {
|
||||
name: name.into(),
|
||||
scope,
|
||||
name: component_name,
|
||||
scope: scope.clone(),
|
||||
description: description.map(Into::into),
|
||||
preview,
|
||||
},
|
||||
@@ -166,6 +170,59 @@ pub fn components() -> AllComponents {
|
||||
all_components
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum ComponentScope {
|
||||
Layout,
|
||||
Input,
|
||||
Notification,
|
||||
Editor,
|
||||
Collaboration,
|
||||
VersionControl,
|
||||
Unknown(SharedString),
|
||||
}
|
||||
|
||||
impl Display for ComponentScope {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ComponentScope::Layout => write!(f, "Layout"),
|
||||
ComponentScope::Input => write!(f, "Input"),
|
||||
ComponentScope::Notification => write!(f, "Notification"),
|
||||
ComponentScope::Editor => write!(f, "Editor"),
|
||||
ComponentScope::Collaboration => write!(f, "Collaboration"),
|
||||
ComponentScope::VersionControl => write!(f, "Version Control"),
|
||||
ComponentScope::Unknown(name) => write!(f, "Unknown: {}", name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for ComponentScope {
|
||||
fn from(value: &str) -> Self {
|
||||
match value {
|
||||
"Layout" => ComponentScope::Layout,
|
||||
"Input" => ComponentScope::Input,
|
||||
"Notification" => ComponentScope::Notification,
|
||||
"Editor" => ComponentScope::Editor,
|
||||
"Collaboration" => ComponentScope::Collaboration,
|
||||
"Version Control" | "VersionControl" => ComponentScope::VersionControl,
|
||||
_ => ComponentScope::Unknown(SharedString::new(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for ComponentScope {
|
||||
fn from(value: String) -> Self {
|
||||
match value.as_str() {
|
||||
"Layout" => ComponentScope::Layout,
|
||||
"Input" => ComponentScope::Input,
|
||||
"Notification" => ComponentScope::Notification,
|
||||
"Editor" => ComponentScope::Editor,
|
||||
"Collaboration" => ComponentScope::Collaboration,
|
||||
"Version Control" | "VersionControl" => ComponentScope::VersionControl,
|
||||
_ => ComponentScope::Unknown(SharedString::new(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Which side of the preview to show labels on
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ExampleLabelSide {
|
||||
@@ -174,8 +231,8 @@ pub enum ExampleLabelSide {
|
||||
/// Right side
|
||||
Right,
|
||||
/// Top side
|
||||
Top,
|
||||
#[default]
|
||||
Top,
|
||||
/// Bottom side
|
||||
Bottom,
|
||||
}
|
||||
@@ -205,6 +262,7 @@ impl RenderOnce for ComponentExample {
|
||||
.text_size(px(10.))
|
||||
.text_color(cx.theme().colors().text_muted)
|
||||
.when(self.grow, |this| this.flex_1())
|
||||
.when(!self.grow, |this| this.flex_none())
|
||||
.child(self.element)
|
||||
.child(self.variant_name)
|
||||
.into_any_element()
|
||||
@@ -235,6 +293,7 @@ pub struct ComponentExampleGroup {
|
||||
pub title: Option<SharedString>,
|
||||
pub examples: Vec<ComponentExample>,
|
||||
pub grow: bool,
|
||||
pub vertical: bool,
|
||||
}
|
||||
|
||||
impl RenderOnce for ComponentExampleGroup {
|
||||
@@ -270,6 +329,7 @@ impl RenderOnce for ComponentExampleGroup {
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.when(self.vertical, |this| this.flex_col())
|
||||
.items_start()
|
||||
.w_full()
|
||||
.gap_6()
|
||||
@@ -287,6 +347,7 @@ impl ComponentExampleGroup {
|
||||
title: None,
|
||||
examples,
|
||||
grow: false,
|
||||
vertical: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,6 +357,7 @@ impl ComponentExampleGroup {
|
||||
title: Some(title.into()),
|
||||
examples,
|
||||
grow: false,
|
||||
vertical: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,6 +366,12 @@ impl ComponentExampleGroup {
|
||||
self.grow = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Lay the group out vertically.
|
||||
pub fn vertical(mut self) -> Self {
|
||||
self.vertical = true;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a single example
|
||||
|
||||
@@ -15,7 +15,11 @@ path = "src/component_preview.rs"
|
||||
default = []
|
||||
|
||||
[dependencies]
|
||||
client.workspace = true
|
||||
component.workspace = true
|
||||
gpui.workspace = true
|
||||
languages.workspace = true
|
||||
project.workspace = true
|
||||
ui.workspace = true
|
||||
workspace.workspace = true
|
||||
notifications.workspace = true
|
||||
|
||||
@@ -2,18 +2,49 @@
|
||||
//!
|
||||
//! A view for exploring Zed components.
|
||||
|
||||
use std::iter::Iterator;
|
||||
use std::sync::Arc;
|
||||
|
||||
use client::UserStore;
|
||||
use component::{components, ComponentMetadata};
|
||||
use gpui::{list, prelude::*, uniform_list, App, EventEmitter, FocusHandle, Focusable, Window};
|
||||
use gpui::{
|
||||
list, prelude::*, uniform_list, App, Entity, EventEmitter, FocusHandle, Focusable, Task,
|
||||
WeakEntity, Window,
|
||||
};
|
||||
|
||||
use gpui::{ListState, ScrollHandle, UniformListScrollHandle};
|
||||
use ui::{prelude::*, ListItem};
|
||||
use languages::LanguageRegistry;
|
||||
use notifications::status_toast::{StatusToast, ToastIcon};
|
||||
use project::Project;
|
||||
use ui::{prelude::*, Divider, ListItem, ListSubHeader};
|
||||
|
||||
use workspace::{item::ItemEvent, Item, Workspace, WorkspaceId};
|
||||
use workspace::{AppState, ItemId, SerializableItem};
|
||||
|
||||
pub fn init(app_state: Arc<AppState>, cx: &mut App) {
|
||||
let app_state = app_state.clone();
|
||||
|
||||
cx.observe_new(move |workspace: &mut Workspace, _, cx| {
|
||||
let app_state = app_state.clone();
|
||||
let weak_workspace = cx.entity().downgrade();
|
||||
|
||||
pub fn init(cx: &mut App) {
|
||||
cx.observe_new(|workspace: &mut Workspace, _, _cx| {
|
||||
workspace.register_action(
|
||||
|workspace, _: &workspace::OpenComponentPreview, window, cx| {
|
||||
let component_preview = cx.new(|cx| ComponentPreview::new(window, cx));
|
||||
move |workspace, _: &workspace::OpenComponentPreview, window, cx| {
|
||||
let app_state = app_state.clone();
|
||||
|
||||
let language_registry = app_state.languages.clone();
|
||||
let user_store = app_state.user_store.clone();
|
||||
|
||||
let component_preview = cx.new(|cx| {
|
||||
ComponentPreview::new(
|
||||
weak_workspace.clone(),
|
||||
language_registry,
|
||||
user_store,
|
||||
None,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
workspace.add_item_to_active_pane(
|
||||
Box::new(component_preview),
|
||||
None,
|
||||
@@ -27,6 +58,23 @@ pub fn init(cx: &mut App) {
|
||||
.detach();
|
||||
}
|
||||
|
||||
enum PreviewEntry {
|
||||
Component(ComponentMetadata),
|
||||
SectionHeader(SharedString),
|
||||
}
|
||||
|
||||
impl From<ComponentMetadata> for PreviewEntry {
|
||||
fn from(component: ComponentMetadata) -> Self {
|
||||
PreviewEntry::Component(component)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SharedString> for PreviewEntry {
|
||||
fn from(section_header: SharedString) -> Self {
|
||||
PreviewEntry::SectionHeader(section_header)
|
||||
}
|
||||
}
|
||||
|
||||
struct ComponentPreview {
|
||||
focus_handle: FocusHandle,
|
||||
_view_scroll_handle: ScrollHandle,
|
||||
@@ -34,31 +82,55 @@ struct ComponentPreview {
|
||||
components: Vec<ComponentMetadata>,
|
||||
component_list: ListState,
|
||||
selected_index: usize,
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
user_store: Entity<UserStore>,
|
||||
}
|
||||
|
||||
impl ComponentPreview {
|
||||
pub fn new(_window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
pub fn new(
|
||||
workspace: WeakEntity<Workspace>,
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
user_store: Entity<UserStore>,
|
||||
selected_index: impl Into<Option<usize>>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let components = components().all_sorted();
|
||||
let initial_length = components.len();
|
||||
let selected_index = selected_index.into().unwrap_or(0);
|
||||
|
||||
let component_list = ListState::new(initial_length, gpui::ListAlignment::Top, px(500.0), {
|
||||
let this = cx.entity().downgrade();
|
||||
move |ix, window: &mut Window, cx: &mut App| {
|
||||
this.update(cx, |this, cx| {
|
||||
this.render_preview(ix, window, cx).into_any_element()
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
});
|
||||
let component_list =
|
||||
ListState::new(initial_length, gpui::ListAlignment::Top, px(1500.0), {
|
||||
let this = cx.entity().downgrade();
|
||||
move |ix, window: &mut Window, cx: &mut App| {
|
||||
this.update(cx, |this, cx| {
|
||||
let component = this.get_component(ix);
|
||||
this.render_preview(ix, &component, window, cx)
|
||||
.into_any_element()
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
});
|
||||
|
||||
Self {
|
||||
let mut component_preview = Self {
|
||||
focus_handle: cx.focus_handle(),
|
||||
_view_scroll_handle: ScrollHandle::new(),
|
||||
nav_scroll_handle: UniformListScrollHandle::new(),
|
||||
language_registry,
|
||||
user_store,
|
||||
workspace,
|
||||
components,
|
||||
component_list,
|
||||
selected_index: 0,
|
||||
selected_index,
|
||||
};
|
||||
|
||||
if component_preview.selected_index > 0 {
|
||||
component_preview.scroll_to_preview(component_preview.selected_index, cx);
|
||||
}
|
||||
|
||||
component_preview.update_component_list(cx);
|
||||
|
||||
component_preview
|
||||
}
|
||||
|
||||
fn scroll_to_preview(&mut self, ix: usize, cx: &mut Context<Self>) {
|
||||
@@ -71,32 +143,158 @@ impl ComponentPreview {
|
||||
self.components[ix].clone()
|
||||
}
|
||||
|
||||
fn scope_ordered_entries(&self) -> Vec<PreviewEntry> {
|
||||
use std::collections::HashMap;
|
||||
|
||||
// Group components by scope
|
||||
let mut scope_groups: HashMap<Option<ComponentScope>, Vec<ComponentMetadata>> =
|
||||
HashMap::default();
|
||||
|
||||
for component in &self.components {
|
||||
scope_groups
|
||||
.entry(component.scope())
|
||||
.or_insert_with(Vec::new)
|
||||
.push(component.clone());
|
||||
}
|
||||
|
||||
// Sort components within each scope by name
|
||||
for components in scope_groups.values_mut() {
|
||||
components.sort_by_key(|c| c.name().to_lowercase());
|
||||
}
|
||||
|
||||
// Build entries with scopes in a defined order
|
||||
let mut entries = Vec::new();
|
||||
|
||||
// Define scope order (we want Unknown at the end)
|
||||
let known_scopes = [
|
||||
ComponentScope::Layout,
|
||||
ComponentScope::Input,
|
||||
ComponentScope::Editor,
|
||||
ComponentScope::Notification,
|
||||
ComponentScope::Collaboration,
|
||||
ComponentScope::VersionControl,
|
||||
];
|
||||
|
||||
// First add components with known scopes
|
||||
for scope in known_scopes.iter() {
|
||||
let scope_key = Some(scope.clone());
|
||||
if let Some(components) = scope_groups.remove(&scope_key) {
|
||||
if !components.is_empty() {
|
||||
// Add section header
|
||||
entries.push(PreviewEntry::SectionHeader(scope.to_string().into()));
|
||||
|
||||
// Add all components under this scope
|
||||
for component in components {
|
||||
entries.push(PreviewEntry::Component(component));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle components with Unknown scope
|
||||
for (scope, components) in &scope_groups {
|
||||
if let Some(ComponentScope::Unknown(_)) = scope {
|
||||
if !components.is_empty() {
|
||||
// Add the unknown scope header
|
||||
if let Some(scope_value) = scope {
|
||||
entries.push(PreviewEntry::SectionHeader(scope_value.to_string().into()));
|
||||
}
|
||||
|
||||
// Add all components under this unknown scope
|
||||
for component in components {
|
||||
entries.push(PreviewEntry::Component(component.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle components with no scope
|
||||
if let Some(components) = scope_groups.get(&None) {
|
||||
if !components.is_empty() {
|
||||
entries.push(PreviewEntry::SectionHeader("Uncategorized".into()));
|
||||
|
||||
for component in components {
|
||||
entries.push(PreviewEntry::Component(component.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
entries
|
||||
}
|
||||
|
||||
fn render_sidebar_entry(
|
||||
&self,
|
||||
ix: usize,
|
||||
entry: &PreviewEntry,
|
||||
selected: bool,
|
||||
cx: &Context<Self>,
|
||||
) -> impl IntoElement {
|
||||
let component = self.get_component(ix);
|
||||
match entry {
|
||||
PreviewEntry::Component(component_metadata) => ListItem::new(ix)
|
||||
.child(Label::new(component_metadata.name().clone()).color(Color::Default))
|
||||
.selectable(true)
|
||||
.toggle_state(selected)
|
||||
.inset(true)
|
||||
.on_click(cx.listener(move |this, _, _, cx| {
|
||||
this.scroll_to_preview(ix, cx);
|
||||
}))
|
||||
.into_any_element(),
|
||||
PreviewEntry::SectionHeader(shared_string) => ListSubHeader::new(shared_string)
|
||||
.inset(true)
|
||||
.into_any_element(),
|
||||
}
|
||||
}
|
||||
|
||||
ListItem::new(ix)
|
||||
.child(Label::new(component.name().clone()).color(Color::Default))
|
||||
.selectable(true)
|
||||
.toggle_state(selected)
|
||||
.inset(true)
|
||||
.on_click(cx.listener(move |this, _, _, cx| {
|
||||
this.scroll_to_preview(ix, cx);
|
||||
}))
|
||||
fn update_component_list(&mut self, cx: &mut Context<Self>) {
|
||||
let new_len = self.scope_ordered_entries().len();
|
||||
let entries = self.scope_ordered_entries();
|
||||
let weak_entity = cx.entity().downgrade();
|
||||
|
||||
let new_list = ListState::new(
|
||||
new_len,
|
||||
gpui::ListAlignment::Top,
|
||||
px(1500.0),
|
||||
move |ix, window, cx| {
|
||||
let entry = &entries[ix];
|
||||
|
||||
weak_entity
|
||||
.update(cx, |this, cx| match entry {
|
||||
PreviewEntry::Component(component) => this
|
||||
.render_preview(ix, component, window, cx)
|
||||
.into_any_element(),
|
||||
PreviewEntry::SectionHeader(shared_string) => this
|
||||
.render_scope_header(ix, shared_string.clone(), window, cx)
|
||||
.into_any_element(),
|
||||
})
|
||||
.unwrap()
|
||||
},
|
||||
);
|
||||
|
||||
self.component_list = new_list;
|
||||
}
|
||||
|
||||
fn render_scope_header(
|
||||
&self,
|
||||
_ix: usize,
|
||||
title: SharedString,
|
||||
_window: &Window,
|
||||
_cx: &App,
|
||||
) -> impl IntoElement {
|
||||
h_flex()
|
||||
.w_full()
|
||||
.h_10()
|
||||
.items_center()
|
||||
.child(Headline::new(title).size(HeadlineSize::XSmall))
|
||||
.child(Divider::horizontal())
|
||||
}
|
||||
|
||||
fn render_preview(
|
||||
&self,
|
||||
ix: usize,
|
||||
_ix: usize,
|
||||
component: &ComponentMetadata,
|
||||
window: &mut Window,
|
||||
cx: &Context<Self>,
|
||||
cx: &mut App,
|
||||
) -> impl IntoElement {
|
||||
let component = self.get_component(ix);
|
||||
|
||||
let name = component.name();
|
||||
let scope = component.scope();
|
||||
|
||||
@@ -142,10 +340,27 @@ impl ComponentPreview {
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn test_status_toast(&self, cx: &mut Context<Self>) {
|
||||
if let Some(workspace) = self.workspace.upgrade() {
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
let status_toast =
|
||||
StatusToast::new("`zed/new-notification-system` created!", cx, |this, _cx| {
|
||||
this.icon(ToastIcon::new(IconName::GitBranchSmall).color(Color::Muted))
|
||||
.action("Open Pull Request", |_, cx| {
|
||||
cx.open_url("https://github.com/")
|
||||
})
|
||||
});
|
||||
workspace.toggle_status_toast(status_toast, cx)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for ComponentPreview {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
|
||||
let sidebar_entries = self.scope_ordered_entries();
|
||||
|
||||
h_flex()
|
||||
.id("component-preview")
|
||||
.key_context("ComponentPreview")
|
||||
@@ -156,21 +371,44 @@ impl Render for ComponentPreview {
|
||||
.px_2()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.child(
|
||||
uniform_list(
|
||||
cx.entity().clone(),
|
||||
"component-nav",
|
||||
self.components.len(),
|
||||
move |this, range, _window, cx| {
|
||||
range
|
||||
.map(|ix| this.render_sidebar_entry(ix, ix == this.selected_index, cx))
|
||||
.collect()
|
||||
},
|
||||
)
|
||||
.track_scroll(self.nav_scroll_handle.clone())
|
||||
.pt_4()
|
||||
.w(px(240.))
|
||||
.h_full()
|
||||
.flex_grow(),
|
||||
v_flex()
|
||||
.h_full()
|
||||
.child(
|
||||
uniform_list(
|
||||
cx.entity().clone(),
|
||||
"component-nav",
|
||||
sidebar_entries.len(),
|
||||
move |this, range, _window, cx| {
|
||||
range
|
||||
.map(|ix| {
|
||||
this.render_sidebar_entry(
|
||||
ix,
|
||||
&sidebar_entries[ix],
|
||||
ix == this.selected_index,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
)
|
||||
.track_scroll(self.nav_scroll_handle.clone())
|
||||
.pt_4()
|
||||
.w(px(240.))
|
||||
.h_full()
|
||||
.flex_1(),
|
||||
)
|
||||
.child(
|
||||
div().w_full().pb_4().child(
|
||||
Button::new("toast-test", "Launch Toast")
|
||||
.on_click(cx.listener({
|
||||
move |this, _, _window, cx| {
|
||||
this.test_status_toast(cx);
|
||||
cx.notify();
|
||||
}
|
||||
}))
|
||||
.full_width(),
|
||||
),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
v_flex()
|
||||
@@ -213,16 +451,86 @@ impl Item for ComponentPreview {
|
||||
fn clone_on_split(
|
||||
&self,
|
||||
_workspace_id: Option<WorkspaceId>,
|
||||
window: &mut Window,
|
||||
_window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Option<gpui::Entity<Self>>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Some(cx.new(|cx| Self::new(window, cx)))
|
||||
let language_registry = self.language_registry.clone();
|
||||
let user_store = self.user_store.clone();
|
||||
let weak_workspace = self.workspace.clone();
|
||||
let selected_index = self.selected_index;
|
||||
|
||||
Some(cx.new(|cx| {
|
||||
Self::new(
|
||||
weak_workspace,
|
||||
language_registry,
|
||||
user_store,
|
||||
selected_index,
|
||||
cx,
|
||||
)
|
||||
}))
|
||||
}
|
||||
|
||||
fn to_item_events(event: &Self::Event, mut f: impl FnMut(workspace::item::ItemEvent)) {
|
||||
f(*event)
|
||||
}
|
||||
}
|
||||
|
||||
impl SerializableItem for ComponentPreview {
|
||||
fn serialized_item_kind() -> &'static str {
|
||||
"ComponentPreview"
|
||||
}
|
||||
|
||||
fn deserialize(
|
||||
project: Entity<Project>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
_workspace_id: WorkspaceId,
|
||||
_item_id: ItemId,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> Task<gpui::Result<Entity<Self>>> {
|
||||
let user_store = project.read(cx).user_store().clone();
|
||||
let language_registry = project.read(cx).languages().clone();
|
||||
|
||||
window.spawn(cx, |mut cx| async move {
|
||||
let user_store = user_store.clone();
|
||||
let language_registry = language_registry.clone();
|
||||
let weak_workspace = workspace.clone();
|
||||
cx.update(|_, cx| {
|
||||
Ok(cx.new(|cx| {
|
||||
ComponentPreview::new(weak_workspace, language_registry, user_store, None, cx)
|
||||
}))
|
||||
})?
|
||||
})
|
||||
}
|
||||
|
||||
fn cleanup(
|
||||
_workspace_id: WorkspaceId,
|
||||
_alive_items: Vec<ItemId>,
|
||||
_window: &mut Window,
|
||||
_cx: &mut App,
|
||||
) -> Task<gpui::Result<()>> {
|
||||
Task::ready(Ok(()))
|
||||
// window.spawn(cx, |_| {
|
||||
// ...
|
||||
// })
|
||||
}
|
||||
|
||||
fn serialize(
|
||||
&mut self,
|
||||
_workspace: &mut Workspace,
|
||||
_item_id: ItemId,
|
||||
_closing: bool,
|
||||
_window: &mut Window,
|
||||
_cx: &mut Context<Self>,
|
||||
) -> Option<Task<gpui::Result<()>>> {
|
||||
// TODO: Serialize the active index so we can re-open to the same place
|
||||
None
|
||||
}
|
||||
|
||||
fn should_serialize(&self, _event: &Self::Event) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -995,7 +995,7 @@ fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.child(
|
||||
StyledText::new(message.clone()).with_highlights(
|
||||
StyledText::new(message.clone()).with_default_highlights(
|
||||
&cx.window.text_style(),
|
||||
code_ranges
|
||||
.iter()
|
||||
|
||||
@@ -35,6 +35,13 @@ pub struct SelectToBeginningOfLine {
|
||||
pub stop_at_indent: bool,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct DeleteToBeginningOfLine {
|
||||
#[serde(default)]
|
||||
pub(super) stop_at_indent: bool,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct MovePageUp {
|
||||
@@ -212,6 +219,7 @@ impl_actions!(
|
||||
ComposeCompletion,
|
||||
ConfirmCodeAction,
|
||||
ConfirmCompletion,
|
||||
DeleteToBeginningOfLine,
|
||||
DeleteToNextWordEnd,
|
||||
DeleteToPreviousWordStart,
|
||||
ExpandExcerpts,
|
||||
@@ -257,7 +265,7 @@ gpui::actions!(
|
||||
ContextMenuFirst,
|
||||
ContextMenuLast,
|
||||
ContextMenuNext,
|
||||
ContextMenuPrev,
|
||||
ContextMenuPrevious,
|
||||
ConvertToKebabCase,
|
||||
ConvertToLowerCamelCase,
|
||||
ConvertToLowerCase,
|
||||
@@ -276,7 +284,6 @@ gpui::actions!(
|
||||
CutToEndOfLine,
|
||||
Delete,
|
||||
DeleteLine,
|
||||
DeleteToBeginningOfLine,
|
||||
DeleteToEndOfLine,
|
||||
DeleteToNextSubwordEnd,
|
||||
DeleteToPreviousSubwordStart,
|
||||
@@ -301,10 +308,10 @@ gpui::actions!(
|
||||
GoToDefinitionSplit,
|
||||
GoToDiagnostic,
|
||||
GoToHunk,
|
||||
GoToPreviousHunk,
|
||||
GoToImplementation,
|
||||
GoToImplementationSplit,
|
||||
GoToPrevDiagnostic,
|
||||
GoToPrevHunk,
|
||||
GoToPreviousDiagnostic,
|
||||
GoToTypeDefinition,
|
||||
GoToTypeDefinitionSplit,
|
||||
HalfPageDown,
|
||||
@@ -333,7 +340,9 @@ gpui::actions!(
|
||||
MoveToPreviousWordStart,
|
||||
MoveToStartOfParagraph,
|
||||
MoveToStartOfExcerpt,
|
||||
MoveToStartOfNextExcerpt,
|
||||
MoveToEndOfExcerpt,
|
||||
MoveToEndOfPreviousExcerpt,
|
||||
MoveUp,
|
||||
Newline,
|
||||
NewlineAbove,
|
||||
@@ -348,6 +357,7 @@ gpui::actions!(
|
||||
OpenPermalinkToLine,
|
||||
OpenSelectionsInMultibuffer,
|
||||
OpenUrl,
|
||||
OrganizeImports,
|
||||
Outdent,
|
||||
AutoIndent,
|
||||
PageDown,
|
||||
@@ -370,7 +380,9 @@ gpui::actions!(
|
||||
SelectAll,
|
||||
SelectAllMatches,
|
||||
SelectToStartOfExcerpt,
|
||||
SelectToStartOfNextExcerpt,
|
||||
SelectToEndOfExcerpt,
|
||||
SelectToEndOfPreviousExcerpt,
|
||||
SelectDown,
|
||||
SelectEnclosingSymbol,
|
||||
SelectLargerSyntaxNode,
|
||||
@@ -398,9 +410,8 @@ gpui::actions!(
|
||||
SplitSelectionIntoLines,
|
||||
SwitchSourceHeader,
|
||||
Tab,
|
||||
TabPrev,
|
||||
Backtab,
|
||||
ToggleAutoSignatureHelp,
|
||||
ToggleGitBlame,
|
||||
ToggleGitBlameInline,
|
||||
ToggleIndentGuides,
|
||||
ToggleInlayHints,
|
||||
|
||||
@@ -2,6 +2,7 @@ use anyhow::Context as _;
|
||||
use gpui::{App, Context, Entity, Window};
|
||||
use language::Language;
|
||||
use url::Url;
|
||||
use workspace::{OpenOptions, OpenVisible};
|
||||
|
||||
use crate::lsp_ext::find_specific_language_server_in_selection;
|
||||
|
||||
@@ -72,7 +73,7 @@ pub fn switch_source_header(
|
||||
|
||||
workspace
|
||||
.update_in(&mut cx, |workspace, window, cx| {
|
||||
workspace.open_abs_path(path, false, window, cx)
|
||||
workspace.open_abs_path(path, OpenOptions { visible: Some(OpenVisible::None), ..Default::default() }, window, cx)
|
||||
})
|
||||
.with_context(|| {
|
||||
format!(
|
||||
|
||||
@@ -514,7 +514,7 @@ impl CompletionsMenu {
|
||||
);
|
||||
|
||||
let completion_label = StyledText::new(completion.label.text.clone())
|
||||
.with_highlights(&style.text, highlights);
|
||||
.with_default_highlights(&style.text, highlights);
|
||||
let documentation_label = if let Some(
|
||||
CompletionDocumentation::SingleLine(text),
|
||||
) = documentation
|
||||
|
||||
@@ -1124,6 +1124,11 @@ impl DisplaySnapshot {
|
||||
self.block_snapshot.is_block_line(BlockRow(display_row.0))
|
||||
}
|
||||
|
||||
pub fn is_folded_buffer_header(&self, display_row: DisplayRow) -> bool {
|
||||
self.block_snapshot
|
||||
.is_folded_buffer_header(BlockRow(display_row.0))
|
||||
}
|
||||
|
||||
pub fn soft_wrap_indent(&self, display_row: DisplayRow) -> Option<u32> {
|
||||
let wrap_row = self
|
||||
.block_snapshot
|
||||
|
||||
@@ -1618,6 +1618,15 @@ impl BlockSnapshot {
|
||||
cursor.item().map_or(false, |t| t.block.is_some())
|
||||
}
|
||||
|
||||
pub(super) fn is_folded_buffer_header(&self, row: BlockRow) -> bool {
|
||||
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
|
||||
cursor.seek(&row, Bias::Right, &());
|
||||
let Some(transform) = cursor.item() else {
|
||||
return false;
|
||||
};
|
||||
matches!(transform.block, Some(Block::FoldedBuffer { .. }))
|
||||
}
|
||||
|
||||
pub(super) fn is_line_replaced(&self, row: MultiBufferRow) -> bool {
|
||||
let wrap_point = self
|
||||
.wrap_snapshot
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ use crate::{
|
||||
},
|
||||
JoinLines,
|
||||
};
|
||||
use buffer_diff::{BufferDiff, DiffHunkStatus, DiffHunkStatusKind};
|
||||
use buffer_diff::{BufferDiff, DiffHunkSecondaryStatus, DiffHunkStatus, DiffHunkStatusKind};
|
||||
use futures::StreamExt;
|
||||
use gpui::{
|
||||
div, BackgroundExecutor, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,
|
||||
@@ -1514,6 +1514,10 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) {
|
||||
stop_at_indent: true,
|
||||
};
|
||||
|
||||
let delete_to_beg = DeleteToBeginningOfLine {
|
||||
stop_at_indent: false,
|
||||
};
|
||||
|
||||
let move_to_end = MoveToEndOfLine {
|
||||
stop_at_soft_wraps: true,
|
||||
};
|
||||
@@ -1672,7 +1676,7 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) {
|
||||
});
|
||||
|
||||
_ = editor.update(cx, |editor, window, cx| {
|
||||
editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, window, cx);
|
||||
editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
|
||||
assert_eq!(editor.display_text(cx), "\n");
|
||||
assert_eq!(
|
||||
editor.selections.display_ranges(cx),
|
||||
@@ -1778,6 +1782,107 @@ fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_beginning_of_line_stop_at_indent(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let move_to_beg = MoveToBeginningOfLine {
|
||||
stop_at_soft_wraps: true,
|
||||
stop_at_indent: true,
|
||||
};
|
||||
|
||||
let select_to_beg = SelectToBeginningOfLine {
|
||||
stop_at_soft_wraps: true,
|
||||
stop_at_indent: true,
|
||||
};
|
||||
|
||||
let delete_to_beg = DeleteToBeginningOfLine {
|
||||
stop_at_indent: true,
|
||||
};
|
||||
|
||||
let move_to_end = MoveToEndOfLine {
|
||||
stop_at_soft_wraps: false,
|
||||
};
|
||||
|
||||
let editor = cx.add_window(|window, cx| {
|
||||
let buffer = MultiBuffer::build_simple("abc\n def", cx);
|
||||
build_editor(buffer, window, cx)
|
||||
});
|
||||
|
||||
_ = editor.update(cx, |editor, window, cx| {
|
||||
editor.change_selections(None, window, cx, |s| {
|
||||
s.select_display_ranges([
|
||||
DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
|
||||
DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
|
||||
]);
|
||||
});
|
||||
|
||||
// Moving to the beginning of the line should put the first cursor at the beginning of the line,
|
||||
// and the second cursor at the first non-whitespace character in the line.
|
||||
editor.move_to_beginning_of_line(&move_to_beg, window, cx);
|
||||
assert_eq!(
|
||||
editor.selections.display_ranges(cx),
|
||||
&[
|
||||
DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
|
||||
DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
|
||||
]
|
||||
);
|
||||
|
||||
// Moving to the beginning of the line again should be a no-op for the first cursor,
|
||||
// and should move the second cursor to the beginning of the line.
|
||||
editor.move_to_beginning_of_line(&move_to_beg, window, cx);
|
||||
assert_eq!(
|
||||
editor.selections.display_ranges(cx),
|
||||
&[
|
||||
DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
|
||||
DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
|
||||
]
|
||||
);
|
||||
|
||||
// Moving to the beginning of the line again should still be a no-op for the first cursor,
|
||||
// and should move the second cursor back to the first non-whitespace character in the line.
|
||||
editor.move_to_beginning_of_line(&move_to_beg, window, cx);
|
||||
assert_eq!(
|
||||
editor.selections.display_ranges(cx),
|
||||
&[
|
||||
DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
|
||||
DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
|
||||
]
|
||||
);
|
||||
|
||||
// Selecting to the beginning of the line should select to the beginning of the line for the first cursor,
|
||||
// and to the first non-whitespace character in the line for the second cursor.
|
||||
editor.move_to_end_of_line(&move_to_end, window, cx);
|
||||
editor.move_left(&MoveLeft, window, cx);
|
||||
editor.select_to_beginning_of_line(&select_to_beg, window, cx);
|
||||
assert_eq!(
|
||||
editor.selections.display_ranges(cx),
|
||||
&[
|
||||
DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
|
||||
DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
|
||||
]
|
||||
);
|
||||
|
||||
// Selecting to the beginning of the line again should be a no-op for the first cursor,
|
||||
// and should select to the beginning of the line for the second cursor.
|
||||
editor.select_to_beginning_of_line(&select_to_beg, window, cx);
|
||||
assert_eq!(
|
||||
editor.selections.display_ranges(cx),
|
||||
&[
|
||||
DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
|
||||
DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
|
||||
]
|
||||
);
|
||||
|
||||
// Deleting to the beginning of the line should delete to the beginning of the line for the first cursor,
|
||||
// and should delete to the first non-whitespace character in the line for the second cursor.
|
||||
editor.move_to_end_of_line(&move_to_end, window, cx);
|
||||
editor.move_left(&MoveLeft, window, cx);
|
||||
editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
|
||||
assert_eq!(editor.text(cx), "c\n f");
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
@@ -2295,7 +2400,13 @@ async fn test_delete_to_beginning_of_line(cx: &mut TestAppContext) {
|
||||
let mut cx = EditorTestContext::new(cx).await;
|
||||
cx.set_state("one «two threeˇ» four");
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, window, cx);
|
||||
editor.delete_to_beginning_of_line(
|
||||
&DeleteToBeginningOfLine {
|
||||
stop_at_indent: false,
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
assert_eq!(editor.text(cx), " four");
|
||||
});
|
||||
}
|
||||
@@ -2854,7 +2965,7 @@ async fn test_indent_outdent(cx: &mut TestAppContext) {
|
||||
four
|
||||
"});
|
||||
|
||||
cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
|
||||
cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
|
||||
cx.assert_editor_state(indoc! {"
|
||||
«oneˇ» «twoˇ»
|
||||
three
|
||||
@@ -2874,7 +2985,7 @@ async fn test_indent_outdent(cx: &mut TestAppContext) {
|
||||
ˇ» four
|
||||
"});
|
||||
|
||||
cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
|
||||
cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
|
||||
cx.assert_editor_state(indoc! {"
|
||||
one two
|
||||
t«hree
|
||||
@@ -2899,7 +3010,7 @@ async fn test_indent_outdent(cx: &mut TestAppContext) {
|
||||
ˇ three
|
||||
four
|
||||
"});
|
||||
cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
|
||||
cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
|
||||
cx.assert_editor_state(indoc! {"
|
||||
one two
|
||||
ˇthree
|
||||
@@ -2933,13 +3044,13 @@ async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
|
||||
three
|
||||
four
|
||||
"});
|
||||
cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
|
||||
cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
|
||||
cx.assert_editor_state(indoc! {"
|
||||
\t«oneˇ» «twoˇ»
|
||||
three
|
||||
four
|
||||
"});
|
||||
cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
|
||||
cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
|
||||
cx.assert_editor_state(indoc! {"
|
||||
«oneˇ» «twoˇ»
|
||||
three
|
||||
@@ -2964,13 +3075,13 @@ async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
|
||||
\t\tt«hree
|
||||
ˇ»four
|
||||
"});
|
||||
cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
|
||||
cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
|
||||
cx.assert_editor_state(indoc! {"
|
||||
one two
|
||||
\tt«hree
|
||||
ˇ»four
|
||||
"});
|
||||
cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
|
||||
cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
|
||||
cx.assert_editor_state(indoc! {"
|
||||
one two
|
||||
t«hree
|
||||
@@ -2983,7 +3094,7 @@ async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
|
||||
ˇthree
|
||||
four
|
||||
"});
|
||||
cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
|
||||
cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
|
||||
cx.assert_editor_state(indoc! {"
|
||||
one two
|
||||
ˇthree
|
||||
@@ -2995,7 +3106,7 @@ async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
|
||||
\tˇthree
|
||||
four
|
||||
"});
|
||||
cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
|
||||
cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
|
||||
cx.assert_editor_state(indoc! {"
|
||||
one two
|
||||
ˇthree
|
||||
@@ -3100,7 +3211,7 @@ fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
|
||||
"},
|
||||
cx,
|
||||
);
|
||||
editor.tab_prev(&TabPrev, window, cx);
|
||||
editor.backtab(&Backtab, window, cx);
|
||||
assert_text_with_selections(
|
||||
&mut editor,
|
||||
indoc! {"
|
||||
@@ -4626,6 +4737,31 @@ async fn test_rewrap(cx: &mut TestAppContext) {
|
||||
}
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_hard_wrap(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
let mut cx = EditorTestContext::new(cx).await;
|
||||
|
||||
cx.update_editor(|editor, _, cx| {
|
||||
editor.set_hard_wrap(Some(14), cx);
|
||||
});
|
||||
|
||||
cx.set_state(indoc!(
|
||||
"
|
||||
one two three ˇ
|
||||
"
|
||||
));
|
||||
cx.simulate_input("four");
|
||||
cx.run_until_parked();
|
||||
|
||||
cx.assert_editor_state(indoc!(
|
||||
"
|
||||
one two three
|
||||
fourˇ
|
||||
"
|
||||
));
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_clipboard(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
@@ -4820,6 +4956,34 @@ async fn test_paste_multiline(cx: &mut TestAppContext) {
|
||||
)
|
||||
);
|
||||
"});
|
||||
|
||||
// Copy an indented block, starting mid-line
|
||||
cx.set_state(indoc! {"
|
||||
const a: B = (
|
||||
c(),
|
||||
somethin«g(
|
||||
e,
|
||||
f
|
||||
)ˇ»
|
||||
);
|
||||
"});
|
||||
cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
|
||||
|
||||
// Paste it on a line with a lower indent level
|
||||
cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
|
||||
cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
|
||||
cx.assert_editor_state(indoc! {"
|
||||
const a: B = (
|
||||
c(),
|
||||
something(
|
||||
e,
|
||||
f
|
||||
)
|
||||
);
|
||||
g(
|
||||
e,
|
||||
f
|
||||
)ˇ"});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
@@ -7875,6 +8039,157 @@ async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
|
||||
init_test(cx, |settings| {
|
||||
settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
|
||||
FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
|
||||
))
|
||||
});
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_file(path!("/file.ts"), Default::default()).await;
|
||||
|
||||
let project = Project::test(fs, [path!("/").as_ref()], cx).await;
|
||||
|
||||
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
|
||||
language_registry.add(Arc::new(Language::new(
|
||||
LanguageConfig {
|
||||
name: "TypeScript".into(),
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["ts".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
..LanguageConfig::default()
|
||||
},
|
||||
Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
|
||||
)));
|
||||
update_test_language_settings(cx, |settings| {
|
||||
settings.defaults.prettier = Some(PrettierSettings {
|
||||
allowed: true,
|
||||
..PrettierSettings::default()
|
||||
});
|
||||
});
|
||||
let mut fake_servers = language_registry.register_fake_lsp(
|
||||
"TypeScript",
|
||||
FakeLspAdapter {
|
||||
capabilities: lsp::ServerCapabilities {
|
||||
code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
let buffer = project
|
||||
.update(cx, |project, cx| {
|
||||
project.open_local_buffer(path!("/file.ts"), cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
|
||||
let (editor, cx) = cx.add_window_view(|window, cx| {
|
||||
build_editor_with_project(project.clone(), buffer, window, cx)
|
||||
});
|
||||
editor.update_in(cx, |editor, window, cx| {
|
||||
editor.set_text(
|
||||
"import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
cx.executor().start_waiting();
|
||||
let fake_server = fake_servers.next().await.unwrap();
|
||||
|
||||
let format = editor
|
||||
.update_in(cx, |editor, window, cx| {
|
||||
editor.perform_code_action_kind(
|
||||
project.clone(),
|
||||
CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.unwrap();
|
||||
fake_server
|
||||
.handle_request::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
|
||||
assert_eq!(
|
||||
params.text_document.uri,
|
||||
lsp::Url::from_file_path(path!("/file.ts")).unwrap()
|
||||
);
|
||||
Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
|
||||
lsp::CodeAction {
|
||||
title: "Organize Imports".to_string(),
|
||||
kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
|
||||
edit: Some(lsp::WorkspaceEdit {
|
||||
changes: Some(
|
||||
[(
|
||||
params.text_document.uri.clone(),
|
||||
vec![lsp::TextEdit::new(
|
||||
lsp::Range::new(
|
||||
lsp::Position::new(1, 0),
|
||||
lsp::Position::new(2, 0),
|
||||
),
|
||||
"".to_string(),
|
||||
)],
|
||||
)]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
)]))
|
||||
})
|
||||
.next()
|
||||
.await;
|
||||
cx.executor().start_waiting();
|
||||
format.await;
|
||||
assert_eq!(
|
||||
editor.update(cx, |editor, cx| editor.text(cx)),
|
||||
"import { a } from 'module';\n\nconst x = a;\n"
|
||||
);
|
||||
|
||||
editor.update_in(cx, |editor, window, cx| {
|
||||
editor.set_text(
|
||||
"import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
// Ensure we don't lock if code action hangs.
|
||||
fake_server.handle_request::<lsp::request::CodeActionRequest, _, _>(
|
||||
move |params, _| async move {
|
||||
assert_eq!(
|
||||
params.text_document.uri,
|
||||
lsp::Url::from_file_path(path!("/file.ts")).unwrap()
|
||||
);
|
||||
futures::future::pending::<()>().await;
|
||||
unreachable!()
|
||||
},
|
||||
);
|
||||
let format = editor
|
||||
.update_in(cx, |editor, window, cx| {
|
||||
editor.perform_code_action_kind(
|
||||
project,
|
||||
CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.unwrap();
|
||||
cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
|
||||
cx.executor().start_waiting();
|
||||
format.await;
|
||||
assert_eq!(
|
||||
editor.update(cx, |editor, cx| editor.text(cx)),
|
||||
"import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
@@ -10764,7 +11079,7 @@ async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mu
|
||||
executor.run_until_parked();
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
|
||||
editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
|
||||
});
|
||||
|
||||
cx.assert_editor_state(indoc! {"
|
||||
@@ -10773,7 +11088,7 @@ async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mu
|
||||
"});
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
|
||||
editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
|
||||
});
|
||||
|
||||
cx.assert_editor_state(indoc! {"
|
||||
@@ -10782,7 +11097,7 @@ async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mu
|
||||
"});
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
|
||||
editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
|
||||
});
|
||||
|
||||
cx.assert_editor_state(indoc! {"
|
||||
@@ -10791,7 +11106,7 @@ async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mu
|
||||
"});
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
|
||||
editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
|
||||
});
|
||||
|
||||
cx.assert_editor_state(indoc! {"
|
||||
@@ -10871,7 +11186,7 @@ async fn cycle_through_same_place_diagnostics(
|
||||
|
||||
// Fourth diagnostic
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
|
||||
editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
|
||||
});
|
||||
cx.assert_editor_state(indoc! {"
|
||||
fn func(abc def: i32) -> ˇu32 {
|
||||
@@ -10880,7 +11195,7 @@ async fn cycle_through_same_place_diagnostics(
|
||||
|
||||
// Third diagnostic
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
|
||||
editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
|
||||
});
|
||||
cx.assert_editor_state(indoc! {"
|
||||
fn func(abc ˇdef: i32) -> u32 {
|
||||
@@ -10889,7 +11204,7 @@ async fn cycle_through_same_place_diagnostics(
|
||||
|
||||
// Second diagnostic, same place
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
|
||||
editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
|
||||
});
|
||||
cx.assert_editor_state(indoc! {"
|
||||
fn func(abc ˇdef: i32) -> u32 {
|
||||
@@ -10898,7 +11213,7 @@ async fn cycle_through_same_place_diagnostics(
|
||||
|
||||
// First diagnostic
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
|
||||
editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
|
||||
});
|
||||
cx.assert_editor_state(indoc! {"
|
||||
fn func(abcˇ def: i32) -> u32 {
|
||||
@@ -10907,7 +11222,7 @@ async fn cycle_through_same_place_diagnostics(
|
||||
|
||||
// Wrapped over, fourth diagnostic
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
|
||||
editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
|
||||
});
|
||||
cx.assert_editor_state(indoc! {"
|
||||
fn func(abc def: i32) -> ˇu32 {
|
||||
@@ -11173,7 +11488,7 @@ async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext)
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
//Wrap around the top of the buffer
|
||||
for _ in 0..2 {
|
||||
editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
|
||||
editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -11193,7 +11508,7 @@ async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext)
|
||||
);
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
|
||||
editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
|
||||
});
|
||||
|
||||
cx.assert_editor_state(
|
||||
@@ -11212,7 +11527,7 @@ async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext)
|
||||
);
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
|
||||
editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
|
||||
});
|
||||
|
||||
cx.assert_editor_state(
|
||||
@@ -11232,7 +11547,7 @@ async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext)
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
for _ in 0..2 {
|
||||
editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
|
||||
editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -11967,7 +12282,7 @@ async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
|
||||
});
|
||||
cx.run_until_parked();
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.context_menu_prev(&ContextMenuPrev, window, cx);
|
||||
editor.context_menu_prev(&ContextMenuPrevious, window, cx);
|
||||
});
|
||||
cx.run_until_parked();
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
@@ -12157,7 +12472,7 @@ async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext)
|
||||
resolved_items.lock().clear();
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.context_menu_prev(&ContextMenuPrev, window, cx);
|
||||
editor.context_menu_prev(&ContextMenuPrevious, window, cx);
|
||||
});
|
||||
cx.run_until_parked();
|
||||
// Completions that have already been resolved are skipped.
|
||||
@@ -12404,7 +12719,7 @@ async fn test_addition_reverts(cx: &mut TestAppContext) {
|
||||
struct Row9.2;
|
||||
struct Row9.3;
|
||||
struct Row10;"#},
|
||||
vec![DiffHunkStatus::added_none(), DiffHunkStatus::added_none()],
|
||||
vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
|
||||
indoc! {r#"struct Row;
|
||||
struct Row1;
|
||||
struct Row1.1;
|
||||
@@ -12442,7 +12757,7 @@ async fn test_addition_reverts(cx: &mut TestAppContext) {
|
||||
struct Row8;
|
||||
struct Row9;
|
||||
struct Row10;"#},
|
||||
vec![DiffHunkStatus::added_none(), DiffHunkStatus::added_none()],
|
||||
vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
|
||||
indoc! {r#"struct Row;
|
||||
struct Row1;
|
||||
struct Row2;
|
||||
@@ -12489,11 +12804,11 @@ async fn test_addition_reverts(cx: &mut TestAppContext) {
|
||||
«ˇ// something on bottom»
|
||||
struct Row10;"#},
|
||||
vec![
|
||||
DiffHunkStatus::added_none(),
|
||||
DiffHunkStatus::added_none(),
|
||||
DiffHunkStatus::added_none(),
|
||||
DiffHunkStatus::added_none(),
|
||||
DiffHunkStatus::added_none(),
|
||||
DiffHunkStatusKind::Added,
|
||||
DiffHunkStatusKind::Added,
|
||||
DiffHunkStatusKind::Added,
|
||||
DiffHunkStatusKind::Added,
|
||||
DiffHunkStatusKind::Added,
|
||||
],
|
||||
indoc! {r#"struct Row;
|
||||
ˇstruct Row1;
|
||||
@@ -12541,10 +12856,7 @@ async fn test_modification_reverts(cx: &mut TestAppContext) {
|
||||
struct Row99;
|
||||
struct Row9;
|
||||
struct Row10;"#},
|
||||
vec![
|
||||
DiffHunkStatus::modified_none(),
|
||||
DiffHunkStatus::modified_none(),
|
||||
],
|
||||
vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
|
||||
indoc! {r#"struct Row;
|
||||
struct Row1;
|
||||
struct Row33;
|
||||
@@ -12571,10 +12883,7 @@ async fn test_modification_reverts(cx: &mut TestAppContext) {
|
||||
struct Row99;
|
||||
struct Row9;
|
||||
struct Row10;"#},
|
||||
vec![
|
||||
DiffHunkStatus::modified_none(),
|
||||
DiffHunkStatus::modified_none(),
|
||||
],
|
||||
vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
|
||||
indoc! {r#"struct Row;
|
||||
struct Row1;
|
||||
struct Row33;
|
||||
@@ -12603,12 +12912,12 @@ async fn test_modification_reverts(cx: &mut TestAppContext) {
|
||||
struct Row9;
|
||||
struct Row1011;ˇ"#},
|
||||
vec![
|
||||
DiffHunkStatus::modified_none(),
|
||||
DiffHunkStatus::modified_none(),
|
||||
DiffHunkStatus::modified_none(),
|
||||
DiffHunkStatus::modified_none(),
|
||||
DiffHunkStatus::modified_none(),
|
||||
DiffHunkStatus::modified_none(),
|
||||
DiffHunkStatusKind::Modified,
|
||||
DiffHunkStatusKind::Modified,
|
||||
DiffHunkStatusKind::Modified,
|
||||
DiffHunkStatusKind::Modified,
|
||||
DiffHunkStatusKind::Modified,
|
||||
DiffHunkStatusKind::Modified,
|
||||
],
|
||||
indoc! {r#"struct Row;
|
||||
ˇstruct Row1;
|
||||
@@ -12686,10 +12995,7 @@ struct Row10;"#};
|
||||
ˇ
|
||||
struct Row8;
|
||||
struct Row10;"#},
|
||||
vec![
|
||||
DiffHunkStatus::deleted_none(),
|
||||
DiffHunkStatus::deleted_none(),
|
||||
],
|
||||
vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
|
||||
indoc! {r#"struct Row;
|
||||
struct Row2;
|
||||
|
||||
@@ -12712,10 +13018,7 @@ struct Row10;"#};
|
||||
ˇ»
|
||||
struct Row8;
|
||||
struct Row10;"#},
|
||||
vec![
|
||||
DiffHunkStatus::deleted_none(),
|
||||
DiffHunkStatus::deleted_none(),
|
||||
],
|
||||
vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
|
||||
indoc! {r#"struct Row;
|
||||
struct Row2;
|
||||
|
||||
@@ -12740,10 +13043,7 @@ struct Row10;"#};
|
||||
|
||||
struct Row8;ˇ
|
||||
struct Row10;"#},
|
||||
vec![
|
||||
DiffHunkStatus::deleted_none(),
|
||||
DiffHunkStatus::deleted_none(),
|
||||
],
|
||||
vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
|
||||
indoc! {r#"struct Row;
|
||||
struct Row1;
|
||||
ˇstruct Row2;
|
||||
@@ -12768,9 +13068,9 @@ struct Row10;"#};
|
||||
struct Row8;ˇ»
|
||||
struct Row10;"#},
|
||||
vec![
|
||||
DiffHunkStatus::deleted_none(),
|
||||
DiffHunkStatus::deleted_none(),
|
||||
DiffHunkStatus::deleted_none(),
|
||||
DiffHunkStatusKind::Deleted,
|
||||
DiffHunkStatusKind::Deleted,
|
||||
DiffHunkStatusKind::Deleted,
|
||||
],
|
||||
indoc! {r#"struct Row;
|
||||
struct Row1;
|
||||
@@ -16687,14 +16987,13 @@ pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsC
|
||||
#[track_caller]
|
||||
fn assert_hunk_revert(
|
||||
not_reverted_text_with_selections: &str,
|
||||
expected_hunk_statuses_before: Vec<DiffHunkStatus>,
|
||||
expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
|
||||
expected_reverted_text_with_selections: &str,
|
||||
base_text: &str,
|
||||
cx: &mut EditorLspTestContext,
|
||||
) {
|
||||
cx.set_state(not_reverted_text_with_selections);
|
||||
cx.set_head_text(base_text);
|
||||
cx.clear_index_text();
|
||||
cx.executor().run_until_parked();
|
||||
|
||||
let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
|
||||
@@ -16702,7 +17001,7 @@ fn assert_hunk_revert(
|
||||
let reverted_hunk_statuses = snapshot
|
||||
.buffer_snapshot
|
||||
.diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
|
||||
.map(|hunk| hunk.status())
|
||||
.map(|hunk| hunk.status().kind)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
editor.git_restore(&Default::default(), window, cx);
|
||||
|
||||
@@ -19,28 +19,29 @@ use crate::{
|
||||
BlockId, ChunkReplacement, CursorShape, CustomBlockId, DisplayDiffHunk, DisplayPoint,
|
||||
DisplayRow, DocumentHighlightRead, DocumentHighlightWrite, EditDisplayMode, Editor, EditorMode,
|
||||
EditorSettings, EditorSnapshot, EditorStyle, ExpandExcerpts, FocusedBlock, GoToHunk,
|
||||
GoToPrevHunk, GutterDimensions, HalfPageDown, HalfPageUp, HandleInput, HoveredCursor,
|
||||
InlayHintRefreshReason, InlineCompletion, JumpData, LineDown, LineUp, OpenExcerpts, PageDown,
|
||||
PageUp, Point, RowExt, RowRangeExt, SelectPhase, SelectedTextHighlight, Selection, SoftWrap,
|
||||
StickyHeaderExcerpt, ToPoint, ToggleFold, COLUMNAR_SELECTION_MODIFIERS, CURSORS_VISIBLE_FOR,
|
||||
FILE_HEADER_HEIGHT, GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED, MAX_LINE_LEN,
|
||||
GoToPreviousHunk, GutterDimensions, HalfPageDown, HalfPageUp, HandleInput, HoveredCursor,
|
||||
InlayHintRefreshReason, InlineCompletion, JumpData, LineDown, LineHighlight, LineUp,
|
||||
OpenExcerpts, PageDown, PageUp, Point, RowExt, RowRangeExt, SelectPhase, SelectedTextHighlight,
|
||||
Selection, SoftWrap, StickyHeaderExcerpt, ToPoint, ToggleFold, COLUMNAR_SELECTION_MODIFIERS,
|
||||
CURSORS_VISIBLE_FOR, FILE_HEADER_HEIGHT, GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED, MAX_LINE_LEN,
|
||||
MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
|
||||
};
|
||||
use buffer_diff::{DiffHunkSecondaryStatus, DiffHunkStatus, DiffHunkStatusKind};
|
||||
use buffer_diff::{DiffHunkStatus, DiffHunkStatusKind};
|
||||
use client::ParticipantIndex;
|
||||
use collections::{BTreeMap, HashMap, HashSet};
|
||||
use file_icons::FileIcons;
|
||||
use git::{blame::BlameEntry, status::FileStatus, Oid};
|
||||
use gpui::{
|
||||
anchored, deferred, div, fill, linear_color_stop, linear_gradient, outline, point, px, quad,
|
||||
relative, size, svg, transparent_black, Action, AnyElement, App, AvailableSpace, Axis, Bounds,
|
||||
ClickEvent, ClipboardItem, ContentMask, Context, Corner, Corners, CursorStyle, DispatchPhase,
|
||||
Edges, Element, ElementInputHandler, Entity, Focusable as _, FontId, GlobalElementId, Hitbox,
|
||||
Hsla, InteractiveElement, IntoElement, Keystroke, Length, ModifiersChangedEvent, MouseButton,
|
||||
MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad, ParentElement, Pixels, ScrollDelta,
|
||||
ScrollWheelEvent, ShapedLine, SharedString, Size, StatefulInteractiveElement, Style, Styled,
|
||||
Subscription, TextRun, TextStyleRefinement, Window,
|
||||
relative, size, solid_background, svg, transparent_black, Action, AnyElement, App,
|
||||
AvailableSpace, Axis, Bounds, ClickEvent, ClipboardItem, ContentMask, Context, Corner, Corners,
|
||||
CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Entity, Focusable as _,
|
||||
FontId, GlobalElementId, Hitbox, Hsla, InteractiveElement, IntoElement, Keystroke, Length,
|
||||
ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad,
|
||||
ParentElement, Pixels, ScrollDelta, ScrollWheelEvent, ShapedLine, SharedString, Size,
|
||||
StatefulInteractiveElement, Style, Styled, Subscription, TextRun, TextStyleRefinement, Window,
|
||||
};
|
||||
use inline_completion::Direction;
|
||||
use itertools::Itertools;
|
||||
use language::{
|
||||
language_settings::{
|
||||
@@ -75,7 +76,7 @@ use ui::{
|
||||
POPOVER_Y_PADDING,
|
||||
};
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
use util::{debug_panic, maybe, RangeExt, ResultExt};
|
||||
use util::{debug_panic, RangeExt, ResultExt};
|
||||
use workspace::{item::Item, notifications::NotifyTaskExt};
|
||||
|
||||
const INLINE_BLAME_PADDING_EM_WIDTHS: f32 = 7.;
|
||||
@@ -195,7 +196,7 @@ impl EditorElement {
|
||||
register_action(editor, window, Editor::backspace);
|
||||
register_action(editor, window, Editor::delete);
|
||||
register_action(editor, window, Editor::tab);
|
||||
register_action(editor, window, Editor::tab_prev);
|
||||
register_action(editor, window, Editor::backtab);
|
||||
register_action(editor, window, Editor::indent);
|
||||
register_action(editor, window, Editor::outdent);
|
||||
register_action(editor, window, Editor::autoindent);
|
||||
@@ -280,7 +281,9 @@ impl EditorElement {
|
||||
register_action(editor, window, Editor::move_to_beginning);
|
||||
register_action(editor, window, Editor::move_to_end);
|
||||
register_action(editor, window, Editor::move_to_start_of_excerpt);
|
||||
register_action(editor, window, Editor::move_to_start_of_next_excerpt);
|
||||
register_action(editor, window, Editor::move_to_end_of_excerpt);
|
||||
register_action(editor, window, Editor::move_to_end_of_previous_excerpt);
|
||||
register_action(editor, window, Editor::select_up);
|
||||
register_action(editor, window, Editor::select_down);
|
||||
register_action(editor, window, Editor::select_left);
|
||||
@@ -294,7 +297,9 @@ impl EditorElement {
|
||||
register_action(editor, window, Editor::select_to_start_of_paragraph);
|
||||
register_action(editor, window, Editor::select_to_end_of_paragraph);
|
||||
register_action(editor, window, Editor::select_to_start_of_excerpt);
|
||||
register_action(editor, window, Editor::select_to_start_of_next_excerpt);
|
||||
register_action(editor, window, Editor::select_to_end_of_excerpt);
|
||||
register_action(editor, window, Editor::select_to_end_of_previous_excerpt);
|
||||
register_action(editor, window, Editor::select_to_beginning);
|
||||
register_action(editor, window, Editor::select_to_end);
|
||||
register_action(editor, window, Editor::select_all);
|
||||
@@ -429,6 +434,13 @@ impl EditorElement {
|
||||
cx.propagate();
|
||||
}
|
||||
});
|
||||
register_action(editor, window, |editor, action, window, cx| {
|
||||
if let Some(task) = editor.organize_imports(action, window, cx) {
|
||||
task.detach_and_notify_err(window, cx);
|
||||
} else {
|
||||
cx.propagate();
|
||||
}
|
||||
});
|
||||
register_action(editor, window, Editor::restart_language_server);
|
||||
register_action(editor, window, Editor::show_character_palette);
|
||||
register_action(editor, window, |editor, action, window, cx| {
|
||||
@@ -1682,7 +1694,7 @@ impl EditorElement {
|
||||
let pos_y = content_origin.y
|
||||
+ line_height * (row.0 as f32 - scroll_pixel_position.y / line_height);
|
||||
|
||||
let window_ix = row.minus(start_row) as usize;
|
||||
let window_ix = row.0.saturating_sub(start_row.0) as usize;
|
||||
let pos_x = {
|
||||
let crease_trailer_layout = &crease_trailers[window_ix];
|
||||
let line_layout = &line_layouts[window_ix];
|
||||
@@ -2009,7 +2021,7 @@ impl EditorElement {
|
||||
scroll_pixel_position: gpui::Point<Pixels>,
|
||||
gutter_dimensions: &GutterDimensions,
|
||||
gutter_hitbox: &Hitbox,
|
||||
rows_with_hunk_bounds: &HashMap<DisplayRow, Bounds<Pixels>>,
|
||||
display_hunks: &[(DisplayDiffHunk, Option<Hitbox>)],
|
||||
snapshot: &EditorSnapshot,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
@@ -2085,7 +2097,7 @@ impl EditorElement {
|
||||
gutter_dimensions,
|
||||
scroll_pixel_position,
|
||||
gutter_hitbox,
|
||||
rows_with_hunk_bounds,
|
||||
display_hunks,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
@@ -2103,7 +2115,7 @@ impl EditorElement {
|
||||
scroll_pixel_position: gpui::Point<Pixels>,
|
||||
gutter_dimensions: &GutterDimensions,
|
||||
gutter_hitbox: &Hitbox,
|
||||
rows_with_hunk_bounds: &HashMap<DisplayRow, Bounds<Pixels>>,
|
||||
display_hunks: &[(DisplayDiffHunk, Option<Hitbox>)],
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> Option<AnyElement> {
|
||||
@@ -2128,7 +2140,7 @@ impl EditorElement {
|
||||
gutter_dimensions,
|
||||
scroll_pixel_position,
|
||||
gutter_hitbox,
|
||||
rows_with_hunk_bounds,
|
||||
display_hunks,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
@@ -2667,24 +2679,21 @@ impl EditorElement {
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> Div {
|
||||
let file_status = maybe!({
|
||||
let project = self.editor.read(cx).project.as_ref()?.read(cx);
|
||||
let (repo, path) =
|
||||
project.repository_and_path_for_buffer_id(for_excerpt.buffer_id, cx)?;
|
||||
let status = repo.read(cx).repository_entry.status_for_path(&path)?;
|
||||
Some(status.status)
|
||||
})
|
||||
.filter(|_| {
|
||||
self.editor
|
||||
.read(cx)
|
||||
.buffer
|
||||
.read(cx)
|
||||
.all_diff_hunks_expanded()
|
||||
});
|
||||
|
||||
let include_root = self
|
||||
.editor
|
||||
let editor = self.editor.read(cx);
|
||||
let file_status = editor
|
||||
.buffer
|
||||
.read(cx)
|
||||
.all_diff_hunks_expanded()
|
||||
.then(|| {
|
||||
editor
|
||||
.project
|
||||
.as_ref()?
|
||||
.read(cx)
|
||||
.status_for_buffer_id(for_excerpt.buffer_id, cx)
|
||||
})
|
||||
.flatten();
|
||||
|
||||
let include_root = editor
|
||||
.project
|
||||
.as_ref()
|
||||
.map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
|
||||
@@ -2696,7 +2705,7 @@ impl EditorElement {
|
||||
let parent_path = path.as_ref().and_then(|path| {
|
||||
Some(path.parent()?.to_string_lossy().to_string() + std::path::MAIN_SEPARATOR_STR)
|
||||
});
|
||||
let focus_handle = self.editor.focus_handle(cx);
|
||||
let focus_handle = editor.focus_handle(cx);
|
||||
let colors = cx.theme().colors();
|
||||
|
||||
div()
|
||||
@@ -2715,7 +2724,10 @@ impl EditorElement {
|
||||
.shadow_md()
|
||||
.border_1()
|
||||
.map(|div| {
|
||||
let border_color = if is_selected && is_folded {
|
||||
let border_color = if is_selected
|
||||
&& is_folded
|
||||
&& focus_handle.contains_focused(window, cx)
|
||||
{
|
||||
colors.border_focused
|
||||
} else {
|
||||
colors.border
|
||||
@@ -2766,8 +2778,7 @@ impl EditorElement {
|
||||
)
|
||||
})
|
||||
.children(
|
||||
self.editor
|
||||
.read(cx)
|
||||
editor
|
||||
.addons
|
||||
.values()
|
||||
.filter_map(|addon| {
|
||||
@@ -3940,6 +3951,7 @@ impl EditorElement {
|
||||
display_row_range,
|
||||
multi_buffer_range,
|
||||
status,
|
||||
is_created_file,
|
||||
..
|
||||
} = &hunk
|
||||
{
|
||||
@@ -3971,6 +3983,7 @@ impl EditorElement {
|
||||
display_row_range.start.0,
|
||||
status,
|
||||
multi_buffer_range.clone(),
|
||||
*is_created_file,
|
||||
line_height,
|
||||
&editor,
|
||||
cx,
|
||||
@@ -4122,46 +4135,74 @@ impl EditorElement {
|
||||
}
|
||||
}
|
||||
|
||||
let mut paint_highlight =
|
||||
|highlight_row_start: DisplayRow, highlight_row_end: DisplayRow, color| {
|
||||
let origin = point(
|
||||
layout.hitbox.origin.x,
|
||||
layout.hitbox.origin.y
|
||||
+ (highlight_row_start.as_f32() - scroll_top)
|
||||
* layout.position_map.line_height,
|
||||
);
|
||||
let size = size(
|
||||
layout.hitbox.size.width,
|
||||
layout.position_map.line_height
|
||||
* highlight_row_end.next_row().minus(highlight_row_start) as f32,
|
||||
);
|
||||
window.paint_quad(fill(Bounds { origin, size }, color));
|
||||
};
|
||||
let mut paint_highlight = |highlight_row_start: DisplayRow,
|
||||
highlight_row_end: DisplayRow,
|
||||
highlight: crate::LineHighlight,
|
||||
edges| {
|
||||
let origin = point(
|
||||
layout.hitbox.origin.x,
|
||||
layout.hitbox.origin.y
|
||||
+ (highlight_row_start.as_f32() - scroll_top)
|
||||
* layout.position_map.line_height,
|
||||
);
|
||||
let size = size(
|
||||
layout.hitbox.size.width,
|
||||
layout.position_map.line_height
|
||||
* highlight_row_end.next_row().minus(highlight_row_start) as f32,
|
||||
);
|
||||
let mut quad = fill(Bounds { origin, size }, highlight.background);
|
||||
if let Some(border_color) = highlight.border {
|
||||
quad.border_color = border_color;
|
||||
quad.border_widths = edges
|
||||
}
|
||||
window.paint_quad(quad);
|
||||
};
|
||||
|
||||
let mut current_paint: Option<(gpui::Background, Range<DisplayRow>)> = None;
|
||||
let mut current_paint: Option<(LineHighlight, Range<DisplayRow>, Edges<Pixels>)> =
|
||||
None;
|
||||
for (&new_row, &new_background) in &layout.highlighted_rows {
|
||||
match &mut current_paint {
|
||||
Some((current_background, current_range)) => {
|
||||
Some((current_background, current_range, mut edges)) => {
|
||||
let current_background = *current_background;
|
||||
let new_range_started = current_background != new_background
|
||||
|| current_range.end.next_row() != new_row;
|
||||
if new_range_started {
|
||||
if current_range.end.next_row() == new_row {
|
||||
edges.bottom = px(0.);
|
||||
};
|
||||
paint_highlight(
|
||||
current_range.start,
|
||||
current_range.end,
|
||||
current_background,
|
||||
edges,
|
||||
);
|
||||
current_paint = Some((new_background, new_row..new_row));
|
||||
let edges = Edges {
|
||||
top: if current_range.end.next_row() != new_row {
|
||||
px(1.)
|
||||
} else {
|
||||
px(0.)
|
||||
},
|
||||
bottom: px(1.),
|
||||
..Default::default()
|
||||
};
|
||||
current_paint = Some((new_background, new_row..new_row, edges));
|
||||
continue;
|
||||
} else {
|
||||
current_range.end = current_range.end.next_row();
|
||||
}
|
||||
}
|
||||
None => current_paint = Some((new_background, new_row..new_row)),
|
||||
None => {
|
||||
let edges = Edges {
|
||||
top: px(1.),
|
||||
bottom: px(1.),
|
||||
..Default::default()
|
||||
};
|
||||
current_paint = Some((new_background, new_row..new_row, edges))
|
||||
}
|
||||
};
|
||||
}
|
||||
if let Some((color, range)) = current_paint {
|
||||
paint_highlight(range.start, range.end, color);
|
||||
if let Some((color, range, edges)) = current_paint {
|
||||
paint_highlight(range.start, range.end, color, edges);
|
||||
}
|
||||
|
||||
let scroll_left =
|
||||
@@ -4336,7 +4377,7 @@ impl EditorElement {
|
||||
}
|
||||
}
|
||||
|
||||
fn paint_diff_hunks(layout: &mut EditorLayout, window: &mut Window, cx: &mut App) {
|
||||
fn paint_gutter_diff_hunks(layout: &mut EditorLayout, window: &mut Window, cx: &mut App) {
|
||||
if layout.display_hunks.is_empty() {
|
||||
return;
|
||||
}
|
||||
@@ -4356,7 +4397,7 @@ impl EditorElement {
|
||||
hunk_bounds,
|
||||
cx.theme().colors().version_control_modified,
|
||||
Corners::all(px(0.)),
|
||||
DiffHunkSecondaryStatus::None,
|
||||
DiffHunkStatus::modified_none(),
|
||||
))
|
||||
}
|
||||
DisplayDiffHunk::Unfolded {
|
||||
@@ -4368,19 +4409,19 @@ impl EditorElement {
|
||||
hunk_hitbox.bounds,
|
||||
cx.theme().colors().version_control_added,
|
||||
Corners::all(px(0.)),
|
||||
status.secondary,
|
||||
*status,
|
||||
),
|
||||
DiffHunkStatusKind::Modified => (
|
||||
hunk_hitbox.bounds,
|
||||
cx.theme().colors().version_control_modified,
|
||||
Corners::all(px(0.)),
|
||||
status.secondary,
|
||||
*status,
|
||||
),
|
||||
DiffHunkStatusKind::Deleted if !display_row_range.is_empty() => (
|
||||
hunk_hitbox.bounds,
|
||||
cx.theme().colors().version_control_deleted,
|
||||
Corners::all(px(0.)),
|
||||
status.secondary,
|
||||
*status,
|
||||
),
|
||||
DiffHunkStatusKind::Deleted => (
|
||||
Bounds::new(
|
||||
@@ -4392,23 +4433,24 @@ impl EditorElement {
|
||||
),
|
||||
cx.theme().colors().version_control_deleted,
|
||||
Corners::all(1. * line_height),
|
||||
status.secondary,
|
||||
*status,
|
||||
),
|
||||
}),
|
||||
};
|
||||
|
||||
if let Some((hunk_bounds, background_color, corner_radii, secondary_status)) =
|
||||
hunk_to_paint
|
||||
{
|
||||
let background_color = if secondary_status != DiffHunkSecondaryStatus::None {
|
||||
background_color.opacity(0.3)
|
||||
} else {
|
||||
background_color.opacity(1.0)
|
||||
};
|
||||
if let Some((hunk_bounds, background_color, corner_radii, _)) = hunk_to_paint {
|
||||
// Flatten the background color with the editor color to prevent
|
||||
// elements below transparent hunks from showing through
|
||||
let flattened_background_color = cx
|
||||
.theme()
|
||||
.colors()
|
||||
.editor_background
|
||||
.blend(background_color);
|
||||
|
||||
window.paint_quad(quad(
|
||||
hunk_bounds,
|
||||
corner_radii,
|
||||
background_color,
|
||||
flattened_background_color,
|
||||
Edges::default(),
|
||||
transparent_black(),
|
||||
));
|
||||
@@ -4536,7 +4578,7 @@ impl EditorElement {
|
||||
)
|
||||
});
|
||||
if show_git_gutter {
|
||||
Self::paint_diff_hunks(layout, window, cx)
|
||||
Self::paint_gutter_diff_hunks(layout, window, cx)
|
||||
}
|
||||
|
||||
let highlight_width = 0.275 * layout.position_map.line_height;
|
||||
@@ -4674,7 +4716,7 @@ impl EditorElement {
|
||||
.read(cx)
|
||||
.buffer
|
||||
.read(cx)
|
||||
.settings_at(0, cx)
|
||||
.language_settings(cx)
|
||||
.show_whitespaces;
|
||||
|
||||
for (ix, line_with_invisibles) in layout.position_map.line_layouts.iter().enumerate() {
|
||||
@@ -5095,9 +5137,15 @@ impl EditorElement {
|
||||
end_display_row.0 -= 1;
|
||||
}
|
||||
let color = match &hunk.status().kind {
|
||||
DiffHunkStatusKind::Added => theme.status().created,
|
||||
DiffHunkStatusKind::Modified => theme.status().modified,
|
||||
DiffHunkStatusKind::Deleted => theme.status().deleted,
|
||||
DiffHunkStatusKind::Added => {
|
||||
theme.colors().version_control_added
|
||||
}
|
||||
DiffHunkStatusKind::Modified => {
|
||||
theme.colors().version_control_modified
|
||||
}
|
||||
DiffHunkStatusKind::Deleted => {
|
||||
theme.colors().version_control_deleted
|
||||
}
|
||||
};
|
||||
ColoredRange {
|
||||
start: start_display_row,
|
||||
@@ -5661,7 +5709,7 @@ fn prepaint_gutter_button(
|
||||
gutter_dimensions: &GutterDimensions,
|
||||
scroll_pixel_position: gpui::Point<Pixels>,
|
||||
gutter_hitbox: &Hitbox,
|
||||
rows_with_hunk_bounds: &HashMap<DisplayRow, Bounds<Pixels>>,
|
||||
display_hunks: &[(DisplayDiffHunk, Option<Hitbox>)],
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> AnyElement {
|
||||
@@ -5673,9 +5721,23 @@ fn prepaint_gutter_button(
|
||||
let indicator_size = button.layout_as_root(available_space, window, cx);
|
||||
|
||||
let blame_width = gutter_dimensions.git_blame_entries_width;
|
||||
let gutter_width = rows_with_hunk_bounds
|
||||
.get(&row)
|
||||
.map(|bounds| bounds.size.width);
|
||||
let gutter_width = display_hunks
|
||||
.binary_search_by(|(hunk, _)| match hunk {
|
||||
DisplayDiffHunk::Folded { display_row } => display_row.cmp(&row),
|
||||
DisplayDiffHunk::Unfolded {
|
||||
display_row_range, ..
|
||||
} => {
|
||||
if display_row_range.end <= row {
|
||||
Ordering::Less
|
||||
} else if display_row_range.start > row {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
}
|
||||
})
|
||||
.ok()
|
||||
.and_then(|ix| Some(display_hunks[ix].1.as_ref()?.size.width));
|
||||
let left_offset = blame_width.max(gutter_width).unwrap_or_default();
|
||||
|
||||
let mut x = left_offset;
|
||||
@@ -6693,14 +6755,13 @@ impl Element for EditorElement {
|
||||
.editor
|
||||
.update(cx, |editor, cx| editor.highlighted_display_rows(window, cx));
|
||||
|
||||
let is_light = cx.theme().appearance().is_light();
|
||||
|
||||
for (ix, row_info) in row_infos.iter().enumerate() {
|
||||
let Some(diff_status) = row_info.diff_status else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let staged_opacity = 0.10;
|
||||
let unstaged_opacity = 0.04;
|
||||
|
||||
let background_color = match diff_status.kind {
|
||||
DiffHunkStatusKind::Added => cx.theme().colors().version_control_added,
|
||||
DiffHunkStatusKind::Deleted => {
|
||||
@@ -6711,16 +6772,36 @@ impl Element for EditorElement {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let background_color =
|
||||
if diff_status.secondary == DiffHunkSecondaryStatus::None {
|
||||
background_color.opacity(staged_opacity)
|
||||
|
||||
let unstaged = diff_status.has_secondary_hunk();
|
||||
let hunk_opacity = if is_light { 0.16 } else { 0.12 };
|
||||
|
||||
let staged_highlight = LineHighlight {
|
||||
background: (background_color.opacity(if is_light {
|
||||
0.08
|
||||
} else {
|
||||
background_color.opacity(unstaged_opacity)
|
||||
};
|
||||
0.06
|
||||
}))
|
||||
.into(),
|
||||
border: Some(if is_light {
|
||||
background_color.opacity(0.48)
|
||||
} else {
|
||||
background_color.opacity(0.36)
|
||||
}),
|
||||
};
|
||||
|
||||
let unstaged_highlight =
|
||||
solid_background(background_color.opacity(hunk_opacity)).into();
|
||||
|
||||
let background = if unstaged {
|
||||
unstaged_highlight
|
||||
} else {
|
||||
staged_highlight
|
||||
};
|
||||
|
||||
highlighted_rows
|
||||
.entry(start_row + DisplayRow(ix as u32))
|
||||
.or_insert(background_color.into());
|
||||
.or_insert(background);
|
||||
}
|
||||
|
||||
let highlighted_ranges = self.editor.read(cx).background_highlights_in_range(
|
||||
@@ -7170,27 +7251,6 @@ impl Element for EditorElement {
|
||||
|
||||
let gutter_settings = EditorSettings::get_global(cx).gutter;
|
||||
|
||||
let rows_with_hunk_bounds = display_hunks
|
||||
.iter()
|
||||
.filter_map(|(hunk, hitbox)| Some((hunk, hitbox.as_ref()?.bounds)))
|
||||
.fold(
|
||||
HashMap::default(),
|
||||
|mut rows_with_hunk_bounds, (hunk, bounds)| {
|
||||
match hunk {
|
||||
DisplayDiffHunk::Folded { display_row } => {
|
||||
rows_with_hunk_bounds.insert(*display_row, bounds);
|
||||
}
|
||||
DisplayDiffHunk::Unfolded {
|
||||
display_row_range, ..
|
||||
} => {
|
||||
for display_row in display_row_range.iter_rows() {
|
||||
rows_with_hunk_bounds.insert(display_row, bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
rows_with_hunk_bounds
|
||||
},
|
||||
);
|
||||
let mut code_actions_indicator = None;
|
||||
if let Some(newest_selection_head) = newest_selection_head {
|
||||
let newest_selection_point =
|
||||
@@ -7240,7 +7300,7 @@ impl Element for EditorElement {
|
||||
scroll_pixel_position,
|
||||
&gutter_dimensions,
|
||||
&gutter_hitbox,
|
||||
&rows_with_hunk_bounds,
|
||||
&display_hunks,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
@@ -7268,7 +7328,7 @@ impl Element for EditorElement {
|
||||
scroll_pixel_position,
|
||||
&gutter_dimensions,
|
||||
&gutter_hitbox,
|
||||
&rows_with_hunk_bounds,
|
||||
&display_hunks,
|
||||
&snapshot,
|
||||
window,
|
||||
cx,
|
||||
@@ -7585,7 +7645,7 @@ pub struct EditorLayout {
|
||||
indent_guides: Option<Vec<IndentGuideLayout>>,
|
||||
visible_display_row_range: Range<DisplayRow>,
|
||||
active_rows: BTreeMap<DisplayRow, bool>,
|
||||
highlighted_rows: BTreeMap<DisplayRow, gpui::Background>,
|
||||
highlighted_rows: BTreeMap<DisplayRow, LineHighlight>,
|
||||
line_elements: SmallVec<[AnyElement; 1]>,
|
||||
line_numbers: Arc<HashMap<MultiBufferRow, LineNumberLayout>>,
|
||||
display_hunks: Vec<(DisplayDiffHunk, Option<Hitbox>)>,
|
||||
@@ -8748,6 +8808,7 @@ fn diff_hunk_controls(
|
||||
row: u32,
|
||||
status: &DiffHunkStatus,
|
||||
hunk_range: Range<Anchor>,
|
||||
is_created_file: bool,
|
||||
line_height: Pixels,
|
||||
editor: &Entity<Editor>,
|
||||
cx: &mut App,
|
||||
@@ -8756,75 +8817,73 @@ fn diff_hunk_controls(
|
||||
.h(line_height)
|
||||
.mr_1()
|
||||
.gap_1()
|
||||
.px_1()
|
||||
.px_0p5()
|
||||
.pb_1()
|
||||
.border_x_1()
|
||||
.border_b_1()
|
||||
.border_color(cx.theme().colors().border_variant)
|
||||
.rounded_b_lg()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.gap_1()
|
||||
.when(status.secondary == DiffHunkSecondaryStatus::None, |el| {
|
||||
el.child(
|
||||
Button::new("unstage", "Unstage")
|
||||
.tooltip({
|
||||
let focus_handle = editor.focus_handle(cx);
|
||||
move |window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
"Unstage Hunk",
|
||||
&::git::ToggleStaged,
|
||||
&focus_handle,
|
||||
window,
|
||||
.occlude()
|
||||
.shadow_md()
|
||||
.child(if status.has_secondary_hunk() {
|
||||
Button::new(("stage", row as u64), "Stage")
|
||||
.alpha(if status.is_pending() { 0.66 } else { 1.0 })
|
||||
.tooltip({
|
||||
let focus_handle = editor.focus_handle(cx);
|
||||
move |window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
"Stage Hunk",
|
||||
&::git::ToggleStaged,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
})
|
||||
.on_click({
|
||||
let editor = editor.clone();
|
||||
move |_event, _window, cx| {
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.stage_or_unstage_diff_hunks(
|
||||
true,
|
||||
vec![hunk_range.start..hunk_range.start],
|
||||
cx,
|
||||
)
|
||||
}
|
||||
})
|
||||
.on_click({
|
||||
let editor = editor.clone();
|
||||
move |_event, window, cx| {
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.stage_or_unstage_diff_hunks(
|
||||
false,
|
||||
&[hunk_range.start..hunk_range.start],
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
});
|
||||
}
|
||||
}),
|
||||
)
|
||||
})
|
||||
.when(status.secondary != DiffHunkSecondaryStatus::None, |el| {
|
||||
el.child(
|
||||
Button::new("stage", "Stage")
|
||||
.tooltip({
|
||||
let focus_handle = editor.focus_handle(cx);
|
||||
move |window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
"Stage Hunk",
|
||||
&::git::ToggleStaged,
|
||||
&focus_handle,
|
||||
window,
|
||||
);
|
||||
});
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Button::new(("unstage", row as u64), "Unstage")
|
||||
.alpha(if status.is_pending() { 0.66 } else { 1.0 })
|
||||
.tooltip({
|
||||
let focus_handle = editor.focus_handle(cx);
|
||||
move |window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
"Unstage Hunk",
|
||||
&::git::ToggleStaged,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
})
|
||||
.on_click({
|
||||
let editor = editor.clone();
|
||||
move |_event, _window, cx| {
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.stage_or_unstage_diff_hunks(
|
||||
false,
|
||||
vec![hunk_range.start..hunk_range.start],
|
||||
cx,
|
||||
)
|
||||
}
|
||||
})
|
||||
.on_click({
|
||||
let editor = editor.clone();
|
||||
move |_event, window, cx| {
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.stage_or_unstage_diff_hunks(
|
||||
true,
|
||||
&[hunk_range.start..hunk_range.start],
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
});
|
||||
}
|
||||
}),
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
})
|
||||
})
|
||||
.child(
|
||||
Button::new("discard", "Restore")
|
||||
Button::new("restore", "Restore")
|
||||
.tooltip({
|
||||
let focus_handle = editor.focus_handle(cx);
|
||||
move |window, cx| {
|
||||
@@ -8846,7 +8905,8 @@ fn diff_hunk_controls(
|
||||
editor.restore_hunks_in_ranges(vec![point..point], window, cx);
|
||||
});
|
||||
}
|
||||
}),
|
||||
})
|
||||
.disabled(is_created_file),
|
||||
)
|
||||
.when(
|
||||
!editor.read(cx).buffer().read(cx).all_diff_hunks_expanded(),
|
||||
@@ -8875,8 +8935,13 @@ fn diff_hunk_controls(
|
||||
let snapshot = editor.snapshot(window, cx);
|
||||
let position =
|
||||
hunk_range.end.to_point(&snapshot.buffer_snapshot);
|
||||
editor
|
||||
.go_to_hunk_after_position(&snapshot, position, window, cx);
|
||||
editor.go_to_hunk_before_or_after_position(
|
||||
&snapshot,
|
||||
position,
|
||||
Direction::Next,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
editor.expand_selected_diff_hunks(cx);
|
||||
});
|
||||
}
|
||||
@@ -8892,7 +8957,7 @@ fn diff_hunk_controls(
|
||||
move |window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
"Previous Hunk",
|
||||
&GoToPrevHunk,
|
||||
&GoToPreviousHunk,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
@@ -8906,7 +8971,13 @@ fn diff_hunk_controls(
|
||||
let snapshot = editor.snapshot(window, cx);
|
||||
let point =
|
||||
hunk_range.start.to_point(&snapshot.buffer_snapshot);
|
||||
editor.go_to_hunk_before_position(&snapshot, point, window, cx);
|
||||
editor.go_to_hunk_before_or_after_position(
|
||||
&snapshot,
|
||||
point,
|
||||
Direction::Prev,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
editor.expand_selected_diff_hunks(cx);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -195,9 +195,12 @@ impl GitBlame {
|
||||
) -> impl 'a + Iterator<Item = Option<BlameEntry>> {
|
||||
self.sync(cx);
|
||||
|
||||
let buffer_id = self.buffer_snapshot.remote_id();
|
||||
let mut cursor = self.entries.cursor::<u32>(&());
|
||||
rows.into_iter().map(move |info| {
|
||||
let row = info.buffer_row?;
|
||||
let row = info
|
||||
.buffer_row
|
||||
.filter(|_| info.buffer_id == Some(buffer_id))?;
|
||||
cursor.seek_forward(&row, Bias::Right, &());
|
||||
cursor.item()?.blame.clone()
|
||||
})
|
||||
@@ -367,7 +370,6 @@ impl GitBlame {
|
||||
async move {
|
||||
let Some(Blame {
|
||||
entries,
|
||||
permalinks,
|
||||
messages,
|
||||
remote_url,
|
||||
}) = blame.await?
|
||||
@@ -376,13 +378,8 @@ impl GitBlame {
|
||||
};
|
||||
|
||||
let entries = build_blame_entry_sum_tree(entries, snapshot.max_point().row);
|
||||
let commit_details = parse_commit_messages(
|
||||
messages,
|
||||
remote_url,
|
||||
&permalinks,
|
||||
provider_registry,
|
||||
)
|
||||
.await;
|
||||
let commit_details =
|
||||
parse_commit_messages(messages, remote_url, provider_registry).await;
|
||||
|
||||
anyhow::Ok(Some((entries, commit_details)))
|
||||
}
|
||||
@@ -474,7 +471,6 @@ fn build_blame_entry_sum_tree(entries: Vec<BlameEntry>, max_row: u32) -> SumTree
|
||||
async fn parse_commit_messages(
|
||||
messages: impl IntoIterator<Item = (Oid, String)>,
|
||||
remote_url: Option<String>,
|
||||
deprecated_permalinks: &HashMap<Oid, Url>,
|
||||
provider_registry: Arc<GitHostingProviderRegistry>,
|
||||
) -> HashMap<Oid, ParsedCommitMessage> {
|
||||
let mut commit_details = HashMap::default();
|
||||
@@ -492,11 +488,7 @@ async fn parse_commit_messages(
|
||||
},
|
||||
))
|
||||
} else {
|
||||
// DEPRECATED (18 Apr 24): Sending permalinks over the wire is deprecated. Clients
|
||||
// now do the parsing. This is here for backwards compatibility, so that
|
||||
// when an old peer sends a client no `parsed_remote_url` but `deprecated_permalinks`,
|
||||
// we fall back to that.
|
||||
deprecated_permalinks.get(&oid).cloned()
|
||||
None
|
||||
};
|
||||
|
||||
let remote = parsed_remote_url
|
||||
@@ -535,6 +527,7 @@ mod tests {
|
||||
use serde_json::json;
|
||||
use settings::SettingsStore;
|
||||
use std::{cmp, env, ops::Range, path::Path};
|
||||
use text::BufferId;
|
||||
use unindent::Unindent as _;
|
||||
use util::{path, RandomCharIter};
|
||||
|
||||
@@ -552,16 +545,18 @@ mod tests {
|
||||
#[track_caller]
|
||||
fn assert_blame_rows(
|
||||
blame: &mut GitBlame,
|
||||
buffer_id: BufferId,
|
||||
rows: Range<u32>,
|
||||
expected: Vec<Option<BlameEntry>>,
|
||||
cx: &mut Context<GitBlame>,
|
||||
) {
|
||||
assert_eq!(
|
||||
pretty_assertions::assert_eq!(
|
||||
blame
|
||||
.blame_for_rows(
|
||||
&rows
|
||||
.map(|row| RowInfo {
|
||||
buffer_row: Some(row),
|
||||
buffer_id: Some(buffer_id),
|
||||
..Default::default()
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
@@ -694,6 +689,7 @@ mod tests {
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
let buffer_id = buffer.update(cx, |buffer, _| buffer.remote_id());
|
||||
|
||||
let git_blame = cx.new(|cx| GitBlame::new(buffer.clone(), project, false, true, cx));
|
||||
|
||||
@@ -701,12 +697,13 @@ mod tests {
|
||||
|
||||
git_blame.update(cx, |blame, cx| {
|
||||
// All lines
|
||||
assert_eq!(
|
||||
pretty_assertions::assert_eq!(
|
||||
blame
|
||||
.blame_for_rows(
|
||||
&(0..8)
|
||||
.map(|buffer_row| RowInfo {
|
||||
buffer_row: Some(buffer_row),
|
||||
buffer_id: Some(buffer_id),
|
||||
..Default::default()
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
@@ -725,12 +722,13 @@ mod tests {
|
||||
]
|
||||
);
|
||||
// Subset of lines
|
||||
assert_eq!(
|
||||
pretty_assertions::assert_eq!(
|
||||
blame
|
||||
.blame_for_rows(
|
||||
&(1..4)
|
||||
.map(|buffer_row| RowInfo {
|
||||
buffer_row: Some(buffer_row),
|
||||
buffer_id: Some(buffer_id),
|
||||
..Default::default()
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
@@ -744,12 +742,13 @@ mod tests {
|
||||
]
|
||||
);
|
||||
// Subset of lines, with some not displayed
|
||||
assert_eq!(
|
||||
pretty_assertions::assert_eq!(
|
||||
blame
|
||||
.blame_for_rows(
|
||||
&[
|
||||
RowInfo {
|
||||
buffer_row: Some(1),
|
||||
buffer_id: Some(buffer_id),
|
||||
..Default::default()
|
||||
},
|
||||
Default::default(),
|
||||
@@ -800,6 +799,7 @@ mod tests {
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
let buffer_id = buffer.update(cx, |buffer, _| buffer.remote_id());
|
||||
|
||||
let git_blame = cx.new(|cx| GitBlame::new(buffer.clone(), project, false, true, cx));
|
||||
|
||||
@@ -810,6 +810,7 @@ mod tests {
|
||||
// lines.
|
||||
assert_blame_rows(
|
||||
blame,
|
||||
buffer_id,
|
||||
0..4,
|
||||
vec![
|
||||
Some(blame_entry("1b1b1b", 0..4)),
|
||||
@@ -828,6 +829,7 @@ mod tests {
|
||||
git_blame.update(cx, |blame, cx| {
|
||||
assert_blame_rows(
|
||||
blame,
|
||||
buffer_id,
|
||||
0..2,
|
||||
vec![None, Some(blame_entry("1b1b1b", 0..4))],
|
||||
cx,
|
||||
@@ -840,6 +842,7 @@ mod tests {
|
||||
git_blame.update(cx, |blame, cx| {
|
||||
assert_blame_rows(
|
||||
blame,
|
||||
buffer_id,
|
||||
1..4,
|
||||
vec![
|
||||
None,
|
||||
@@ -852,7 +855,13 @@ mod tests {
|
||||
|
||||
// Before we insert a newline at the end, sanity check:
|
||||
git_blame.update(cx, |blame, cx| {
|
||||
assert_blame_rows(blame, 3..4, vec![Some(blame_entry("1b1b1b", 0..4))], cx);
|
||||
assert_blame_rows(
|
||||
blame,
|
||||
buffer_id,
|
||||
3..4,
|
||||
vec![Some(blame_entry("1b1b1b", 0..4))],
|
||||
cx,
|
||||
);
|
||||
});
|
||||
// Insert a newline at the end
|
||||
buffer.update(cx, |buffer, cx| {
|
||||
@@ -862,6 +871,7 @@ mod tests {
|
||||
git_blame.update(cx, |blame, cx| {
|
||||
assert_blame_rows(
|
||||
blame,
|
||||
buffer_id,
|
||||
3..5,
|
||||
vec![Some(blame_entry("1b1b1b", 0..4)), None],
|
||||
cx,
|
||||
@@ -870,7 +880,13 @@ mod tests {
|
||||
|
||||
// Before we insert a newline at the start, sanity check:
|
||||
git_blame.update(cx, |blame, cx| {
|
||||
assert_blame_rows(blame, 2..3, vec![Some(blame_entry("1b1b1b", 0..4))], cx);
|
||||
assert_blame_rows(
|
||||
blame,
|
||||
buffer_id,
|
||||
2..3,
|
||||
vec![Some(blame_entry("1b1b1b", 0..4))],
|
||||
cx,
|
||||
);
|
||||
});
|
||||
|
||||
// Usage example
|
||||
@@ -882,6 +898,7 @@ mod tests {
|
||||
git_blame.update(cx, |blame, cx| {
|
||||
assert_blame_rows(
|
||||
blame,
|
||||
buffer_id,
|
||||
2..4,
|
||||
vec![None, Some(blame_entry("1b1b1b", 0..4))],
|
||||
cx,
|
||||
|
||||
@@ -25,7 +25,7 @@ use theme::ThemeSettings;
|
||||
use ui::{prelude::*, theme_is_transparent, Scrollbar, ScrollbarState};
|
||||
use url::Url;
|
||||
use util::TryFutureExt;
|
||||
use workspace::Workspace;
|
||||
use workspace::{OpenOptions, OpenVisible, Workspace};
|
||||
pub const HOVER_REQUEST_DELAY_MILLIS: u64 = 200;
|
||||
|
||||
pub const MIN_POPOVER_CHARACTER_WIDTH: f32 = 20.;
|
||||
@@ -632,8 +632,15 @@ pub fn open_markdown_url(link: SharedString, window: &mut Window, cx: &mut App)
|
||||
if uri.scheme() == "file" {
|
||||
if let Some(workspace) = window.root::<Workspace>().flatten() {
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
let task =
|
||||
workspace.open_abs_path(PathBuf::from(uri.path()), false, window, cx);
|
||||
let task = workspace.open_abs_path(
|
||||
PathBuf::from(uri.path()),
|
||||
OpenOptions {
|
||||
visible: Some(OpenVisible::None),
|
||||
..Default::default()
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
|
||||
cx.spawn_in(window, |_, mut cx| async move {
|
||||
let item = task.await?;
|
||||
|
||||
@@ -581,6 +581,7 @@ impl InlayHintCache {
|
||||
self.version += 1;
|
||||
}
|
||||
self.update_tasks.clear();
|
||||
self.refresh_task = Task::ready(());
|
||||
self.hints.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -38,10 +38,14 @@ use text::{BufferId, Selection};
|
||||
use theme::{Theme, ThemeSettings};
|
||||
use ui::{prelude::*, IconDecorationKind};
|
||||
use util::{paths::PathExt, ResultExt, TryFutureExt};
|
||||
use workspace::item::{Dedup, ItemSettings, SerializableItem, TabContentParams};
|
||||
use workspace::{
|
||||
item::{BreadcrumbText, FollowEvent},
|
||||
searchable::SearchOptions,
|
||||
OpenVisible,
|
||||
};
|
||||
use workspace::{
|
||||
item::{Dedup, ItemSettings, SerializableItem, TabContentParams},
|
||||
OpenOptions,
|
||||
};
|
||||
use workspace::{
|
||||
item::{FollowableItem, Item, ItemEvent, ProjectItem},
|
||||
@@ -1160,7 +1164,15 @@ impl SerializableItem for Editor {
|
||||
}
|
||||
None => {
|
||||
let open_by_abs_path = workspace.update(cx, |workspace, cx| {
|
||||
workspace.open_abs_path(abs_path.clone(), false, window, cx)
|
||||
workspace.open_abs_path(
|
||||
abs_path.clone(),
|
||||
OpenOptions {
|
||||
visible: Some(OpenVisible::None),
|
||||
..Default::default()
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
window.spawn(cx, |mut cx| async move {
|
||||
let editor = open_by_abs_path?.await?.downcast::<Editor>().with_context(|| format!("Failed to downcast to Editor after opening abs path {abs_path:?}"))?;
|
||||
|
||||
@@ -448,7 +448,9 @@ pub fn end_of_excerpt(
|
||||
if start.row() > DisplayRow(0) {
|
||||
*start.row_mut() -= 1;
|
||||
}
|
||||
map.clip_point(start, Bias::Left)
|
||||
start = map.clip_point(start, Bias::Left);
|
||||
*start.column_mut() = 0;
|
||||
start
|
||||
}
|
||||
Direction::Next => {
|
||||
let mut end = excerpt.end_anchor().to_display_point(&map);
|
||||
|
||||
@@ -185,7 +185,7 @@ impl ProposedChangesEditor {
|
||||
} else {
|
||||
branch_buffer = location.buffer.update(cx, |buffer, cx| buffer.branch(cx));
|
||||
new_diffs.push(cx.new(|cx| {
|
||||
let mut diff = BufferDiff::new(branch_buffer.read(cx));
|
||||
let mut diff = BufferDiff::new(&branch_buffer.read(cx).snapshot(), cx);
|
||||
let _ = diff.set_base_text(
|
||||
location.buffer.clone(),
|
||||
branch_buffer.read(cx).text_snapshot(),
|
||||
|
||||
@@ -310,7 +310,7 @@ impl SignatureHelpPopover {
|
||||
.child(
|
||||
div().px_4().pb_1().child(
|
||||
StyledText::new(self.label.clone())
|
||||
.with_highlights(&self.style, self.highlights.iter().cloned()),
|
||||
.with_default_highlights(&self.style, self.highlights.iter().cloned()),
|
||||
),
|
||||
)
|
||||
.into_any_element()
|
||||
|
||||
@@ -12,7 +12,7 @@ use gpui::{
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use language::{Buffer, BufferSnapshot, LanguageRegistry};
|
||||
use multi_buffer::{ExcerptRange, MultiBufferRow};
|
||||
use multi_buffer::{Anchor, ExcerptRange, MultiBufferRow};
|
||||
use parking_lot::RwLock;
|
||||
use project::{FakeFs, Project};
|
||||
use std::{
|
||||
@@ -89,6 +89,16 @@ impl EditorTestContext {
|
||||
Path::new("/root")
|
||||
}
|
||||
|
||||
pub async fn for_editor_in(editor: Entity<Editor>, cx: &mut gpui::VisualTestContext) -> Self {
|
||||
cx.focus(&editor);
|
||||
Self {
|
||||
window: cx.windows()[0],
|
||||
cx: cx.clone(),
|
||||
editor,
|
||||
assertion_cx: AssertionContextManager::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn for_editor(editor: WindowHandle<Editor>, cx: &mut gpui::TestAppContext) -> Self {
|
||||
let editor_view = editor.root(cx).unwrap();
|
||||
Self {
|
||||
@@ -381,6 +391,85 @@ impl EditorTestContext {
|
||||
assert_state_with_diff(&self.editor, &mut self.cx, &expected_diff_text);
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn assert_excerpts_with_selections(&mut self, marked_text: &str) {
|
||||
let expected_excerpts = marked_text
|
||||
.strip_prefix("[EXCERPT]\n")
|
||||
.unwrap()
|
||||
.split("[EXCERPT]\n")
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let (multibuffer_snapshot, selections, excerpts) = self.update_editor(|editor, _, cx| {
|
||||
let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
|
||||
|
||||
let selections = editor.selections.disjoint_anchors();
|
||||
let excerpts = multibuffer_snapshot
|
||||
.excerpts()
|
||||
.map(|(e_id, snapshot, range)| (e_id, snapshot.clone(), range))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
(multibuffer_snapshot, selections, excerpts)
|
||||
});
|
||||
|
||||
assert!(
|
||||
excerpts.len() == expected_excerpts.len(),
|
||||
"should have {} excerpts, got {}",
|
||||
expected_excerpts.len(),
|
||||
excerpts.len()
|
||||
);
|
||||
|
||||
for (ix, (excerpt_id, snapshot, range)) in excerpts.into_iter().enumerate() {
|
||||
let is_folded = self
|
||||
.update_editor(|editor, _, cx| editor.is_buffer_folded(snapshot.remote_id(), cx));
|
||||
let (expected_text, expected_selections) =
|
||||
marked_text_ranges(expected_excerpts[ix], true);
|
||||
if expected_text == "[FOLDED]\n" {
|
||||
assert!(is_folded, "excerpt {} should be folded", ix);
|
||||
let is_selected = selections.iter().any(|s| s.head().excerpt_id == excerpt_id);
|
||||
if expected_selections.len() > 0 {
|
||||
assert!(
|
||||
is_selected,
|
||||
"excerpt {} should be selected. Got {:?}",
|
||||
ix,
|
||||
self.editor_state()
|
||||
);
|
||||
} else {
|
||||
assert!(!is_selected, "excerpt {} should not be selected", ix);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
assert!(!is_folded, "excerpt {} should not be folded", ix);
|
||||
assert_eq!(
|
||||
multibuffer_snapshot
|
||||
.text_for_range(Anchor::range_in_buffer(
|
||||
excerpt_id,
|
||||
snapshot.remote_id(),
|
||||
range.context.clone()
|
||||
))
|
||||
.collect::<String>(),
|
||||
expected_text
|
||||
);
|
||||
|
||||
let selections = selections
|
||||
.iter()
|
||||
.filter(|s| s.head().excerpt_id == excerpt_id)
|
||||
.map(|s| {
|
||||
let head = text::ToOffset::to_offset(&s.head().text_anchor, &snapshot)
|
||||
- text::ToOffset::to_offset(&range.context.start, &snapshot);
|
||||
let tail = text::ToOffset::to_offset(&s.head().text_anchor, &snapshot)
|
||||
- text::ToOffset::to_offset(&range.context.start, &snapshot);
|
||||
tail..head
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
// todo: selections that cross excerpt boundaries..
|
||||
assert_eq!(
|
||||
selections, expected_selections,
|
||||
"excerpt {} has incorrect selections",
|
||||
ix,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Make an assertion about the editor's text and the ranges and directions
|
||||
/// of its selections using a string containing embedded range markers.
|
||||
///
|
||||
@@ -392,6 +481,17 @@ impl EditorTestContext {
|
||||
self.assert_selections(expected_selections, marked_text.to_string())
|
||||
}
|
||||
|
||||
/// Make an assertion about the editor's text and the ranges and directions
|
||||
/// of its selections using a string containing embedded range markers.
|
||||
///
|
||||
/// See the `util::test::marked_text_ranges` function for more information.
|
||||
#[track_caller]
|
||||
pub fn assert_display_state(&mut self, marked_text: &str) {
|
||||
let (expected_text, expected_selections) = marked_text_ranges(marked_text, true);
|
||||
pretty_assertions::assert_eq!(self.display_text(), expected_text, "unexpected buffer text");
|
||||
self.assert_selections(expected_selections, marked_text.to_string())
|
||||
}
|
||||
|
||||
pub fn editor_state(&mut self) -> String {
|
||||
generate_marked_text(self.buffer_text().as_str(), &self.editor_selections(), true)
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ collections.workspace = true
|
||||
env_logger.workspace = true
|
||||
feature_flags.workspace = true
|
||||
fs.workspace = true
|
||||
git.workspace = true
|
||||
gpui.workspace = true
|
||||
http_client.workspace = true
|
||||
language.workspace = true
|
||||
|
||||
@@ -5,7 +5,6 @@ use client::{Client, UserStore};
|
||||
use clock::RealSystemClock;
|
||||
use collections::BTreeMap;
|
||||
use feature_flags::FeatureFlagAppExt as _;
|
||||
use git::GitHostingProviderRegistry;
|
||||
use gpui::{AppContext as _, AsyncApp, BackgroundExecutor, Entity};
|
||||
use http_client::{HttpClient, Method};
|
||||
use language::LanguageRegistry;
|
||||
@@ -274,8 +273,7 @@ async fn run_evaluation(
|
||||
let repos_dir = Path::new(EVAL_REPOS_DIR);
|
||||
let db_path = Path::new(EVAL_DB_PATH);
|
||||
let api_key = std::env::var("OPENAI_API_KEY").unwrap();
|
||||
let git_hosting_provider_registry = Arc::new(GitHostingProviderRegistry::new());
|
||||
let fs = Arc::new(RealFs::new(git_hosting_provider_registry, None)) as Arc<dyn Fs>;
|
||||
let fs = Arc::new(RealFs::new(None)) as Arc<dyn Fs>;
|
||||
let clock = Arc::new(RealSystemClock);
|
||||
let client = cx
|
||||
.update(|cx| {
|
||||
|
||||
@@ -339,6 +339,20 @@ async fn test_themes(
|
||||
let theme_path = extension_path.join(relative_theme_path);
|
||||
let theme_family = theme::read_user_theme(&theme_path, fs.clone()).await?;
|
||||
log::info!("loaded theme family {}", theme_family.name);
|
||||
|
||||
for theme in &theme_family.themes {
|
||||
if theme
|
||||
.style
|
||||
.colors
|
||||
.deprecated_scrollbar_thumb_background
|
||||
.is_some()
|
||||
{
|
||||
bail!(
|
||||
r#"Theme "{theme_name}" is using a deprecated style property: scrollbar_thumb.background. Use `scrollbar.thumb.background` instead."#,
|
||||
theme_name = theme.name
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -168,11 +168,14 @@ pub(crate) fn suggest(buffer: Entity<Buffer>, window: &mut Window, cx: &mut Cont
|
||||
);
|
||||
|
||||
workspace.show_notification(notification_id, cx, |cx| {
|
||||
cx.new(move |_cx| {
|
||||
MessageNotification::new(format!(
|
||||
"Do you want to install the recommended '{}' extension for '{}' files?",
|
||||
extension_id, file_name_or_extension
|
||||
))
|
||||
cx.new(move |cx| {
|
||||
MessageNotification::new(
|
||||
format!(
|
||||
"Do you want to install the recommended '{}' extension for '{}' files?",
|
||||
extension_id, file_name_or_extension
|
||||
),
|
||||
cx,
|
||||
)
|
||||
.primary_message("Yes, install extension")
|
||||
.primary_icon(IconName::Check)
|
||||
.primary_icon_color(Color::Success)
|
||||
|
||||
@@ -522,7 +522,7 @@ impl ExtensionsPage {
|
||||
extension.authors.join(", ")
|
||||
))
|
||||
.size(LabelSize::Small)
|
||||
.text_ellipsis(),
|
||||
.truncate(),
|
||||
)
|
||||
.child(Label::new("<>").size(LabelSize::Small)),
|
||||
)
|
||||
@@ -534,7 +534,7 @@ impl ExtensionsPage {
|
||||
Label::new(description.clone())
|
||||
.size(LabelSize::Small)
|
||||
.color(Color::Default)
|
||||
.text_ellipsis()
|
||||
.truncate()
|
||||
}))
|
||||
.children(repository_url.map(|repository_url| {
|
||||
IconButton::new(
|
||||
@@ -665,7 +665,7 @@ impl ExtensionsPage {
|
||||
extension.manifest.authors.join(", ")
|
||||
))
|
||||
.size(LabelSize::Small)
|
||||
.text_ellipsis(),
|
||||
.truncate(),
|
||||
)
|
||||
.child(
|
||||
Label::new(format!(
|
||||
@@ -683,7 +683,7 @@ impl ExtensionsPage {
|
||||
Label::new(description.clone())
|
||||
.size(LabelSize::Small)
|
||||
.color(Color::Default)
|
||||
.text_ellipsis()
|
||||
.truncate()
|
||||
}))
|
||||
.child(
|
||||
h_flex()
|
||||
|
||||
@@ -80,11 +80,6 @@ impl FeatureFlag for PredictEditsNonEagerModeFeatureFlag {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GitUiFeatureFlag;
|
||||
impl FeatureFlag for GitUiFeatureFlag {
|
||||
const NAME: &'static str = "git-ui";
|
||||
}
|
||||
|
||||
pub struct Remoting {}
|
||||
impl FeatureFlag for Remoting {
|
||||
const NAME: &'static str = "remoting";
|
||||
|
||||
@@ -40,11 +40,11 @@ use ui::{
|
||||
};
|
||||
use util::{maybe, paths::PathWithPosition, post_inc, ResultExt};
|
||||
use workspace::{
|
||||
item::PreviewTabsSettings, notifications::NotifyResultExt, pane, ModalView, SplitDirection,
|
||||
Workspace,
|
||||
item::PreviewTabsSettings, notifications::NotifyResultExt, pane, ModalView, OpenOptions,
|
||||
OpenVisible, SplitDirection, Workspace,
|
||||
};
|
||||
|
||||
actions!(file_finder, [SelectPrev, ToggleMenu]);
|
||||
actions!(file_finder, [SelectPrevious, ToggleMenu]);
|
||||
|
||||
impl ModalView for FileFinder {
|
||||
fn on_before_dismiss(
|
||||
@@ -199,9 +199,14 @@ impl FileFinder {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_select_prev(&mut self, _: &SelectPrev, window: &mut Window, cx: &mut Context<Self>) {
|
||||
fn handle_select_prev(
|
||||
&mut self,
|
||||
_: &SelectPrevious,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.init_modifiers = Some(window.modifiers());
|
||||
window.dispatch_action(Box::new(menu::SelectPrev), cx);
|
||||
window.dispatch_action(Box::new(menu::SelectPrevious), cx);
|
||||
}
|
||||
|
||||
fn handle_toggle_menu(&mut self, _: &ToggleMenu, window: &mut Window, cx: &mut Context<Self>) {
|
||||
@@ -1232,7 +1237,10 @@ impl PickerDelegate for FileFinderDelegate {
|
||||
} else {
|
||||
workspace.open_abs_path(
|
||||
abs_path.to_path_buf(),
|
||||
false,
|
||||
OpenOptions {
|
||||
visible: Some(OpenVisible::None),
|
||||
..Default::default()
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
|
||||
@@ -3,11 +3,11 @@ use std::{assert_eq, future::IntoFuture, path::Path, time::Duration};
|
||||
use super::*;
|
||||
use editor::Editor;
|
||||
use gpui::{Entity, TestAppContext, VisualTestContext};
|
||||
use menu::{Confirm, SelectNext, SelectPrev};
|
||||
use menu::{Confirm, SelectNext, SelectPrevious};
|
||||
use project::{RemoveOptions, FS_WATCH_LATENCY};
|
||||
use serde_json::json;
|
||||
use util::path;
|
||||
use workspace::{AppState, ToggleFileFinder, Workspace};
|
||||
use workspace::{AppState, OpenOptions, ToggleFileFinder, Workspace};
|
||||
|
||||
#[ctor::ctor]
|
||||
fn init_logger() {
|
||||
@@ -951,7 +951,10 @@ async fn test_external_files_history(cx: &mut gpui::TestAppContext) {
|
||||
.update_in(cx, |workspace, window, cx| {
|
||||
workspace.open_abs_path(
|
||||
PathBuf::from(path!("/external-src/test/third.rs")),
|
||||
false,
|
||||
OpenOptions {
|
||||
visible: Some(OpenVisible::None),
|
||||
..Default::default()
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
@@ -2059,7 +2062,7 @@ async fn test_switches_between_release_norelease_modes_on_backward_nav(
|
||||
// Switch to navigating with other shortcuts
|
||||
// Don't open file on modifiers release
|
||||
cx.simulate_modifiers_change(Modifiers::control());
|
||||
cx.dispatch_action(menu::SelectPrev);
|
||||
cx.dispatch_action(menu::SelectPrevious);
|
||||
cx.simulate_modifiers_change(Modifiers::none());
|
||||
picker.update(cx, |finder, _| {
|
||||
assert_eq!(finder.delegate.matches.len(), 3);
|
||||
@@ -2071,7 +2074,7 @@ async fn test_switches_between_release_norelease_modes_on_backward_nav(
|
||||
// Back to navigation with initial shortcut
|
||||
// Open file on modifiers release
|
||||
cx.simulate_modifiers_change(Modifiers::secondary_key());
|
||||
cx.dispatch_action(SelectPrev); // <-- File Finder's SelectPrev, not menu's
|
||||
cx.dispatch_action(SelectPrevious); // <-- File Finder's SelectPrevious, not menu's
|
||||
cx.simulate_modifiers_change(Modifiers::none());
|
||||
cx.read(|cx| {
|
||||
let active_editor = workspace.read(cx).active_item_as::<Editor>(cx).unwrap();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user