diff --git a/.cargo/config.toml b/.cargo/config.toml
index 8db58d238003c29df6dbc9fa733c6d5521340103..717c5e18c8d294bacf65207bc6b8ecb7dba1b152 100644
--- a/.cargo/config.toml
+++ b/.cargo/config.toml
@@ -19,8 +19,6 @@ rustflags = [
"windows_slim_errors", # This cfg will reduce the size of `windows::core::Error` from 16 bytes to 4 bytes
"-C",
"target-feature=+crt-static", # This fixes the linking issue when compiling livekit on Windows
- "-C",
- "link-arg=-fuse-ld=lld",
]
[env]
diff --git a/.config/hakari.toml b/.config/hakari.toml
index 8ce0b77490482ab5ff2d781fb78fd86b56959a6a..e8f094e618b39138df95bbdb58e5800cd396fad5 100644
--- a/.config/hakari.toml
+++ b/.config/hakari.toml
@@ -41,5 +41,4 @@ workspace-members = [
"slash_commands_example",
"zed_snippets",
"zed_test_extension",
- "zed_toml",
]
diff --git a/.gitattributes b/.gitattributes
index 9973cfb4db9ce8e9c79e84b9861a946f2f1c2f15..57afd4ea6942bd3985fb7395101800706d7b4ae6 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,2 +1,5 @@
# Prevent GitHub from displaying comments within JSON files as errors.
*.json linguist-language=JSON-with-Comments
+
+# Ensure the WSL script always has LF line endings, even on Windows
+crates/zed/resources/windows/zed.sh text eol=lf
diff --git a/.github/ISSUE_TEMPLATE/10_bug_report.yml b/.github/ISSUE_TEMPLATE/10_bug_report.yml
index e132eca1e52bc617f35fc2ec6e4e34fe3c796b11..1bf6c80e4073dafa90e736f995053c570f0ba2da 100644
--- a/.github/ISSUE_TEMPLATE/10_bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/10_bug_report.yml
@@ -14,7 +14,7 @@ body:
### Description
diff --git a/.github/actionlint.yml b/.github/actionlint.yml
index 0ee6af8a1d38e005f66b79f6c548d9f79396ea35..6d8e0107e9b42e71bb7266c0629393b9057e05bc 100644
--- a/.github/actionlint.yml
+++ b/.github/actionlint.yml
@@ -19,14 +19,27 @@ self-hosted-runner:
- namespace-profile-16x32-ubuntu-2004-arm
- namespace-profile-32x64-ubuntu-2004-arm
# Namespace Ubuntu 22.04 (Everything else)
- - namespace-profile-2x4-ubuntu-2204
- namespace-profile-4x8-ubuntu-2204
- namespace-profile-8x16-ubuntu-2204
- namespace-profile-16x32-ubuntu-2204
- namespace-profile-32x64-ubuntu-2204
+ # Namespace Ubuntu 24.04 (like ubuntu-latest)
+ - namespace-profile-2x4-ubuntu-2404
# Namespace Limited Preview
- namespace-profile-8x16-ubuntu-2004-arm-m4
- namespace-profile-8x32-ubuntu-2004-arm-m4
# Self Hosted Runners
- self-mini-macos
- self-32vcpu-windows-2022
+
+# Disable shellcheck because it doesn't like powershell
+# This should have been triggered with initial rollout of actionlint
+# but https://github.com/zed-industries/zed/pull/36693
+# somehow caused actionlint to actually check those windows jobs
+# where previously they were being skipped. Likely caused by an
+# unknown bug in actionlint where parsing of `runs-on: [ ]`
+# breaks something else. (yuck)
+paths:
+ .github/workflows/{ci,release_nightly}.yml:
+ ignore:
+ - "shellcheck"
diff --git a/.github/actions/run_tests_windows/action.yml b/.github/actions/run_tests_windows/action.yml
index 0a550c7d32b823db22cf205cf991020182a7d3b5..8392ca1d375856c7f649e73d2445ce4f873924b1 100644
--- a/.github/actions/run_tests_windows/action.yml
+++ b/.github/actions/run_tests_windows/action.yml
@@ -20,167 +20,8 @@ runs:
with:
node-version: "18"
- - name: Configure crash dumps
- shell: powershell
- run: |
- # Record the start time for this CI run
- $runStartTime = Get-Date
- $runStartTimeStr = $runStartTime.ToString("yyyy-MM-dd HH:mm:ss")
- Write-Host "CI run started at: $runStartTimeStr"
-
- # Save the timestamp for later use
- echo "CI_RUN_START_TIME=$($runStartTime.Ticks)" >> $env:GITHUB_ENV
-
- # Create crash dump directory in workspace (non-persistent)
- $dumpPath = "$env:GITHUB_WORKSPACE\crash_dumps"
- New-Item -ItemType Directory -Force -Path $dumpPath | Out-Null
-
- Write-Host "Setting up crash dump detection..."
- Write-Host "Workspace dump path: $dumpPath"
-
- # Note: We're NOT modifying registry on stateful runners
- # Instead, we'll check default Windows crash locations after tests
-
- name: Run tests
shell: powershell
working-directory: ${{ inputs.working-directory }}
run: |
- $env:RUST_BACKTRACE = "full"
-
- # Enable Windows debugging features
- $env:_NT_SYMBOL_PATH = "srv*https://msdl.microsoft.com/download/symbols"
-
- # .NET crash dump environment variables (ephemeral)
- $env:COMPlus_DbgEnableMiniDump = "1"
- $env:COMPlus_DbgMiniDumpType = "4"
- $env:COMPlus_CreateDumpDiagnostics = "1"
-
cargo nextest run --workspace --no-fail-fast
-
- - name: Analyze crash dumps
- if: always()
- shell: powershell
- run: |
- Write-Host "Checking for crash dumps..."
-
- # Get the CI run start time from the environment
- $runStartTime = [DateTime]::new([long]$env:CI_RUN_START_TIME)
- Write-Host "Only analyzing dumps created after: $($runStartTime.ToString('yyyy-MM-dd HH:mm:ss'))"
-
- # Check all possible crash dump locations
- $searchPaths = @(
- "$env:GITHUB_WORKSPACE\crash_dumps",
- "$env:LOCALAPPDATA\CrashDumps",
- "$env:TEMP",
- "$env:GITHUB_WORKSPACE",
- "$env:USERPROFILE\AppData\Local\CrashDumps",
- "C:\Windows\System32\config\systemprofile\AppData\Local\CrashDumps"
- )
-
- $dumps = @()
- foreach ($path in $searchPaths) {
- if (Test-Path $path) {
- Write-Host "Searching in: $path"
- $found = Get-ChildItem "$path\*.dmp" -ErrorAction SilentlyContinue | Where-Object {
- $_.CreationTime -gt $runStartTime
- }
- if ($found) {
- $dumps += $found
- Write-Host " Found $($found.Count) dump(s) from this CI run"
- }
- }
- }
-
- if ($dumps) {
- Write-Host "Found $($dumps.Count) crash dump(s)"
-
- # Install debugging tools if not present
- $cdbPath = "C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\cdb.exe"
- if (-not (Test-Path $cdbPath)) {
- Write-Host "Installing Windows Debugging Tools..."
- $url = "https://go.microsoft.com/fwlink/?linkid=2237387"
- Invoke-WebRequest -Uri $url -OutFile winsdksetup.exe
- Start-Process -Wait winsdksetup.exe -ArgumentList "/features OptionId.WindowsDesktopDebuggers /quiet"
- }
-
- foreach ($dump in $dumps) {
- Write-Host "`n=================================="
- Write-Host "Analyzing crash dump: $($dump.Name)"
- Write-Host "Size: $([math]::Round($dump.Length / 1MB, 2)) MB"
- Write-Host "Time: $($dump.CreationTime)"
- Write-Host "=================================="
-
- # Set symbol path
- $env:_NT_SYMBOL_PATH = "srv*C:\symbols*https://msdl.microsoft.com/download/symbols"
-
- # Run analysis
- $analysisOutput = & $cdbPath -z $dump.FullName -c "!analyze -v; ~*k; lm; q" 2>&1 | Out-String
-
- # Extract key information
- if ($analysisOutput -match "ExceptionCode:\s*([\w]+)") {
- Write-Host "Exception Code: $($Matches[1])"
- if ($Matches[1] -eq "c0000005") {
- Write-Host "Exception Type: ACCESS VIOLATION"
- }
- }
-
- if ($analysisOutput -match "EXCEPTION_RECORD:\s*(.+)") {
- Write-Host "Exception Record: $($Matches[1])"
- }
-
- if ($analysisOutput -match "FAULTING_IP:\s*\n(.+)") {
- Write-Host "Faulting Instruction: $($Matches[1])"
- }
-
- # Save full analysis
- $analysisFile = "$($dump.FullName).analysis.txt"
- $analysisOutput | Out-File -FilePath $analysisFile
- Write-Host "`nFull analysis saved to: $analysisFile"
-
- # Print stack trace section
- Write-Host "`n--- Stack Trace Preview ---"
- $stackSection = $analysisOutput -split "STACK_TEXT:" | Select-Object -Last 1
- $stackLines = $stackSection -split "`n" | Select-Object -First 20
- $stackLines | ForEach-Object { Write-Host $_ }
- Write-Host "--- End Stack Trace Preview ---"
- }
-
- Write-Host "`n⚠️ Crash dumps detected! Download the 'crash-dumps' artifact for detailed analysis."
-
- # Copy dumps to workspace for artifact upload
- $artifactPath = "$env:GITHUB_WORKSPACE\crash_dumps_collected"
- New-Item -ItemType Directory -Force -Path $artifactPath | Out-Null
-
- foreach ($dump in $dumps) {
- $destName = "$($dump.Directory.Name)_$($dump.Name)"
- Copy-Item $dump.FullName -Destination "$artifactPath\$destName"
- if (Test-Path "$($dump.FullName).analysis.txt") {
- Copy-Item "$($dump.FullName).analysis.txt" -Destination "$artifactPath\$destName.analysis.txt"
- }
- }
-
- Write-Host "Copied $($dumps.Count) dump(s) to artifact directory"
- } else {
- Write-Host "No crash dumps from this CI run found"
- }
-
- - name: Upload crash dumps
- if: always()
- uses: actions/upload-artifact@v4
- with:
- name: crash-dumps-${{ github.run_id }}-${{ github.run_attempt }}
- path: |
- crash_dumps_collected/*.dmp
- crash_dumps_collected/*.txt
- if-no-files-found: ignore
- retention-days: 7
-
- - name: Check test results
- shell: powershell
- working-directory: ${{ inputs.working-directory }}
- run: |
- # Re-check test results to fail the job if tests failed
- if ($LASTEXITCODE -ne 0) {
- Write-Host "Tests failed with exit code: $LASTEXITCODE"
- exit $LASTEXITCODE
- }
diff --git a/.github/workflows/bump_collab_staging.yml b/.github/workflows/bump_collab_staging.yml
index d8eaa6019ec29b5dd908564d05f430d3e7f01909..d400905b4da3304a8b916d3a38ae9d8a2855dbf5 100644
--- a/.github/workflows/bump_collab_staging.yml
+++ b/.github/workflows/bump_collab_staging.yml
@@ -8,7 +8,7 @@ on:
jobs:
update-collab-staging-tag:
if: github.repository_owner == 'zed-industries'
- runs-on: ubuntu-latest
+ runs-on: namespace-profile-2x4-ubuntu-2404
steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f4ba227168fb9cec10e1b5e23223b48e7a4ca222..d416b4af0eedf38da249e39181bd8017b57f752c 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -37,7 +37,7 @@ jobs:
run_nix: ${{ steps.filter.outputs.run_nix }}
run_actionlint: ${{ steps.filter.outputs.run_actionlint }}
runs-on:
- - ubuntu-latest
+ - namespace-profile-2x4-ubuntu-2404
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
@@ -81,6 +81,7 @@ jobs:
echo "run_license=false" >> "$GITHUB_OUTPUT"
echo "$CHANGED_FILES" | grep -qP '^(nix/|flake\.|Cargo\.|rust-toolchain.toml|\.cargo/config.toml)' && \
+ echo "$GITHUB_REF_NAME" | grep -qvP '^v[0-9]+\.[0-9]+\.[0-9x](-pre)?$' && \
echo "run_nix=true" >> "$GITHUB_OUTPUT" || \
echo "run_nix=false" >> "$GITHUB_OUTPUT"
@@ -237,7 +238,7 @@ jobs:
uses: ./.github/actions/build_docs
actionlint:
- runs-on: ubuntu-latest
+ runs-on: namespace-profile-2x4-ubuntu-2404
if: github.repository_owner == 'zed-industries' && needs.job_spec.outputs.run_actionlint == 'true'
needs: [job_spec]
steps:
@@ -418,7 +419,7 @@ jobs:
if: |
github.repository_owner == 'zed-industries' &&
needs.job_spec.outputs.run_tests == 'true'
- runs-on: [self-hosted, Windows, X64]
+ runs-on: [self-32vcpu-windows-2022]
steps:
- name: Environment Setup
run: |
@@ -458,7 +459,7 @@ jobs:
tests_pass:
name: Tests Pass
- runs-on: ubuntu-latest
+ runs-on: namespace-profile-2x4-ubuntu-2404
needs:
- job_spec
- style
@@ -784,7 +785,7 @@ jobs:
bundle-windows-x64:
timeout-minutes: 120
name: Create a Windows installer
- runs-on: [self-hosted, Windows, X64]
+ runs-on: [self-32vcpu-windows-2022]
if: 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: [windows_tests]
diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml
index 15c82643aef1e14c85daaaf2c8c3c61f62f1b3aa..3f84179278d1baaa7a299e2292b3041830d9ca60 100644
--- a/.github/workflows/danger.yml
+++ b/.github/workflows/danger.yml
@@ -12,7 +12,7 @@ on:
jobs:
danger:
if: github.repository_owner == 'zed-industries'
- runs-on: ubuntu-latest
+ runs-on: namespace-profile-2x4-ubuntu-2404
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
diff --git a/.github/workflows/release_nightly.yml b/.github/workflows/release_nightly.yml
index 0cc6737a45106713021c769b75dbbb180008dffe..2026ee7b730698cd7e40eebcd141f5b8a6ee9d04 100644
--- a/.github/workflows/release_nightly.yml
+++ b/.github/workflows/release_nightly.yml
@@ -59,7 +59,7 @@ jobs:
timeout-minutes: 60
name: Run tests on Windows
if: github.repository_owner == 'zed-industries'
- runs-on: [self-hosted, Windows, X64]
+ runs-on: [self-32vcpu-windows-2022]
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
@@ -206,9 +206,6 @@ jobs:
runs-on: github-8vcpu-ubuntu-2404
needs: tests
name: Build Zed on FreeBSD
- # env:
- # MYTOKEN : ${{ secrets.MYTOKEN }}
- # MYTOKEN2: "value2"
steps:
- uses: actions/checkout@v4
- name: Build FreeBSD remote-server
@@ -243,7 +240,6 @@ jobs:
bundle-nix:
name: Build and cache Nix package
- if: false
needs: tests
secrets: inherit
uses: ./.github/workflows/nix.yml
@@ -252,7 +248,7 @@ jobs:
timeout-minutes: 60
name: Create a Windows installer
if: github.repository_owner == 'zed-industries'
- runs-on: [self-hosted, Windows, X64]
+ runs-on: [self-32vcpu-windows-2022]
needs: windows-tests
env:
AZURE_TENANT_ID: ${{ secrets.AZURE_SIGNING_TENANT_ID }}
@@ -294,7 +290,7 @@ jobs:
update-nightly-tag:
name: Update nightly tag
if: github.repository_owner == 'zed-industries'
- runs-on: ubuntu-latest
+ runs-on: namespace-profile-2x4-ubuntu-2404
needs:
- bundle-mac
- bundle-linux-x86
diff --git a/.github/workflows/script_checks.yml b/.github/workflows/script_checks.yml
index c32a433e46a6fc5381fa1abbe19b2814fe423c1d..5dbfc9cb7fa9a51b9e0aca972d125c2a27677584 100644
--- a/.github/workflows/script_checks.yml
+++ b/.github/workflows/script_checks.yml
@@ -12,7 +12,7 @@ jobs:
shellcheck:
name: "ShellCheck Scripts"
if: github.repository_owner == 'zed-industries'
- runs-on: ubuntu-latest
+ runs-on: namespace-profile-2x4-ubuntu-2404
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
diff --git a/.rules b/.rules
index da009f1877b4c6ef2f0613995391852d4bf1dc8a..2f2b9cd705d95775bedf092bc4e6254136da6117 100644
--- a/.rules
+++ b/.rules
@@ -12,6 +12,19 @@
- Example: avoid `let _ = client.request(...).await?;` - use `client.request(...).await?;` instead
* When implementing async operations that may fail, ensure errors propagate to the UI layer so users get meaningful feedback.
* Never create files with `mod.rs` paths - prefer `src/some_module.rs` instead of `src/some_module/mod.rs`.
+* When creating new crates, prefer specifying the library root path in `Cargo.toml` using `[lib] path = "...rs"` instead of the default `lib.rs`, to maintain consistent and descriptive naming (e.g., `gpui.rs` or `main.rs`).
+* Avoid creative additions unless explicitly requested
+* Use full words for variable names (no abbreviations like "q" for "queue")
+* Use variable shadowing to scope clones in async contexts for clarity, minimizing the lifetime of borrowed references.
+ Example:
+ ```rust
+ executor.spawn({
+ let task_ran = task_ran.clone();
+ async move {
+ *task_ran.borrow_mut() = true;
+ }
+ });
+ ```
# GPUI
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 91b1b75f8292f37b122c152d71fe1e38eeccf817..1c0b1e363ed0f04ff33c070a4a84815cece78545 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -27,6 +27,22 @@ By effectively engaging with the Zed team and community early in your process, w
We plan to set aside time each week to pair program with contributors on promising pull requests in Zed. This will be an experiment. We tend to prefer pairing over async code review on our team, and we'd like to see how well it works in an open source setting. If we're finding it difficult to get on the same page with async review, we may ask you to pair with us if you're open to it. The closer a contribution is to the goals outlined in our roadmap, the more likely we'll be to spend time pairing on it.
+## Mandatory PR contents
+
+Please ensure the PR contains
+
+- Before & after screenshots, if there are visual adjustments introduced.
+
+Examples of visual adjustments: tree-sitter query updates, UI changes, etc.
+
+- A disclosure of the AI assistance usage, if any was used.
+
+Any kind of AI assistance must be disclosed in the PR, along with the extent to which AI assistance was used (e.g. docs only vs. code generation).
+
+If the PR responses are being generated by an AI, disclose that as well.
+
+As a small exception, trivial tab-completion doesn't need to be disclosed, as long as it's limited to single keywords or short phrases.
+
## Tips to improve the chances of your PR getting reviewed and merged
- Discuss your plans ahead of time with the team
@@ -49,6 +65,8 @@ If you would like to add a new icon to the Zed icon theme, [open a Discussion](h
## Bird's-eye view of Zed
+We suggest you keep the [zed glossary](docs/src/development/glossary.md) at your side when starting out. It lists and explains some of the structures and terms you will see throughout the codebase.
+
Zed is made up of several smaller crates - let's go over those you're most likely to interact with:
- [`gpui`](/crates/gpui) is a GPU-accelerated UI framework which provides all of the building blocks for Zed. **We recommend familiarizing yourself with the root level GPUI documentation.**
diff --git a/Cargo.lock b/Cargo.lock
index 4178d91c71bb04f0d8d1395bbb1e2a4b40305c29..19b84645e822d2939b3ed99366d6274f62a87f59 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -7,8 +7,8 @@ name = "acp_thread"
version = "0.1.0"
dependencies = [
"action_log",
- "agent",
"agent-client-protocol",
+ "agent_settings",
"anyhow",
"buffer_diff",
"collections",
@@ -23,13 +23,15 @@ dependencies = [
"language_model",
"markdown",
"parking_lot",
+ "portable-pty",
"project",
"prompt_store",
- "rand 0.8.5",
+ "rand 0.9.1",
"serde",
"serde_json",
"settings",
"smol",
+ "task",
"tempfile",
"terminal",
"ui",
@@ -37,6 +39,27 @@ dependencies = [
"util",
"uuid",
"watch",
+ "which 6.0.3",
+ "workspace-hack",
+]
+
+[[package]]
+name = "acp_tools"
+version = "0.1.0"
+dependencies = [
+ "agent-client-protocol",
+ "collections",
+ "gpui",
+ "language",
+ "markdown",
+ "project",
+ "serde",
+ "serde_json",
+ "settings",
+ "theme",
+ "ui",
+ "util",
+ "workspace",
"workspace-hack",
]
@@ -56,7 +79,7 @@ dependencies = [
"log",
"pretty_assertions",
"project",
- "rand 0.8.5",
+ "rand 0.9.1",
"serde_json",
"settings",
"text",
@@ -149,7 +172,7 @@ dependencies = [
"pretty_assertions",
"project",
"prompt_store",
- "rand 0.8.5",
+ "rand 0.9.1",
"ref-cast",
"rope",
"schemars",
@@ -167,16 +190,18 @@ dependencies = [
"uuid",
"workspace",
"workspace-hack",
+ "zed_env_vars",
"zstd",
]
[[package]]
name = "agent-client-protocol"
-version = "0.0.26"
+version = "0.2.0-alpha.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "160971bb53ca0b2e70ebc857c21e24eb448745f1396371015f4c59e9a9e51ed0"
+checksum = "6d02292efd75080932b6466471d428c70e2ac06908ae24792fc7c36ecbaf67ca"
dependencies = [
"anyhow",
+ "async-broadcast",
"futures 0.3.31",
"log",
"parking_lot",
@@ -206,6 +231,7 @@ dependencies = [
"collections",
"context_server",
"ctor",
+ "db",
"editor",
"env_logger 0.11.8",
"fs",
@@ -226,7 +252,6 @@ dependencies = [
"open",
"parking_lot",
"paths",
- "portable-pty",
"pretty_assertions",
"project",
"prompt_store",
@@ -239,10 +264,12 @@ dependencies = [
"smol",
"sqlez",
"task",
+ "telemetry",
"tempfile",
"terminal",
"text",
"theme",
+ "thiserror 2.0.12",
"tree-sitter-rust",
"ui",
"unindent",
@@ -250,9 +277,9 @@ dependencies = [
"uuid",
"watch",
"web_search",
- "which 6.0.3",
"workspace-hack",
"worktree",
+ "zed_env_vars",
"zlog",
"zstd",
]
@@ -262,40 +289,37 @@ name = "agent_servers"
version = "0.1.0"
dependencies = [
"acp_thread",
+ "acp_tools",
"action_log",
"agent-client-protocol",
"agent_settings",
- "agentic-coding-protocol",
"anyhow",
+ "client",
"collections",
- "context_server",
"env_logger 0.11.8",
+ "fs",
"futures 0.3.31",
"gpui",
+ "gpui_tokio",
"indoc",
- "itertools 0.14.0",
"language",
"language_model",
"language_models",
"libc",
"log",
"nix 0.29.0",
- "paths",
"project",
- "rand 0.8.5",
- "schemars",
+ "reqwest_client",
"serde",
"serde_json",
"settings",
"smol",
- "strum 0.27.1",
+ "task",
"tempfile",
"thiserror 2.0.12",
"ui",
"util",
- "uuid",
"watch",
- "which 6.0.3",
"workspace-hack",
]
@@ -375,11 +399,12 @@ dependencies = [
"parking_lot",
"paths",
"picker",
+ "postage",
"pretty_assertions",
"project",
"prompt_store",
"proto",
- "rand 0.8.5",
+ "rand 0.9.1",
"release_channel",
"rope",
"rules_library",
@@ -389,6 +414,7 @@ dependencies = [
"serde_json",
"serde_json_lenient",
"settings",
+ "shlex",
"smol",
"streaming_diff",
"task",
@@ -414,24 +440,6 @@ dependencies = [
"zed_actions",
]
-[[package]]
-name = "agentic-coding-protocol"
-version = "0.0.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3e6ae951b36fa2f8d9dd6e1af6da2fcaba13d7c866cf6a9e65deda9dc6c5fe4"
-dependencies = [
- "anyhow",
- "chrono",
- "derive_more 2.0.1",
- "futures 0.3.31",
- "log",
- "parking_lot",
- "schemars",
- "semver",
- "serde",
- "serde_json",
-]
-
[[package]]
name = "ahash"
version = "0.7.8"
@@ -498,7 +506,7 @@ dependencies = [
"parking_lot",
"piper",
"polling",
- "regex-automata 0.4.9",
+ "regex-automata",
"rustix-openpty",
"serde",
"signal-hook",
@@ -822,7 +830,7 @@ dependencies = [
"project",
"prompt_store",
"proto",
- "rand 0.8.5",
+ "rand 0.9.1",
"regex",
"rpc",
"serde",
@@ -838,6 +846,7 @@ dependencies = [
"uuid",
"workspace",
"workspace-hack",
+ "zed_env_vars",
]
[[package]]
@@ -847,7 +856,7 @@ dependencies = [
"anyhow",
"async-trait",
"collections",
- "derive_more 0.99.19",
+ "derive_more",
"extension",
"futures 0.3.31",
"gpui",
@@ -910,7 +919,7 @@ dependencies = [
"clock",
"collections",
"ctor",
- "derive_more 0.99.19",
+ "derive_more",
"gpui",
"icons",
"indoc",
@@ -920,7 +929,7 @@ dependencies = [
"parking_lot",
"pretty_assertions",
"project",
- "rand 0.8.5",
+ "rand 0.9.1",
"regex",
"serde",
"serde_json",
@@ -947,7 +956,7 @@ dependencies = [
"cloud_llm_client",
"collections",
"component",
- "derive_more 0.99.19",
+ "derive_more",
"diffy",
"editor",
"feature_flags",
@@ -972,7 +981,7 @@ dependencies = [
"pretty_assertions",
"project",
"prompt_store",
- "rand 0.8.5",
+ "rand 0.9.1",
"regex",
"reqwest_client",
"rust-embed",
@@ -1373,10 +1382,11 @@ version = "0.1.0"
dependencies = [
"anyhow",
"collections",
- "derive_more 0.99.19",
"gpui",
- "parking_lot",
"rodio",
+ "schemars",
+ "serde",
+ "settings",
"util",
"workspace-hack",
]
@@ -2277,7 +2287,7 @@ dependencies = [
[[package]]
name = "blade-graphics"
version = "0.6.0"
-source = "git+https://github.com/kvark/blade?rev=e0ec4e720957edd51b945b64dd85605ea54bcfe5#e0ec4e720957edd51b945b64dd85605ea54bcfe5"
+source = "git+https://github.com/kvark/blade?rev=bfa594ea697d4b6326ea29f747525c85ecf933b9#bfa594ea697d4b6326ea29f747525c85ecf933b9"
dependencies = [
"ash",
"ash-window",
@@ -2310,7 +2320,7 @@ dependencies = [
[[package]]
name = "blade-macros"
version = "0.3.0"
-source = "git+https://github.com/kvark/blade?rev=e0ec4e720957edd51b945b64dd85605ea54bcfe5#e0ec4e720957edd51b945b64dd85605ea54bcfe5"
+source = "git+https://github.com/kvark/blade?rev=bfa594ea697d4b6326ea29f747525c85ecf933b9#bfa594ea697d4b6326ea29f747525c85ecf933b9"
dependencies = [
"proc-macro2",
"quote",
@@ -2320,7 +2330,7 @@ dependencies = [
[[package]]
name = "blade-util"
version = "0.2.0"
-source = "git+https://github.com/kvark/blade?rev=e0ec4e720957edd51b945b64dd85605ea54bcfe5#e0ec4e720957edd51b945b64dd85605ea54bcfe5"
+source = "git+https://github.com/kvark/blade?rev=bfa594ea697d4b6326ea29f747525c85ecf933b9#bfa594ea697d4b6326ea29f747525c85ecf933b9"
dependencies = [
"blade-graphics",
"bytemuck",
@@ -2337,19 +2347,6 @@ dependencies = [
"digest",
]
-[[package]]
-name = "blake3"
-version = "1.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0"
-dependencies = [
- "arrayref",
- "arrayvec",
- "cc",
- "cfg-if",
- "constant_time_eq 0.3.1",
-]
-
[[package]]
name = "block"
version = "0.1.6"
@@ -2447,7 +2444,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
dependencies = [
"memchr",
- "regex-automata 0.4.9",
+ "regex-automata",
"serde",
]
@@ -2464,7 +2461,7 @@ dependencies = [
"language",
"log",
"pretty_assertions",
- "rand 0.8.5",
+ "rand 0.9.1",
"rope",
"serde_json",
"sum_tree",
@@ -2885,7 +2882,7 @@ dependencies = [
"language",
"log",
"postage",
- "rand 0.8.5",
+ "rand 0.9.1",
"release_channel",
"rpc",
"settings",
@@ -3056,10 +3053,9 @@ dependencies = [
"clock",
"cloud_api_client",
"cloud_llm_client",
- "cocoa 0.26.0",
"collections",
"credentials_provider",
- "derive_more 0.99.19",
+ "derive_more",
"feature_flags",
"fs",
"futures 0.3.31",
@@ -3069,10 +3065,11 @@ dependencies = [
"http_client_tls",
"httparse",
"log",
+ "objc2-foundation",
"parking_lot",
"paths",
"postage",
- "rand 0.8.5",
+ "rand 0.9.1",
"regex",
"release_channel",
"rpc",
@@ -3321,7 +3318,7 @@ dependencies = [
"prometheus",
"prompt_store",
"prost 0.9.0",
- "rand 0.8.5",
+ "rand 0.9.1",
"recent_projects",
"release_channel",
"remote",
@@ -3491,7 +3488,7 @@ name = "command_palette_hooks"
version = "0.1.0"
dependencies = [
"collections",
- "derive_more 0.99.19",
+ "derive_more",
"gpui",
"workspace-hack",
]
@@ -3501,6 +3498,7 @@ name = "component"
version = "0.1.0"
dependencies = [
"collections",
+ "documented",
"gpui",
"inventory",
"parking_lot",
@@ -3563,12 +3561,6 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
-[[package]]
-name = "constant_time_eq"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
-
[[package]]
name = "context_server"
version = "0.1.0"
@@ -3872,7 +3864,7 @@ dependencies = [
"jni",
"js-sys",
"libc",
- "mach2",
+ "mach2 0.4.2",
"ndk",
"ndk-context",
"num-derive",
@@ -4022,7 +4014,7 @@ checksum = "031ed29858d90cfdf27fe49fae28028a1f20466db97962fa2f4ea34809aeebf3"
dependencies = [
"cfg-if",
"libc",
- "mach2",
+ "mach2 0.4.2",
]
[[package]]
@@ -4034,7 +4026,7 @@ dependencies = [
"cfg-if",
"crash-context",
"libc",
- "mach2",
+ "mach2 0.4.2",
"parking_lot",
]
@@ -4042,14 +4034,17 @@ dependencies = [
name = "crashes"
version = "0.1.0"
dependencies = [
+ "bincode",
"crash-handler",
"log",
+ "mach2 0.5.0",
"minidumper",
"paths",
"release_channel",
"serde",
"serde_json",
"smol",
+ "system_specs",
"workspace-hack",
]
@@ -4475,6 +4470,7 @@ dependencies = [
"tempfile",
"util",
"workspace-hack",
+ "zed_env_vars",
]
[[package]]
@@ -4651,27 +4647,6 @@ dependencies = [
"syn 2.0.101",
]
-[[package]]
-name = "derive_more"
-version = "2.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
-dependencies = [
- "derive_more-impl",
-]
-
-[[package]]
-name = "derive_more-impl"
-version = "2.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.101",
- "unicode-xid",
-]
-
[[package]]
name = "derive_refineable"
version = "0.1.0"
@@ -4692,7 +4667,6 @@ dependencies = [
"component",
"ctor",
"editor",
- "futures 0.3.31",
"gpui",
"indoc",
"language",
@@ -4701,7 +4675,7 @@ dependencies = [
"markdown",
"pretty_assertions",
"project",
- "rand 0.8.5",
+ "rand 0.9.1",
"serde",
"serde_json",
"settings",
@@ -4741,7 +4715,7 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b545b8c50194bdd008283985ab0b31dba153cfd5b3066a92770634fbc0d7d291"
dependencies = [
- "nu-ansi-term 0.50.1",
+ "nu-ansi-term",
]
[[package]]
@@ -5073,7 +5047,7 @@ dependencies = [
"postage",
"pretty_assertions",
"project",
- "rand 0.8.5",
+ "rand 0.9.1",
"regex",
"release_channel",
"rpc",
@@ -5568,7 +5542,7 @@ dependencies = [
"parking_lot",
"paths",
"project",
- "rand 0.8.5",
+ "rand 0.9.1",
"release_channel",
"remote",
"reqwest_client",
@@ -5641,8 +5615,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "531e46835a22af56d1e3b66f04844bed63158bc094a628bec1d321d9b4c44bf2"
dependencies = [
"bit-set 0.5.3",
- "regex-automata 0.4.9",
- "regex-syntax 0.8.5",
+ "regex-automata",
+ "regex-syntax",
]
[[package]]
@@ -5652,8 +5626,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e24cb5a94bcae1e5408b0effca5cd7172ea3c5755049c5f3af4cd283a165298"
dependencies = [
"bit-set 0.8.0",
- "regex-automata 0.4.9",
- "regex-syntax 0.8.5",
+ "regex-automata",
+ "regex-syntax",
]
[[package]]
@@ -5730,14 +5704,10 @@ dependencies = [
name = "feedback"
version = "0.1.0"
dependencies = [
- "client",
"editor",
"gpui",
- "human_bytes",
"menu",
- "release_channel",
- "serde",
- "sysinfo",
+ "system_specs",
"ui",
"urlencoding",
"util",
@@ -6172,17 +6142,6 @@ dependencies = [
"futures-util",
]
-[[package]]
-name = "futures-batch"
-version = "0.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f444c45a1cb86f2a7e301469fd50a82084a60dadc25d94529a8312276ecb71a"
-dependencies = [
- "futures 0.3.31",
- "futures-timer",
- "pin-utils",
-]
-
[[package]]
name = "futures-channel"
version = "0.3.31"
@@ -6278,12 +6237,6 @@ version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
-[[package]]
-name = "futures-timer"
-version = "3.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
-
[[package]]
name = "futures-util"
version = "0.3.31"
@@ -6413,7 +6366,7 @@ dependencies = [
"askpass",
"async-trait",
"collections",
- "derive_more 0.99.19",
+ "derive_more",
"futures 0.3.31",
"git2",
"gpui",
@@ -6421,7 +6374,7 @@ dependencies = [
"log",
"parking_lot",
"pretty_assertions",
- "rand 0.8.5",
+ "rand 0.9.1",
"regex",
"rope",
"schemars",
@@ -7307,8 +7260,8 @@ dependencies = [
"aho-corasick",
"bstr",
"log",
- "regex-automata 0.4.9",
- "regex-syntax 0.8.5",
+ "regex-automata",
+ "regex-syntax",
]
[[package]]
@@ -7443,7 +7396,7 @@ dependencies = [
"core-video",
"cosmic-text",
"ctor",
- "derive_more 0.99.19",
+ "derive_more",
"embed-resource",
"env_logger 0.11.8",
"etagere",
@@ -7474,7 +7427,7 @@ dependencies = [
"pathfinder_geometry",
"postage",
"profiling",
- "rand 0.8.5",
+ "rand 0.9.1",
"raw-window-handle",
"refineable",
"reqwest_client",
@@ -7531,6 +7484,7 @@ dependencies = [
name = "gpui_tokio"
version = "0.1.0"
dependencies = [
+ "anyhow",
"gpui",
"tokio",
"util",
@@ -7967,7 +7921,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"bytes 1.10.1",
- "derive_more 0.99.19",
+ "derive_more",
"futures 0.3.31",
"http 1.3.1",
"http-body 1.0.1",
@@ -8312,7 +8266,7 @@ dependencies = [
"globset",
"log",
"memchr",
- "regex-automata 0.4.9",
+ "regex-automata",
"same-file",
"walkdir",
"winapi-util",
@@ -8479,6 +8433,7 @@ dependencies = [
"theme",
"ui",
"util",
+ "util_macros",
"workspace",
"workspace-hack",
"zed_actions",
@@ -8910,7 +8865,7 @@ dependencies = [
"percent-encoding",
"referencing",
"regex",
- "regex-syntax 0.8.5",
+ "regex-syntax",
"reqwest 0.12.15 (registry+https://github.com/rust-lang/crates.io-index)",
"serde",
"serde_json",
@@ -8963,6 +8918,44 @@ dependencies = [
"uuid",
]
+[[package]]
+name = "keymap_editor"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "collections",
+ "command_palette",
+ "component",
+ "db",
+ "editor",
+ "fs",
+ "fuzzy",
+ "gpui",
+ "itertools 0.14.0",
+ "language",
+ "log",
+ "menu",
+ "notifications",
+ "paths",
+ "project",
+ "search",
+ "serde",
+ "serde_json",
+ "settings",
+ "telemetry",
+ "tempfile",
+ "theme",
+ "tree-sitter-json",
+ "tree-sitter-rust",
+ "ui",
+ "ui_input",
+ "util",
+ "vim",
+ "workspace",
+ "workspace-hack",
+ "zed_actions",
+]
+
[[package]]
name = "khronos-egl"
version = "6.0.0"
@@ -9047,7 +9040,7 @@ dependencies = [
"parking_lot",
"postage",
"pretty_assertions",
- "rand 0.8.5",
+ "rand 0.9.1",
"regex",
"rpc",
"schemars",
@@ -9120,6 +9113,7 @@ dependencies = [
"icons",
"image",
"log",
+ "open_router",
"parking_lot",
"proto",
"schemars",
@@ -9188,6 +9182,19 @@ dependencies = [
"x_ai",
]
+[[package]]
+name = "language_onboarding"
+version = "0.1.0"
+dependencies = [
+ "db",
+ "editor",
+ "gpui",
+ "project",
+ "ui",
+ "workspace",
+ "workspace-hack",
+]
+
[[package]]
name = "language_selector"
version = "0.1.0"
@@ -9215,6 +9222,7 @@ dependencies = [
"anyhow",
"client",
"collections",
+ "command_palette_hooks",
"copilot",
"editor",
"futures 0.3.31",
@@ -9223,6 +9231,7 @@ dependencies = [
"language",
"lsp",
"project",
+ "proto",
"release_channel",
"serde_json",
"settings",
@@ -9248,7 +9257,6 @@ dependencies = [
"chrono",
"collections",
"dap",
- "feature_flags",
"futures 0.3.31",
"gpui",
"http_client",
@@ -9484,6 +9492,21 @@ dependencies = [
"vcpkg",
]
+[[package]]
+name = "line_ending_selector"
+version = "0.1.0"
+dependencies = [
+ "editor",
+ "gpui",
+ "language",
+ "picker",
+ "project",
+ "ui",
+ "util",
+ "workspace",
+ "workspace-hack",
+]
+
[[package]]
name = "link-cplusplus"
version = "1.0.10"
@@ -9615,6 +9638,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
+ "audio",
"collections",
"core-foundation 0.10.0",
"core-video",
@@ -9637,6 +9661,7 @@ dependencies = [
"scap",
"serde",
"serde_json",
+ "settings",
"sha2",
"simplelog",
"smallvec",
@@ -9709,7 +9734,7 @@ dependencies = [
"lazy_static",
"proc-macro2",
"quote",
- "regex-syntax 0.8.5",
+ "regex-syntax",
"rustc_version",
"syn 2.0.101",
]
@@ -9781,7 +9806,7 @@ dependencies = [
[[package]]
name = "lsp-types"
version = "0.95.1"
-source = "git+https://github.com/zed-industries/lsp-types?rev=39f629bdd03d59abd786ed9fc27e8bca02c0c0ec#39f629bdd03d59abd786ed9fc27e8bca02c0c0ec"
+source = "git+https://github.com/zed-industries/lsp-types?rev=0874f8742fe55b4dc94308c1e3c0069710d8eeaf#0874f8742fe55b4dc94308c1e3c0069710d8eeaf"
dependencies = [
"bitflags 1.3.2",
"serde",
@@ -9867,6 +9892,15 @@ dependencies = [
"libc",
]
+[[package]]
+name = "mach2"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a1b95cd5421ec55b445b5ae102f5ea0e768de1f82bd3001e11f426c269c3aea"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "malloc_buf"
version = "0.0.6"
@@ -9915,9 +9949,11 @@ dependencies = [
"editor",
"fs",
"gpui",
+ "html5ever 0.27.0",
"language",
"linkify",
"log",
+ "markup5ever_rcdom",
"pretty_assertions",
"pulldown-cmark 0.12.2",
"settings",
@@ -9978,11 +10014,11 @@ dependencies = [
[[package]]
name = "matchers"
-version = "0.1.0"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
+checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
dependencies = [
- "regex-automata 0.1.10",
+ "regex-automata",
]
[[package]]
@@ -10203,7 +10239,7 @@ dependencies = [
"goblin",
"libc",
"log",
- "mach2",
+ "mach2 0.4.2",
"memmap2",
"memoffset",
"minidump-common",
@@ -10346,7 +10382,7 @@ dependencies = [
"parking_lot",
"pretty_assertions",
"project",
- "rand 0.8.5",
+ "rand 0.9.1",
"rope",
"serde",
"settings",
@@ -10683,16 +10719,6 @@ dependencies = [
"winapi",
]
-[[package]]
-name = "nu-ansi-term"
-version = "0.46.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
-dependencies = [
- "overload",
- "winapi",
-]
-
[[package]]
name = "nu-ansi-term"
version = "0.50.1"
@@ -11191,6 +11217,8 @@ dependencies = [
"schemars",
"serde",
"serde_json",
+ "strum 0.27.1",
+ "thiserror 2.0.12",
"workspace-hack",
]
@@ -11386,12 +11414,6 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e"
-[[package]]
-name = "overload"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
-
[[package]]
name = "p256"
version = "0.11.1"
@@ -11614,6 +11636,12 @@ dependencies = [
"hmac",
]
+[[package]]
+name = "pciid-parser"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0008e816fcdaf229cdd540e9b6ca2dc4a10d65c31624abb546c6420a02846e61"
+
[[package]]
name = "pem"
version = "3.0.5"
@@ -12580,12 +12608,13 @@ dependencies = [
"postage",
"prettier",
"pretty_assertions",
- "rand 0.8.5",
+ "rand 0.9.1",
"regex",
"release_channel",
"remote",
"rpc",
"schemars",
+ "semver",
"serde",
"serde_json",
"settings",
@@ -12605,6 +12634,7 @@ dependencies = [
"unindent",
"url",
"util",
+ "watch",
"which 6.0.3",
"workspace-hack",
"worktree",
@@ -13376,17 +13406,8 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
- "regex-automata 0.4.9",
- "regex-syntax 0.8.5",
-]
-
-[[package]]
-name = "regex-automata"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
-dependencies = [
- "regex-syntax 0.6.29",
+ "regex-automata",
+ "regex-syntax",
]
[[package]]
@@ -13397,7 +13418,7 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
- "regex-syntax 0.8.5",
+ "regex-syntax",
]
[[package]]
@@ -13406,12 +13427,6 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a"
-[[package]]
-name = "regex-syntax"
-version = "0.6.29"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
-
[[package]]
name = "regex-syntax"
version = "0.8.5"
@@ -13514,6 +13529,7 @@ dependencies = [
"smol",
"sysinfo",
"telemetry_events",
+ "thiserror 2.0.12",
"toml 0.8.20",
"unindent",
"util",
@@ -13728,7 +13744,6 @@ dependencies = [
"regex",
"reqwest 0.12.15 (git+https://github.com/zed-industries/reqwest.git?rev=951c770a32f1998d6e999cef3e59e0013e6c4415)",
"serde",
- "smol",
"tokio",
"workspace-hack",
]
@@ -13869,7 +13884,7 @@ dependencies = [
"ctor",
"gpui",
"log",
- "rand 0.8.5",
+ "rand 0.9.1",
"rayon",
"smallvec",
"sum_tree",
@@ -13898,7 +13913,7 @@ dependencies = [
"gpui",
"parking_lot",
"proto",
- "rand 0.8.5",
+ "rand 0.9.1",
"rsa",
"serde",
"serde_json",
@@ -14333,6 +14348,19 @@ dependencies = [
"windows-sys 0.59.0",
]
+[[package]]
+name = "scheduler"
+version = "0.1.0"
+dependencies = [
+ "async-task",
+ "chrono",
+ "futures 0.3.31",
+ "parking",
+ "parking_lot",
+ "rand 0.9.1",
+ "workspace-hack",
+]
+
[[package]]
name = "schema_generator"
version = "0.1.0"
@@ -14353,12 +14381,10 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe8c9d1c68d67dd9f97ecbc6f932b60eb289c5dbddd8aa1405484a8fd2fcd984"
dependencies = [
- "chrono",
"dyn-clone",
"indexmap",
"ref-cast",
"schemars_derive",
- "semver",
"serde",
"serde_json",
]
@@ -14637,49 +14663,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749"
-[[package]]
-name = "semantic_index"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "arrayvec",
- "blake3",
- "client",
- "clock",
- "collections",
- "feature_flags",
- "fs",
- "futures 0.3.31",
- "futures-batch",
- "gpui",
- "heed",
- "http_client",
- "language",
- "language_model",
- "languages",
- "log",
- "open_ai",
- "parking_lot",
- "project",
- "reqwest_client",
- "serde",
- "serde_json",
- "settings",
- "sha2",
- "smol",
- "streaming-iterator",
- "tempfile",
- "theme",
- "tree-sitter",
- "ui",
- "unindent",
- "util",
- "workspace",
- "workspace-hack",
- "worktree",
- "zlog",
-]
-
[[package]]
name = "semantic_version"
version = "0.1.0"
@@ -14851,6 +14834,8 @@ dependencies = [
"serde_derive",
"serde_json",
"serde_json_lenient",
+ "serde_path_to_error",
+ "settings_ui_macros",
"smallvec",
"tree-sitter",
"tree-sitter-json",
@@ -14886,39 +14871,30 @@ name = "settings_ui"
version = "0.1.0"
dependencies = [
"anyhow",
- "collections",
- "command_palette",
"command_palette_hooks",
- "component",
- "db",
+ "debugger_ui",
"editor",
"feature_flags",
- "fs",
- "fuzzy",
"gpui",
- "itertools 0.14.0",
- "language",
- "log",
- "menu",
- "notifications",
- "paths",
- "project",
- "search",
"serde",
"serde_json",
"settings",
- "telemetry",
- "tempfile",
+ "smallvec",
"theme",
- "tree-sitter-json",
- "tree-sitter-rust",
"ui",
- "ui_input",
- "util",
- "vim",
"workspace",
"workspace-hack",
- "zed_actions",
+]
+
+[[package]]
+name = "settings_ui_macros"
+version = "0.1.0"
+dependencies = [
+ "heck 0.5.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+ "workspace-hack",
]
[[package]]
@@ -15304,6 +15280,7 @@ dependencies = [
"futures 0.3.31",
"indoc",
"libsqlite3-sys",
+ "log",
"parking_lot",
"smol",
"sqlformat",
@@ -15641,7 +15618,7 @@ name = "streaming_diff"
version = "0.1.0"
dependencies = [
"ordered-float 2.10.1",
- "rand 0.8.5",
+ "rand 0.9.1",
"rope",
"util",
"workspace-hack",
@@ -15755,7 +15732,7 @@ dependencies = [
"arrayvec",
"ctor",
"log",
- "rand 0.8.5",
+ "rand 0.9.1",
"rayon",
"workspace-hack",
"zlog",
@@ -16134,6 +16111,21 @@ dependencies = [
"winx",
]
+[[package]]
+name = "system_specs"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "client",
+ "gpui",
+ "human_bytes",
+ "pciid-parser",
+ "release_channel",
+ "serde",
+ "sysinfo",
+ "workspace-hack",
+]
+
[[package]]
name = "tab_switcher"
version = "0.1.0"
@@ -16331,7 +16323,7 @@ dependencies = [
"futures 0.3.31",
"gpui",
"libc",
- "rand 0.8.5",
+ "rand 0.9.1",
"regex",
"release_channel",
"schemars",
@@ -16379,7 +16371,7 @@ dependencies = [
"language",
"log",
"project",
- "rand 0.8.5",
+ "rand 0.9.1",
"regex",
"schemars",
"search",
@@ -16411,7 +16403,7 @@ dependencies = [
"log",
"parking_lot",
"postage",
- "rand 0.8.5",
+ "rand 0.9.1",
"regex",
"rope",
"smallvec",
@@ -16427,7 +16419,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"collections",
- "derive_more 0.99.19",
+ "derive_more",
"fs",
"futures 0.3.31",
"gpui",
@@ -16719,6 +16711,7 @@ dependencies = [
"db",
"gpui",
"http_client",
+ "keymap_editor",
"notifications",
"pretty_assertions",
"project",
@@ -16727,7 +16720,6 @@ dependencies = [
"schemars",
"serde",
"settings",
- "settings_ui",
"smallvec",
"story",
"telemetry",
@@ -16942,10 +16934,15 @@ checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076"
name = "toolchain_selector"
version = "0.1.0"
dependencies = [
+ "anyhow",
+ "convert_case 0.8.0",
"editor",
+ "file_finder",
+ "futures 0.3.31",
"fuzzy",
"gpui",
"language",
+ "menu",
"picker",
"project",
"ui",
@@ -17096,14 +17093,14 @@ dependencies = [
[[package]]
name = "tracing-subscriber"
-version = "0.3.19"
+version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
+checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
dependencies = [
"matchers",
- "nu-ansi-term 0.46.0",
+ "nu-ansi-term",
"once_cell",
- "regex",
+ "regex-automata",
"serde",
"serde_json",
"sharded-slab",
@@ -17134,7 +17131,7 @@ checksum = "a7cf18d43cbf0bfca51f657132cc616a5097edc4424d538bae6fa60142eaf9f0"
dependencies = [
"cc",
"regex",
- "regex-syntax 0.8.5",
+ "regex-syntax",
"serde_json",
"streaming-iterator",
"tree-sitter-language",
@@ -17164,8 +17161,7 @@ dependencies = [
[[package]]
name = "tree-sitter-cpp"
version = "0.23.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df2196ea9d47b4ab4a31b9297eaa5a5d19a0b121dceb9f118f6790ad0ab94743"
+source = "git+https://github.com/tree-sitter/tree-sitter-cpp?rev=5cb9b693cfd7bfacab1d9ff4acac1a4150700609#5cb9b693cfd7bfacab1d9ff4acac1a4150700609"
dependencies = [
"cc",
"tree-sitter-language",
@@ -17769,7 +17765,7 @@ dependencies = [
"libc",
"log",
"nix 0.29.0",
- "rand 0.8.5",
+ "rand 0.9.1",
"regex",
"rust-embed",
"schemars",
@@ -17954,6 +17950,8 @@ version = "0.1.0"
dependencies = [
"anyhow",
"gpui",
+ "schemars",
+ "serde",
"settings",
"workspace-hack",
]
@@ -18293,7 +18291,7 @@ dependencies = [
"indexmap",
"libc",
"log",
- "mach2",
+ "mach2 0.4.2",
"memfd",
"object",
"once_cell",
@@ -18560,7 +18558,7 @@ dependencies = [
"futures 0.3.31",
"gpui",
"parking_lot",
- "rand 0.8.5",
+ "rand 0.9.1",
"workspace-hack",
"zlog",
]
@@ -19768,7 +19766,6 @@ dependencies = [
"any_vec",
"anyhow",
"async-recursion",
- "bincode",
"call",
"client",
"clock",
@@ -19787,6 +19784,7 @@ dependencies = [
"node_runtime",
"parking_lot",
"postage",
+ "pretty_assertions",
"project",
"remote",
"schemars",
@@ -19933,8 +19931,8 @@ dependencies = [
"rand_core 0.6.4",
"regalloc2",
"regex",
- "regex-automata 0.4.9",
- "regex-syntax 0.8.5",
+ "regex-automata",
+ "regex-syntax",
"ring",
"rust_decimal",
"rustc-hash 1.1.0",
@@ -19942,7 +19940,6 @@ dependencies = [
"rustix 1.0.7",
"rustls 0.23.26",
"rustls-webpki 0.103.1",
- "schemars",
"scopeguard",
"sea-orm",
"sea-query-binder",
@@ -20020,7 +20017,7 @@ dependencies = [
"paths",
"postage",
"pretty_assertions",
- "rand 0.8.5",
+ "rand 0.9.1",
"rpc",
"schemars",
"serde",
@@ -20117,9 +20114,9 @@ dependencies = [
[[package]]
name = "xcb"
-version = "1.5.0"
+version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1e2f212bb1a92cd8caac8051b829a6582ede155ccb60b5d5908b81b100952be"
+checksum = "f07c123b796139bfe0603e654eaf08e132e52387ba95b252c78bad3640ba37ea"
dependencies = [
"bitflags 1.3.2",
"libc",
@@ -20376,11 +20373,11 @@ dependencies = [
[[package]]
name = "zed"
-version = "0.201.0"
+version = "0.204.0"
dependencies = [
+ "acp_tools",
"activity_indicator",
"agent",
- "agent_servers",
"agent_settings",
"agent_ui",
"anyhow",
@@ -20393,6 +20390,7 @@ dependencies = [
"auto_update",
"auto_update_ui",
"backtrace",
+ "bincode",
"breadcrumbs",
"call",
"channel",
@@ -20438,14 +20436,17 @@ dependencies = [
"itertools 0.14.0",
"jj_ui",
"journal",
+ "keymap_editor",
"language",
"language_extension",
"language_model",
"language_models",
+ "language_onboarding",
"language_selector",
"language_tools",
"languages",
"libc",
+ "line_ending_selector",
"livekit_client",
"log",
"markdown",
@@ -20492,6 +20493,7 @@ dependencies = [
"supermaven",
"svg_preview",
"sysinfo",
+ "system_specs",
"tab_switcher",
"task",
"tasks_ui",
@@ -20523,6 +20525,7 @@ dependencies = [
"workspace",
"workspace-hack",
"zed_actions",
+ "zed_env_vars",
"zeta",
"zlog",
"zlog_settings",
@@ -20539,6 +20542,13 @@ dependencies = [
"workspace-hack",
]
+[[package]]
+name = "zed_env_vars"
+version = "0.1.0"
+dependencies = [
+ "workspace-hack",
+]
+
[[package]]
name = "zed_extension_api"
version = "0.1.0"
@@ -20568,7 +20578,7 @@ dependencies = [
[[package]]
name = "zed_html"
-version = "0.2.1"
+version = "0.2.2"
dependencies = [
"zed_extension_api 0.1.0",
]
@@ -20589,7 +20599,7 @@ dependencies = [
[[package]]
name = "zed_snippets"
-version = "0.0.5"
+version = "0.0.6"
dependencies = [
"serde_json",
"zed_extension_api 0.1.0",
@@ -20602,13 +20612,6 @@ dependencies = [
"zed_extension_api 0.6.0",
]
-[[package]]
-name = "zed_toml"
-version = "0.1.4"
-dependencies = [
- "zed_extension_api 0.1.0",
-]
-
[[package]]
name = "zeno"
version = "0.3.2"
@@ -20767,13 +20770,15 @@ dependencies = [
"gpui",
"http_client",
"indoc",
+ "itertools 0.14.0",
"language",
"language_model",
"log",
"menu",
+ "parking_lot",
"postage",
"project",
- "rand 0.8.5",
+ "rand 0.9.1",
"regex",
"release_channel",
"reqwest_client",
@@ -20781,6 +20786,7 @@ dependencies = [
"serde",
"serde_json",
"settings",
+ "strum 0.27.1",
"telemetry",
"telemetry_events",
"theme",
@@ -20788,7 +20794,6 @@ dependencies = [
"tree-sitter-go",
"tree-sitter-rust",
"ui",
- "unindent",
"util",
"uuid",
"workspace",
@@ -20843,7 +20848,7 @@ dependencies = [
"aes",
"byteorder",
"bzip2",
- "constant_time_eq 0.1.5",
+ "constant_time_eq",
"crc32fast",
"crossbeam-utils",
"flate2",
diff --git a/Cargo.toml b/Cargo.toml
index 46c5646c90daf90c4cee00cf7fc66b5239e54f4d..0f13835f0c87b94c5acf335a63b0067d50fff156 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,7 @@
[workspace]
resolver = "2"
members = [
+ "crates/acp_tools",
"crates/acp_thread",
"crates/action_log",
"crates/activity_indicator",
@@ -53,6 +54,8 @@ members = [
"crates/deepseek",
"crates/diagnostics",
"crates/docs_preprocessor",
+ "crates/edit_prediction",
+ "crates/edit_prediction_button",
"crates/editor",
"crates/eval",
"crates/explorer_command_injector",
@@ -81,20 +84,21 @@ members = [
"crates/http_client_tls",
"crates/icons",
"crates/image_viewer",
- "crates/edit_prediction",
- "crates/edit_prediction_button",
"crates/inspector_ui",
"crates/install_cli",
"crates/jj",
"crates/jj_ui",
"crates/journal",
+ "crates/keymap_editor",
"crates/language",
"crates/language_extension",
"crates/language_model",
"crates/language_models",
+ "crates/language_onboarding",
"crates/language_selector",
"crates/language_tools",
"crates/languages",
+ "crates/line_ending_selector",
"crates/livekit_api",
"crates/livekit_client",
"crates/lmstudio",
@@ -129,6 +133,7 @@ members = [
"crates/refineable",
"crates/refineable/derive_refineable",
"crates/release_channel",
+ "crates/scheduler",
"crates/remote",
"crates/remote_server",
"crates/repl",
@@ -139,12 +144,12 @@ members = [
"crates/rules_library",
"crates/schema_generator",
"crates/search",
- "crates/semantic_index",
"crates/semantic_version",
"crates/session",
"crates/settings",
"crates/settings_profile_selector",
"crates/settings_ui",
+ "crates/settings_ui_macros",
"crates/snippet",
"crates/snippet_provider",
"crates/snippets_ui",
@@ -157,6 +162,7 @@ members = [
"crates/supermaven",
"crates/supermaven_api",
"crates/svg_preview",
+ "crates/system_specs",
"crates/tab_switcher",
"crates/task",
"crates/tasks_ui",
@@ -189,6 +195,7 @@ members = [
"crates/x_ai",
"crates/zed",
"crates/zed_actions",
+ "crates/zed_env_vars",
"crates/zeta",
"crates/zeta_cli",
"crates/zlog",
@@ -205,7 +212,6 @@ members = [
"extensions/slash-commands-example",
"extensions/snippets",
"extensions/test-extension",
- "extensions/toml",
#
# Tooling
@@ -226,6 +232,7 @@ edition = "2024"
# Workspace member crates
#
+acp_tools = { path = "crates/acp_tools" }
acp_thread = { path = "crates/acp_thread" }
action_log = { path = "crates/action_log" }
agent = { path = "crates/agent" }
@@ -294,9 +301,7 @@ git_hosting_providers = { path = "crates/git_hosting_providers" }
git_ui = { path = "crates/git_ui" }
go_to_line = { path = "crates/go_to_line" }
google_ai = { path = "crates/google_ai" }
-gpui = { path = "crates/gpui", default-features = false, features = [
- "http_client",
-] }
+gpui = { path = "crates/gpui", default-features = false }
gpui_macros = { path = "crates/gpui_macros" }
gpui_tokio = { path = "crates/gpui_tokio" }
html_to_markdown = { path = "crates/html_to_markdown" }
@@ -311,13 +316,16 @@ install_cli = { path = "crates/install_cli" }
jj = { path = "crates/jj" }
jj_ui = { path = "crates/jj_ui" }
journal = { path = "crates/journal" }
+keymap_editor = { path = "crates/keymap_editor" }
language = { path = "crates/language" }
language_extension = { path = "crates/language_extension" }
language_model = { path = "crates/language_model" }
language_models = { path = "crates/language_models" }
+language_onboarding = { path = "crates/language_onboarding" }
language_selector = { path = "crates/language_selector" }
language_tools = { path = "crates/language_tools" }
languages = { path = "crates/languages" }
+line_ending_selector = { path = "crates/line_ending_selector" }
livekit_api = { path = "crates/livekit_api" }
livekit_client = { path = "crates/livekit_client" }
lmstudio = { path = "crates/lmstudio" }
@@ -355,6 +363,7 @@ proto = { path = "crates/proto" }
recent_projects = { path = "crates/recent_projects" }
refineable = { path = "crates/refineable" }
release_channel = { path = "crates/release_channel" }
+scheduler = { path = "crates/scheduler" }
remote = { path = "crates/remote" }
remote_server = { path = "crates/remote_server" }
repl = { path = "crates/repl" }
@@ -365,11 +374,11 @@ rope = { path = "crates/rope" }
rpc = { path = "crates/rpc" }
rules_library = { path = "crates/rules_library" }
search = { path = "crates/search" }
-semantic_index = { path = "crates/semantic_index" }
semantic_version = { path = "crates/semantic_version" }
session = { path = "crates/session" }
settings = { path = "crates/settings" }
settings_ui = { path = "crates/settings_ui" }
+settings_ui_macros = { path = "crates/settings_ui_macros" }
snippet = { path = "crates/snippet" }
snippet_provider = { path = "crates/snippet_provider" }
snippets_ui = { path = "crates/snippets_ui" }
@@ -381,6 +390,7 @@ streaming_diff = { path = "crates/streaming_diff" }
sum_tree = { path = "crates/sum_tree" }
supermaven = { path = "crates/supermaven" }
supermaven_api = { path = "crates/supermaven_api" }
+system_specs = { path = "crates/system_specs" }
tab_switcher = { path = "crates/tab_switcher" }
task = { path = "crates/task" }
tasks_ui = { path = "crates/tasks_ui" }
@@ -414,6 +424,7 @@ worktree = { path = "crates/worktree" }
x_ai = { path = "crates/x_ai" }
zed = { path = "crates/zed" }
zed_actions = { path = "crates/zed_actions" }
+zed_env_vars = { path = "crates/zed_env_vars" }
zeta = { path = "crates/zeta" }
zlog = { path = "crates/zlog" }
zlog_settings = { path = "crates/zlog_settings" }
@@ -422,8 +433,7 @@ zlog_settings = { path = "crates/zlog_settings" }
# External crates
#
-agentic-coding-protocol = "0.0.10"
-agent-client-protocol = "0.0.26"
+agent-client-protocol = { version = "0.2.0-alpha.6", features = ["unstable"]}
aho-corasick = "1.1"
alacritty_terminal = { git = "https://github.com/zed-industries/alacritty.git", branch = "add-hush-login-flag" }
any_vec = "0.14"
@@ -437,6 +447,7 @@ async-fs = "2.1"
async-pipe = { git = "https://github.com/zed-industries/async-pipe-rs", rev = "82d00a04211cf4e1236029aa03e6b6ce2a74c553" }
async-recursion = "1.0.0"
async-tar = "0.5.0"
+async-task = "4.7"
async-trait = "0.1"
async-tungstenite = "0.29.1"
async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
@@ -450,10 +461,11 @@ aws-sdk-bedrockruntime = { version = "1.80.0", features = [
aws-smithy-runtime-api = { version = "1.7.4", features = ["http-1x", "client"] }
aws-smithy-types = { version = "1.3.0", features = ["http-body-1-x"] }
base64 = "0.22"
+bincode = "1.2.1"
bitflags = "2.6.0"
-blade-graphics = { git = "https://github.com/kvark/blade", rev = "e0ec4e720957edd51b945b64dd85605ea54bcfe5" }
-blade-macros = { git = "https://github.com/kvark/blade", rev = "e0ec4e720957edd51b945b64dd85605ea54bcfe5" }
-blade-util = { git = "https://github.com/kvark/blade", rev = "e0ec4e720957edd51b945b64dd85605ea54bcfe5" }
+blade-graphics = { git = "https://github.com/kvark/blade", rev = "bfa594ea697d4b6326ea29f747525c85ecf933b9" }
+blade-macros = { git = "https://github.com/kvark/blade", rev = "bfa594ea697d4b6326ea29f747525c85ecf933b9" }
+blade-util = { git = "https://github.com/kvark/blade", rev = "bfa594ea697d4b6326ea29f747525c85ecf933b9" }
blake3 = "1.5.3"
bytes = "1.0"
cargo_metadata = "0.19"
@@ -493,6 +505,7 @@ handlebars = "4.3"
heck = "0.5"
heed = { version = "0.21.0", features = ["read-txn-no-tls"] }
hex = "0.4.3"
+human_bytes = "0.4.1"
html5ever = "0.27.0"
http = "1.1"
http-body = "1.0"
@@ -514,7 +527,8 @@ libc = "0.2"
libsqlite3-sys = { version = "0.30.1", features = ["bundled"] }
linkify = "0.10.0"
log = { version = "0.4.16", features = ["kv_unstable_serde", "serde"] }
-lsp-types = { git = "https://github.com/zed-industries/lsp-types", rev = "39f629bdd03d59abd786ed9fc27e8bca02c0c0ec" }
+lsp-types = { git = "https://github.com/zed-industries/lsp-types", rev = "0874f8742fe55b4dc94308c1e3c0069710d8eeaf" }
+mach2 = "0.5"
markup5ever_rcdom = "0.3.0"
metal = "0.29"
minidumper = "0.8"
@@ -525,12 +539,39 @@ nbformat = { git = "https://github.com/ConradIrwin/runtimed", rev = "7130c80421
nix = "0.29"
num-format = "0.4.4"
objc = "0.2"
+objc2-foundation = { version = "0.3", default-features = false, features = [
+ "NSArray",
+ "NSAttributedString",
+ "NSBundle",
+ "NSCoder",
+ "NSData",
+ "NSDate",
+ "NSDictionary",
+ "NSEnumerator",
+ "NSError",
+ "NSGeometry",
+ "NSNotification",
+ "NSNull",
+ "NSObjCRuntime",
+ "NSObject",
+ "NSProcessInfo",
+ "NSRange",
+ "NSRunLoop",
+ "NSString",
+ "NSURL",
+ "NSUndoManager",
+ "NSValue",
+ "objc2-core-foundation",
+ "std"
+] }
open = "5.0.0"
ordered-float = "2.1.1"
palette = { version = "0.7.5", default-features = false, features = ["std"] }
+parking = "2.0"
parking_lot = "0.12.1"
partial-json-fixer = "0.5.3"
parse_int = "0.9"
+pciid-parser = "0.8.0"
pathdiff = "0.2"
pet = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
pet-conda = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
@@ -549,7 +590,7 @@ prost-build = "0.9"
prost-types = "0.9"
pulldown-cmark = { version = "0.12.0", default-features = false }
quote = "1.0.9"
-rand = "0.8.5"
+rand = "0.9"
rayon = "1.8"
ref-cast = "1.0.24"
regex = "1.5"
@@ -581,6 +622,7 @@ serde_json_lenient = { version = "0.2", features = [
"preserve_order",
"raw_value",
] }
+serde_path_to_error = "0.1.17"
serde_repr = "0.1"
serde_urlencoded = "0.7"
sha2 = "0.10"
@@ -617,7 +659,7 @@ tower-http = "0.4.4"
tree-sitter = { version = "0.25.6", features = ["wasm"] }
tree-sitter-bash = "0.25.0"
tree-sitter-c = "0.23"
-tree-sitter-cpp = "0.23"
+tree-sitter-cpp = { git = "https://github.com/tree-sitter/tree-sitter-cpp", rev = "5cb9b693cfd7bfacab1d9ff4acac1a4150700609" }
tree-sitter-css = "0.23"
tree-sitter-diff = "0.1.0"
tree-sitter-elixir = "0.3"
@@ -684,6 +726,7 @@ features = [
"Win32_Graphics_Dxgi_Common",
"Win32_Graphics_Gdi",
"Win32_Graphics_Imaging",
+ "Win32_Graphics_Hlsl",
"Win32_Networking_WinSock",
"Win32_Security",
"Win32_Security_Credentials",
@@ -801,54 +844,32 @@ unexpected_cfgs = { level = "allow" }
dbg_macro = "deny"
todo = "deny"
-# Motivation: We use `vec![a..b]` a lot when dealing with ranges in text, so
-# warning on this rule produces a lot of noise.
-single_range_in_vec_init = "allow"
+# This is not a style lint, see https://github.com/rust-lang/rust-clippy/pull/15454
+# Remove when the lint gets promoted to `suspicious`.
+declare_interior_mutable_const = "deny"
-# These are all of the rules that currently have violations in the Zed
-# codebase.
+redundant_clone = "deny"
+
+# We currently do not restrict any style rules
+# as it slows down shipping code to Zed.
#
-# We'll want to drive this list down by either:
-# 1. fixing violations of the rule and begin enforcing it
-# 2. deciding we want to allow the rule permanently, at which point
-# we should codify that separately above.
+# Running ./script/clippy can take several minutes, and so it's
+# common to skip that step and let CI do it. Any unexpected failures
+# (which also take minutes to discover) thus require switching back
+# to an old branch, manual fixing, and re-pushing.
#
-# This list shouldn't be added to; it should only get shorter.
-# =============================================================================
-
-# There are a bunch of rules currently failing in the `style` group, so
-# allow all of those, for now.
+# In the future we could improve this by either making sure
+# Zed can surface clippy errors in diagnostics (in addition to the
+# rust-analyzer errors), or by having CI fix style nits automatically.
style = { level = "allow", priority = -1 }
-# Temporary list of style lints that we've fixed so far.
-comparison_to_empty = "warn"
-iter_cloned_collect = "warn"
-iter_next_slice = "warn"
-iter_nth = "warn"
-iter_nth_zero = "warn"
-iter_skip_next = "warn"
-module_inception = { level = "deny" }
-question_mark = { level = "deny" }
-redundant_closure = { level = "deny" }
-declare_interior_mutable_const = { level = "deny" }
-collapsible_if = { level = "warn"}
-collapsible_else_if = { level = "warn" }
-needless_borrow = { level = "warn"}
-needless_return = { level = "warn" }
-unnecessary_mut_passed = {level = "warn"}
-unnecessary_map_or = { level = "warn" }
-unused_unit = "warn"
-
# Individual rules that have violations in the codebase:
type_complexity = "allow"
-# We often return trait objects from `new` functions.
-new_ret_no_self = { level = "allow" }
-# We have a few `next` functions that differ in lifetimes
-# compared to Iterator::next. Yet, clippy complains about those.
-should_implement_trait = { level = "allow" }
let_underscore_future = "allow"
-# It doesn't make sense to implement `Default` unilaterally.
-new_without_default = "allow"
+
+# Motivation: We use `vec![a..b]` a lot when dealing with ranges in text, so
+# warning on this rule produces a lot of noise.
+single_range_in_vec_init = "allow"
# in Rust it can be very tedious to reduce argument count without
# running afoul of the borrow checker.
@@ -857,6 +878,9 @@ too_many_arguments = "allow"
# We often have large enum variants yet we rarely actually bother with splitting them up.
large_enum_variant = "allow"
+# Boolean expressions can be hard to read, requiring only the minimal form gets in the way
+nonminimal_bool = "allow"
+
[workspace.metadata.cargo-machete]
ignored = [
"bindgen",
diff --git a/Procfile.web b/Procfile.web
new file mode 100644
index 0000000000000000000000000000000000000000..814055514498124d1f20b1fed51f23a5809819a9
--- /dev/null
+++ b/Procfile.web
@@ -0,0 +1,2 @@
+postgrest_llm: postgrest crates/collab/postgrest_llm.conf
+website: cd ../zed.dev; npm run dev -- --port=3000
diff --git a/assets/icons/attach.svg b/assets/icons/attach.svg
new file mode 100644
index 0000000000000000000000000000000000000000..f923a3c7c8841fd358cf940d99e7371f010a6f4d
--- /dev/null
+++ b/assets/icons/attach.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/copy.svg b/assets/icons/copy.svg
index bca13f8d56a1b644051c5be2f17c0e4cc1cdb43b..aba193930bd1e93062b1e7eef3e4a0de2e7f4ab6 100644
--- a/assets/icons/copy.svg
+++ b/assets/icons/copy.svg
@@ -1 +1,4 @@
-
+
diff --git a/assets/icons/list_filter.svg b/assets/icons/list_filter.svg
new file mode 100644
index 0000000000000000000000000000000000000000..82f41f5f6832a8cb35e2703e0f8ce36d148454dd
--- /dev/null
+++ b/assets/icons/list_filter.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/menu_alt.svg b/assets/icons/menu_alt.svg
index f73102e286c51e5c52fcec40cb976a3bd6a981cf..b9cc19e22febe045ca9ccf4a7e86d69b258f875c 100644
--- a/assets/icons/menu_alt.svg
+++ b/assets/icons/menu_alt.svg
@@ -1 +1,3 @@
-
+
diff --git a/assets/icons/menu_alt_temp.svg b/assets/icons/menu_alt_temp.svg
new file mode 100644
index 0000000000000000000000000000000000000000..87add13216d9eb8c4c3d8f345ff1695e98be2d5d
--- /dev/null
+++ b/assets/icons/menu_alt_temp.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/pencil_unavailable.svg b/assets/icons/pencil_unavailable.svg
new file mode 100644
index 0000000000000000000000000000000000000000..4241d766ace9ec5873553e0c1d77b8c19f6caa79
--- /dev/null
+++ b/assets/icons/pencil_unavailable.svg
@@ -0,0 +1,6 @@
+
diff --git a/assets/icons/terminal_ghost.svg b/assets/icons/terminal_ghost.svg
new file mode 100644
index 0000000000000000000000000000000000000000..7d0d0e068e8a6f01837e860e8223690a95541769
--- /dev/null
+++ b/assets/icons/terminal_ghost.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/icons/tool_think.svg b/assets/icons/tool_think.svg
index efd5908a907b21c573ebc69fc13f5a210ab5d848..773f5e7fa7795d7bc56bba061d808418897f9287 100644
--- a/assets/icons/tool_think.svg
+++ b/assets/icons/tool_think.svg
@@ -1,3 +1,3 @@
diff --git a/assets/icons/x_circle_filled.svg b/assets/icons/x_circle_filled.svg
new file mode 100644
index 0000000000000000000000000000000000000000..52215acda8a6b7fc57820fa90f6ed405e6af637c
--- /dev/null
+++ b/assets/icons/x_circle_filled.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/zed_agent.svg b/assets/icons/zed_agent.svg
new file mode 100644
index 0000000000000000000000000000000000000000..0c80e22c51233fff40b7605d0835b463786b4e84
--- /dev/null
+++ b/assets/icons/zed_agent.svg
@@ -0,0 +1,27 @@
+
diff --git a/assets/icons/zed_assistant.svg b/assets/icons/zed_assistant.svg
index 470eb0fedeab7535287db64b601b5dfd99b6c05d..812277a100b7e6e4ad44de357fc3556b686a90a0 100644
--- a/assets/icons/zed_assistant.svg
+++ b/assets/icons/zed_assistant.svg
@@ -1,5 +1,5 @@
diff --git a/assets/images/acp_grid.svg b/assets/images/acp_grid.svg
new file mode 100644
index 0000000000000000000000000000000000000000..8ebff8e1bc87b17e536c7f97dfa2118130233258
--- /dev/null
+++ b/assets/images/acp_grid.svg
@@ -0,0 +1,1257 @@
+
diff --git a/assets/images/acp_logo.svg b/assets/images/acp_logo.svg
new file mode 100644
index 0000000000000000000000000000000000000000..efaa46707be0a893917c3fc072a14b9c7b6b0c9b
--- /dev/null
+++ b/assets/images/acp_logo.svg
@@ -0,0 +1 @@
+
diff --git a/assets/images/acp_logo_serif.svg b/assets/images/acp_logo_serif.svg
new file mode 100644
index 0000000000000000000000000000000000000000..a04d32e51c43acf358baa733f03284dbb6de1369
--- /dev/null
+++ b/assets/images/acp_logo_serif.svg
@@ -0,0 +1,46 @@
+
diff --git a/assets/keymaps/default-linux.json b/assets/keymaps/default-linux.json
index 01c0b4e9696f3ee31d599f171acd27f4c00fdf3c..ac44b3f1ae55feb11b0027efea14c6afed8cb62a 100644
--- a/assets/keymaps/default-linux.json
+++ b/assets/keymaps/default-linux.json
@@ -41,7 +41,7 @@
"shift-f11": "debugger::StepOut",
"f11": "zed::ToggleFullScreen",
"ctrl-alt-z": "edit_prediction::RateCompletions",
- "ctrl-shift-i": "edit_prediction::ToggleMenu",
+ "ctrl-alt-shift-i": "edit_prediction::ToggleMenu",
"ctrl-alt-l": "lsp_tool::ToggleMenu"
}
},
@@ -64,8 +64,8 @@
"ctrl-k": "editor::CutToEndOfLine",
"ctrl-k ctrl-q": "editor::Rewrap",
"ctrl-k q": "editor::Rewrap",
- "ctrl-backspace": "editor::DeleteToPreviousWordStart",
- "ctrl-delete": "editor::DeleteToNextWordEnd",
+ "ctrl-backspace": ["editor::DeleteToPreviousWordStart", { "ignore_newlines": false, "ignore_brackets": false }],
+ "ctrl-delete": ["editor::DeleteToNextWordEnd", { "ignore_newlines": false, "ignore_brackets": false }],
"cut": "editor::Cut",
"shift-delete": "editor::Cut",
"ctrl-x": "editor::Cut",
@@ -121,7 +121,7 @@
"alt-g m": "git::OpenModifiedFiles",
"menu": "editor::OpenContextMenu",
"shift-f10": "editor::OpenContextMenu",
- "ctrl-shift-e": "editor::ToggleEditPrediction",
+ "ctrl-alt-shift-e": "editor::ToggleEditPrediction",
"f9": "editor::ToggleBreakpoint",
"shift-f9": "editor::EditLogBreakpoint"
}
@@ -131,14 +131,14 @@
"bindings": {
"shift-enter": "editor::Newline",
"enter": "editor::Newline",
- "ctrl-enter": "editor::NewlineAbove",
- "ctrl-shift-enter": "editor::NewlineBelow",
+ "ctrl-enter": "editor::NewlineBelow",
+ "ctrl-shift-enter": "editor::NewlineAbove",
"ctrl-k ctrl-z": "editor::ToggleSoftWrap",
"ctrl-k z": "editor::ToggleSoftWrap",
"find": "buffer_search::Deploy",
"ctrl-f": "buffer_search::Deploy",
"ctrl-h": "buffer_search::DeployReplace",
- "ctrl->": "assistant::QuoteSelection",
+ "ctrl->": "agent::QuoteSelection",
"ctrl-<": "assistant::InsertIntoEditor",
"ctrl-alt-e": "editor::SelectEnclosingSymbol",
"ctrl-shift-backspace": "editor::GoToPreviousChange",
@@ -171,6 +171,7 @@
"context": "Markdown",
"bindings": {
"copy": "markdown::Copy",
+ "ctrl-insert": "markdown::Copy",
"ctrl-c": "markdown::Copy"
}
},
@@ -241,7 +242,7 @@
"ctrl-shift-i": "agent::ToggleOptionsMenu",
"ctrl-alt-shift-n": "agent::ToggleNewThreadMenu",
"shift-alt-escape": "agent::ExpandMessageEditor",
- "ctrl->": "assistant::QuoteSelection",
+ "ctrl->": "agent::QuoteSelection",
"ctrl-alt-e": "agent::RemoveAllContext",
"ctrl-shift-e": "project_panel::ToggleFocus",
"ctrl-shift-enter": "agent::ContinueThread",
@@ -259,6 +260,7 @@
"context": "AgentPanel > Markdown",
"bindings": {
"copy": "markdown::CopyAsMarkdown",
+ "ctrl-insert": "markdown::CopyAsMarkdown",
"ctrl-c": "markdown::CopyAsMarkdown"
}
},
@@ -327,7 +329,7 @@
}
},
{
- "context": "AcpThread > Editor",
+ "context": "AcpThread > Editor && !use_modifier_to_send",
"use_key_equivalents": true,
"bindings": {
"enter": "agent::Chat",
@@ -336,6 +338,16 @@
"ctrl-shift-n": "agent::RejectAll"
}
},
+ {
+ "context": "AcpThread > Editor && use_modifier_to_send",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-enter": "agent::Chat",
+ "shift-ctrl-r": "agent::OpenAgentDiff",
+ "ctrl-shift-y": "agent::KeepAll",
+ "ctrl-shift-n": "agent::RejectAll"
+ }
+ },
{
"context": "ThreadHistory",
"bindings": {
@@ -571,7 +583,7 @@
"ctrl-n": "workspace::NewFile",
"shift-new": "workspace::NewWindow",
"ctrl-shift-n": "workspace::NewWindow",
- "ctrl-`": "terminal_panel::ToggleFocus",
+ "ctrl-`": "terminal_panel::Toggle",
"f10": ["app_menu::OpenApplicationMenu", "Zed"],
"alt-1": ["workspace::ActivatePane", 0],
"alt-2": ["workspace::ActivatePane", 1],
@@ -616,6 +628,7 @@
"alt-save": "workspace::SaveAll",
"ctrl-alt-s": "workspace::SaveAll",
"ctrl-k m": "language_selector::Toggle",
+ "ctrl-k ctrl-m": "toolchain::AddToolchain",
"escape": "workspace::Unfollow",
"ctrl-k ctrl-left": "workspace::ActivatePaneLeft",
"ctrl-k ctrl-right": "workspace::ActivatePaneRight",
@@ -846,7 +859,7 @@
"ctrl-backspace": ["project_panel::Delete", { "skip_prompt": false }],
"ctrl-delete": ["project_panel::Delete", { "skip_prompt": false }],
"alt-ctrl-r": "project_panel::RevealInFileManager",
- "ctrl-shift-enter": "project_panel::OpenWithSystem",
+ "ctrl-shift-enter": "workspace::OpenWithSystem",
"alt-d": "project_panel::CompareMarkedFiles",
"shift-find": "project_panel::NewSearchInDirectory",
"ctrl-alt-shift-f": "project_panel::NewSearchInDirectory",
@@ -1016,6 +1029,13 @@
"tab": "channel_modal::ToggleMode"
}
},
+ {
+ "context": "ToolchainSelector",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-shift-a": "toolchain::AddToolchain"
+ }
+ },
{
"context": "FileFinder || (FileFinder > Picker > Editor)",
"bindings": {
@@ -1185,9 +1205,16 @@
"ctrl-1": "onboarding::ActivateBasicsPage",
"ctrl-2": "onboarding::ActivateEditingPage",
"ctrl-3": "onboarding::ActivateAISetupPage",
- "ctrl-escape": "onboarding::Finish",
- "alt-tab": "onboarding::SignIn",
+ "ctrl-enter": "onboarding::Finish",
+ "alt-shift-l": "onboarding::SignIn",
"alt-shift-a": "onboarding::OpenAccount"
}
+ },
+ {
+ "context": "InvalidBuffer",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-shift-enter": "workspace::OpenWithSystem"
+ }
}
]
diff --git a/assets/keymaps/default-macos.json b/assets/keymaps/default-macos.json
index e5b7fff9e1ce269f4f1c2f630f6bd41d790ffd21..337915527ca22f04afc8450cf6a366d1f2995551 100644
--- a/assets/keymaps/default-macos.json
+++ b/assets/keymaps/default-macos.json
@@ -70,9 +70,9 @@
"cmd-k q": "editor::Rewrap",
"cmd-backspace": "editor::DeleteToBeginningOfLine",
"cmd-delete": "editor::DeleteToEndOfLine",
- "alt-backspace": "editor::DeleteToPreviousWordStart",
- "ctrl-w": "editor::DeleteToPreviousWordStart",
- "alt-delete": "editor::DeleteToNextWordEnd",
+ "alt-backspace": ["editor::DeleteToPreviousWordStart", { "ignore_newlines": false, "ignore_brackets": false }],
+ "ctrl-w": ["editor::DeleteToPreviousWordStart", { "ignore_newlines": false, "ignore_brackets": false }],
+ "alt-delete": ["editor::DeleteToNextWordEnd", { "ignore_newlines": false, "ignore_brackets": false }],
"cmd-x": "editor::Cut",
"cmd-c": "editor::Copy",
"cmd-v": "editor::Paste",
@@ -162,7 +162,7 @@
"cmd-alt-f": "buffer_search::DeployReplace",
"cmd-alt-l": ["buffer_search::Deploy", { "selection_search_enabled": true }],
"cmd-e": ["buffer_search::Deploy", { "focus": false }],
- "cmd->": "assistant::QuoteSelection",
+ "cmd->": "agent::QuoteSelection",
"cmd-<": "assistant::InsertIntoEditor",
"cmd-alt-e": "editor::SelectEnclosingSymbol",
"alt-enter": "editor::OpenSelectionsInMultibuffer"
@@ -281,7 +281,7 @@
"cmd-shift-i": "agent::ToggleOptionsMenu",
"cmd-alt-shift-n": "agent::ToggleNewThreadMenu",
"shift-alt-escape": "agent::ExpandMessageEditor",
- "cmd->": "assistant::QuoteSelection",
+ "cmd->": "agent::QuoteSelection",
"cmd-alt-e": "agent::RemoveAllContext",
"cmd-shift-e": "project_panel::ToggleFocus",
"cmd-ctrl-b": "agent::ToggleBurnMode",
@@ -379,7 +379,7 @@
}
},
{
- "context": "AcpThread > Editor",
+ "context": "AcpThread > Editor && !use_modifier_to_send",
"use_key_equivalents": true,
"bindings": {
"enter": "agent::Chat",
@@ -388,6 +388,16 @@
"cmd-shift-n": "agent::RejectAll"
}
},
+ {
+ "context": "AcpThread > Editor && use_modifier_to_send",
+ "use_key_equivalents": true,
+ "bindings": {
+ "cmd-enter": "agent::Chat",
+ "shift-ctrl-r": "agent::OpenAgentDiff",
+ "cmd-shift-y": "agent::KeepAll",
+ "cmd-shift-n": "agent::RejectAll"
+ }
+ },
{
"context": "ThreadHistory",
"bindings": {
@@ -639,7 +649,7 @@
"alt-shift-enter": "toast::RunAction",
"cmd-shift-s": "workspace::SaveAs",
"cmd-shift-n": "workspace::NewWindow",
- "ctrl-`": "terminal_panel::ToggleFocus",
+ "ctrl-`": "terminal_panel::Toggle",
"cmd-1": ["workspace::ActivatePane", 0],
"cmd-2": ["workspace::ActivatePane", 1],
"cmd-3": ["workspace::ActivatePane", 2],
@@ -680,6 +690,7 @@
"cmd-?": "agent::ToggleFocus",
"cmd-alt-s": "workspace::SaveAll",
"cmd-k m": "language_selector::Toggle",
+ "cmd-k cmd-m": "toolchain::AddToolchain",
"escape": "workspace::Unfollow",
"cmd-k cmd-left": "workspace::ActivatePaneLeft",
"cmd-k cmd-right": "workspace::ActivatePaneRight",
@@ -905,7 +916,7 @@
"cmd-backspace": ["project_panel::Trash", { "skip_prompt": true }],
"cmd-delete": ["project_panel::Delete", { "skip_prompt": false }],
"alt-cmd-r": "project_panel::RevealInFileManager",
- "ctrl-shift-enter": "project_panel::OpenWithSystem",
+ "ctrl-shift-enter": "workspace::OpenWithSystem",
"alt-d": "project_panel::CompareMarkedFiles",
"cmd-alt-backspace": ["project_panel::Delete", { "skip_prompt": false }],
"cmd-alt-shift-f": "project_panel::NewSearchInDirectory",
@@ -1084,6 +1095,13 @@
"tab": "channel_modal::ToggleMode"
}
},
+ {
+ "context": "ToolchainSelector",
+ "use_key_equivalents": true,
+ "bindings": {
+ "cmd-shift-a": "toolchain::AddToolchain"
+ }
+ },
{
"context": "FileFinder || (FileFinder > Picker > Editor)",
"use_key_equivalents": true,
@@ -1291,5 +1309,12 @@
"alt-tab": "onboarding::SignIn",
"alt-shift-a": "onboarding::OpenAccount"
}
+ },
+ {
+ "context": "InvalidBuffer",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-shift-enter": "workspace::OpenWithSystem"
+ }
}
]
diff --git a/assets/keymaps/default-windows.json b/assets/keymaps/default-windows.json
new file mode 100644
index 0000000000000000000000000000000000000000..de0d97b52e2b0fe9bac931cb46debc812a56a70b
--- /dev/null
+++ b/assets/keymaps/default-windows.json
@@ -0,0 +1,1231 @@
+[
+ // Standard Windows bindings
+ {
+ "use_key_equivalents": true,
+ "bindings": {
+ "home": "menu::SelectFirst",
+ "shift-pageup": "menu::SelectFirst",
+ "pageup": "menu::SelectFirst",
+ "end": "menu::SelectLast",
+ "shift-pagedown": "menu::SelectLast",
+ "pagedown": "menu::SelectLast",
+ "ctrl-n": "menu::SelectNext",
+ "tab": "menu::SelectNext",
+ "down": "menu::SelectNext",
+ "ctrl-p": "menu::SelectPrevious",
+ "shift-tab": "menu::SelectPrevious",
+ "up": "menu::SelectPrevious",
+ "enter": "menu::Confirm",
+ "ctrl-enter": "menu::SecondaryConfirm",
+ "ctrl-escape": "menu::Cancel",
+ "ctrl-c": "menu::Cancel",
+ "escape": "menu::Cancel",
+ "shift-alt-enter": "menu::Restart",
+ "alt-enter": ["picker::ConfirmInput", { "secondary": false }],
+ "ctrl-alt-enter": ["picker::ConfirmInput", { "secondary": true }],
+ "ctrl-shift-w": "workspace::CloseWindow",
+ "shift-escape": "workspace::ToggleZoom",
+ "ctrl-o": "workspace::Open",
+ "ctrl-=": ["zed::IncreaseBufferFontSize", { "persist": false }],
+ "ctrl-shift-=": ["zed::IncreaseBufferFontSize", { "persist": false }],
+ "ctrl--": ["zed::DecreaseBufferFontSize", { "persist": false }],
+ "ctrl-0": ["zed::ResetBufferFontSize", { "persist": false }],
+ "ctrl-,": "zed::OpenSettings",
+ "ctrl-q": "zed::Quit",
+ "f4": "debugger::Start",
+ "shift-f5": "debugger::Stop",
+ "ctrl-shift-f5": "debugger::RerunSession",
+ "f6": "debugger::Pause",
+ "f7": "debugger::StepOver",
+ "ctrl-f11": "debugger::StepInto",
+ "shift-f11": "debugger::StepOut",
+ "f11": "zed::ToggleFullScreen",
+ "ctrl-shift-i": "edit_prediction::ToggleMenu",
+ "shift-alt-l": "lsp_tool::ToggleMenu"
+ }
+ },
+ {
+ "context": "Picker || menu",
+ "use_key_equivalents": true,
+ "bindings": {
+ "up": "menu::SelectPrevious",
+ "down": "menu::SelectNext"
+ }
+ },
+ {
+ "context": "Editor",
+ "use_key_equivalents": true,
+ "bindings": {
+ "escape": "editor::Cancel",
+ "shift-backspace": "editor::Backspace",
+ "backspace": "editor::Backspace",
+ "delete": "editor::Delete",
+ "tab": "editor::Tab",
+ "shift-tab": "editor::Backtab",
+ "ctrl-k": "editor::CutToEndOfLine",
+ "ctrl-k ctrl-q": "editor::Rewrap",
+ "ctrl-k q": "editor::Rewrap",
+ "ctrl-backspace": ["editor::DeleteToPreviousWordStart", { "ignore_newlines": false, "ignore_brackets": false }],
+ "ctrl-delete": ["editor::DeleteToNextWordEnd", { "ignore_newlines": false, "ignore_brackets": false }],
+ "shift-delete": "editor::Cut",
+ "ctrl-x": "editor::Cut",
+ "ctrl-insert": "editor::Copy",
+ "ctrl-c": "editor::Copy",
+ "shift-insert": "editor::Paste",
+ "ctrl-v": "editor::Paste",
+ "ctrl-z": "editor::Undo",
+ "ctrl-y": "editor::Redo",
+ "ctrl-shift-z": "editor::Redo",
+ "up": "editor::MoveUp",
+ "ctrl-up": "editor::LineUp",
+ "ctrl-down": "editor::LineDown",
+ "pageup": "editor::MovePageUp",
+ "alt-pageup": "editor::PageUp",
+ "shift-pageup": "editor::SelectPageUp",
+ "home": ["editor::MoveToBeginningOfLine", { "stop_at_soft_wraps": true, "stop_at_indent": true }],
+ "down": "editor::MoveDown",
+ "pagedown": "editor::MovePageDown",
+ "alt-pagedown": "editor::PageDown",
+ "shift-pagedown": "editor::SelectPageDown",
+ "end": ["editor::MoveToEndOfLine", { "stop_at_soft_wraps": true }],
+ "left": "editor::MoveLeft",
+ "right": "editor::MoveRight",
+ "ctrl-left": "editor::MoveToPreviousWordStart",
+ "ctrl-right": "editor::MoveToNextWordEnd",
+ "ctrl-home": "editor::MoveToBeginning",
+ "ctrl-end": "editor::MoveToEnd",
+ "shift-up": "editor::SelectUp",
+ "shift-down": "editor::SelectDown",
+ "shift-left": "editor::SelectLeft",
+ "shift-right": "editor::SelectRight",
+ "ctrl-shift-left": "editor::SelectToPreviousWordStart",
+ "ctrl-shift-right": "editor::SelectToNextWordEnd",
+ "ctrl-shift-home": "editor::SelectToBeginning",
+ "ctrl-shift-end": "editor::SelectToEnd",
+ "ctrl-a": "editor::SelectAll",
+ "ctrl-l": "editor::SelectLine",
+ "shift-alt-f": "editor::Format",
+ "shift-alt-o": "editor::OrganizeImports",
+ "shift-home": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true, "stop_at_indent": true }],
+ "shift-end": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": true }],
+ "ctrl-alt-space": "editor::ShowCharacterPalette",
+ "ctrl-;": "editor::ToggleLineNumbers",
+ "ctrl-'": "editor::ToggleSelectedDiffHunks",
+ "ctrl-\"": "editor::ExpandAllDiffHunks",
+ "ctrl-i": "editor::ShowSignatureHelp",
+ "alt-g b": "git::Blame",
+ "alt-g m": "git::OpenModifiedFiles",
+ "menu": "editor::OpenContextMenu",
+ "shift-f10": "editor::OpenContextMenu",
+ "ctrl-shift-e": "editor::ToggleEditPrediction",
+ "f9": "editor::ToggleBreakpoint",
+ "shift-f9": "editor::EditLogBreakpoint"
+ }
+ },
+ {
+ "context": "Editor && mode == full",
+ "use_key_equivalents": true,
+ "bindings": {
+ "shift-enter": "editor::Newline",
+ "enter": "editor::Newline",
+ "ctrl-enter": "editor::NewlineBelow",
+ "ctrl-shift-enter": "editor::NewlineAbove",
+ "ctrl-k ctrl-z": "editor::ToggleSoftWrap",
+ "ctrl-k z": "editor::ToggleSoftWrap",
+ "ctrl-f": "buffer_search::Deploy",
+ "ctrl-h": "buffer_search::DeployReplace",
+ "ctrl-shift-.": "assistant::QuoteSelection",
+ "ctrl-shift-,": "assistant::InsertIntoEditor",
+ "shift-alt-e": "editor::SelectEnclosingSymbol",
+ "ctrl-shift-backspace": "editor::GoToPreviousChange",
+ "ctrl-shift-alt-backspace": "editor::GoToNextChange",
+ "alt-enter": "editor::OpenSelectionsInMultibuffer"
+ }
+ },
+ {
+ "context": "Editor && mode == full && edit_prediction",
+ "use_key_equivalents": true,
+ "bindings": {
+ "alt-]": "editor::NextEditPrediction",
+ "alt-[": "editor::PreviousEditPrediction"
+ }
+ },
+ {
+ "context": "Editor && !edit_prediction",
+ "use_key_equivalents": true,
+ "bindings": {
+ "alt-\\": "editor::ShowEditPrediction"
+ }
+ },
+ {
+ "context": "Editor && mode == auto_height",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-enter": "editor::Newline",
+ "shift-enter": "editor::Newline",
+ "ctrl-shift-enter": "editor::NewlineBelow"
+ }
+ },
+ {
+ "context": "Markdown",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-c": "markdown::Copy"
+ }
+ },
+ {
+ "context": "Editor && jupyter && !ContextEditor",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-shift-enter": "repl::Run",
+ "ctrl-alt-enter": "repl::RunInPlace"
+ }
+ },
+ {
+ "context": "Editor && !agent_diff",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-k ctrl-r": "git::Restore",
+ "alt-y": "git::StageAndNext",
+ "shift-alt-y": "git::UnstageAndNext"
+ }
+ },
+ {
+ "context": "Editor && editor_agent_diff",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-y": "agent::Keep",
+ "ctrl-n": "agent::Reject",
+ "ctrl-shift-y": "agent::KeepAll",
+ "ctrl-shift-n": "agent::RejectAll",
+ "ctrl-shift-r": "agent::OpenAgentDiff"
+ }
+ },
+ {
+ "context": "AgentDiff",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-y": "agent::Keep",
+ "ctrl-n": "agent::Reject",
+ "ctrl-shift-y": "agent::KeepAll",
+ "ctrl-shift-n": "agent::RejectAll"
+ }
+ },
+ {
+ "context": "ContextEditor > Editor",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-enter": "assistant::Assist",
+ "ctrl-s": "workspace::Save",
+ "ctrl-shift-,": "assistant::InsertIntoEditor",
+ "shift-enter": "assistant::Split",
+ "ctrl-r": "assistant::CycleMessageRole",
+ "enter": "assistant::ConfirmCommand",
+ "alt-enter": "editor::Newline",
+ "ctrl-k c": "assistant::CopyCode",
+ "ctrl-g": "search::SelectNextMatch",
+ "ctrl-shift-g": "search::SelectPreviousMatch",
+ "ctrl-k l": "agent::OpenRulesLibrary"
+ }
+ },
+ {
+ "context": "AgentPanel",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-n": "agent::NewThread",
+ "shift-alt-n": "agent::NewTextThread",
+ "ctrl-shift-h": "agent::OpenHistory",
+ "shift-alt-c": "agent::OpenSettings",
+ "shift-alt-p": "agent::OpenRulesLibrary",
+ "ctrl-i": "agent::ToggleProfileSelector",
+ "shift-alt-/": "agent::ToggleModelSelector",
+ "ctrl-shift-a": "agent::ToggleContextPicker",
+ "ctrl-shift-j": "agent::ToggleNavigationMenu",
+ "ctrl-shift-i": "agent::ToggleOptionsMenu",
+ // "ctrl-shift-alt-n": "agent::ToggleNewThreadMenu",
+ "shift-alt-escape": "agent::ExpandMessageEditor",
+ "ctrl-shift-.": "assistant::QuoteSelection",
+ "shift-alt-e": "agent::RemoveAllContext",
+ "ctrl-shift-e": "project_panel::ToggleFocus",
+ "ctrl-shift-enter": "agent::ContinueThread",
+ "super-ctrl-b": "agent::ToggleBurnMode",
+ "alt-enter": "agent::ContinueWithBurnMode"
+ }
+ },
+ {
+ "context": "AgentPanel > NavigationMenu",
+ "use_key_equivalents": true,
+ "bindings": {
+ "shift-backspace": "agent::DeleteRecentlyOpenThread"
+ }
+ },
+ {
+ "context": "AgentPanel > Markdown",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-c": "markdown::CopyAsMarkdown"
+ }
+ },
+ {
+ "context": "AgentPanel && prompt_editor",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-n": "agent::NewTextThread",
+ "ctrl-alt-t": "agent::NewThread"
+ }
+ },
+ {
+ "context": "AgentPanel && external_agent_thread",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-n": "agent::NewExternalAgentThread",
+ "ctrl-alt-t": "agent::NewThread"
+ }
+ },
+ {
+ "context": "MessageEditor && !Picker > Editor && !use_modifier_to_send",
+ "use_key_equivalents": true,
+ "bindings": {
+ "enter": "agent::Chat",
+ "ctrl-enter": "agent::ChatWithFollow",
+ "ctrl-i": "agent::ToggleProfileSelector",
+ "ctrl-shift-r": "agent::OpenAgentDiff",
+ "ctrl-shift-y": "agent::KeepAll",
+ "ctrl-shift-n": "agent::RejectAll"
+ }
+ },
+ {
+ "context": "MessageEditor && !Picker > Editor && use_modifier_to_send",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-enter": "agent::Chat",
+ "enter": "editor::Newline",
+ "ctrl-i": "agent::ToggleProfileSelector",
+ "ctrl-shift-r": "agent::OpenAgentDiff",
+ "ctrl-shift-y": "agent::KeepAll",
+ "ctrl-shift-n": "agent::RejectAll"
+ }
+ },
+ {
+ "context": "EditMessageEditor > Editor",
+ "use_key_equivalents": true,
+ "bindings": {
+ "escape": "menu::Cancel",
+ "enter": "menu::Confirm",
+ "alt-enter": "editor::Newline"
+ }
+ },
+ {
+ "context": "AgentFeedbackMessageEditor > Editor",
+ "use_key_equivalents": true,
+ "bindings": {
+ "escape": "menu::Cancel",
+ "enter": "menu::Confirm",
+ "alt-enter": "editor::Newline"
+ }
+ },
+ {
+ "context": "ContextStrip",
+ "use_key_equivalents": true,
+ "bindings": {
+ "up": "agent::FocusUp",
+ "right": "agent::FocusRight",
+ "left": "agent::FocusLeft",
+ "down": "agent::FocusDown",
+ "backspace": "agent::RemoveFocusedContext",
+ "enter": "agent::AcceptSuggestedContext"
+ }
+ },
+ {
+ "context": "AcpThread > Editor",
+ "use_key_equivalents": true,
+ "bindings": {
+ "enter": "agent::Chat",
+ "ctrl-shift-r": "agent::OpenAgentDiff",
+ "ctrl-shift-y": "agent::KeepAll",
+ "ctrl-shift-n": "agent::RejectAll"
+ }
+ },
+ {
+ "context": "ThreadHistory",
+ "use_key_equivalents": true,
+ "bindings": {
+ "backspace": "agent::RemoveSelectedThread"
+ }
+ },
+ {
+ "context": "PromptLibrary",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-n": "rules_library::NewRule",
+ "ctrl-shift-s": "rules_library::ToggleDefaultRule"
+ }
+ },
+ {
+ "context": "BufferSearchBar",
+ "use_key_equivalents": true,
+ "bindings": {
+ "escape": "buffer_search::Dismiss",
+ "tab": "buffer_search::FocusEditor",
+ "enter": "search::SelectNextMatch",
+ "shift-enter": "search::SelectPreviousMatch",
+ "alt-enter": "search::SelectAllMatches",
+ "ctrl-f": "search::FocusSearch",
+ "ctrl-h": "search::ToggleReplace",
+ "ctrl-l": "search::ToggleSelection"
+ }
+ },
+ {
+ "context": "BufferSearchBar && in_replace > Editor",
+ "use_key_equivalents": true,
+ "bindings": {
+ "enter": "search::ReplaceNext",
+ "ctrl-enter": "search::ReplaceAll"
+ }
+ },
+ {
+ "context": "BufferSearchBar && !in_replace > Editor",
+ "use_key_equivalents": true,
+ "bindings": {
+ "up": "search::PreviousHistoryQuery",
+ "down": "search::NextHistoryQuery"
+ }
+ },
+ {
+ "context": "ProjectSearchBar",
+ "use_key_equivalents": true,
+ "bindings": {
+ "escape": "project_search::ToggleFocus",
+ "ctrl-shift-f": "search::FocusSearch",
+ "ctrl-shift-h": "search::ToggleReplace",
+ "alt-r": "search::ToggleRegex" // vscode
+ }
+ },
+ {
+ "context": "ProjectSearchBar > Editor",
+ "use_key_equivalents": true,
+ "bindings": {
+ "up": "search::PreviousHistoryQuery",
+ "down": "search::NextHistoryQuery"
+ }
+ },
+ {
+ "context": "ProjectSearchBar && in_replace > Editor",
+ "use_key_equivalents": true,
+ "bindings": {
+ "enter": "search::ReplaceNext",
+ "ctrl-alt-enter": "search::ReplaceAll"
+ }
+ },
+ {
+ "context": "ProjectSearchView",
+ "use_key_equivalents": true,
+ "bindings": {
+ "escape": "project_search::ToggleFocus",
+ "ctrl-shift-h": "search::ToggleReplace",
+ "alt-r": "search::ToggleRegex" // vscode
+ }
+ },
+ {
+ "context": "Pane",
+ "use_key_equivalents": true,
+ "bindings": {
+ "alt-1": ["pane::ActivateItem", 0],
+ "alt-2": ["pane::ActivateItem", 1],
+ "alt-3": ["pane::ActivateItem", 2],
+ "alt-4": ["pane::ActivateItem", 3],
+ "alt-5": ["pane::ActivateItem", 4],
+ "alt-6": ["pane::ActivateItem", 5],
+ "alt-7": ["pane::ActivateItem", 6],
+ "alt-8": ["pane::ActivateItem", 7],
+ "alt-9": ["pane::ActivateItem", 8],
+ "alt-0": "pane::ActivateLastItem",
+ "ctrl-pageup": "pane::ActivatePreviousItem",
+ "ctrl-pagedown": "pane::ActivateNextItem",
+ "ctrl-shift-pageup": "pane::SwapItemLeft",
+ "ctrl-shift-pagedown": "pane::SwapItemRight",
+ "ctrl-f4": ["pane::CloseActiveItem", { "close_pinned": false }],
+ "ctrl-w": ["pane::CloseActiveItem", { "close_pinned": false }],
+ "ctrl-shift-alt-t": ["pane::CloseOtherItems", { "close_pinned": false }],
+ "ctrl-shift-alt-w": "workspace::CloseInactiveTabsAndPanes",
+ "ctrl-k e": ["pane::CloseItemsToTheLeft", { "close_pinned": false }],
+ "ctrl-k t": ["pane::CloseItemsToTheRight", { "close_pinned": false }],
+ "ctrl-k u": ["pane::CloseCleanItems", { "close_pinned": false }],
+ "ctrl-k w": ["pane::CloseAllItems", { "close_pinned": false }],
+ "ctrl-k ctrl-w": "workspace::CloseAllItemsAndPanes",
+ "back": "pane::GoBack",
+ "alt--": "pane::GoBack",
+ "alt-=": "pane::GoForward",
+ "forward": "pane::GoForward",
+ "f3": "search::SelectNextMatch",
+ "shift-f3": "search::SelectPreviousMatch",
+ "ctrl-shift-f": "project_search::ToggleFocus",
+ "shift-alt-h": "search::ToggleReplace",
+ "alt-l": "search::ToggleSelection",
+ "alt-enter": "search::SelectAllMatches",
+ "alt-c": "search::ToggleCaseSensitive",
+ "alt-w": "search::ToggleWholeWord",
+ "alt-f": "project_search::ToggleFilters",
+ "alt-r": "search::ToggleRegex",
+ // "ctrl-shift-alt-x": "search::ToggleRegex",
+ "ctrl-k shift-enter": "pane::TogglePinTab"
+ }
+ },
+ // Bindings from VS Code
+ {
+ "context": "Editor",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-[": "editor::Outdent",
+ "ctrl-]": "editor::Indent",
+ "ctrl-shift-alt-up": "editor::AddSelectionAbove", // Insert Cursor Above
+ "ctrl-shift-alt-down": "editor::AddSelectionBelow", // Insert Cursor Below
+ "ctrl-shift-k": "editor::DeleteLine",
+ "alt-up": "editor::MoveLineUp",
+ "alt-down": "editor::MoveLineDown",
+ "shift-alt-up": "editor::DuplicateLineUp",
+ "shift-alt-down": "editor::DuplicateLineDown",
+ "shift-alt-right": "editor::SelectLargerSyntaxNode", // Expand Selection
+ "shift-alt-left": "editor::SelectSmallerSyntaxNode", // Shrink Selection
+ "ctrl-shift-l": "editor::SelectAllMatches", // Select all occurrences of current selection
+ "ctrl-f2": "editor::SelectAllMatches", // Select all occurrences of current word
+ "ctrl-d": ["editor::SelectNext", { "replace_newest": false }], // editor.action.addSelectionToNextFindMatch / find_under_expand
+ "ctrl-shift-down": ["editor::SelectNext", { "replace_newest": false }], // editor.action.addSelectionToNextFindMatch
+ "ctrl-shift-up": ["editor::SelectPrevious", { "replace_newest": false }], // editor.action.addSelectionToPreviousFindMatch
+ "ctrl-k ctrl-d": ["editor::SelectNext", { "replace_newest": true }], // editor.action.moveSelectionToNextFindMatch / find_under_expand_skip
+ "ctrl-k ctrl-shift-d": ["editor::SelectPrevious", { "replace_newest": true }], // editor.action.moveSelectionToPreviousFindMatch
+ "ctrl-k ctrl-i": "editor::Hover",
+ "ctrl-k ctrl-b": "editor::BlameHover",
+ "ctrl-/": ["editor::ToggleComments", { "advance_downwards": false }],
+ "f8": ["editor::GoToDiagnostic", { "severity": { "min": "hint", "max": "error" } }],
+ "shift-f8": ["editor::GoToPreviousDiagnostic", { "severity": { "min": "hint", "max": "error" } }],
+ "f2": "editor::Rename",
+ "f12": "editor::GoToDefinition",
+ "alt-f12": "editor::GoToDefinitionSplit",
+ "ctrl-shift-f10": "editor::GoToDefinitionSplit",
+ "ctrl-f12": "editor::GoToImplementation",
+ "shift-f12": "editor::GoToTypeDefinition",
+ "ctrl-alt-f12": "editor::GoToTypeDefinitionSplit",
+ "shift-alt-f12": "editor::FindAllReferences",
+ "ctrl-m": "editor::MoveToEnclosingBracket", // from jetbrains
+ "ctrl-shift-\\": "editor::MoveToEnclosingBracket",
+ "ctrl-shift-[": "editor::Fold",
+ "ctrl-shift-]": "editor::UnfoldLines",
+ "ctrl-k ctrl-l": "editor::ToggleFold",
+ "ctrl-k ctrl-[": "editor::FoldRecursive",
+ "ctrl-k ctrl-]": "editor::UnfoldRecursive",
+ "ctrl-k ctrl-1": ["editor::FoldAtLevel", 1],
+ "ctrl-k ctrl-2": ["editor::FoldAtLevel", 2],
+ "ctrl-k ctrl-3": ["editor::FoldAtLevel", 3],
+ "ctrl-k ctrl-4": ["editor::FoldAtLevel", 4],
+ "ctrl-k ctrl-5": ["editor::FoldAtLevel", 5],
+ "ctrl-k ctrl-6": ["editor::FoldAtLevel", 6],
+ "ctrl-k ctrl-7": ["editor::FoldAtLevel", 7],
+ "ctrl-k ctrl-8": ["editor::FoldAtLevel", 8],
+ "ctrl-k ctrl-9": ["editor::FoldAtLevel", 9],
+ "ctrl-k ctrl-0": "editor::FoldAll",
+ "ctrl-k ctrl-j": "editor::UnfoldAll",
+ "ctrl-space": "editor::ShowCompletions",
+ "ctrl-shift-space": "editor::ShowWordCompletions",
+ "ctrl-.": "editor::ToggleCodeActions",
+ "ctrl-k r": "editor::RevealInFileManager",
+ "ctrl-k p": "editor::CopyPath",
+ "ctrl-\\": "pane::SplitRight",
+ "ctrl-shift-alt-c": "editor::DisplayCursorNames",
+ "alt-.": "editor::GoToHunk",
+ "alt-,": "editor::GoToPreviousHunk"
+ }
+ },
+ {
+ "context": "Editor && extension == md",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-k v": "markdown::OpenPreviewToTheSide",
+ "ctrl-shift-v": "markdown::OpenPreview"
+ }
+ },
+ {
+ "context": "Editor && extension == svg",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-k v": "svg::OpenPreviewToTheSide",
+ "ctrl-shift-v": "svg::OpenPreview"
+ }
+ },
+ {
+ "context": "Editor && mode == full",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-shift-o": "outline::Toggle",
+ "ctrl-g": "go_to_line::Toggle"
+ }
+ },
+ {
+ "context": "Workspace",
+ "use_key_equivalents": true,
+ "bindings": {
+ // Change the default action on `menu::Confirm` by setting the parameter
+ // "ctrl-alt-o": ["projects::OpenRecent", { "create_new_window": true }],
+ "ctrl-r": ["projects::OpenRecent", { "create_new_window": false }],
+ // Change to open path modal for existing remote connection by setting the parameter
+ // "ctrl-shift-alt-o": "["projects::OpenRemote", { "from_existing_connection": true }]",
+ "ctrl-shift-alt-o": ["projects::OpenRemote", { "from_existing_connection": false, "create_new_window": false }],
+ "shift-alt-b": "branches::OpenRecent",
+ "shift-alt-enter": "toast::RunAction",
+ "ctrl-shift-`": "workspace::NewTerminal",
+ "ctrl-s": "workspace::Save",
+ "ctrl-k ctrl-shift-s": "workspace::SaveWithoutFormat",
+ "ctrl-shift-s": "workspace::SaveAs",
+ "ctrl-n": "workspace::NewFile",
+ "ctrl-shift-n": "workspace::NewWindow",
+ "ctrl-`": "terminal_panel::Toggle",
+ "f10": ["app_menu::OpenApplicationMenu", "Zed"],
+ "alt-1": ["workspace::ActivatePane", 0],
+ "alt-2": ["workspace::ActivatePane", 1],
+ "alt-3": ["workspace::ActivatePane", 2],
+ "alt-4": ["workspace::ActivatePane", 3],
+ "alt-5": ["workspace::ActivatePane", 4],
+ "alt-6": ["workspace::ActivatePane", 5],
+ "alt-7": ["workspace::ActivatePane", 6],
+ "alt-8": ["workspace::ActivatePane", 7],
+ "alt-9": ["workspace::ActivatePane", 8],
+ "ctrl-alt-b": "workspace::ToggleRightDock",
+ "ctrl-b": "workspace::ToggleLeftDock",
+ "ctrl-j": "workspace::ToggleBottomDock",
+ "ctrl-shift-y": "workspace::CloseAllDocks",
+ "alt-r": "workspace::ResetActiveDockSize",
+ // For 0px parameter, uses UI font size value.
+ "shift-alt--": ["workspace::DecreaseActiveDockSize", { "px": 0 }],
+ "shift-alt-=": ["workspace::IncreaseActiveDockSize", { "px": 0 }],
+ "shift-alt-0": "workspace::ResetOpenDocksSize",
+ "ctrl-shift-alt--": ["workspace::DecreaseOpenDocksSize", { "px": 0 }],
+ "ctrl-shift-alt-=": ["workspace::IncreaseOpenDocksSize", { "px": 0 }],
+ "ctrl-shift-f": "pane::DeploySearch",
+ "ctrl-shift-h": ["pane::DeploySearch", { "replace_enabled": true }],
+ "ctrl-shift-t": "pane::ReopenClosedItem",
+ "ctrl-k ctrl-s": "zed::OpenKeymapEditor",
+ "ctrl-k ctrl-t": "theme_selector::Toggle",
+ "ctrl-alt-super-p": "settings_profile_selector::Toggle",
+ "ctrl-t": "project_symbols::Toggle",
+ "ctrl-p": "file_finder::Toggle",
+ "ctrl-tab": "tab_switcher::Toggle",
+ "ctrl-shift-tab": ["tab_switcher::Toggle", { "select_last": true }],
+ "ctrl-e": "file_finder::Toggle",
+ "f1": "command_palette::Toggle",
+ "ctrl-shift-p": "command_palette::Toggle",
+ "ctrl-shift-m": "diagnostics::Deploy",
+ "ctrl-shift-e": "project_panel::ToggleFocus",
+ "ctrl-shift-b": "outline_panel::ToggleFocus",
+ "ctrl-shift-g": "git_panel::ToggleFocus",
+ "ctrl-shift-d": "debug_panel::ToggleFocus",
+ "ctrl-shift-/": "agent::ToggleFocus",
+ "ctrl-k s": "workspace::SaveAll",
+ "ctrl-k m": "language_selector::Toggle",
+ "ctrl-m ctrl-m": "toolchain::AddToolchain",
+ "escape": "workspace::Unfollow",
+ "ctrl-k ctrl-left": "workspace::ActivatePaneLeft",
+ "ctrl-k ctrl-right": "workspace::ActivatePaneRight",
+ "ctrl-k ctrl-up": "workspace::ActivatePaneUp",
+ "ctrl-k ctrl-down": "workspace::ActivatePaneDown",
+ "ctrl-k shift-left": "workspace::SwapPaneLeft",
+ "ctrl-k shift-right": "workspace::SwapPaneRight",
+ "ctrl-k shift-up": "workspace::SwapPaneUp",
+ "ctrl-k shift-down": "workspace::SwapPaneDown",
+ "ctrl-shift-x": "zed::Extensions",
+ "ctrl-shift-r": "task::Rerun",
+ "alt-t": "task::Rerun",
+ "shift-alt-t": "task::Spawn",
+ "shift-alt-r": ["task::Spawn", { "reveal_target": "center" }],
+ // also possible to spawn tasks by name:
+ // "foo-bar": ["task::Spawn", { "task_name": "MyTask", "reveal_target": "dock" }]
+ // or by tag:
+ // "foo-bar": ["task::Spawn", { "task_tag": "MyTag" }],
+ "f5": "debugger::Rerun",
+ "ctrl-f4": "workspace::CloseActiveDock",
+ "ctrl-w": "workspace::CloseActiveDock"
+ }
+ },
+ {
+ "context": "Workspace && debugger_running",
+ "use_key_equivalents": true,
+ "bindings": {
+ "f5": "zed::NoAction"
+ }
+ },
+ {
+ "context": "Workspace && debugger_stopped",
+ "use_key_equivalents": true,
+ "bindings": {
+ "f5": "debugger::Continue"
+ }
+ },
+ {
+ "context": "ApplicationMenu",
+ "use_key_equivalents": true,
+ "bindings": {
+ "f10": "menu::Cancel",
+ "left": "app_menu::ActivateMenuLeft",
+ "right": "app_menu::ActivateMenuRight"
+ }
+ },
+ // Bindings from Sublime Text
+ {
+ "context": "Editor",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-u": "editor::UndoSelection",
+ "ctrl-shift-u": "editor::RedoSelection",
+ "ctrl-shift-j": "editor::JoinLines",
+ "ctrl-alt-backspace": "editor::DeleteToPreviousSubwordStart",
+ "shift-alt-h": "editor::DeleteToPreviousSubwordStart",
+ "ctrl-alt-delete": "editor::DeleteToNextSubwordEnd",
+ "shift-alt-d": "editor::DeleteToNextSubwordEnd",
+ "ctrl-alt-left": "editor::MoveToPreviousSubwordStart",
+ "ctrl-alt-right": "editor::MoveToNextSubwordEnd",
+ "ctrl-shift-alt-left": "editor::SelectToPreviousSubwordStart",
+ "ctrl-shift-alt-right": "editor::SelectToNextSubwordEnd"
+ }
+ },
+ // Bindings from Atom
+ {
+ "context": "Pane",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-k up": "pane::SplitUp",
+ "ctrl-k down": "pane::SplitDown",
+ "ctrl-k left": "pane::SplitLeft",
+ "ctrl-k right": "pane::SplitRight"
+ }
+ },
+ // Bindings that should be unified with bindings for more general actions
+ {
+ "context": "Editor && renaming",
+ "use_key_equivalents": true,
+ "bindings": {
+ "enter": "editor::ConfirmRename"
+ }
+ },
+ {
+ "context": "Editor && showing_completions",
+ "use_key_equivalents": true,
+ "bindings": {
+ "enter": "editor::ConfirmCompletion",
+ "shift-enter": "editor::ConfirmCompletionReplace",
+ "tab": "editor::ComposeCompletion"
+ }
+ },
+ // Bindings for accepting edit predictions
+ //
+ // alt-l is provided as an alternative to tab/alt-tab. and will be displayed in the UI. This is
+ // because alt-tab may not be available, as it is often used for window switching.
+ {
+ "context": "Editor && edit_prediction",
+ "use_key_equivalents": true,
+ "bindings": {
+ "alt-tab": "editor::AcceptEditPrediction",
+ "alt-l": "editor::AcceptEditPrediction",
+ "tab": "editor::AcceptEditPrediction",
+ "alt-right": "editor::AcceptPartialEditPrediction"
+ }
+ },
+ {
+ "context": "Editor && edit_prediction_conflict",
+ "use_key_equivalents": true,
+ "bindings": {
+ "alt-tab": "editor::AcceptEditPrediction",
+ "alt-l": "editor::AcceptEditPrediction",
+ "alt-right": "editor::AcceptPartialEditPrediction"
+ }
+ },
+ {
+ "context": "Editor && showing_code_actions",
+ "use_key_equivalents": true,
+ "bindings": {
+ "enter": "editor::ConfirmCodeAction"
+ }
+ },
+ {
+ "context": "Editor && (showing_code_actions || showing_completions)",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-p": "editor::ContextMenuPrevious",
+ "up": "editor::ContextMenuPrevious",
+ "ctrl-n": "editor::ContextMenuNext",
+ "down": "editor::ContextMenuNext",
+ "pageup": "editor::ContextMenuFirst",
+ "pagedown": "editor::ContextMenuLast"
+ }
+ },
+ {
+ "context": "Editor && showing_signature_help && !showing_completions",
+ "use_key_equivalents": true,
+ "bindings": {
+ "up": "editor::SignatureHelpPrevious",
+ "down": "editor::SignatureHelpNext"
+ }
+ },
+ // Custom bindings
+ {
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-shift-alt-f": "workspace::FollowNextCollaborator",
+ // Only available in debug builds: opens an element inspector for development.
+ "shift-alt-i": "dev::ToggleInspector"
+ }
+ },
+ {
+ "context": "!Terminal",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-shift-c": "collab_panel::ToggleFocus"
+ }
+ },
+ {
+ "context": "!ContextEditor > Editor && mode == full",
+ "use_key_equivalents": true,
+ "bindings": {
+ "alt-enter": "editor::OpenExcerpts",
+ "shift-enter": "editor::ExpandExcerpts",
+ "ctrl-alt-enter": "editor::OpenExcerptsSplit",
+ "ctrl-shift-e": "pane::RevealInProjectPanel",
+ "ctrl-f8": "editor::GoToHunk",
+ "ctrl-shift-f8": "editor::GoToPreviousHunk",
+ "ctrl-enter": "assistant::InlineAssist",
+ "ctrl-shift-;": "editor::ToggleInlayHints"
+ }
+ },
+ {
+ "context": "PromptEditor",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-[": "agent::CyclePreviousInlineAssist",
+ "ctrl-]": "agent::CycleNextInlineAssist",
+ "shift-alt-e": "agent::RemoveAllContext"
+ }
+ },
+ {
+ "context": "Prompt",
+ "use_key_equivalents": true,
+ "bindings": {
+ "left": "menu::SelectPrevious",
+ "right": "menu::SelectNext",
+ "h": "menu::SelectPrevious",
+ "l": "menu::SelectNext"
+ }
+ },
+ {
+ "context": "ProjectSearchBar && !in_replace",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-enter": "project_search::SearchInNew"
+ }
+ },
+ {
+ "context": "OutlinePanel && not_editing",
+ "use_key_equivalents": true,
+ "bindings": {
+ "left": "outline_panel::CollapseSelectedEntry",
+ "right": "outline_panel::ExpandSelectedEntry",
+ "shift-alt-c": "outline_panel::CopyPath",
+ "ctrl-shift-alt-c": "workspace::CopyRelativePath",
+ "ctrl-alt-r": "outline_panel::RevealInFileManager",
+ "space": "outline_panel::OpenSelectedEntry",
+ "shift-down": "menu::SelectNext",
+ "shift-up": "menu::SelectPrevious",
+ "alt-enter": "editor::OpenExcerpts",
+ "ctrl-alt-enter": "editor::OpenExcerptsSplit"
+ }
+ },
+ {
+ "context": "ProjectPanel",
+ "use_key_equivalents": true,
+ "bindings": {
+ "left": "project_panel::CollapseSelectedEntry",
+ "right": "project_panel::ExpandSelectedEntry",
+ "ctrl-n": "project_panel::NewFile",
+ "alt-n": "project_panel::NewDirectory",
+ "ctrl-x": "project_panel::Cut",
+ "ctrl-insert": "project_panel::Copy",
+ "ctrl-c": "project_panel::Copy",
+ "shift-insert": "project_panel::Paste",
+ "ctrl-v": "project_panel::Paste",
+ "shift-alt-c": "project_panel::CopyPath",
+ "ctrl-k ctrl-shift-c": "workspace::CopyRelativePath",
+ "enter": "project_panel::Rename",
+ "f2": "project_panel::Rename",
+ "backspace": ["project_panel::Trash", { "skip_prompt": false }],
+ "delete": ["project_panel::Trash", { "skip_prompt": false }],
+ "shift-delete": ["project_panel::Delete", { "skip_prompt": false }],
+ "ctrl-backspace": ["project_panel::Delete", { "skip_prompt": false }],
+ "ctrl-delete": ["project_panel::Delete", { "skip_prompt": false }],
+ "ctrl-alt-r": "project_panel::RevealInFileManager",
+ "ctrl-shift-enter": "project_panel::OpenWithSystem",
+ "alt-d": "project_panel::CompareMarkedFiles",
+ "ctrl-k ctrl-shift-f": "project_panel::NewSearchInDirectory",
+ "shift-down": "menu::SelectNext",
+ "shift-up": "menu::SelectPrevious",
+ "escape": "menu::Cancel"
+ }
+ },
+ {
+ "context": "ProjectPanel && not_editing",
+ "use_key_equivalents": true,
+ "bindings": {
+ "space": "project_panel::Open"
+ }
+ },
+ {
+ "context": "GitPanel && ChangesList",
+ "use_key_equivalents": true,
+ "bindings": {
+ "up": "menu::SelectPrevious",
+ "down": "menu::SelectNext",
+ "enter": "menu::Confirm",
+ "alt-y": "git::StageFile",
+ "shift-alt-y": "git::UnstageFile",
+ "space": "git::ToggleStaged",
+ "shift-space": "git::StageRange",
+ "tab": "git_panel::FocusEditor",
+ "shift-tab": "git_panel::FocusEditor",
+ "escape": "git_panel::ToggleFocus",
+ "alt-enter": "menu::SecondaryConfirm",
+ "delete": ["git::RestoreFile", { "skip_prompt": false }],
+ "backspace": ["git::RestoreFile", { "skip_prompt": false }],
+ "shift-delete": ["git::RestoreFile", { "skip_prompt": false }],
+ "ctrl-backspace": ["git::RestoreFile", { "skip_prompt": false }],
+ "ctrl-delete": ["git::RestoreFile", { "skip_prompt": false }]
+ }
+ },
+ {
+ "context": "GitPanel && CommitEditor",
+ "use_key_equivalents": true,
+ "bindings": {
+ "escape": "git::Cancel"
+ }
+ },
+ {
+ "context": "GitCommit > Editor",
+ "use_key_equivalents": true,
+ "bindings": {
+ "escape": "menu::Cancel",
+ "enter": "editor::Newline",
+ "ctrl-enter": "git::Commit",
+ "ctrl-shift-enter": "git::Amend",
+ "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",
+ "ctrl-enter": "git::Commit",
+ "ctrl-shift-enter": "git::Amend"
+ }
+ },
+ {
+ "context": "GitDiff > Editor",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-enter": "git::Commit",
+ "ctrl-shift-enter": "git::Amend",
+ "ctrl-space": "git::StageAll",
+ "ctrl-shift-space": "git::UnstageAll"
+ }
+ },
+ {
+ "context": "AskPass > Editor",
+ "use_key_equivalents": true,
+ "bindings": {
+ "enter": "menu::Confirm"
+ }
+ },
+ {
+ "context": "CommitEditor > Editor",
+ "use_key_equivalents": true,
+ "bindings": {
+ "escape": "git_panel::FocusChanges",
+ "tab": "git_panel::FocusChanges",
+ "shift-tab": "git_panel::FocusChanges",
+ "enter": "editor::Newline",
+ "ctrl-enter": "git::Commit",
+ "ctrl-shift-enter": "git::Amend",
+ "alt-up": "git_panel::FocusChanges",
+ "alt-l": "git::GenerateCommitMessage"
+ }
+ },
+ {
+ "context": "DebugPanel",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-t": "debugger::ToggleThreadPicker",
+ "ctrl-i": "debugger::ToggleSessionPicker",
+ "shift-alt-escape": "debugger::ToggleExpandItem"
+ }
+ },
+ {
+ "context": "VariableList",
+ "use_key_equivalents": true,
+ "bindings": {
+ "left": "variable_list::CollapseSelectedEntry",
+ "right": "variable_list::ExpandSelectedEntry",
+ "enter": "variable_list::EditVariable",
+ "ctrl-c": "variable_list::CopyVariableValue",
+ "ctrl-alt-c": "variable_list::CopyVariableName",
+ "delete": "variable_list::RemoveWatch",
+ "backspace": "variable_list::RemoveWatch",
+ "alt-enter": "variable_list::AddWatch"
+ }
+ },
+ {
+ "context": "BreakpointList",
+ "use_key_equivalents": true,
+ "bindings": {
+ "space": "debugger::ToggleEnableBreakpoint",
+ "backspace": "debugger::UnsetBreakpoint",
+ "left": "debugger::PreviousBreakpointProperty",
+ "right": "debugger::NextBreakpointProperty"
+ }
+ },
+ {
+ "context": "CollabPanel && not_editing",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-backspace": "collab_panel::Remove",
+ "space": "menu::Confirm"
+ }
+ },
+ {
+ "context": "CollabPanel",
+ "use_key_equivalents": true,
+ "bindings": {
+ "alt-up": "collab_panel::MoveChannelUp",
+ "alt-down": "collab_panel::MoveChannelDown"
+ }
+ },
+ {
+ "context": "(CollabPanel && editing) > Editor",
+ "use_key_equivalents": true,
+ "bindings": {
+ "space": "collab_panel::InsertSpace"
+ }
+ },
+ {
+ "context": "ChannelModal",
+ "use_key_equivalents": true,
+ "bindings": {
+ "tab": "channel_modal::ToggleMode"
+ }
+ },
+ {
+ "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 }]
+ }
+ },
+ {
+ "context": "ChannelModal > Picker > Editor",
+ "use_key_equivalents": true,
+ "bindings": {
+ "tab": "channel_modal::ToggleMode"
+ }
+ },
+ {
+ "context": "ToolchainSelector",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-shift-a": "toolchain::AddToolchain"
+ }
+ },
+ {
+ "context": "FileFinder || (FileFinder > Picker > Editor)",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-p": "file_finder::Toggle",
+ "ctrl-shift-a": "file_finder::ToggleSplitMenu",
+ "ctrl-shift-i": "file_finder::ToggleFilterMenu"
+ }
+ },
+ {
+ "context": "FileFinder || (FileFinder > Picker > Editor) || (FileFinder > Picker > menu)",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-shift-p": "file_finder::SelectPrevious",
+ "ctrl-j": "pane::SplitDown",
+ "ctrl-k": "pane::SplitUp",
+ "ctrl-h": "pane::SplitLeft",
+ "ctrl-l": "pane::SplitRight"
+ }
+ },
+ {
+ "context": "TabSwitcher",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-shift-tab": "menu::SelectPrevious",
+ "ctrl-up": "menu::SelectPrevious",
+ "ctrl-down": "menu::SelectNext",
+ "ctrl-backspace": "tab_switcher::CloseSelectedItem"
+ }
+ },
+ {
+ "context": "Terminal",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-alt-space": "terminal::ShowCharacterPalette",
+ "ctrl-insert": "terminal::Copy",
+ "ctrl-shift-c": "terminal::Copy",
+ "shift-insert": "terminal::Paste",
+ "ctrl-shift-v": "terminal::Paste",
+ "ctrl-enter": "assistant::InlineAssist",
+ "alt-b": ["terminal::SendText", "\u001bb"],
+ "alt-f": ["terminal::SendText", "\u001bf"],
+ "alt-.": ["terminal::SendText", "\u001b."],
+ "ctrl-delete": ["terminal::SendText", "\u001bd"],
+ // Overrides for conflicting keybindings
+ "ctrl-b": ["terminal::SendKeystroke", "ctrl-b"],
+ "ctrl-c": ["terminal::SendKeystroke", "ctrl-c"],
+ "ctrl-e": ["terminal::SendKeystroke", "ctrl-e"],
+ "ctrl-o": ["terminal::SendKeystroke", "ctrl-o"],
+ "ctrl-w": ["terminal::SendKeystroke", "ctrl-w"],
+ "ctrl-backspace": ["terminal::SendKeystroke", "ctrl-w"],
+ "ctrl-shift-a": "editor::SelectAll",
+ "ctrl-shift-f": "buffer_search::Deploy",
+ "ctrl-shift-l": "terminal::Clear",
+ "ctrl-shift-w": "pane::CloseActiveItem",
+ "up": ["terminal::SendKeystroke", "up"],
+ "pageup": ["terminal::SendKeystroke", "pageup"],
+ "down": ["terminal::SendKeystroke", "down"],
+ "pagedown": ["terminal::SendKeystroke", "pagedown"],
+ "escape": ["terminal::SendKeystroke", "escape"],
+ "enter": ["terminal::SendKeystroke", "enter"],
+ "shift-pageup": "terminal::ScrollPageUp",
+ "shift-pagedown": "terminal::ScrollPageDown",
+ "shift-up": "terminal::ScrollLineUp",
+ "shift-down": "terminal::ScrollLineDown",
+ "shift-home": "terminal::ScrollToTop",
+ "shift-end": "terminal::ScrollToBottom",
+ "ctrl-shift-space": "terminal::ToggleViMode",
+ "ctrl-shift-r": "terminal::RerunTask",
+ "ctrl-alt-r": "terminal::RerunTask",
+ "alt-t": "terminal::RerunTask"
+ }
+ },
+ {
+ "context": "ZedPredictModal",
+ "use_key_equivalents": true,
+ "bindings": {
+ "escape": "menu::Cancel"
+ }
+ },
+ {
+ "context": "ConfigureContextServerModal > Editor",
+ "use_key_equivalents": true,
+ "bindings": {
+ "escape": "menu::Cancel",
+ "enter": "editor::Newline",
+ "ctrl-enter": "menu::Confirm"
+ }
+ },
+ {
+ "context": "OnboardingAiConfigurationModal",
+ "use_key_equivalents": true,
+ "bindings": {
+ "escape": "menu::Cancel"
+ }
+ },
+ {
+ "context": "Diagnostics",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-r": "diagnostics::ToggleDiagnosticsRefresh"
+ }
+ },
+ {
+ "context": "DebugConsole > Editor",
+ "use_key_equivalents": true,
+ "bindings": {
+ "enter": "menu::Confirm",
+ "alt-enter": "console::WatchExpression"
+ }
+ },
+ {
+ "context": "RunModal",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-tab": "pane::ActivateNextItem",
+ "ctrl-shift-tab": "pane::ActivatePreviousItem"
+ }
+ },
+ {
+ "context": "MarkdownPreview",
+ "use_key_equivalents": true,
+ "bindings": {
+ "pageup": "markdown::MovePageUp",
+ "pagedown": "markdown::MovePageDown"
+ }
+ },
+ {
+ "context": "KeymapEditor",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-f": "search::FocusSearch",
+ "alt-f": "keymap_editor::ToggleKeystrokeSearch",
+ "alt-c": "keymap_editor::ToggleConflictFilter",
+ "enter": "keymap_editor::EditBinding",
+ "alt-enter": "keymap_editor::CreateBinding",
+ "ctrl-c": "keymap_editor::CopyAction",
+ "ctrl-shift-c": "keymap_editor::CopyContext",
+ "ctrl-t": "keymap_editor::ShowMatchingKeybinds"
+ }
+ },
+ {
+ "context": "KeystrokeInput",
+ "use_key_equivalents": true,
+ "bindings": {
+ "enter": "keystroke_input::StartRecording",
+ "escape escape escape": "keystroke_input::StopRecording",
+ "delete": "keystroke_input::ClearKeystrokes"
+ }
+ },
+ {
+ "context": "KeybindEditorModal",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-enter": "menu::Confirm",
+ "escape": "menu::Cancel"
+ }
+ },
+ {
+ "context": "KeybindEditorModal > Editor",
+ "use_key_equivalents": true,
+ "bindings": {
+ "up": "menu::SelectPrevious",
+ "down": "menu::SelectNext"
+ }
+ },
+ {
+ "context": "Onboarding",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-1": "onboarding::ActivateBasicsPage",
+ "ctrl-2": "onboarding::ActivateEditingPage",
+ "ctrl-3": "onboarding::ActivateAISetupPage",
+ "ctrl-escape": "onboarding::Finish",
+ "alt-tab": "onboarding::SignIn",
+ "shift-alt-a": "onboarding::OpenAccount"
+ }
+ }
+]
diff --git a/assets/keymaps/linux/cursor.json b/assets/keymaps/linux/cursor.json
index 1c381b0cf05531e7fd5743d71be1b4d662bb4c0d..2e27158e1167f0840cadfb0d86dc06614f6076c6 100644
--- a/assets/keymaps/linux/cursor.json
+++ b/assets/keymaps/linux/cursor.json
@@ -17,8 +17,8 @@
"bindings": {
"ctrl-i": "agent::ToggleFocus",
"ctrl-shift-i": "agent::ToggleFocus",
- "ctrl-shift-l": "assistant::QuoteSelection", // In cursor uses "Ask" mode
- "ctrl-l": "assistant::QuoteSelection", // In cursor uses "Agent" mode
+ "ctrl-shift-l": "agent::QuoteSelection", // In cursor uses "Ask" mode
+ "ctrl-l": "agent::QuoteSelection", // In cursor uses "Agent" mode
"ctrl-k": "assistant::InlineAssist",
"ctrl-shift-k": "assistant::InsertIntoEditor"
}
diff --git a/assets/keymaps/linux/emacs.json b/assets/keymaps/linux/emacs.json
index 0ff3796f03d85affdae88d009e88e73516ba385a..0f936ba2f968abe0759e4bb294271a5e5f501848 100755
--- a/assets/keymaps/linux/emacs.json
+++ b/assets/keymaps/linux/emacs.json
@@ -38,10 +38,11 @@
"alt-;": ["editor::ToggleComments", { "advance_downwards": false }],
"ctrl-x ctrl-;": "editor::ToggleComments",
"alt-.": "editor::GoToDefinition", // xref-find-definitions
+ "alt-?": "editor::FindAllReferences", // xref-find-references
"alt-,": "pane::GoBack", // xref-pop-marker-stack
"ctrl-x h": "editor::SelectAll", // mark-whole-buffer
"ctrl-d": "editor::Delete", // delete-char
- "alt-d": "editor::DeleteToNextWordEnd", // kill-word
+ "alt-d": ["editor::DeleteToNextWordEnd", { "ignore_newlines": false, "ignore_brackets": false }], // kill-word
"ctrl-k": "editor::KillRingCut", // kill-line
"ctrl-w": "editor::Cut", // kill-region
"alt-w": "editor::Copy", // kill-ring-save
diff --git a/assets/keymaps/linux/jetbrains.json b/assets/keymaps/linux/jetbrains.json
index 3df1243feda88680a4ce03cd0b25ab9ea9a36edd..59a182a968a849edb3359927e7647f611bcd44da 100644
--- a/assets/keymaps/linux/jetbrains.json
+++ b/assets/keymaps/linux/jetbrains.json
@@ -125,7 +125,7 @@
{
"context": "Workspace || Editor",
"bindings": {
- "alt-f12": "terminal_panel::ToggleFocus",
+ "alt-f12": "terminal_panel::Toggle",
"ctrl-shift-k": "git::Push"
}
},
diff --git a/assets/keymaps/linux/sublime_text.json b/assets/keymaps/linux/sublime_text.json
index ece9d69dd102c019072678373e9328f302d4cb07..f526db45ff29e0828ce58df6ca9816bd71a4cbe5 100644
--- a/assets/keymaps/linux/sublime_text.json
+++ b/assets/keymaps/linux/sublime_text.json
@@ -50,8 +50,8 @@
"ctrl-k ctrl-u": "editor::ConvertToUpperCase",
"ctrl-k ctrl-l": "editor::ConvertToLowerCase",
"shift-alt-m": "markdown::OpenPreviewToTheSide",
- "ctrl-backspace": "editor::DeleteToPreviousWordStart",
- "ctrl-delete": "editor::DeleteToNextWordEnd",
+ "ctrl-backspace": ["editor::DeleteToPreviousWordStart", { "ignore_newlines": false, "ignore_brackets": false }],
+ "ctrl-delete": ["editor::DeleteToNextWordEnd", { "ignore_newlines": false, "ignore_brackets": false }],
"alt-right": "editor::MoveToNextSubwordEnd",
"alt-left": "editor::MoveToPreviousSubwordStart",
"alt-shift-right": "editor::SelectToNextSubwordEnd",
diff --git a/assets/keymaps/macos/cursor.json b/assets/keymaps/macos/cursor.json
index fdf9c437cf395c074e42ae9c9dc53c1aa6ff66c2..1d723bd75bb788aa1ea63335f9fa555cb50d2df0 100644
--- a/assets/keymaps/macos/cursor.json
+++ b/assets/keymaps/macos/cursor.json
@@ -17,8 +17,8 @@
"bindings": {
"cmd-i": "agent::ToggleFocus",
"cmd-shift-i": "agent::ToggleFocus",
- "cmd-shift-l": "assistant::QuoteSelection", // In cursor uses "Ask" mode
- "cmd-l": "assistant::QuoteSelection", // In cursor uses "Agent" mode
+ "cmd-shift-l": "agent::QuoteSelection", // In cursor uses "Ask" mode
+ "cmd-l": "agent::QuoteSelection", // In cursor uses "Agent" mode
"cmd-k": "assistant::InlineAssist",
"cmd-shift-k": "assistant::InsertIntoEditor"
}
diff --git a/assets/keymaps/macos/emacs.json b/assets/keymaps/macos/emacs.json
index 0ff3796f03d85affdae88d009e88e73516ba385a..0f936ba2f968abe0759e4bb294271a5e5f501848 100755
--- a/assets/keymaps/macos/emacs.json
+++ b/assets/keymaps/macos/emacs.json
@@ -38,10 +38,11 @@
"alt-;": ["editor::ToggleComments", { "advance_downwards": false }],
"ctrl-x ctrl-;": "editor::ToggleComments",
"alt-.": "editor::GoToDefinition", // xref-find-definitions
+ "alt-?": "editor::FindAllReferences", // xref-find-references
"alt-,": "pane::GoBack", // xref-pop-marker-stack
"ctrl-x h": "editor::SelectAll", // mark-whole-buffer
"ctrl-d": "editor::Delete", // delete-char
- "alt-d": "editor::DeleteToNextWordEnd", // kill-word
+ "alt-d": ["editor::DeleteToNextWordEnd", { "ignore_newlines": false, "ignore_brackets": false }], // kill-word
"ctrl-k": "editor::KillRingCut", // kill-line
"ctrl-w": "editor::Cut", // kill-region
"alt-w": "editor::Copy", // kill-ring-save
diff --git a/assets/keymaps/macos/jetbrains.json b/assets/keymaps/macos/jetbrains.json
index 66962811f48a429f2f5d036241c64d6549f60334..2c757c3a30a08eb55e8344945ab66baf91ce0c6b 100644
--- a/assets/keymaps/macos/jetbrains.json
+++ b/assets/keymaps/macos/jetbrains.json
@@ -127,7 +127,7 @@
{
"context": "Workspace || Editor",
"bindings": {
- "alt-f12": "terminal_panel::ToggleFocus",
+ "alt-f12": "terminal_panel::Toggle",
"cmd-shift-k": "git::Push"
}
},
diff --git a/assets/keymaps/macos/sublime_text.json b/assets/keymaps/macos/sublime_text.json
index 9fa528c75fa75061c34d767c3e9f9082c9eb2a81..a1e61bf8859e2e4ea227ed3dbe22ec29eb35a149 100644
--- a/assets/keymaps/macos/sublime_text.json
+++ b/assets/keymaps/macos/sublime_text.json
@@ -52,8 +52,8 @@
"cmd-k cmd-l": "editor::ConvertToLowerCase",
"cmd-shift-j": "editor::JoinLines",
"shift-alt-m": "markdown::OpenPreviewToTheSide",
- "ctrl-backspace": "editor::DeleteToPreviousWordStart",
- "ctrl-delete": "editor::DeleteToNextWordEnd",
+ "ctrl-backspace": ["editor::DeleteToPreviousWordStart", { "ignore_newlines": false, "ignore_brackets": false }],
+ "ctrl-delete": ["editor::DeleteToNextWordEnd", { "ignore_newlines": false, "ignore_brackets": false }],
"ctrl-right": "editor::MoveToNextSubwordEnd",
"ctrl-left": "editor::MoveToPreviousSubwordStart",
"ctrl-shift-right": "editor::SelectToNextSubwordEnd",
diff --git a/assets/keymaps/macos/textmate.json b/assets/keymaps/macos/textmate.json
index 0bd8873b1749d2423d97df480b1aadeb28fe9bab..f91f39b7f5c079f81b5fcf8e28e2092a33ff1aa4 100644
--- a/assets/keymaps/macos/textmate.json
+++ b/assets/keymaps/macos/textmate.json
@@ -21,10 +21,10 @@
{
"context": "Editor",
"bindings": {
- "alt-backspace": "editor::DeleteToPreviousWordStart",
- "alt-shift-backspace": "editor::DeleteToNextWordEnd",
- "alt-delete": "editor::DeleteToNextWordEnd",
- "alt-shift-delete": "editor::DeleteToNextWordEnd",
+ "alt-backspace": ["editor::DeleteToPreviousWordStart", { "ignore_newlines": false, "ignore_brackets": false }],
+ "alt-shift-backspace": ["editor::DeleteToNextWordEnd", { "ignore_newlines": false, "ignore_brackets": false }],
+ "alt-delete": ["editor::DeleteToNextWordEnd", { "ignore_newlines": false, "ignore_brackets": false }],
+ "alt-shift-delete": ["editor::DeleteToNextWordEnd", { "ignore_newlines": false, "ignore_brackets": false }],
"ctrl-backspace": "editor::DeleteToPreviousSubwordStart",
"ctrl-delete": "editor::DeleteToNextSubwordEnd",
"alt-left": ["editor::MoveToPreviousWordStart", { "stop_at_soft_wraps": true }],
diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json
index be6d34a1342b6fabe0561643c74034d3c99a04b6..508d8949d2e38bfcf324ad611461d7077621a301 100644
--- a/assets/keymaps/vim.json
+++ b/assets/keymaps/vim.json
@@ -32,34 +32,6 @@
"(": "vim::SentenceBackward",
")": "vim::SentenceForward",
"|": "vim::GoToColumn",
- "] ]": "vim::NextSectionStart",
- "] [": "vim::NextSectionEnd",
- "[ [": "vim::PreviousSectionStart",
- "[ ]": "vim::PreviousSectionEnd",
- "] m": "vim::NextMethodStart",
- "] shift-m": "vim::NextMethodEnd",
- "[ m": "vim::PreviousMethodStart",
- "[ shift-m": "vim::PreviousMethodEnd",
- "[ *": "vim::PreviousComment",
- "[ /": "vim::PreviousComment",
- "] *": "vim::NextComment",
- "] /": "vim::NextComment",
- "[ -": "vim::PreviousLesserIndent",
- "[ +": "vim::PreviousGreaterIndent",
- "[ =": "vim::PreviousSameIndent",
- "] -": "vim::NextLesserIndent",
- "] +": "vim::NextGreaterIndent",
- "] =": "vim::NextSameIndent",
- "] b": "pane::ActivateNextItem",
- "[ b": "pane::ActivatePreviousItem",
- "] shift-b": "pane::ActivateLastItem",
- "[ shift-b": ["pane::ActivateItem", 0],
- "] space": "vim::InsertEmptyLineBelow",
- "[ space": "vim::InsertEmptyLineAbove",
- "[ e": "editor::MoveLineUp",
- "] e": "editor::MoveLineDown",
- "[ f": "workspace::FollowNextCollaborator",
- "] f": "workspace::FollowNextCollaborator",
// Word motions
"w": "vim::NextWordStart",
@@ -83,10 +55,6 @@
"n": "vim::MoveToNextMatch",
"shift-n": "vim::MoveToPreviousMatch",
"%": "vim::Matching",
- "] }": ["vim::UnmatchedForward", { "char": "}" }],
- "[ {": ["vim::UnmatchedBackward", { "char": "{" }],
- "] )": ["vim::UnmatchedForward", { "char": ")" }],
- "[ (": ["vim::UnmatchedBackward", { "char": "(" }],
"f": ["vim::PushFindForward", { "before": false, "multiline": false }],
"t": ["vim::PushFindForward", { "before": true, "multiline": false }],
"shift-f": ["vim::PushFindBackward", { "after": false, "multiline": false }],
@@ -219,6 +187,46 @@
".": "vim::Repeat"
}
},
+ {
+ "context": "vim_mode == normal || vim_mode == visual || vim_mode == operator",
+ "bindings": {
+ "] ]": "vim::NextSectionStart",
+ "] [": "vim::NextSectionEnd",
+ "[ [": "vim::PreviousSectionStart",
+ "[ ]": "vim::PreviousSectionEnd",
+ "] m": "vim::NextMethodStart",
+ "] shift-m": "vim::NextMethodEnd",
+ "[ m": "vim::PreviousMethodStart",
+ "[ shift-m": "vim::PreviousMethodEnd",
+ "[ *": "vim::PreviousComment",
+ "[ /": "vim::PreviousComment",
+ "] *": "vim::NextComment",
+ "] /": "vim::NextComment",
+ "[ -": "vim::PreviousLesserIndent",
+ "[ +": "vim::PreviousGreaterIndent",
+ "[ =": "vim::PreviousSameIndent",
+ "] -": "vim::NextLesserIndent",
+ "] +": "vim::NextGreaterIndent",
+ "] =": "vim::NextSameIndent",
+ "] b": "pane::ActivateNextItem",
+ "[ b": "pane::ActivatePreviousItem",
+ "] shift-b": "pane::ActivateLastItem",
+ "[ shift-b": ["pane::ActivateItem", 0],
+ "] space": "vim::InsertEmptyLineBelow",
+ "[ space": "vim::InsertEmptyLineAbove",
+ "[ e": "editor::MoveLineUp",
+ "] e": "editor::MoveLineDown",
+ "[ f": "workspace::FollowNextCollaborator",
+ "] f": "workspace::FollowNextCollaborator",
+ "] }": ["vim::UnmatchedForward", { "char": "}" }],
+ "[ {": ["vim::UnmatchedBackward", { "char": "{" }],
+ "] )": ["vim::UnmatchedForward", { "char": ")" }],
+ "[ (": ["vim::UnmatchedBackward", { "char": "(" }],
+ // tree-sitter related commands
+ "[ x": "vim::SelectLargerSyntaxNode",
+ "] x": "vim::SelectSmallerSyntaxNode"
+ }
+ },
{
"context": "vim_mode == normal",
"bindings": {
@@ -249,9 +257,6 @@
"g w": "vim::PushRewrap",
"g q": "vim::PushRewrap",
"insert": "vim::InsertBefore",
- // tree-sitter related commands
- "[ x": "vim::SelectLargerSyntaxNode",
- "] x": "vim::SelectSmallerSyntaxNode",
"] d": "editor::GoToDiagnostic",
"[ d": "editor::GoToPreviousDiagnostic",
"] c": "editor::GoToHunk",
@@ -317,10 +322,7 @@
"g w": "vim::Rewrap",
"g ?": "vim::ConvertToRot13",
// "g ?": "vim::ConvertToRot47",
- "\"": "vim::PushRegister",
- // tree-sitter related commands
- "[ x": "editor::SelectLargerSyntaxNode",
- "] x": "editor::SelectSmallerSyntaxNode"
+ "\"": "vim::PushRegister"
}
},
{
@@ -337,7 +339,7 @@
"ctrl-x ctrl-z": "editor::Cancel",
"ctrl-x ctrl-e": "vim::LineDown",
"ctrl-x ctrl-y": "vim::LineUp",
- "ctrl-w": "editor::DeleteToPreviousWordStart",
+ "ctrl-w": ["editor::DeleteToPreviousWordStart", { "ignore_newlines": false, "ignore_brackets": false }],
"ctrl-u": "editor::DeleteToBeginningOfLine",
"ctrl-t": "vim::Indent",
"ctrl-d": "vim::Outdent",
@@ -354,6 +356,15 @@
"ctrl-s": "editor::ShowSignatureHelp"
}
},
+ {
+ "context": "showing_completions",
+ "bindings": {
+ "ctrl-d": "vim::ScrollDown",
+ "ctrl-u": "vim::ScrollUp",
+ "ctrl-e": "vim::LineDown",
+ "ctrl-y": "vim::LineUp"
+ }
+ },
{
"context": "(vim_mode == normal || vim_mode == helix_normal) && !menu",
"bindings": {
@@ -388,6 +399,9 @@
"ctrl-[": "editor::Cancel",
";": "vim::HelixCollapseSelection",
":": "command_palette::Toggle",
+ "m": "vim::PushHelixMatch",
+ "]": ["vim::PushHelixNext", { "around": true }],
+ "[": ["vim::PushHelixPrevious", { "around": true }],
"left": "vim::WrappingLeft",
"right": "vim::WrappingRight",
"h": "vim::WrappingLeft",
@@ -410,13 +424,6 @@
"insert": "vim::InsertBefore",
"alt-.": "vim::RepeatFind",
"alt-s": ["editor::SplitSelectionIntoLines", { "keep_selections": true }],
- // tree-sitter related commands
- "[ x": "editor::SelectLargerSyntaxNode",
- "] x": "editor::SelectSmallerSyntaxNode",
- "] d": "editor::GoToDiagnostic",
- "[ d": "editor::GoToPreviousDiagnostic",
- "] c": "editor::GoToHunk",
- "[ c": "editor::GoToPreviousHunk",
// Goto mode
"g n": "pane::ActivateNextItem",
"g p": "pane::ActivatePreviousItem",
@@ -428,12 +435,14 @@
"g h": "vim::StartOfLine",
"g s": "vim::FirstNonWhitespace", // "g s" default behavior is "space s"
"g e": "vim::EndOfDocument",
+ "g .": "vim::HelixGotoLastModification", // go to last modification
"g r": "editor::FindAllReferences", // zed specific
"g t": "vim::WindowTop",
"g c": "vim::WindowMiddle",
"g b": "vim::WindowBottom",
- "x": "editor::SelectLine",
+ "shift-r": "editor::Paste",
+ "x": "vim::HelixSelectLine",
"shift-x": "editor::SelectLine",
"%": "editor::SelectAll",
// Window mode
@@ -458,9 +467,6 @@
"space c": "editor::ToggleComments",
"space y": "editor::Copy",
"space p": "editor::Paste",
- // Match mode
- "m m": "vim::Matching",
- "m i w": ["workspace::SendKeystrokes", "v i w"],
"shift-u": "editor::Redo",
"ctrl-c": "editor::ToggleComments",
"d": "vim::HelixDelete",
@@ -529,7 +535,7 @@
}
},
{
- "context": "vim_operator == a || vim_operator == i || vim_operator == cs",
+ "context": "vim_operator == a || vim_operator == i || vim_operator == cs || vim_operator == helix_next || vim_operator == helix_previous",
"bindings": {
"w": "vim::Word",
"shift-w": ["vim::Word", { "ignore_punctuation": true }],
@@ -566,6 +572,48 @@
"e": "vim::EntireFile"
}
},
+ {
+ "context": "vim_operator == helix_m",
+ "bindings": {
+ "m": "vim::Matching"
+ }
+ },
+ {
+ "context": "vim_operator == helix_next",
+ "bindings": {
+ "z": "vim::NextSectionStart",
+ "shift-z": "vim::NextSectionEnd",
+ "*": "vim::NextComment",
+ "/": "vim::NextComment",
+ "-": "vim::NextLesserIndent",
+ "+": "vim::NextGreaterIndent",
+ "=": "vim::NextSameIndent",
+ "b": "pane::ActivateNextItem",
+ "shift-b": "pane::ActivateLastItem",
+ "x": "editor::SelectSmallerSyntaxNode",
+ "d": "editor::GoToDiagnostic",
+ "c": "editor::GoToHunk",
+ "space": "vim::InsertEmptyLineBelow"
+ }
+ },
+ {
+ "context": "vim_operator == helix_previous",
+ "bindings": {
+ "z": "vim::PreviousSectionStart",
+ "shift-z": "vim::PreviousSectionEnd",
+ "*": "vim::PreviousComment",
+ "/": "vim::PreviousComment",
+ "-": "vim::PreviousLesserIndent",
+ "+": "vim::PreviousGreaterIndent",
+ "=": "vim::PreviousSameIndent",
+ "b": "pane::ActivatePreviousItem",
+ "shift-b": ["pane::ActivateItem", 0],
+ "x": "editor::SelectLargerSyntaxNode",
+ "d": "editor::GoToPreviousDiagnostic",
+ "c": "editor::GoToPreviousHunk",
+ "space": "vim::InsertEmptyLineAbove"
+ }
+ },
{
"context": "vim_operator == c",
"bindings": {
@@ -819,7 +867,7 @@
"v": "project_panel::OpenPermanent",
"p": "project_panel::Open",
"x": "project_panel::RevealInFileManager",
- "s": "project_panel::OpenWithSystem",
+ "s": "workspace::OpenWithSystem",
"z d": "project_panel::CompareMarkedFiles",
"] c": "project_panel::SelectNextGitEntry",
"[ c": "project_panel::SelectPrevGitEntry",
diff --git a/assets/prompts/assistant_system_prompt.hbs b/assets/prompts/assistant_system_prompt.hbs
index b4545f5a7449bf8c562ea15d722ae8199c42e97a..f47c1ffa908b861eb81d37642a7634616c92a0d9 100644
--- a/assets/prompts/assistant_system_prompt.hbs
+++ b/assets/prompts/assistant_system_prompt.hbs
@@ -172,7 +172,7 @@ The user has specified the following rules that should be applied:
Rules title: {{title}}
{{/if}}
``````
-{{contents}}}
+{{contents}}
``````
{{/each}}
{{/if}}
diff --git a/assets/settings/default.json b/assets/settings/default.json
index c290baf0038d1b731f041e9c828746758bf9ffe3..63a11403d3dd4b30926a6a1f32e86dadf3804054 100644
--- a/assets/settings/default.json
+++ b/assets/settings/default.json
@@ -162,6 +162,12 @@
// 2. Always quit the application
// "on_last_window_closed": "quit_app",
"on_last_window_closed": "platform_default",
+ // Whether to show padding for zoomed panels.
+ // When enabled, zoomed center panels (e.g. code editor) will have padding all around,
+ // while zoomed bottom/left/right panels will have padding to the top/right/left (respectively).
+ //
+ // Default: true
+ "zoomed_padding": true,
// Whether to use the system provided dialogs for Open and Save As.
// When set to false, Zed will use the built-in keyboard-first pickers.
"use_system_path_prompts": true,
@@ -182,8 +188,8 @@
// 4. A box drawn around the following character
// "hollow"
//
- // Default: not set, defaults to "bar"
- "cursor_shape": null,
+ // Default: "bar"
+ "cursor_shape": "bar",
// Determines when the mouse cursor should be hidden in an editor or input box.
//
// 1. Never hide the mouse cursor:
@@ -217,9 +223,25 @@
"current_line_highlight": "all",
// Whether to highlight all occurrences of the selected text in an editor.
"selection_highlight": true,
+ // Whether the text selection should have rounded corners.
+ "rounded_selection": true,
// The debounce delay before querying highlights from the language
// server based on the current cursor location.
"lsp_highlight_debounce": 75,
+ // The minimum APCA perceptual contrast between foreground and background colors.
+ // APCA (Accessible Perceptual Contrast Algorithm) is more accurate than WCAG 2.x,
+ // especially for dark mode. Values range from 0 to 106.
+ //
+ // Based on APCA Readability Criterion (ARC) Bronze Simple Mode:
+ // https://readtech.org/ARC/tests/bronze-simple-mode/
+ // - 0: No contrast adjustment
+ // - 45: Minimum for large fluent text (36px+)
+ // - 60: Minimum for other content text
+ // - 75: Minimum for body text
+ // - 90: Preferred for body text
+ //
+ // This only affects text drawn over highlight backgrounds in the editor.
+ "minimum_contrast_for_highlights": 45,
// Whether to pop the completions menu while typing in an editor without
// explicitly requesting it.
"show_completions_on_input": true,
@@ -260,8 +282,8 @@
// - "warning"
// - "info"
// - "hint"
- // - null — allow all diagnostics (default)
- "diagnostics_max_severity": null,
+ // - "all" — allow all diagnostics (default)
+ "diagnostics_max_severity": "all",
// Whether to show wrap guides (vertical rulers) in the editor.
// Setting this to true will show a guide at the 'preferred_line_length' value
// if 'soft_wrap' is set to 'preferred_line_length', and will show any
@@ -273,6 +295,8 @@
"redact_private_values": false,
// The default number of lines to expand excerpts in the multibuffer by.
"expand_excerpt_lines": 5,
+ // The default number of context lines shown in multibuffer excerpts.
+ "excerpt_context_lines": 2,
// Globs to match against file paths to determine if a file is private.
"private_files": ["**/.env*", "**/*.pem", "**/*.key", "**/*.cert", "**/*.crt", "**/secrets.yml"],
// Whether to use additional LSP queries to format (and amend) the code after
@@ -357,6 +381,8 @@
// Whether to show code action buttons in the editor toolbar.
"code_actions": false
},
+ // Whether to allow windows to tab together based on the user’s tabbing preference (macOS only).
+ "use_system_window_tabs": false,
// Titlebar related settings
"title_bar": {
// Whether to show the branch icon beside branch switcher in the titlebar.
@@ -647,6 +673,8 @@
// "never"
"show": "always"
},
+ // Whether to enable drag-and-drop operations in the project panel.
+ "drag_and_drop": true,
// Whether to hide the root entry when only one folder is open in the window.
"hide_root": false
},
@@ -934,7 +962,7 @@
// 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"]
+ // One of: ["right", "left"]
"close_position": "right",
// Whether to show the file icon for a tab.
"file_icons": false,
@@ -1133,11 +1161,6 @@
// The minimum severity of the diagnostics to show inline.
// Inherits editor's diagnostics' max severity settings when `null`.
"max_severity": null
- },
- "cargo": {
- // When enabled, Zed disables rust-analyzer's check on save and starts to query
- // Cargo diagnostics separately.
- "fetch_cargo_diagnostics": false
}
},
// Files or globs of files that will be excluded by Zed entirely. They will be skipped during file
@@ -1503,6 +1526,11 @@
//
// Default: fallback
"words": "fallback",
+ // Minimum number of characters required to automatically trigger word-based completions.
+ // Before that value, it's still possible to trigger the words-based completion manually with the corresponding editor command.
+ //
+ // Default: 3
+ "words_min_length": 3,
// Whether to fetch LSP completions or not.
//
// Default: true
@@ -1575,7 +1603,7 @@
"ensure_final_newline_on_save": false
},
"Elixir": {
- "language_servers": ["elixir-ls", "!next-ls", "!lexical", "..."]
+ "language_servers": ["elixir-ls", "!expert", "!next-ls", "!lexical", "..."]
},
"Elm": {
"tab_size": 4
@@ -1600,7 +1628,7 @@
}
},
"HEEX": {
- "language_servers": ["elixir-ls", "!next-ls", "!lexical", "..."]
+ "language_servers": ["elixir-ls", "!expert", "!next-ls", "!lexical", "..."]
},
"HTML": {
"prettier": {
@@ -1629,6 +1657,9 @@
"allowed": true
}
},
+ "Kotlin": {
+ "language_servers": ["kotlin-language-server", "!kotlin-lsp", "..."]
+ },
"LaTeX": {
"formatter": "language_server",
"language_servers": ["texlab", "..."],
@@ -1642,9 +1673,6 @@
"use_on_type_format": false,
"allow_rewrap": "anywhere",
"soft_wrap": "editor_width",
- "completions": {
- "words": "disabled"
- },
"prettier": {
"allowed": true
}
@@ -1658,9 +1686,6 @@
}
},
"Plain Text": {
- "completions": {
- "words": "disabled"
- },
"allow_rewrap": "anywhere"
},
"Python": {
@@ -1751,7 +1776,7 @@
"api_url": "http://localhost:1234/api/v0"
},
"deepseek": {
- "api_url": "https://api.deepseek.com"
+ "api_url": "https://api.deepseek.com/v1"
},
"mistral": {
"api_url": "https://api.mistral.ai/v1"
@@ -1899,7 +1924,10 @@
"debugger": {
"stepping_granularity": "line",
"save_breakpoints": true,
+ "timeout": 2000,
"dock": "bottom",
+ "log_dap_communications": true,
+ "format_dap_log_messages": true,
"button": true
},
// Configures any number of settings profiles that are temporarily applied on
diff --git a/assets/settings/initial_tasks.json b/assets/settings/initial_tasks.json
index a79c550671f85d7b107db5e85883caa28fe41411..5cead67b6d5bb89e878e3bfb8d250dcbbd2ce447 100644
--- a/assets/settings/initial_tasks.json
+++ b/assets/settings/initial_tasks.json
@@ -43,8 +43,8 @@
// "args": ["--login"]
// }
// }
- "shell": "system",
+ "shell": "system"
// Represents the tags for inline runnable indicators, or spawning multiple tasks at once.
- "tags": []
+ // "tags": []
}
]
diff --git a/assets/themes/ayu/ayu.json b/assets/themes/ayu/ayu.json
index f9f8720729008efb9a17cf45bd23ce51df7d3657..0ffbb9f61e76ba8e9bc1335de6b7ae4eb2e00418 100644
--- a/assets/themes/ayu/ayu.json
+++ b/assets/themes/ayu/ayu.json
@@ -93,7 +93,7 @@
"terminal.ansi.bright_cyan": "#4c806fff",
"terminal.ansi.dim_cyan": "#cbf2e4ff",
"terminal.ansi.white": "#bfbdb6ff",
- "terminal.ansi.bright_white": "#bfbdb6ff",
+ "terminal.ansi.bright_white": "#fafafaff",
"terminal.ansi.dim_white": "#787876ff",
"link_text.hover": "#5ac1feff",
"conflict": "#feb454ff",
@@ -479,7 +479,7 @@
"terminal.ansi.bright_cyan": "#ace0cbff",
"terminal.ansi.dim_cyan": "#2a5f4aff",
"terminal.ansi.white": "#fcfcfcff",
- "terminal.ansi.bright_white": "#fcfcfcff",
+ "terminal.ansi.bright_white": "#ffffffff",
"terminal.ansi.dim_white": "#bcbec0ff",
"link_text.hover": "#3b9ee5ff",
"conflict": "#f1ad49ff",
@@ -865,7 +865,7 @@
"terminal.ansi.bright_cyan": "#4c806fff",
"terminal.ansi.dim_cyan": "#cbf2e4ff",
"terminal.ansi.white": "#cccac2ff",
- "terminal.ansi.bright_white": "#cccac2ff",
+ "terminal.ansi.bright_white": "#fafafaff",
"terminal.ansi.dim_white": "#898a8aff",
"link_text.hover": "#72cffeff",
"conflict": "#fecf72ff",
diff --git a/assets/themes/gruvbox/gruvbox.json b/assets/themes/gruvbox/gruvbox.json
index 459825c733dbf2eae1e5269885b1b2c135bd72c4..f0f0358b764526fd1d45aae3b710d20f3cce1ce8 100644
--- a/assets/themes/gruvbox/gruvbox.json
+++ b/assets/themes/gruvbox/gruvbox.json
@@ -94,7 +94,7 @@
"terminal.ansi.bright_cyan": "#45603eff",
"terminal.ansi.dim_cyan": "#c7dfbdff",
"terminal.ansi.white": "#fbf1c7ff",
- "terminal.ansi.bright_white": "#fbf1c7ff",
+ "terminal.ansi.bright_white": "#ffffffff",
"terminal.ansi.dim_white": "#b0a189ff",
"link_text.hover": "#83a598ff",
"version_control.added": "#b7bb26ff",
@@ -494,7 +494,7 @@
"terminal.ansi.bright_cyan": "#45603eff",
"terminal.ansi.dim_cyan": "#c7dfbdff",
"terminal.ansi.white": "#fbf1c7ff",
- "terminal.ansi.bright_white": "#fbf1c7ff",
+ "terminal.ansi.bright_white": "#ffffffff",
"terminal.ansi.dim_white": "#b0a189ff",
"link_text.hover": "#83a598ff",
"version_control.added": "#b7bb26ff",
@@ -894,7 +894,7 @@
"terminal.ansi.bright_cyan": "#45603eff",
"terminal.ansi.dim_cyan": "#c7dfbdff",
"terminal.ansi.white": "#fbf1c7ff",
- "terminal.ansi.bright_white": "#fbf1c7ff",
+ "terminal.ansi.bright_white": "#ffffffff",
"terminal.ansi.dim_white": "#b0a189ff",
"link_text.hover": "#83a598ff",
"version_control.added": "#b7bb26ff",
@@ -1294,7 +1294,7 @@
"terminal.ansi.bright_cyan": "#9fbca8ff",
"terminal.ansi.dim_cyan": "#253e2eff",
"terminal.ansi.white": "#fbf1c7ff",
- "terminal.ansi.bright_white": "#fbf1c7ff",
+ "terminal.ansi.bright_white": "#ffffffff",
"terminal.ansi.dim_white": "#b0a189ff",
"link_text.hover": "#0b6678ff",
"version_control.added": "#797410ff",
@@ -1694,7 +1694,7 @@
"terminal.ansi.bright_cyan": "#9fbca8ff",
"terminal.ansi.dim_cyan": "#253e2eff",
"terminal.ansi.white": "#f9f5d7ff",
- "terminal.ansi.bright_white": "#f9f5d7ff",
+ "terminal.ansi.bright_white": "#ffffffff",
"terminal.ansi.dim_white": "#b0a189ff",
"link_text.hover": "#0b6678ff",
"version_control.added": "#797410ff",
@@ -2094,7 +2094,7 @@
"terminal.ansi.bright_cyan": "#9fbca8ff",
"terminal.ansi.dim_cyan": "#253e2eff",
"terminal.ansi.white": "#f2e5bcff",
- "terminal.ansi.bright_white": "#f2e5bcff",
+ "terminal.ansi.bright_white": "#ffffffff",
"terminal.ansi.dim_white": "#b0a189ff",
"link_text.hover": "#0b6678ff",
"version_control.added": "#797410ff",
diff --git a/assets/themes/one/one.json b/assets/themes/one/one.json
index 23ebbcc67efaa9ca45748a5726ac1fd72488c451..33f6d3c6221969f1453e0dea43dcde12c5549088 100644
--- a/assets/themes/one/one.json
+++ b/assets/themes/one/one.json
@@ -93,7 +93,7 @@
"terminal.ansi.bright_cyan": "#3a565bff",
"terminal.ansi.dim_cyan": "#b9d9dfff",
"terminal.ansi.white": "#dce0e5ff",
- "terminal.ansi.bright_white": "#dce0e5ff",
+ "terminal.ansi.bright_white": "#fafafaff",
"terminal.ansi.dim_white": "#575d65ff",
"link_text.hover": "#74ade8ff",
"version_control.added": "#27a657ff",
@@ -468,7 +468,7 @@
"terminal.bright_foreground": "#242529ff",
"terminal.dim_foreground": "#fafafaff",
"terminal.ansi.black": "#242529ff",
- "terminal.ansi.bright_black": "#242529ff",
+ "terminal.ansi.bright_black": "#747579ff",
"terminal.ansi.dim_black": "#97979aff",
"terminal.ansi.red": "#d36151ff",
"terminal.ansi.bright_red": "#f0b0a4ff",
@@ -489,7 +489,7 @@
"terminal.ansi.bright_cyan": "#a3bedaff",
"terminal.ansi.dim_cyan": "#254058ff",
"terminal.ansi.white": "#fafafaff",
- "terminal.ansi.bright_white": "#fafafaff",
+ "terminal.ansi.bright_white": "#ffffffff",
"terminal.ansi.dim_white": "#aaaaaaff",
"link_text.hover": "#5c78e2ff",
"version_control.added": "#27a657ff",
diff --git a/crates/acp_thread/Cargo.toml b/crates/acp_thread/Cargo.toml
index 173f4c42083bb0fb9a43b7174dad69fb0c6acbe2..8d7bea8659c3f22d053e47d4b050bc4072e521ba 100644
--- a/crates/acp_thread/Cargo.toml
+++ b/crates/acp_thread/Cargo.toml
@@ -18,8 +18,8 @@ test-support = ["gpui/test-support", "project/test-support", "dep:parking_lot"]
[dependencies]
action_log.workspace = true
agent-client-protocol.workspace = true
-agent.workspace = true
anyhow.workspace = true
+agent_settings.workspace = true
buffer_diff.workspace = true
collections.workspace = true
editor.workspace = true
@@ -31,18 +31,21 @@ language.workspace = true
language_model.workspace = true
markdown.workspace = true
parking_lot = { workspace = true, optional = true }
+portable-pty.workspace = true
project.workspace = true
prompt_store.workspace = true
serde.workspace = true
serde_json.workspace = true
settings.workspace = true
smol.workspace = true
+task.workspace = true
terminal.workspace = true
ui.workspace = true
url.workspace = true
util.workspace = true
uuid.workspace = true
watch.workspace = true
+which.workspace = true
workspace-hack.workspace = true
[dev-dependencies]
diff --git a/crates/acp_thread/src/acp_thread.rs b/crates/acp_thread/src/acp_thread.rs
index d4d73e1eddd6ae0015049e82d825d6ecb508a985..8afa466bb607c02b7cdfe795b3168c2e20a0ba10 100644
--- a/crates/acp_thread/src/acp_thread.rs
+++ b/crates/acp_thread/src/acp_thread.rs
@@ -3,13 +3,20 @@ mod diff;
mod mention;
mod terminal;
+use agent_settings::AgentSettings;
+use collections::HashSet;
pub use connection::*;
pub use diff::*;
+use futures::future::Shared;
+use language::language_settings::FormatOnSave;
pub use mention::*;
+use project::lsp_store::{FormatTrigger, LspFormatTarget};
+use serde::{Deserialize, Serialize};
+use settings::Settings as _;
pub use terminal::*;
use action_log::ActionLog;
-use agent_client_protocol as acp;
+use agent_client_protocol::{self as acp};
use anyhow::{Context as _, Result, anyhow};
use editor::Bias;
use futures::{FutureExt, channel::oneshot, future::BoxFuture};
@@ -27,7 +34,8 @@ use std::rc::Rc;
use std::time::{Duration, Instant};
use std::{fmt::Display, mem, path::PathBuf, sync::Arc};
use ui::App;
-use util::ResultExt;
+use util::{ResultExt, get_system_shell};
+use uuid::Uuid;
#[derive(Debug)]
pub struct UserMessage {
@@ -177,38 +185,46 @@ impl ToolCall {
tool_call: acp::ToolCall,
status: ToolCallStatus,
language_registry: Arc,
+ terminals: &HashMap>,
cx: &mut App,
- ) -> Self {
- Self {
+ ) -> Result {
+ let title = if let Some((first_line, _)) = tool_call.title.split_once("\n") {
+ first_line.to_owned() + "…"
+ } else {
+ tool_call.title
+ };
+ let mut content = Vec::with_capacity(tool_call.content.len());
+ for item in tool_call.content {
+ content.push(ToolCallContent::from_acp(
+ item,
+ language_registry.clone(),
+ terminals,
+ cx,
+ )?);
+ }
+
+ let result = Self {
id: tool_call.id,
- label: cx.new(|cx| {
- Markdown::new(
- tool_call.title.into(),
- Some(language_registry.clone()),
- None,
- cx,
- )
- }),
+ label: cx
+ .new(|cx| Markdown::new(title.into(), Some(language_registry.clone()), None, cx)),
kind: tool_call.kind,
- content: tool_call
- .content
- .into_iter()
- .map(|content| ToolCallContent::from_acp(content, language_registry.clone(), cx))
- .collect(),
+ content,
locations: tool_call.locations,
resolved_locations: Vec::default(),
status,
raw_input: tool_call.raw_input,
raw_output: tool_call.raw_output,
- }
+ };
+ Ok(result)
}
fn update_fields(
&mut self,
fields: acp::ToolCallUpdateFields,
language_registry: Arc,
+ terminals: &HashMap>,
cx: &mut App,
- ) {
+ ) -> Result<()> {
let acp::ToolCallUpdateFields {
kind,
status,
@@ -229,15 +245,31 @@ impl ToolCall {
if let Some(title) = title {
self.label.update(cx, |label, cx| {
- label.replace(title, cx);
+ if let Some((first_line, _)) = title.split_once("\n") {
+ label.replace(first_line.to_owned() + "…", cx)
+ } else {
+ label.replace(title, cx);
+ }
});
}
if let Some(content) = content {
- self.content = content
- .into_iter()
- .map(|chunk| ToolCallContent::from_acp(chunk, language_registry.clone(), cx))
- .collect();
+ let new_content_len = content.len();
+ let mut content = content.into_iter();
+
+ // Reuse existing content if we can
+ for (old, new) in self.content.iter_mut().zip(content.by_ref()) {
+ old.update_from_acp(new, language_registry.clone(), terminals, cx)?;
+ }
+ for new in content {
+ self.content.push(ToolCallContent::from_acp(
+ new,
+ language_registry.clone(),
+ terminals,
+ cx,
+ )?)
+ }
+ self.content.truncate(new_content_len);
}
if let Some(locations) = locations {
@@ -259,6 +291,7 @@ impl ToolCall {
}
self.raw_output = Some(raw_output);
}
+ Ok(())
}
pub fn diffs(&self) -> impl Iterator- > {
@@ -297,11 +330,9 @@ impl ToolCall {
) -> Option {
let buffer = project
.update(cx, |project, cx| {
- if let Some(path) = project.project_path_for_absolute_path(&location.path, cx) {
- Some(project.open_buffer(path, cx))
- } else {
- None
- }
+ project
+ .project_path_for_absolute_path(&location.path, cx)
+ .map(|path| project.open_buffer(path, cx))
})
.ok()??;
let buffer = buffer.await.log_err()?;
@@ -467,7 +498,7 @@ impl ContentBlock {
fn block_string_contents(&self, block: acp::ContentBlock) -> String {
match block {
- acp::ContentBlock::Text(text_content) => text_content.text.clone(),
+ acp::ContentBlock::Text(text_content) => text_content.text,
acp::ContentBlock::ResourceLink(resource_link) => {
Self::resource_link_md(&resource_link.uri)
}
@@ -496,7 +527,7 @@ impl ContentBlock {
"`Image`".into()
}
- fn to_markdown<'a>(&'a self, cx: &'a App) -> &'a str {
+ pub fn to_markdown<'a>(&'a self, cx: &'a App) -> &'a str {
match self {
ContentBlock::Empty => "",
ContentBlock::Markdown { markdown } => markdown.read(cx).source(),
@@ -531,13 +562,16 @@ impl ToolCallContent {
pub fn from_acp(
content: acp::ToolCallContent,
language_registry: Arc,
+ terminals: &HashMap>,
cx: &mut App,
- ) -> Self {
+ ) -> Result {
match content {
- acp::ToolCallContent::Content { content } => {
- Self::ContentBlock(ContentBlock::new(content, &language_registry, cx))
- }
- acp::ToolCallContent::Diff { diff } => Self::Diff(cx.new(|cx| {
+ acp::ToolCallContent::Content { content } => Ok(Self::ContentBlock(ContentBlock::new(
+ content,
+ &language_registry,
+ cx,
+ ))),
+ acp::ToolCallContent::Diff { diff } => Ok(Self::Diff(cx.new(|cx| {
Diff::finalized(
diff.path,
diff.old_text,
@@ -545,8 +579,37 @@ impl ToolCallContent {
language_registry,
cx,
)
- })),
+ }))),
+ acp::ToolCallContent::Terminal { terminal_id } => terminals
+ .get(&terminal_id)
+ .cloned()
+ .map(Self::Terminal)
+ .ok_or_else(|| anyhow::anyhow!("Terminal with id `{}` not found", terminal_id)),
+ }
+ }
+
+ pub fn update_from_acp(
+ &mut self,
+ new: acp::ToolCallContent,
+ language_registry: Arc,
+ terminals: &HashMap>,
+ cx: &mut App,
+ ) -> Result<()> {
+ let needs_update = match (&self, &new) {
+ (Self::Diff(old_diff), acp::ToolCallContent::Diff { diff: new_diff }) => {
+ old_diff.read(cx).needs_update(
+ new_diff.old_text.as_deref().unwrap_or(""),
+ &new_diff.new_text,
+ cx,
+ )
+ }
+ _ => true,
+ };
+
+ if needs_update {
+ *self = Self::from_acp(new, language_registry, terminals, cx)?;
}
+ Ok(())
}
pub fn to_markdown(&self, cx: &App) -> String {
@@ -664,6 +727,43 @@ impl PlanEntry {
}
}
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct TokenUsage {
+ pub max_tokens: u64,
+ pub used_tokens: u64,
+}
+
+impl TokenUsage {
+ pub fn ratio(&self) -> TokenUsageRatio {
+ #[cfg(debug_assertions)]
+ let warning_threshold: f32 = std::env::var("ZED_THREAD_WARNING_THRESHOLD")
+ .unwrap_or("0.8".to_string())
+ .parse()
+ .unwrap();
+ #[cfg(not(debug_assertions))]
+ let warning_threshold: f32 = 0.8;
+
+ // When the maximum is unknown because there is no selected model,
+ // avoid showing the token limit warning.
+ if self.max_tokens == 0 {
+ TokenUsageRatio::Normal
+ } else if self.used_tokens >= self.max_tokens {
+ TokenUsageRatio::Exceeded
+ } else if self.used_tokens as f32 / self.max_tokens as f32 >= warning_threshold {
+ TokenUsageRatio::Warning
+ } else {
+ TokenUsageRatio::Normal
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum TokenUsageRatio {
+ Normal,
+ Warning,
+ Exceeded,
+}
+
#[derive(Debug, Clone)]
pub struct RetryStatus {
pub last_error: SharedString,
@@ -683,24 +783,33 @@ pub struct AcpThread {
send_task: Option>,
connection: Rc,
session_id: acp::SessionId,
+ token_usage: Option,
+ prompt_capabilities: acp::PromptCapabilities,
+ _observe_prompt_capabilities: Task>,
+ determine_shell: Shared>,
+ terminals: HashMap>,
}
#[derive(Debug)]
pub enum AcpThreadEvent {
NewEntry,
TitleUpdated,
+ TokenUsageUpdated,
EntryUpdated(usize),
EntriesRemoved(Range),
ToolAuthorizationRequired,
Retry(RetryStatus),
Stopped,
Error,
- ServerExited(ExitStatus),
+ LoadError(LoadError),
+ PromptCapabilitiesUpdated,
+ Refusal,
+ AvailableCommandsUpdated(Vec),
}
impl EventEmitter for AcpThread {}
-#[derive(PartialEq, Eq)]
+#[derive(PartialEq, Eq, Debug)]
pub enum ThreadStatus {
Idle,
WaitingForToolConfirmation,
@@ -710,20 +819,33 @@ pub enum ThreadStatus {
#[derive(Debug, Clone)]
pub enum LoadError {
Unsupported {
- error_message: SharedString,
- upgrade_message: SharedString,
- upgrade_command: String,
+ command: SharedString,
+ current_version: SharedString,
+ minimum_version: SharedString,
+ },
+ FailedToInstall(SharedString),
+ Exited {
+ status: ExitStatus,
},
- Exited(i32),
Other(SharedString),
}
impl Display for LoadError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
- LoadError::Unsupported { error_message, .. } => write!(f, "{}", error_message),
- LoadError::Exited(status) => write!(f, "Server exited with status {}", status),
- LoadError::Other(msg) => write!(f, "{}", msg),
+ LoadError::Unsupported {
+ command: path,
+ current_version,
+ minimum_version,
+ } => {
+ write!(
+ f,
+ "version {current_version} from {path} is not supported (need at least {minimum_version})"
+ )
+ }
+ LoadError::FailedToInstall(msg) => write!(f, "Failed to install: {msg}"),
+ LoadError::Exited { status } => write!(f, "Server exited with status {status}"),
+ LoadError::Other(msg) => write!(f, "{msg}"),
}
}
}
@@ -737,7 +859,34 @@ impl AcpThread {
project: Entity,
action_log: Entity,
session_id: acp::SessionId,
+ mut prompt_capabilities_rx: watch::Receiver,
+ cx: &mut Context,
) -> Self {
+ let prompt_capabilities = *prompt_capabilities_rx.borrow();
+ let task = cx.spawn::<_, anyhow::Result<()>>(async move |this, cx| {
+ loop {
+ let caps = prompt_capabilities_rx.recv().await?;
+ this.update(cx, |this, cx| {
+ this.prompt_capabilities = caps;
+ cx.emit(AcpThreadEvent::PromptCapabilitiesUpdated);
+ })?;
+ }
+ });
+
+ let determine_shell = cx
+ .background_spawn(async move {
+ if cfg!(windows) {
+ return get_system_shell();
+ }
+
+ if which::which("bash").is_ok() {
+ "bash".into()
+ } else {
+ get_system_shell()
+ }
+ })
+ .shared();
+
Self {
action_log,
shared_buffers: Default::default(),
@@ -748,9 +897,18 @@ impl AcpThread {
send_task: None,
connection,
session_id,
+ token_usage: None,
+ prompt_capabilities,
+ _observe_prompt_capabilities: task,
+ terminals: HashMap::default(),
+ determine_shell,
}
}
+ pub fn prompt_capabilities(&self) -> acp::PromptCapabilities {
+ self.prompt_capabilities
+ }
+
pub fn connection(&self) -> &Rc {
&self.connection
}
@@ -787,6 +945,10 @@ impl AcpThread {
}
}
+ pub fn token_usage(&self) -> Option<&TokenUsage> {
+ self.token_usage.as_ref()
+ }
+
pub fn has_pending_edit_tool_calls(&self) -> bool {
for entry in self.entries.iter().rev() {
match entry {
@@ -842,6 +1004,9 @@ impl AcpThread {
acp::SessionUpdate::Plan(plan) => {
self.update_plan(plan, cx);
}
+ acp::SessionUpdate::AvailableCommandsUpdate { available_commands } => {
+ cx.emit(AcpThreadEvent::AvailableCommandsUpdated(available_commands))
+ }
}
Ok(())
}
@@ -931,10 +1096,24 @@ impl AcpThread {
cx.emit(AcpThreadEvent::NewEntry);
}
- pub fn update_title(&mut self, title: SharedString, cx: &mut Context) -> Result<()> {
- self.title = title;
- cx.emit(AcpThreadEvent::TitleUpdated);
- Ok(())
+ pub fn can_set_title(&mut self, cx: &mut Context) -> bool {
+ self.connection.set_title(&self.session_id, cx).is_some()
+ }
+
+ pub fn set_title(&mut self, title: SharedString, cx: &mut Context) -> Task> {
+ if title != self.title {
+ self.title = title.clone();
+ cx.emit(AcpThreadEvent::TitleUpdated);
+ if let Some(set_title) = self.connection.set_title(&self.session_id, cx) {
+ return set_title.run(title, cx);
+ }
+ }
+ Task::ready(Ok(()))
+ }
+
+ pub fn update_token_usage(&mut self, usage: Option, cx: &mut Context) {
+ self.token_usage = usage;
+ cx.emit(AcpThreadEvent::TokenUsageUpdated);
}
pub fn update_retry_status(&mut self, status: RetryStatus, cx: &mut Context) {
@@ -949,27 +1128,28 @@ impl AcpThread {
let update = update.into();
let languages = self.project.read(cx).languages().clone();
- let (ix, current_call) = self
- .tool_call_mut(update.id())
+ let ix = self
+ .index_for_tool_call(update.id())
.context("Tool call not found")?;
+ let AgentThreadEntry::ToolCall(call) = &mut self.entries[ix] else {
+ unreachable!()
+ };
+
match update {
ToolCallUpdate::UpdateFields(update) => {
let location_updated = update.fields.locations.is_some();
- current_call.update_fields(update.fields, languages, cx);
+ call.update_fields(update.fields, languages, &self.terminals, cx)?;
if location_updated {
- self.resolve_locations(update.id.clone(), cx);
+ self.resolve_locations(update.id, cx);
}
}
ToolCallUpdate::UpdateDiff(update) => {
- current_call.content.clear();
- current_call
- .content
- .push(ToolCallContent::Diff(update.diff));
+ call.content.clear();
+ call.content.push(ToolCallContent::Diff(update.diff));
}
ToolCallUpdate::UpdateTerminal(update) => {
- current_call.content.clear();
- current_call
- .content
+ call.content.clear();
+ call.content
.push(ToolCallContent::Terminal(update.terminal));
}
}
@@ -992,21 +1172,30 @@ impl AcpThread {
/// Fails if id does not match an existing entry.
pub fn upsert_tool_call_inner(
&mut self,
- tool_call_update: acp::ToolCallUpdate,
+ update: acp::ToolCallUpdate,
status: ToolCallStatus,
cx: &mut Context,
) -> Result<(), acp::Error> {
let language_registry = self.project.read(cx).languages().clone();
- let id = tool_call_update.id.clone();
+ let id = update.id.clone();
+
+ if let Some(ix) = self.index_for_tool_call(&id) {
+ let AgentThreadEntry::ToolCall(call) = &mut self.entries[ix] else {
+ unreachable!()
+ };
- if let Some((ix, current_call)) = self.tool_call_mut(&id) {
- current_call.update_fields(tool_call_update.fields, language_registry, cx);
- current_call.status = status;
+ call.update_fields(update.fields, language_registry, &self.terminals, cx)?;
+ call.status = status;
cx.emit(AcpThreadEvent::EntryUpdated(ix));
} else {
- let call =
- ToolCall::from_acp(tool_call_update.try_into()?, status, language_registry, cx);
+ let call = ToolCall::from_acp(
+ update.try_into()?,
+ status,
+ language_registry,
+ &self.terminals,
+ cx,
+ )?;
self.push_entry(AgentThreadEntry::ToolCall(call), cx);
};
@@ -1014,6 +1203,22 @@ impl AcpThread {
Ok(())
}
+ fn index_for_tool_call(&self, id: &acp::ToolCallId) -> Option {
+ self.entries
+ .iter()
+ .enumerate()
+ .rev()
+ .find_map(|(index, entry)| {
+ if let AgentThreadEntry::ToolCall(tool_call) = entry
+ && &tool_call.id == id
+ {
+ Some(index)
+ } else {
+ None
+ }
+ })
+ }
+
fn tool_call_mut(&mut self, id: &acp::ToolCallId) -> Option<(usize, &mut ToolCall)> {
// The tool call we are looking for is typically the last one, or very close to the end.
// At the moment, it doesn't seem like a hashmap would be a good fit for this use case.
@@ -1032,6 +1237,22 @@ impl AcpThread {
})
}
+ pub fn tool_call(&mut self, id: &acp::ToolCallId) -> Option<(usize, &ToolCall)> {
+ self.entries
+ .iter()
+ .enumerate()
+ .rev()
+ .find_map(|(index, tool_call)| {
+ if let AgentThreadEntry::ToolCall(tool_call) = tool_call
+ && &tool_call.id == id
+ {
+ Some((index, tool_call))
+ } else {
+ None
+ }
+ })
+ }
+
pub fn resolve_locations(&mut self, id: acp::ToolCallId, cx: &mut Context) {
let project = self.project.clone();
let Some((_, tool_call)) = self.tool_call_mut(&id) else {
@@ -1083,9 +1304,29 @@ impl AcpThread {
tool_call: acp::ToolCallUpdate,
options: Vec,
cx: &mut Context,
- ) -> Result, acp::Error> {
+ ) -> Result> {
let (tx, rx) = oneshot::channel();
+ if AgentSettings::get_global(cx).always_allow_tool_actions {
+ // Don't use AllowAlways, because then if you were to turn off always_allow_tool_actions,
+ // some tools would (incorrectly) continue to auto-accept.
+ if let Some(allow_once_option) = options.iter().find_map(|option| {
+ if matches!(option.kind, acp::PermissionOptionKind::AllowOnce) {
+ Some(option.id.clone())
+ } else {
+ None
+ }
+ }) {
+ self.upsert_tool_call_inner(tool_call, ToolCallStatus::Pending, cx)?;
+ return Ok(async {
+ acp::RequestPermissionOutcome::Selected {
+ option_id: allow_once_option,
+ }
+ }
+ .boxed());
+ }
+ }
+
let status = ToolCallStatus::WaitingForConfirmation {
options,
respond_tx: tx,
@@ -1093,7 +1334,16 @@ impl AcpThread {
self.upsert_tool_call_inner(tool_call, status, cx)?;
cx.emit(AcpThreadEvent::ToolAuthorizationRequired);
- Ok(rx)
+
+ let fut = async {
+ match rx.await {
+ Ok(option) => acp::RequestPermissionOutcome::Selected { option_id: option },
+ Err(oneshot::Canceled) => acp::RequestPermissionOutcome::Cancelled,
+ }
+ }
+ .boxed();
+
+ Ok(fut)
}
pub fn authorize_tool_call(
@@ -1216,11 +1466,7 @@ impl AcpThread {
};
let git_store = self.project.read(cx).git_store().clone();
- let message_id = if self
- .connection
- .session_editor(&self.session_id, cx)
- .is_some()
- {
+ let message_id = if self.connection.truncate(&self.session_id, cx).is_some() {
Some(UserMessageId::new())
} else {
None
@@ -1258,6 +1504,10 @@ impl AcpThread {
})
}
+ pub fn can_resume(&self, cx: &App) -> bool {
+ self.connection.resume(&self.session_id, cx).is_some()
+ }
+
pub fn resume(&mut self, cx: &mut Context) -> BoxFuture<'static, Result<()>> {
self.run_turn(cx, async move |this, cx| {
this.update(cx, |this, cx| {
@@ -1304,7 +1554,7 @@ impl AcpThread {
let canceled = matches!(
result,
Ok(Ok(acp::PromptResponse {
- stop_reason: acp::StopReason::Canceled
+ stop_reason: acp::StopReason::Cancelled
}))
);
@@ -1317,6 +1567,44 @@ impl AcpThread {
this.send_task.take();
}
+ // Handle refusal - distinguish between user prompt and tool call refusals
+ if let Ok(Ok(acp::PromptResponse {
+ stop_reason: acp::StopReason::Refusal,
+ })) = result
+ {
+ if let Some((user_msg_ix, _)) = this.last_user_message() {
+ // Check if there's a completed tool call with results after the last user message
+ // This indicates the refusal is in response to tool output, not the user's prompt
+ let has_completed_tool_call_after_user_msg =
+ this.entries.iter().skip(user_msg_ix + 1).any(|entry| {
+ if let AgentThreadEntry::ToolCall(tool_call) = entry {
+ // Check if the tool call has completed and has output
+ matches!(tool_call.status, ToolCallStatus::Completed)
+ && tool_call.raw_output.is_some()
+ } else {
+ false
+ }
+ });
+
+ if has_completed_tool_call_after_user_msg {
+ // Refusal is due to tool output - don't truncate, just notify
+ // The model refused based on what the tool returned
+ cx.emit(AcpThreadEvent::Refusal);
+ } else {
+ // User prompt was refused - truncate back to before the user message
+ let range = user_msg_ix..this.entries.len();
+ if range.start < range.end {
+ this.entries.truncate(user_msg_ix);
+ cx.emit(AcpThreadEvent::EntriesRemoved(range));
+ }
+ cx.emit(AcpThreadEvent::Refusal);
+ }
+ } else {
+ // No user message found, treat as general refusal
+ cx.emit(AcpThreadEvent::Refusal);
+ }
+ }
+
cx.emit(AcpThreadEvent::Stopped);
Ok(())
}
@@ -1355,7 +1643,7 @@ impl AcpThread {
/// Rewinds this thread to before the entry at `index`, removing it and all
/// subsequent entries while reverting any changes made from that point.
pub fn rewind(&mut self, id: UserMessageId, cx: &mut Context) -> Task> {
- let Some(session_editor) = self.connection.session_editor(&self.session_id, cx) else {
+ let Some(truncate) = self.connection.truncate(&self.session_id, cx) else {
return Task::ready(Err(anyhow!("not supported")));
};
let Some(message) = self.user_message(&id) else {
@@ -1375,8 +1663,7 @@ impl AcpThread {
.await?;
}
- cx.update(|cx| session_editor.truncate(id.clone(), cx))?
- .await?;
+ cx.update(|cx| truncate.run(id.clone(), cx))?.await?;
this.update(cx, |this, cx| {
if let Some((ix, _)) = this.user_message_mut(&id) {
let range = ix..this.entries.len();
@@ -1582,42 +1869,198 @@ impl AcpThread {
.collect::>()
})
.await;
- cx.update(|cx| {
- project.update(cx, |project, cx| {
- project.set_agent_location(
- Some(AgentLocation {
- buffer: buffer.downgrade(),
- position: edits
- .last()
- .map(|(range, _)| range.end)
- .unwrap_or(Anchor::MIN),
- }),
- cx,
- );
- });
+ project.update(cx, |project, cx| {
+ project.set_agent_location(
+ Some(AgentLocation {
+ buffer: buffer.downgrade(),
+ position: edits
+ .last()
+ .map(|(range, _)| range.end)
+ .unwrap_or(Anchor::MIN),
+ }),
+ cx,
+ );
+ })?;
+
+ let format_on_save = cx.update(|cx| {
action_log.update(cx, |action_log, cx| {
action_log.buffer_read(buffer.clone(), cx);
});
- buffer.update(cx, |buffer, cx| {
+
+ let format_on_save = buffer.update(cx, |buffer, cx| {
buffer.edit(edits, None, cx);
+
+ let settings = language::language_settings::language_settings(
+ buffer.language().map(|l| l.name()),
+ buffer.file(),
+ cx,
+ );
+
+ settings.format_on_save != FormatOnSave::Off
});
action_log.update(cx, |action_log, cx| {
action_log.buffer_edited(buffer.clone(), cx);
});
+ format_on_save
})?;
+
+ if format_on_save {
+ let format_task = project.update(cx, |project, cx| {
+ project.format(
+ HashSet::from_iter([buffer.clone()]),
+ LspFormatTarget::Buffers,
+ false,
+ FormatTrigger::Save,
+ cx,
+ )
+ })?;
+ format_task.await.log_err();
+
+ action_log.update(cx, |action_log, cx| {
+ action_log.buffer_edited(buffer.clone(), cx);
+ })?;
+ }
+
project
.update(cx, |project, cx| project.save_buffer(buffer, cx))?
.await
})
}
+ pub fn create_terminal(
+ &self,
+ mut command: String,
+ args: Vec,
+ extra_env: Vec,
+ cwd: Option,
+ output_byte_limit: Option,
+ cx: &mut Context,
+ ) -> Task>> {
+ for arg in args {
+ command.push(' ');
+ command.push_str(&arg);
+ }
+
+ let shell_command = if cfg!(windows) {
+ format!("$null | & {{{}}}", command.replace("\"", "'"))
+ } else if let Some(cwd) = cwd.as_ref().and_then(|cwd| cwd.as_os_str().to_str()) {
+ // Make sure once we're *inside* the shell, we cd into `cwd`
+ format!("(cd {cwd}; {}) self.project.update(cx, |project, cx| {
+ project.directory_environment(dir.as_path().into(), cx)
+ }),
+ None => Task::ready(None).shared(),
+ };
+
+ let env = cx.spawn(async move |_, _| {
+ let mut env = env.await.unwrap_or_default();
+ if cfg!(unix) {
+ env.insert("PAGER".into(), "cat".into());
+ }
+ for var in extra_env {
+ env.insert(var.name, var.value);
+ }
+ env
+ });
+
+ let project = self.project.clone();
+ let language_registry = project.read(cx).languages().clone();
+ let determine_shell = self.determine_shell.clone();
+
+ let terminal_id = acp::TerminalId(Uuid::new_v4().to_string().into());
+ let terminal_task = cx.spawn({
+ let terminal_id = terminal_id.clone();
+ async move |_this, cx| {
+ let program = determine_shell.await;
+ let env = env.await;
+ let terminal = project
+ .update(cx, |project, cx| {
+ project.create_terminal_task(
+ task::SpawnInTerminal {
+ command: Some(program),
+ args,
+ cwd: cwd.clone(),
+ env,
+ ..Default::default()
+ },
+ cx,
+ )
+ })?
+ .await?;
+
+ cx.new(|cx| {
+ Terminal::new(
+ terminal_id,
+ command,
+ cwd,
+ output_byte_limit.map(|l| l as usize),
+ terminal,
+ language_registry,
+ cx,
+ )
+ })
+ }
+ });
+
+ cx.spawn(async move |this, cx| {
+ let terminal = terminal_task.await?;
+ this.update(cx, |this, _cx| {
+ this.terminals.insert(terminal_id, terminal.clone());
+ terminal
+ })
+ })
+ }
+
+ pub fn kill_terminal(
+ &mut self,
+ terminal_id: acp::TerminalId,
+ cx: &mut Context,
+ ) -> Result<()> {
+ self.terminals
+ .get(&terminal_id)
+ .context("Terminal not found")?
+ .update(cx, |terminal, cx| {
+ terminal.kill(cx);
+ });
+
+ Ok(())
+ }
+
+ pub fn release_terminal(
+ &mut self,
+ terminal_id: acp::TerminalId,
+ cx: &mut Context,
+ ) -> Result<()> {
+ self.terminals
+ .remove(&terminal_id)
+ .context("Terminal not found")?
+ .update(cx, |terminal, cx| {
+ terminal.kill(cx);
+ });
+
+ Ok(())
+ }
+
+ pub fn terminal(&self, terminal_id: acp::TerminalId) -> Result> {
+ self.terminals
+ .get(&terminal_id)
+ .context("Terminal not found")
+ .cloned()
+ }
+
pub fn to_markdown(&self, cx: &App) -> String {
self.entries.iter().map(|e| e.to_markdown(cx)).collect()
}
- pub fn emit_server_exited(&mut self, status: ExitStatus, cx: &mut Context) {
- cx.emit(AcpThreadEvent::ServerExited(status));
+ pub fn emit_load_error(&mut self, error: LoadError, cx: &mut Context) {
+ cx.emit(AcpThreadEvent::LoadError(error));
}
}
@@ -1671,7 +2114,7 @@ mod tests {
use gpui::{App, AsyncApp, TestAppContext, WeakEntity};
use indoc::indoc;
use project::{FakeFs, Fs};
- use rand::Rng as _;
+ use rand::{distr, prelude::*};
use serde_json::json;
use settings::SettingsStore;
use smol::stream::StreamExt as _;
@@ -2263,6 +2706,273 @@ mod tests {
assert_eq!(fs.files(), vec![Path::new(path!("/test/file-0"))]);
}
+ #[gpui::test]
+ async fn test_tool_result_refusal(cx: &mut TestAppContext) {
+ use std::sync::atomic::AtomicUsize;
+ init_test(cx);
+
+ let fs = FakeFs::new(cx.executor());
+ let project = Project::test(fs, None, cx).await;
+
+ // Create a connection that simulates refusal after tool result
+ let prompt_count = Arc::new(AtomicUsize::new(0));
+ let connection = Rc::new(FakeAgentConnection::new().on_user_message({
+ let prompt_count = prompt_count.clone();
+ move |_request, thread, mut cx| {
+ let count = prompt_count.fetch_add(1, SeqCst);
+ async move {
+ if count == 0 {
+ // First prompt: Generate a tool call with result
+ thread.update(&mut cx, |thread, cx| {
+ thread
+ .handle_session_update(
+ acp::SessionUpdate::ToolCall(acp::ToolCall {
+ id: acp::ToolCallId("tool1".into()),
+ title: "Test Tool".into(),
+ kind: acp::ToolKind::Fetch,
+ status: acp::ToolCallStatus::Completed,
+ content: vec![],
+ locations: vec![],
+ raw_input: Some(serde_json::json!({"query": "test"})),
+ raw_output: Some(
+ serde_json::json!({"result": "inappropriate content"}),
+ ),
+ }),
+ cx,
+ )
+ .unwrap();
+ })?;
+
+ // Now return refusal because of the tool result
+ Ok(acp::PromptResponse {
+ stop_reason: acp::StopReason::Refusal,
+ })
+ } else {
+ Ok(acp::PromptResponse {
+ stop_reason: acp::StopReason::EndTurn,
+ })
+ }
+ }
+ .boxed_local()
+ }
+ }));
+
+ let thread = cx
+ .update(|cx| connection.new_thread(project, Path::new(path!("/test")), cx))
+ .await
+ .unwrap();
+
+ // Track if we see a Refusal event
+ let saw_refusal_event = Arc::new(std::sync::Mutex::new(false));
+ let saw_refusal_event_captured = saw_refusal_event.clone();
+ thread.update(cx, |_thread, cx| {
+ cx.subscribe(
+ &thread,
+ move |_thread, _event_thread, event: &AcpThreadEvent, _cx| {
+ if matches!(event, AcpThreadEvent::Refusal) {
+ *saw_refusal_event_captured.lock().unwrap() = true;
+ }
+ },
+ )
+ .detach();
+ });
+
+ // Send a user message - this will trigger tool call and then refusal
+ let send_task = thread.update(cx, |thread, cx| {
+ thread.send(
+ vec![acp::ContentBlock::Text(acp::TextContent {
+ text: "Hello".into(),
+ annotations: None,
+ })],
+ cx,
+ )
+ });
+ cx.background_executor.spawn(send_task).detach();
+ cx.run_until_parked();
+
+ // Verify that:
+ // 1. A Refusal event WAS emitted (because it's a tool result refusal, not user prompt)
+ // 2. The user message was NOT truncated
+ assert!(
+ *saw_refusal_event.lock().unwrap(),
+ "Refusal event should be emitted for tool result refusals"
+ );
+
+ thread.read_with(cx, |thread, _| {
+ let entries = thread.entries();
+ assert!(entries.len() >= 2, "Should have user message and tool call");
+
+ // Verify user message is still there
+ assert!(
+ matches!(entries[0], AgentThreadEntry::UserMessage(_)),
+ "User message should not be truncated"
+ );
+
+ // Verify tool call is there with result
+ if let AgentThreadEntry::ToolCall(tool_call) = &entries[1] {
+ assert!(
+ tool_call.raw_output.is_some(),
+ "Tool call should have output"
+ );
+ } else {
+ panic!("Expected tool call at index 1");
+ }
+ });
+ }
+
+ #[gpui::test]
+ async fn test_user_prompt_refusal_emits_event(cx: &mut TestAppContext) {
+ init_test(cx);
+
+ let fs = FakeFs::new(cx.executor());
+ let project = Project::test(fs, None, cx).await;
+
+ let refuse_next = Arc::new(AtomicBool::new(false));
+ let connection = Rc::new(FakeAgentConnection::new().on_user_message({
+ let refuse_next = refuse_next.clone();
+ move |_request, _thread, _cx| {
+ if refuse_next.load(SeqCst) {
+ async move {
+ Ok(acp::PromptResponse {
+ stop_reason: acp::StopReason::Refusal,
+ })
+ }
+ .boxed_local()
+ } else {
+ async move {
+ Ok(acp::PromptResponse {
+ stop_reason: acp::StopReason::EndTurn,
+ })
+ }
+ .boxed_local()
+ }
+ }
+ }));
+
+ let thread = cx
+ .update(|cx| connection.new_thread(project, Path::new(path!("/test")), cx))
+ .await
+ .unwrap();
+
+ // Track if we see a Refusal event
+ let saw_refusal_event = Arc::new(std::sync::Mutex::new(false));
+ let saw_refusal_event_captured = saw_refusal_event.clone();
+ thread.update(cx, |_thread, cx| {
+ cx.subscribe(
+ &thread,
+ move |_thread, _event_thread, event: &AcpThreadEvent, _cx| {
+ if matches!(event, AcpThreadEvent::Refusal) {
+ *saw_refusal_event_captured.lock().unwrap() = true;
+ }
+ },
+ )
+ .detach();
+ });
+
+ // Send a message that will be refused
+ refuse_next.store(true, SeqCst);
+ cx.update(|cx| thread.update(cx, |thread, cx| thread.send(vec!["hello".into()], cx)))
+ .await
+ .unwrap();
+
+ // Verify that a Refusal event WAS emitted for user prompt refusal
+ assert!(
+ *saw_refusal_event.lock().unwrap(),
+ "Refusal event should be emitted for user prompt refusals"
+ );
+
+ // Verify the message was truncated (user prompt refusal)
+ thread.read_with(cx, |thread, cx| {
+ assert_eq!(thread.to_markdown(cx), "");
+ });
+ }
+
+ #[gpui::test]
+ async fn test_refusal(cx: &mut TestAppContext) {
+ init_test(cx);
+ let fs = FakeFs::new(cx.background_executor.clone());
+ fs.insert_tree(path!("/"), json!({})).await;
+ let project = Project::test(fs.clone(), [path!("/").as_ref()], cx).await;
+
+ let refuse_next = Arc::new(AtomicBool::new(false));
+ let connection = Rc::new(FakeAgentConnection::new().on_user_message({
+ let refuse_next = refuse_next.clone();
+ move |request, thread, mut cx| {
+ let refuse_next = refuse_next.clone();
+ async move {
+ if refuse_next.load(SeqCst) {
+ return Ok(acp::PromptResponse {
+ stop_reason: acp::StopReason::Refusal,
+ });
+ }
+
+ let acp::ContentBlock::Text(content) = &request.prompt[0] else {
+ panic!("expected text content block");
+ };
+ thread.update(&mut cx, |thread, cx| {
+ thread
+ .handle_session_update(
+ acp::SessionUpdate::AgentMessageChunk {
+ content: content.text.to_uppercase().into(),
+ },
+ cx,
+ )
+ .unwrap();
+ })?;
+ Ok(acp::PromptResponse {
+ stop_reason: acp::StopReason::EndTurn,
+ })
+ }
+ .boxed_local()
+ }
+ }));
+ let thread = cx
+ .update(|cx| connection.new_thread(project, Path::new(path!("/test")), cx))
+ .await
+ .unwrap();
+
+ cx.update(|cx| thread.update(cx, |thread, cx| thread.send(vec!["hello".into()], cx)))
+ .await
+ .unwrap();
+ thread.read_with(cx, |thread, cx| {
+ assert_eq!(
+ thread.to_markdown(cx),
+ indoc! {"
+ ## User
+
+ hello
+
+ ## Assistant
+
+ HELLO
+
+ "}
+ );
+ });
+
+ // Simulate refusing the second message. The message should be truncated
+ // when a user prompt is refused.
+ refuse_next.store(true, SeqCst);
+ cx.update(|cx| thread.update(cx, |thread, cx| thread.send(vec!["world".into()], cx)))
+ .await
+ .unwrap();
+ thread.read_with(cx, |thread, cx| {
+ assert_eq!(
+ thread.to_markdown(cx),
+ indoc! {"
+ ## User
+
+ hello
+
+ ## Assistant
+
+ HELLO
+
+ "}
+ );
+ });
+ }
+
async fn run_until_first_tool_call(
thread: &Entity,
cx: &mut TestAppContext,
@@ -2347,21 +3057,27 @@ mod tests {
cx: &mut App,
) -> Task>> {
let session_id = acp::SessionId(
- rand::thread_rng()
- .sample_iter(&rand::distributions::Alphanumeric)
+ rand::rng()
+ .sample_iter(&distr::Alphanumeric)
.take(7)
.map(char::from)
.collect::()
.into(),
);
let action_log = cx.new(|_| ActionLog::new(project.clone()));
- let thread = cx.new(|_cx| {
+ let thread = cx.new(|cx| {
AcpThread::new(
"Test",
self.clone(),
project,
action_log,
session_id.clone(),
+ watch::Receiver::constant(acp::PromptCapabilities {
+ image: true,
+ audio: true,
+ embedded_context: true,
+ }),
+ cx,
)
});
self.sessions.lock().insert(session_id, thread.downgrade());
@@ -2408,11 +3124,11 @@ mod tests {
.detach();
}
- fn session_editor(
+ fn truncate(
&self,
session_id: &acp::SessionId,
- _cx: &mut App,
- ) -> Option> {
+ _cx: &App,
+ ) -> Option> {
Some(Rc::new(FakeAgentSessionEditor {
_session_id: session_id.clone(),
}))
@@ -2427,8 +3143,8 @@ mod tests {
_session_id: acp::SessionId,
}
- impl AgentSessionEditor for FakeAgentSessionEditor {
- fn truncate(&self, _message_id: UserMessageId, _cx: &mut App) -> Task> {
+ impl AgentSessionTruncate for FakeAgentSessionEditor {
+ fn run(&self, _message_id: UserMessageId, _cx: &mut App) -> Task> {
Task::ready(Ok(()))
}
}
diff --git a/crates/acp_thread/src/connection.rs b/crates/acp_thread/src/connection.rs
index b09f383029d0d2e635076a729204b5eddadd5ad5..1c465a4cdd466e34dcb8fc31ed910f84a4469582 100644
--- a/crates/acp_thread/src/connection.rs
+++ b/crates/acp_thread/src/connection.rs
@@ -10,7 +10,7 @@ use std::{any::Any, error::Error, fmt, path::Path, rc::Rc, sync::Arc};
use ui::{App, IconName};
use uuid::Uuid;
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
pub struct UserMessageId(Arc);
impl UserMessageId {
@@ -41,18 +41,26 @@ pub trait AgentConnection {
fn resume(
&self,
_session_id: &acp::SessionId,
- _cx: &mut App,
+ _cx: &App,
) -> Option> {
None
}
fn cancel(&self, session_id: &acp::SessionId, cx: &mut App);
- fn session_editor(
+ fn truncate(
&self,
_session_id: &acp::SessionId,
- _cx: &mut App,
- ) -> Option> {
+ _cx: &App,
+ ) -> Option> {
+ None
+ }
+
+ fn set_title(
+ &self,
+ _session_id: &acp::SessionId,
+ _cx: &App,
+ ) -> Option> {
None
}
@@ -64,6 +72,9 @@ pub trait AgentConnection {
None
}
+ fn telemetry(&self) -> Option> {
+ None
+ }
fn into_any(self: Rc) -> Rc;
}
@@ -73,14 +84,31 @@ impl dyn AgentConnection {
}
}
-pub trait AgentSessionEditor {
- fn truncate(&self, message_id: UserMessageId, cx: &mut App) -> Task>;
+pub trait AgentSessionTruncate {
+ fn run(&self, message_id: UserMessageId, cx: &mut App) -> Task>;
}
pub trait AgentSessionResume {
fn run(&self, cx: &mut App) -> Task>;
}
+pub trait AgentSessionSetTitle {
+ fn run(&self, title: SharedString, cx: &mut App) -> Task>;
+}
+
+pub trait AgentTelemetry {
+ /// The name of the agent used for telemetry.
+ fn agent_name(&self) -> String;
+
+ /// A representation of the current thread state that can be serialized for
+ /// storage with telemetry events.
+ fn thread_data(
+ &self,
+ session_id: &acp::SessionId,
+ cx: &mut App,
+ ) -> Task>;
+}
+
#[derive(Debug)]
pub struct AuthRequired {
pub description: Option,
@@ -298,13 +326,19 @@ mod test_support {
) -> Task>> {
let session_id = acp::SessionId(self.sessions.lock().len().to_string().into());
let action_log = cx.new(|_| ActionLog::new(project.clone()));
- let thread = cx.new(|_cx| {
+ let thread = cx.new(|cx| {
AcpThread::new(
"Test",
self.clone(),
project,
action_log,
session_id.clone(),
+ watch::Receiver::constant(acp::PromptCapabilities {
+ image: true,
+ audio: true,
+ embedded_context: true,
+ }),
+ cx,
)
});
self.sessions.lock().insert(
@@ -358,14 +392,15 @@ mod test_support {
};
let task = cx.spawn(async move |cx| {
if let Some((tool_call, options)) = permission_request {
- let permission = thread.update(cx, |thread, cx| {
- thread.request_tool_call_authorization(
- tool_call.clone().into(),
- options.clone(),
- cx,
- )
- })?;
- permission?.await?;
+ thread
+ .update(cx, |thread, cx| {
+ thread.request_tool_call_authorization(
+ tool_call.clone().into(),
+ options.clone(),
+ cx,
+ )
+ })??
+ .await;
}
thread.update(cx, |thread, cx| {
thread.handle_session_update(update.clone(), cx).unwrap();
@@ -393,15 +428,15 @@ mod test_support {
.response_tx
.take()
{
- end_turn_tx.send(acp::StopReason::Canceled).unwrap();
+ end_turn_tx.send(acp::StopReason::Cancelled).unwrap();
}
}
- fn session_editor(
+ fn truncate(
&self,
_session_id: &agent_client_protocol::SessionId,
- _cx: &mut App,
- ) -> Option> {
+ _cx: &App,
+ ) -> Option> {
Some(Rc::new(StubAgentSessionEditor))
}
@@ -412,8 +447,8 @@ mod test_support {
struct StubAgentSessionEditor;
- impl AgentSessionEditor for StubAgentSessionEditor {
- fn truncate(&self, _: UserMessageId, _: &mut App) -> Task> {
+ impl AgentSessionTruncate for StubAgentSessionEditor {
+ fn run(&self, _: UserMessageId, _: &mut App) -> Task> {
Task::ready(Ok(()))
}
}
diff --git a/crates/acp_thread/src/diff.rs b/crates/acp_thread/src/diff.rs
index 4b779931c5d333089fa8055c0403e0ef2cf8dbaa..f75af0543e373b47b0c6de36760ba18b5d9da318 100644
--- a/crates/acp_thread/src/diff.rs
+++ b/crates/acp_thread/src/diff.rs
@@ -1,6 +1,6 @@
use anyhow::Result;
use buffer_diff::{BufferDiff, BufferDiffSnapshot};
-use editor::{MultiBuffer, PathKey};
+use editor::{MultiBuffer, PathKey, multibuffer_context_lines};
use gpui::{App, AppContext, AsyncApp, Context, Entity, Subscription, Task};
use itertools::Itertools;
use language::{
@@ -28,57 +28,46 @@ impl Diff {
cx: &mut Context,
) -> Self {
let multibuffer = cx.new(|_cx| MultiBuffer::without_headers(Capability::ReadOnly));
-
let new_buffer = cx.new(|cx| Buffer::local(new_text, cx));
- let old_buffer = cx.new(|cx| Buffer::local(old_text.unwrap_or("".into()), cx));
- let new_buffer_snapshot = new_buffer.read(cx).text_snapshot();
- let buffer_diff = cx.new(|cx| BufferDiff::new(&new_buffer_snapshot, cx));
-
+ let base_text = old_text.clone().unwrap_or(String::new()).into();
let task = cx.spawn({
let multibuffer = multibuffer.clone();
let path = path.clone();
+ let buffer = new_buffer.clone();
async move |_, cx| {
let language = language_registry
.language_for_file_path(&path)
.await
.log_err();
- new_buffer.update(cx, |buffer, cx| buffer.set_language(language.clone(), cx))?;
-
- let old_buffer_snapshot = old_buffer.update(cx, |buffer, cx| {
- buffer.set_language(language, cx);
- buffer.snapshot()
- })?;
+ buffer.update(cx, |buffer, cx| buffer.set_language(language.clone(), cx))?;
- buffer_diff
- .update(cx, |diff, cx| {
- diff.set_base_text(
- old_buffer_snapshot,
- Some(language_registry),
- new_buffer_snapshot,
- cx,
- )
- })?
- .await?;
+ let diff = build_buffer_diff(
+ old_text.unwrap_or("".into()).into(),
+ &buffer,
+ Some(language_registry.clone()),
+ cx,
+ )
+ .await?;
multibuffer
.update(cx, |multibuffer, cx| {
let hunk_ranges = {
- let buffer = new_buffer.read(cx);
- let diff = buffer_diff.read(cx);
+ let buffer = buffer.read(cx);
+ let diff = diff.read(cx);
diff.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, buffer, cx)
.map(|diff_hunk| diff_hunk.buffer_range.to_point(buffer))
.collect::>()
};
multibuffer.set_excerpts_for_path(
- PathKey::for_buffer(&new_buffer, cx),
- new_buffer.clone(),
+ PathKey::for_buffer(&buffer, cx),
+ buffer.clone(),
hunk_ranges,
- editor::DEFAULT_MULTIBUFFER_CONTEXT,
+ multibuffer_context_lines(cx),
cx,
);
- multibuffer.add_diff(buffer_diff, cx);
+ multibuffer.add_diff(diff, cx);
})
.log_err();
@@ -89,23 +78,26 @@ impl Diff {
Self::Finalized(FinalizedDiff {
multibuffer,
path,
+ base_text,
+ new_buffer,
_update_diff: task,
})
}
pub fn new(buffer: Entity, cx: &mut Context) -> Self {
- let buffer_snapshot = buffer.read(cx).snapshot();
- let base_text = buffer_snapshot.text();
- let language_registry = buffer.read(cx).language_registry();
- let text_snapshot = buffer.read(cx).text_snapshot();
+ let buffer_text_snapshot = buffer.read(cx).text_snapshot();
+ let base_text_snapshot = buffer.read(cx).snapshot();
+ let base_text = base_text_snapshot.text();
+ debug_assert_eq!(buffer_text_snapshot.text(), base_text);
let buffer_diff = cx.new(|cx| {
- let mut diff = BufferDiff::new(&text_snapshot, cx);
- let _ = diff.set_base_text(
- buffer_snapshot.clone(),
- language_registry,
- text_snapshot,
- cx,
- );
+ let mut diff = BufferDiff::new_unchanged(&buffer_text_snapshot, base_text_snapshot);
+ let snapshot = diff.snapshot(cx);
+ let secondary_diff = cx.new(|cx| {
+ let mut diff = BufferDiff::new(&buffer_text_snapshot, cx);
+ diff.set_snapshot(snapshot, &buffer_text_snapshot, cx);
+ diff
+ });
+ diff.set_secondary_diff(secondary_diff);
diff
});
@@ -123,7 +115,7 @@ impl Diff {
diff.update(cx);
}
}),
- buffer,
+ new_buffer: buffer,
diff: buffer_diff,
revealed_ranges: Vec::new(),
update_diff: Task::ready(Ok(())),
@@ -158,9 +150,9 @@ impl Diff {
.map(|buffer| buffer.read(cx).text())
.join("\n");
let path = match self {
- Diff::Pending(PendingDiff { buffer, .. }) => {
- buffer.read(cx).file().map(|file| file.path().as_ref())
- }
+ Diff::Pending(PendingDiff {
+ new_buffer: buffer, ..
+ }) => buffer.read(cx).file().map(|file| file.path().as_ref()),
Diff::Finalized(FinalizedDiff { path, .. }) => Some(path.as_path()),
};
format!(
@@ -173,12 +165,33 @@ impl Diff {
pub fn has_revealed_range(&self, cx: &App) -> bool {
self.multibuffer().read(cx).excerpt_paths().next().is_some()
}
+
+ pub fn needs_update(&self, old_text: &str, new_text: &str, cx: &App) -> bool {
+ match self {
+ Diff::Pending(PendingDiff {
+ base_text,
+ new_buffer,
+ ..
+ }) => {
+ base_text.as_str() != old_text
+ || !new_buffer.read(cx).as_rope().chunks().equals_str(new_text)
+ }
+ Diff::Finalized(FinalizedDiff {
+ base_text,
+ new_buffer,
+ ..
+ }) => {
+ base_text.as_str() != old_text
+ || !new_buffer.read(cx).as_rope().chunks().equals_str(new_text)
+ }
+ }
+ }
}
pub struct PendingDiff {
multibuffer: Entity,
base_text: Arc,
- buffer: Entity,
+ new_buffer: Entity,
diff: Entity,
revealed_ranges: Vec>,
_subscription: Subscription,
@@ -187,7 +200,7 @@ pub struct PendingDiff {
impl PendingDiff {
pub fn update(&mut self, cx: &mut Context) {
- let buffer = self.buffer.clone();
+ let buffer = self.new_buffer.clone();
let buffer_diff = self.diff.clone();
let base_text = self.base_text.clone();
self.update_diff = cx.spawn(async move |diff, cx| {
@@ -204,7 +217,10 @@ impl PendingDiff {
)
.await?;
buffer_diff.update(cx, |diff, cx| {
- diff.set_snapshot(diff_snapshot, &text_snapshot, cx)
+ diff.set_snapshot(diff_snapshot.clone(), &text_snapshot, cx);
+ diff.secondary_diff().unwrap().update(cx, |diff, cx| {
+ diff.set_snapshot(diff_snapshot.clone(), &text_snapshot, cx);
+ });
})?;
diff.update(cx, |diff, cx| {
if let Diff::Pending(diff) = diff {
@@ -222,10 +238,10 @@ impl PendingDiff {
fn finalize(&self, cx: &mut Context) -> FinalizedDiff {
let ranges = self.excerpt_ranges(cx);
let base_text = self.base_text.clone();
- let language_registry = self.buffer.read(cx).language_registry().clone();
+ let language_registry = self.new_buffer.read(cx).language_registry();
let path = self
- .buffer
+ .new_buffer
.read(cx)
.file()
.map(|file| file.path().as_ref())
@@ -234,12 +250,12 @@ impl PendingDiff {
// Replace the buffer in the multibuffer with the snapshot
let buffer = cx.new(|cx| {
- let language = self.buffer.read(cx).language().cloned();
+ let language = self.new_buffer.read(cx).language().cloned();
let buffer = TextBuffer::new_normalized(
0,
cx.entity_id().as_non_zero_u64().into(),
- self.buffer.read(cx).line_ending(),
- self.buffer.read(cx).as_rope().clone(),
+ self.new_buffer.read(cx).line_ending(),
+ self.new_buffer.read(cx).as_rope().clone(),
);
let mut buffer = Buffer::build(buffer, None, Capability::ReadWrite);
buffer.set_language(language, cx);
@@ -248,7 +264,6 @@ impl PendingDiff {
let buffer_diff = cx.spawn({
let buffer = buffer.clone();
- let language_registry = language_registry.clone();
async move |_this, cx| {
build_buffer_diff(base_text, &buffer, language_registry, cx).await
}
@@ -264,7 +279,7 @@ impl PendingDiff {
path_key,
buffer,
ranges,
- editor::DEFAULT_MULTIBUFFER_CONTEXT,
+ multibuffer_context_lines(cx),
cx,
);
multibuffer.add_diff(buffer_diff.clone(), cx);
@@ -276,7 +291,9 @@ impl PendingDiff {
FinalizedDiff {
path,
+ base_text: self.base_text.clone(),
multibuffer: self.multibuffer.clone(),
+ new_buffer: self.new_buffer.clone(),
_update_diff: update_diff,
}
}
@@ -285,10 +302,10 @@ impl PendingDiff {
let ranges = self.excerpt_ranges(cx);
self.multibuffer.update(cx, |multibuffer, cx| {
multibuffer.set_excerpts_for_path(
- PathKey::for_buffer(&self.buffer, cx),
- self.buffer.clone(),
+ PathKey::for_buffer(&self.new_buffer, cx),
+ self.new_buffer.clone(),
ranges,
- editor::DEFAULT_MULTIBUFFER_CONTEXT,
+ multibuffer_context_lines(cx),
cx,
);
let end = multibuffer.len(cx);
@@ -298,7 +315,7 @@ impl PendingDiff {
}
fn excerpt_ranges(&self, cx: &App) -> Vec> {
- let buffer = self.buffer.read(cx);
+ let buffer = self.new_buffer.read(cx);
let diff = self.diff.read(cx);
let mut ranges = diff
.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, buffer, cx)
@@ -332,6 +349,8 @@ impl PendingDiff {
pub struct FinalizedDiff {
path: PathBuf,
+ base_text: Arc,
+ new_buffer: Entity,
multibuffer: Entity,
_update_diff: Task>,
}
@@ -385,3 +404,21 @@ async fn build_buffer_diff(
diff
})
}
+
+#[cfg(test)]
+mod tests {
+ use gpui::{AppContext as _, TestAppContext};
+ use language::Buffer;
+
+ use crate::Diff;
+
+ #[gpui::test]
+ async fn test_pending_diff(cx: &mut TestAppContext) {
+ let buffer = cx.new(|cx| Buffer::local("hello!", cx));
+ let _diff = cx.new(|cx| Diff::new(buffer.clone(), cx));
+ buffer.update(cx, |buffer, cx| {
+ buffer.set_text("HELLO!", cx);
+ });
+ cx.run_until_parked();
+ }
+}
diff --git a/crates/acp_thread/src/mention.rs b/crates/acp_thread/src/mention.rs
index 4615e9a5515ef29b1dc4b9c79ff49fc018cbfd2c..6fa0887e2278467dae9887516d882da90a78d0df 100644
--- a/crates/acp_thread/src/mention.rs
+++ b/crates/acp_thread/src/mention.rs
@@ -1,32 +1,33 @@
-use agent::ThreadId;
+use agent_client_protocol as acp;
use anyhow::{Context as _, Result, bail};
use file_icons::FileIcons;
use prompt_store::{PromptId, UserPromptId};
use serde::{Deserialize, Serialize};
use std::{
fmt,
- ops::Range,
+ ops::RangeInclusive,
path::{Path, PathBuf},
str::FromStr,
};
use ui::{App, IconName, SharedString};
use url::Url;
-#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
+#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Hash)]
pub enum MentionUri {
File {
abs_path: PathBuf,
},
+ PastedImage,
Directory {
abs_path: PathBuf,
},
Symbol {
- path: PathBuf,
+ abs_path: PathBuf,
name: String,
- line_range: Range,
+ line_range: RangeInclusive,
},
Thread {
- id: ThreadId,
+ id: acp::SessionId,
name: String,
},
TextThread {
@@ -38,8 +39,9 @@ pub enum MentionUri {
name: String,
},
Selection {
- path: PathBuf,
- line_range: Range,
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ abs_path: Option,
+ line_range: RangeInclusive,
},
Fetch {
url: Url,
@@ -48,36 +50,44 @@ pub enum MentionUri {
impl MentionUri {
pub fn parse(input: &str) -> Result {
+ fn parse_line_range(fragment: &str) -> Result> {
+ let range = fragment
+ .strip_prefix("L")
+ .context("Line range must start with \"L\"")?;
+ let (start, end) = range
+ .split_once(":")
+ .context("Line range must use colon as separator")?;
+ let range = start
+ .parse::()
+ .context("Parsing line range start")?
+ .checked_sub(1)
+ .context("Line numbers should be 1-based")?
+ ..=end
+ .parse::()
+ .context("Parsing line range end")?
+ .checked_sub(1)
+ .context("Line numbers should be 1-based")?;
+ Ok(range)
+ }
+
let url = url::Url::parse(input)?;
let path = url.path();
match url.scheme() {
"file" => {
let path = url.to_file_path().ok().context("Extracting file path")?;
if let Some(fragment) = url.fragment() {
- let range = fragment
- .strip_prefix("L")
- .context("Line range must start with \"L\"")?;
- let (start, end) = range
- .split_once(":")
- .context("Line range must use colon as separator")?;
- let line_range = start
- .parse::()
- .context("Parsing line range start")?
- .checked_sub(1)
- .context("Line numbers should be 1-based")?
- ..end
- .parse::()
- .context("Parsing line range end")?
- .checked_sub(1)
- .context("Line numbers should be 1-based")?;
+ let line_range = parse_line_range(fragment)?;
if let Some(name) = single_query_param(&url, "symbol")? {
Ok(Self::Symbol {
name,
- path,
+ abs_path: path,
line_range,
})
} else {
- Ok(Self::Selection { path, line_range })
+ Ok(Self::Selection {
+ abs_path: Some(path),
+ line_range,
+ })
}
} else if input.ends_with("/") {
Ok(Self::Directory { abs_path: path })
@@ -89,7 +99,7 @@ impl MentionUri {
if let Some(thread_id) = path.strip_prefix("/agent/thread/") {
let name = single_query_param(&url, "name")?.context("Missing thread name")?;
Ok(Self::Thread {
- id: thread_id.into(),
+ id: acp::SessionId(thread_id.into()),
name,
})
} else if let Some(path) = path.strip_prefix("/agent/text-thread/") {
@@ -105,6 +115,17 @@ impl MentionUri {
id: rule_id.into(),
name,
})
+ } else if path.starts_with("/agent/pasted-image") {
+ Ok(Self::PastedImage)
+ } else if path.starts_with("/agent/untitled-buffer") {
+ let fragment = url
+ .fragment()
+ .context("Missing fragment for untitled buffer selection")?;
+ let line_range = parse_line_range(fragment)?;
+ Ok(Self::Selection {
+ abs_path: None,
+ line_range,
+ })
} else {
bail!("invalid zed url: {:?}", input);
}
@@ -121,13 +142,16 @@ impl MentionUri {
.unwrap_or_default()
.to_string_lossy()
.into_owned(),
+ MentionUri::PastedImage => "Image".to_string(),
MentionUri::Symbol { name, .. } => name.clone(),
MentionUri::Thread { name, .. } => name.clone(),
MentionUri::TextThread { name, .. } => name.clone(),
MentionUri::Rule { name, .. } => name.clone(),
MentionUri::Selection {
- path, line_range, ..
- } => selection_name(path, line_range),
+ abs_path: path,
+ line_range,
+ ..
+ } => selection_name(path.as_deref(), line_range),
MentionUri::Fetch { url } => url.to_string(),
}
}
@@ -137,6 +161,7 @@ impl MentionUri {
MentionUri::File { abs_path } => {
FileIcons::get_icon(abs_path, cx).unwrap_or_else(|| IconName::File.path().into())
}
+ MentionUri::PastedImage => IconName::Image.path().into(),
MentionUri::Directory { .. } => FileIcons::get_folder_icon(false, cx)
.unwrap_or_else(|| IconName::Folder.path().into()),
MentionUri::Symbol { .. } => IconName::Code.path().into(),
@@ -157,29 +182,40 @@ impl MentionUri {
MentionUri::File { abs_path } => {
Url::from_file_path(abs_path).expect("mention path should be absolute")
}
+ MentionUri::PastedImage => Url::parse("zed:///agent/pasted-image").unwrap(),
MentionUri::Directory { abs_path } => {
Url::from_directory_path(abs_path).expect("mention path should be absolute")
}
MentionUri::Symbol {
- path,
+ abs_path,
name,
line_range,
} => {
- let mut url = Url::from_file_path(path).expect("mention path should be absolute");
+ let mut url =
+ Url::from_file_path(abs_path).expect("mention path should be absolute");
url.query_pairs_mut().append_pair("symbol", name);
url.set_fragment(Some(&format!(
"L{}:{}",
- line_range.start + 1,
- line_range.end + 1
+ line_range.start() + 1,
+ line_range.end() + 1
)));
url
}
- MentionUri::Selection { path, line_range } => {
- let mut url = Url::from_file_path(path).expect("mention path should be absolute");
+ MentionUri::Selection {
+ abs_path: path,
+ line_range,
+ } => {
+ let mut url = if let Some(path) = path {
+ Url::from_file_path(path).expect("mention path should be absolute")
+ } else {
+ let mut url = Url::parse("zed:///").unwrap();
+ url.set_path("/agent/untitled-buffer");
+ url
+ };
url.set_fragment(Some(&format!(
"L{}:{}",
- line_range.start + 1,
- line_range.end + 1
+ line_range.start() + 1,
+ line_range.end() + 1
)));
url
}
@@ -191,7 +227,10 @@ impl MentionUri {
}
MentionUri::TextThread { path, name } => {
let mut url = Url::parse("zed:///").unwrap();
- url.set_path(&format!("/agent/text-thread/{}", path.to_string_lossy()));
+ url.set_path(&format!(
+ "/agent/text-thread/{}",
+ path.to_string_lossy().trim_start_matches('/')
+ ));
url.query_pairs_mut().append_pair("name", name);
url
}
@@ -237,12 +276,14 @@ fn single_query_param(url: &Url, name: &'static str) -> Result