Compare commits
17 Commits
commit-whi
...
load_diffs
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9fc72f136d | ||
|
|
f213251e2c | ||
|
|
8e79ab6f6a | ||
|
|
ecddbd470f | ||
|
|
42787904b0 | ||
|
|
2bdc385fdf | ||
|
|
615803646f | ||
|
|
101bbebe52 | ||
|
|
d6af4d3cdd | ||
|
|
a32319374d | ||
|
|
716937c9c9 | ||
|
|
6ee35cb43e | ||
|
|
d76c326ff5 | ||
|
|
6effe1f48e | ||
|
|
8d3153abd4 | ||
|
|
3a301afbc6 | ||
|
|
78add792c7 |
@@ -16,9 +16,7 @@ rustflags = ["-D", "warnings"]
|
||||
debug = "limited"
|
||||
|
||||
# Use Mold on Linux, because it's faster than GNU ld and LLD.
|
||||
#
|
||||
# We no longer set this in the default `config.toml` so that developers can opt in to Wild, which
|
||||
# is faster than Mold, in their own ~/.cargo/config.toml.
|
||||
# We dont use wild in CI as its not production ready.
|
||||
[target.x86_64-unknown-linux-gnu]
|
||||
linker = "clang"
|
||||
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
|
||||
|
||||
@@ -8,6 +8,14 @@ perf-test = ["test", "--profile", "release-fast", "--lib", "--bins", "--tests",
|
||||
# Keep similar flags here to share some ccache
|
||||
perf-compare = ["run", "--profile", "release-fast", "-p", "perf", "--config", "target.'cfg(true)'.rustflags=[\"--cfg\", \"perf_enabled\"]", "--", "compare"]
|
||||
|
||||
# [target.x86_64-unknown-linux-gnu]
|
||||
# linker = "clang"
|
||||
# rustflags = ["-C", "link-arg=-fuse-ld=mold"]
|
||||
|
||||
[target.aarch64-unknown-linux-gnu]
|
||||
linker = "clang"
|
||||
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
|
||||
|
||||
[target.'cfg(target_os = "windows")']
|
||||
rustflags = [
|
||||
"--cfg",
|
||||
|
||||
6
.github/workflows/after_release.yml
vendored
6
.github/workflows/after_release.yml
vendored
@@ -7,7 +7,7 @@ on:
|
||||
- published
|
||||
jobs:
|
||||
rebuild_releases_page:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on: namespace-profile-2x4-ubuntu-2404
|
||||
steps:
|
||||
- name: after_release::rebuild_releases_page::refresh_cloud_releases
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
post_to_discord:
|
||||
needs:
|
||||
- rebuild_releases_page
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on: namespace-profile-2x4-ubuntu-2404
|
||||
steps:
|
||||
- id: get-release-url
|
||||
@@ -71,7 +71,7 @@ jobs:
|
||||
max-versions-to-keep: 5
|
||||
token: ${{ secrets.WINGET_TOKEN }}
|
||||
create_sentry_release:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on: namespace-profile-2x4-ubuntu-2404
|
||||
steps:
|
||||
- name: steps::checkout_repo
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: "Close Stale Issues"
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 8 31 DEC *"
|
||||
- cron: "0 7,9,11 * * 3"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
||||
2
.github/workflows/danger.yml
vendored
2
.github/workflows/danger.yml
vendored
@@ -12,7 +12,7 @@ on:
|
||||
- main
|
||||
jobs:
|
||||
danger:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on: namespace-profile-2x4-ubuntu-2404
|
||||
steps:
|
||||
- name: steps::checkout_repo
|
||||
|
||||
138
.github/workflows/extension_tests.yml
vendored
138
.github/workflows/extension_tests.yml
vendored
@@ -1,138 +0,0 @@
|
||||
# Generated from xtask::workflows::extension_tests
|
||||
# Rebuild with `cargo xtask workflows`.
|
||||
name: extension_tests
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
RUST_BACKTRACE: '1'
|
||||
CARGO_INCREMENTAL: '0'
|
||||
ZED_EXTENSION_CLI_SHA: 7cfce605704d41ca247e3f84804bf323f6c6caaf
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
run_tests:
|
||||
description: Whether the workflow should run rust tests
|
||||
required: true
|
||||
type: boolean
|
||||
jobs:
|
||||
orchestrate:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
runs-on: namespace-profile-2x4-ubuntu-2404
|
||||
steps:
|
||||
- name: steps::checkout_repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
clean: false
|
||||
fetch-depth: ${{ github.ref == 'refs/heads/main' && 2 || 350 }}
|
||||
- id: filter
|
||||
name: filter
|
||||
run: |
|
||||
if [ -z "$GITHUB_BASE_REF" ]; then
|
||||
echo "Not in a PR context (i.e., push to main/stable/preview)"
|
||||
COMPARE_REV="$(git rev-parse HEAD~1)"
|
||||
else
|
||||
echo "In a PR context comparing to pull_request.base.ref"
|
||||
git fetch origin "$GITHUB_BASE_REF" --depth=350
|
||||
COMPARE_REV="$(git merge-base "origin/${GITHUB_BASE_REF}" HEAD)"
|
||||
fi
|
||||
CHANGED_FILES="$(git diff --name-only "$COMPARE_REV" ${{ github.sha }})"
|
||||
|
||||
check_pattern() {
|
||||
local output_name="$1"
|
||||
local pattern="$2"
|
||||
local grep_arg="$3"
|
||||
|
||||
echo "$CHANGED_FILES" | grep "$grep_arg" "$pattern" && \
|
||||
echo "${output_name}=true" >> "$GITHUB_OUTPUT" || \
|
||||
echo "${output_name}=false" >> "$GITHUB_OUTPUT"
|
||||
}
|
||||
|
||||
check_pattern "check_rust" '^(Cargo.lock|Cargo.toml|.*\.rs)$' -qP
|
||||
check_pattern "check_extension" '^.*\.scm$' -qP
|
||||
shell: bash -euxo pipefail {0}
|
||||
outputs:
|
||||
check_rust: ${{ steps.filter.outputs.check_rust }}
|
||||
check_extension: ${{ steps.filter.outputs.check_extension }}
|
||||
check_rust:
|
||||
needs:
|
||||
- orchestrate
|
||||
if: needs.orchestrate.outputs.check_rust == 'true'
|
||||
runs-on: namespace-profile-16x32-ubuntu-2204
|
||||
steps:
|
||||
- name: steps::checkout_repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
clean: false
|
||||
- name: steps::cache_rust_dependencies_namespace
|
||||
uses: namespacelabs/nscloud-cache-action@v1
|
||||
with:
|
||||
cache: rust
|
||||
- name: steps::cargo_fmt
|
||||
run: cargo fmt --all -- --check
|
||||
shell: bash -euxo pipefail {0}
|
||||
- name: extension_tests::run_clippy
|
||||
run: cargo clippy --release --all-targets --all-features -- --deny warnings
|
||||
shell: bash -euxo pipefail {0}
|
||||
- name: steps::cargo_install_nextest
|
||||
if: inputs.run_tests
|
||||
uses: taiki-e/install-action@nextest
|
||||
- name: steps::cargo_nextest
|
||||
if: inputs.run_tests
|
||||
run: cargo nextest run --workspace --no-fail-fast --failure-output immediate-final
|
||||
shell: bash -euxo pipefail {0}
|
||||
timeout-minutes: 3
|
||||
check_extension:
|
||||
needs:
|
||||
- orchestrate
|
||||
if: needs.orchestrate.outputs.check_extension == 'true'
|
||||
runs-on: namespace-profile-2x4-ubuntu-2404
|
||||
steps:
|
||||
- name: steps::checkout_repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
clean: false
|
||||
- id: cache-zed-extension-cli
|
||||
name: extension_tests::cache_zed_extension_cli
|
||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830
|
||||
with:
|
||||
path: zed-extension
|
||||
key: zed-extension-${{ env.ZED_EXTENSION_CLI_SHA }}
|
||||
- name: extension_tests::download_zed_extension_cli
|
||||
if: steps.cache-zed-extension-cli.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
wget --quiet "https://zed-extension-cli.nyc3.digitaloceanspaces.com/$ZED_EXTENSION_CLI_SHA/x86_64-unknown-linux-gnu/zed-extension"
|
||||
chmod +x zed-extension
|
||||
shell: bash -euxo pipefail {0}
|
||||
- name: extension_tests::check
|
||||
run: |
|
||||
mkdir -p /tmp/ext-scratch
|
||||
mkdir -p /tmp/ext-output
|
||||
./zed-extension --source-dir . --scratch-dir /tmp/ext-scratch --output-dir /tmp/ext-output
|
||||
shell: bash -euxo pipefail {0}
|
||||
timeout-minutes: 1
|
||||
tests_pass:
|
||||
needs:
|
||||
- orchestrate
|
||||
- check_rust
|
||||
- check_extension
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') && always()
|
||||
runs-on: namespace-profile-2x4-ubuntu-2404
|
||||
steps:
|
||||
- name: run_tests::tests_pass
|
||||
run: |
|
||||
set +x
|
||||
EXIT_CODE=0
|
||||
|
||||
check_result() {
|
||||
echo "* $1: $2"
|
||||
if [[ "$2" != "skipped" && "$2" != "success" ]]; then EXIT_CODE=1; fi
|
||||
}
|
||||
|
||||
check_result "orchestrate" "${{ needs.orchestrate.result }}"
|
||||
check_result "check_rust" "${{ needs.check_rust.result }}"
|
||||
check_result "check_extension" "${{ needs.check_extension.result }}"
|
||||
|
||||
exit $EXIT_CODE
|
||||
shell: bash -euxo pipefail {0}
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.ref_name == 'main' && github.sha || 'anysha' }}
|
||||
cancel-in-progress: true
|
||||
10
.github/workflows/release.yml
vendored
10
.github/workflows/release.yml
vendored
@@ -10,7 +10,7 @@ on:
|
||||
- v*
|
||||
jobs:
|
||||
run_tests_mac:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on: self-mini-macos
|
||||
steps:
|
||||
- name: steps::checkout_repo
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
shell: bash -euxo pipefail {0}
|
||||
timeout-minutes: 60
|
||||
run_tests_linux:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on: namespace-profile-16x32-ubuntu-2204
|
||||
steps:
|
||||
- name: steps::checkout_repo
|
||||
@@ -89,7 +89,7 @@ jobs:
|
||||
shell: bash -euxo pipefail {0}
|
||||
timeout-minutes: 60
|
||||
run_tests_windows:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on: self-32vcpu-windows-2022
|
||||
steps:
|
||||
- name: steps::checkout_repo
|
||||
@@ -121,7 +121,7 @@ jobs:
|
||||
shell: pwsh
|
||||
timeout-minutes: 60
|
||||
check_scripts:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on: namespace-profile-2x4-ubuntu-2404
|
||||
steps:
|
||||
- name: steps::checkout_repo
|
||||
@@ -150,7 +150,7 @@ jobs:
|
||||
shell: bash -euxo pipefail {0}
|
||||
timeout-minutes: 60
|
||||
create_draft_release:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on: namespace-profile-2x4-ubuntu-2404
|
||||
steps:
|
||||
- name: steps::checkout_repo
|
||||
|
||||
10
.github/workflows/release_nightly.yml
vendored
10
.github/workflows/release_nightly.yml
vendored
@@ -12,7 +12,7 @@ on:
|
||||
- cron: 0 7 * * *
|
||||
jobs:
|
||||
check_style:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on: self-mini-macos
|
||||
steps:
|
||||
- name: steps::checkout_repo
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
shell: bash -euxo pipefail {0}
|
||||
timeout-minutes: 60
|
||||
run_tests_windows:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on: self-32vcpu-windows-2022
|
||||
steps:
|
||||
- name: steps::checkout_repo
|
||||
@@ -361,7 +361,7 @@ jobs:
|
||||
needs:
|
||||
- check_style
|
||||
- run_tests_windows
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on: namespace-profile-32x64-ubuntu-2004
|
||||
env:
|
||||
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
|
||||
@@ -392,7 +392,7 @@ jobs:
|
||||
needs:
|
||||
- check_style
|
||||
- run_tests_windows
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on: self-mini-macos
|
||||
env:
|
||||
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
|
||||
@@ -434,7 +434,7 @@ jobs:
|
||||
- bundle_mac_x86_64
|
||||
- bundle_windows_aarch64
|
||||
- bundle_windows_x86_64
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on: namespace-profile-4x8-ubuntu-2204
|
||||
steps:
|
||||
- name: steps::checkout_repo
|
||||
|
||||
11
.github/workflows/run_tests.yml
vendored
11
.github/workflows/run_tests.yml
vendored
@@ -15,7 +15,7 @@ on:
|
||||
- v[0-9]+.[0-9]+.x
|
||||
jobs:
|
||||
orchestrate:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on: namespace-profile-2x4-ubuntu-2404
|
||||
steps:
|
||||
- name: steps::checkout_repo
|
||||
@@ -59,7 +59,7 @@ jobs:
|
||||
run_nix: ${{ steps.filter.outputs.run_nix }}
|
||||
run_tests: ${{ steps.filter.outputs.run_tests }}
|
||||
check_style:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on: namespace-profile-4x8-ubuntu-2204
|
||||
steps:
|
||||
- name: steps::checkout_repo
|
||||
@@ -493,10 +493,7 @@ jobs:
|
||||
needs:
|
||||
- orchestrate
|
||||
if: needs.orchestrate.outputs.run_tests == 'true'
|
||||
runs-on: namespace-profile-16x32-ubuntu-2204
|
||||
env:
|
||||
GIT_AUTHOR_NAME: Protobuf Action
|
||||
GIT_AUTHOR_EMAIL: ci@zed.dev
|
||||
runs-on: self-mini-macos
|
||||
steps:
|
||||
- name: steps::checkout_repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
@@ -541,7 +538,7 @@ jobs:
|
||||
- check_scripts
|
||||
- build_nix_linux_x86_64
|
||||
- build_nix_mac_aarch64
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') && always()
|
||||
if: github.repository_owner == 'zed-industries' && always()
|
||||
runs-on: namespace-profile-2x4-ubuntu-2404
|
||||
steps:
|
||||
- name: run_tests::tests_pass
|
||||
|
||||
46
Cargo.lock
generated
46
Cargo.lock
generated
@@ -1240,15 +1240,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async_zip"
|
||||
version = "0.0.18"
|
||||
version = "0.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d8c50d65ce1b0e0cb65a785ff615f78860d7754290647d3b983208daa4f85e6"
|
||||
checksum = "00b9f7252833d5ed4b00aa9604b563529dd5e11de9c23615de2dcdf91eb87b52"
|
||||
dependencies = [
|
||||
"async-compression",
|
||||
"crc32fast",
|
||||
"futures-lite 2.6.1",
|
||||
"pin-project",
|
||||
"thiserror 2.0.17",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2618,7 +2618,6 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "calloop"
|
||||
version = "0.14.3"
|
||||
source = "git+https://github.com/zed-industries/calloop#eb6b4fd17b9af5ecc226546bdd04185391b3e265"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"polling",
|
||||
@@ -3209,7 +3208,6 @@ dependencies = [
|
||||
"rustc-hash 2.1.1",
|
||||
"schemars 1.0.4",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"strum 0.27.2",
|
||||
]
|
||||
|
||||
@@ -3689,7 +3687,6 @@ dependencies = [
|
||||
"collections",
|
||||
"futures 0.3.31",
|
||||
"gpui",
|
||||
"http_client",
|
||||
"log",
|
||||
"net",
|
||||
"parking_lot",
|
||||
@@ -5314,13 +5311,13 @@ dependencies = [
|
||||
"serde_json",
|
||||
"settings",
|
||||
"supermaven",
|
||||
"sweep_ai",
|
||||
"telemetry",
|
||||
"theme",
|
||||
"ui",
|
||||
"workspace",
|
||||
"zed_actions",
|
||||
"zeta",
|
||||
"zeta2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8728,6 +8725,7 @@ dependencies = [
|
||||
"ui",
|
||||
"ui_input",
|
||||
"util",
|
||||
"vim",
|
||||
"workspace",
|
||||
"zed_actions",
|
||||
]
|
||||
@@ -10029,6 +10027,7 @@ name = "miniprofiler_ui"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"gpui",
|
||||
"log",
|
||||
"serde_json",
|
||||
"smol",
|
||||
"util",
|
||||
@@ -16561,10 +16560,10 @@ checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb"
|
||||
name = "svg_preview"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"editor",
|
||||
"file_icons",
|
||||
"gpui",
|
||||
"language",
|
||||
"multi_buffer",
|
||||
"ui",
|
||||
"workspace",
|
||||
]
|
||||
@@ -16590,6 +16589,33 @@ dependencies = [
|
||||
"zeno",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sweep_ai"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arrayvec",
|
||||
"brotli",
|
||||
"client",
|
||||
"collections",
|
||||
"edit_prediction",
|
||||
"feature_flags",
|
||||
"futures 0.3.31",
|
||||
"gpui",
|
||||
"http_client",
|
||||
"indoc",
|
||||
"language",
|
||||
"project",
|
||||
"release_channel",
|
||||
"reqwest_client",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tree-sitter-rust",
|
||||
"util",
|
||||
"workspace",
|
||||
"zlog",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia"
|
||||
version = "0.5.5"
|
||||
@@ -21204,7 +21230,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed"
|
||||
version = "0.215.0"
|
||||
version = "0.214.0"
|
||||
dependencies = [
|
||||
"acp_tools",
|
||||
"activity_indicator",
|
||||
@@ -21316,6 +21342,7 @@ dependencies = [
|
||||
"snippets_ui",
|
||||
"supermaven",
|
||||
"svg_preview",
|
||||
"sweep_ai",
|
||||
"sysinfo 0.37.2",
|
||||
"system_specs",
|
||||
"tab_switcher",
|
||||
@@ -21726,7 +21753,6 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arrayvec",
|
||||
"brotli",
|
||||
"chrono",
|
||||
"client",
|
||||
"clock",
|
||||
|
||||
@@ -165,6 +165,7 @@ members = [
|
||||
"crates/sum_tree",
|
||||
"crates/supermaven",
|
||||
"crates/supermaven_api",
|
||||
"crates/sweep_ai",
|
||||
"crates/codestral",
|
||||
"crates/svg_preview",
|
||||
"crates/system_specs",
|
||||
@@ -398,6 +399,7 @@ streaming_diff = { path = "crates/streaming_diff" }
|
||||
sum_tree = { path = "crates/sum_tree" }
|
||||
supermaven = { path = "crates/supermaven" }
|
||||
supermaven_api = { path = "crates/supermaven_api" }
|
||||
sweep_ai = { path = "crates/sweep_ai" }
|
||||
codestral = { path = "crates/codestral" }
|
||||
system_specs = { path = "crates/system_specs" }
|
||||
tab_switcher = { path = "crates/tab_switcher" }
|
||||
@@ -461,7 +463,7 @@ async-tar = "0.5.1"
|
||||
async-task = "4.7"
|
||||
async-trait = "0.1"
|
||||
async-tungstenite = "0.31.0"
|
||||
async_zip = { version = "0.0.18", features = ["deflate", "deflate64"] }
|
||||
async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
|
||||
aws-config = { version = "1.6.1", features = ["behavior-version-latest"] }
|
||||
aws-credential-types = { version = "1.2.2", features = [
|
||||
"hardcoded-credentials",
|
||||
@@ -782,7 +784,7 @@ features = [
|
||||
notify = { git = "https://github.com/zed-industries/notify.git", rev = "b4588b2e5aee68f4c0e100f140e808cbce7b1419" }
|
||||
notify-types = { git = "https://github.com/zed-industries/notify.git", rev = "b4588b2e5aee68f4c0e100f140e808cbce7b1419" }
|
||||
windows-capture = { git = "https://github.com/zed-industries/windows-capture.git", rev = "f0d6c1b6691db75461b732f6d5ff56eed002eeb9" }
|
||||
calloop = { git = "https://github.com/zed-industries/calloop" }
|
||||
calloop = { path = "/home/davidsk/tmp/calloop" }
|
||||
|
||||
[profile.dev]
|
||||
split-debuginfo = "unpacked"
|
||||
@@ -859,7 +861,7 @@ ui_input = { codegen-units = 1 }
|
||||
zed_actions = { codegen-units = 1 }
|
||||
|
||||
[profile.release]
|
||||
debug = "limited"
|
||||
debug = "full"
|
||||
lto = "thin"
|
||||
codegen-units = 1
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 9.3 KiB |
@@ -43,7 +43,8 @@
|
||||
"f11": "zed::ToggleFullScreen",
|
||||
"ctrl-alt-z": "edit_prediction::RateCompletions",
|
||||
"ctrl-alt-shift-i": "edit_prediction::ToggleMenu",
|
||||
"ctrl-alt-l": "lsp_tool::ToggleMenu"
|
||||
"ctrl-alt-l": "lsp_tool::ToggleMenu",
|
||||
"ctrl-alt-.": "project_panel::ToggleHideHidden"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -865,7 +866,6 @@
|
||||
"context": "ProjectPanel",
|
||||
"bindings": {
|
||||
"left": "project_panel::CollapseSelectedEntry",
|
||||
"ctrl-left": "project_panel::CollapseAllEntries",
|
||||
"right": "project_panel::ExpandSelectedEntry",
|
||||
"new": "project_panel::NewFile",
|
||||
"ctrl-n": "project_panel::NewFile",
|
||||
|
||||
@@ -49,7 +49,8 @@
|
||||
"ctrl-cmd-f": "zed::ToggleFullScreen",
|
||||
"ctrl-cmd-z": "edit_prediction::RateCompletions",
|
||||
"ctrl-cmd-i": "edit_prediction::ToggleMenu",
|
||||
"ctrl-cmd-l": "lsp_tool::ToggleMenu"
|
||||
"ctrl-cmd-l": "lsp_tool::ToggleMenu",
|
||||
"cmd-alt-.": "project_panel::ToggleHideHidden"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -935,7 +936,6 @@
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"left": "project_panel::CollapseSelectedEntry",
|
||||
"cmd-left": "project_panel::CollapseAllEntries",
|
||||
"right": "project_panel::ExpandSelectedEntry",
|
||||
"cmd-n": "project_panel::NewFile",
|
||||
"cmd-d": "project_panel::Duplicate",
|
||||
|
||||
@@ -41,7 +41,8 @@
|
||||
"shift-f11": "debugger::StepOut",
|
||||
"f11": "zed::ToggleFullScreen",
|
||||
"ctrl-shift-i": "edit_prediction::ToggleMenu",
|
||||
"shift-alt-l": "lsp_tool::ToggleMenu"
|
||||
"shift-alt-l": "lsp_tool::ToggleMenu",
|
||||
"ctrl-alt-.": "project_panel::ToggleHideHidden"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -879,7 +880,6 @@
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"left": "project_panel::CollapseSelectedEntry",
|
||||
"ctrl-left": "project_panel::CollapseAllEntries",
|
||||
"right": "project_panel::ExpandSelectedEntry",
|
||||
"ctrl-n": "project_panel::NewFile",
|
||||
"alt-n": "project_panel::NewDirectory",
|
||||
|
||||
@@ -421,12 +421,6 @@
|
||||
"ctrl-[": "editor::Cancel"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "vim_mode == helix_select && !menu",
|
||||
"bindings": {
|
||||
"escape": "vim::SwitchToHelixNormalMode"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "(vim_mode == helix_normal || vim_mode == helix_select) && !menu",
|
||||
"bindings": {
|
||||
@@ -476,9 +470,6 @@
|
||||
"alt-p": "editor::SelectPreviousSyntaxNode",
|
||||
"alt-n": "editor::SelectNextSyntaxNode",
|
||||
|
||||
"n": "vim::HelixSelectNext",
|
||||
"shift-n": "vim::HelixSelectPrevious",
|
||||
|
||||
// Goto mode
|
||||
"g e": "vim::EndOfDocument",
|
||||
"g h": "vim::StartOfLine",
|
||||
|
||||
@@ -1316,10 +1316,7 @@
|
||||
// "hunk_style": "staged_hollow"
|
||||
// 2. Show unstaged hunks hollow and staged hunks filled:
|
||||
// "hunk_style": "unstaged_hollow"
|
||||
"hunk_style": "staged_hollow",
|
||||
// Should the name or path be displayed first in the git view.
|
||||
// "path_style": "file_name_first" or "file_path_first"
|
||||
"path_style": "file_name_first"
|
||||
"hunk_style": "staged_hollow"
|
||||
},
|
||||
// The list of custom Git hosting providers.
|
||||
"git_hosting_providers": [
|
||||
@@ -2062,18 +2059,6 @@
|
||||
"dev": {
|
||||
// "theme": "Andromeda"
|
||||
},
|
||||
// Settings overrides to use when using linux
|
||||
"linux": {},
|
||||
// Settings overrides to use when using macos
|
||||
"macos": {},
|
||||
// Settings overrides to use when using windows
|
||||
"windows": {
|
||||
"languages": {
|
||||
"PHP": {
|
||||
"language_servers": ["intelephense", "!phpactor", "..."]
|
||||
}
|
||||
}
|
||||
},
|
||||
// Whether to show full labels in line indicator or short ones
|
||||
//
|
||||
// Values:
|
||||
|
||||
@@ -150,7 +150,6 @@ impl DbThread {
|
||||
.unwrap_or_default(),
|
||||
input: tool_use.input,
|
||||
is_input_complete: true,
|
||||
thought_signature: None,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
@@ -1108,7 +1108,6 @@ fn tool_use(
|
||||
raw_input: serde_json::to_string_pretty(&input).unwrap(),
|
||||
input: serde_json::to_value(input).unwrap(),
|
||||
is_input_complete: true,
|
||||
thought_signature: None,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -274,7 +274,6 @@ async fn test_prompt_caching(cx: &mut TestAppContext) {
|
||||
raw_input: json!({"text": "test"}).to_string(),
|
||||
input: json!({"text": "test"}),
|
||||
is_input_complete: true,
|
||||
thought_signature: None,
|
||||
};
|
||||
fake_model
|
||||
.send_last_completion_stream_event(LanguageModelCompletionEvent::ToolUse(tool_use.clone()));
|
||||
@@ -462,7 +461,6 @@ async fn test_tool_authorization(cx: &mut TestAppContext) {
|
||||
raw_input: "{}".into(),
|
||||
input: json!({}),
|
||||
is_input_complete: true,
|
||||
thought_signature: None,
|
||||
},
|
||||
));
|
||||
fake_model.send_last_completion_stream_event(LanguageModelCompletionEvent::ToolUse(
|
||||
@@ -472,7 +470,6 @@ async fn test_tool_authorization(cx: &mut TestAppContext) {
|
||||
raw_input: "{}".into(),
|
||||
input: json!({}),
|
||||
is_input_complete: true,
|
||||
thought_signature: None,
|
||||
},
|
||||
));
|
||||
fake_model.end_last_completion_stream();
|
||||
@@ -523,7 +520,6 @@ async fn test_tool_authorization(cx: &mut TestAppContext) {
|
||||
raw_input: "{}".into(),
|
||||
input: json!({}),
|
||||
is_input_complete: true,
|
||||
thought_signature: None,
|
||||
},
|
||||
));
|
||||
fake_model.end_last_completion_stream();
|
||||
@@ -558,7 +554,6 @@ async fn test_tool_authorization(cx: &mut TestAppContext) {
|
||||
raw_input: "{}".into(),
|
||||
input: json!({}),
|
||||
is_input_complete: true,
|
||||
thought_signature: None,
|
||||
},
|
||||
));
|
||||
fake_model.end_last_completion_stream();
|
||||
@@ -597,7 +592,6 @@ async fn test_tool_hallucination(cx: &mut TestAppContext) {
|
||||
raw_input: "{}".into(),
|
||||
input: json!({}),
|
||||
is_input_complete: true,
|
||||
thought_signature: None,
|
||||
},
|
||||
));
|
||||
fake_model.end_last_completion_stream();
|
||||
@@ -627,7 +621,6 @@ async fn test_resume_after_tool_use_limit(cx: &mut TestAppContext) {
|
||||
raw_input: "{}".into(),
|
||||
input: serde_json::to_value(&EchoToolInput { text: "def".into() }).unwrap(),
|
||||
is_input_complete: true,
|
||||
thought_signature: None,
|
||||
};
|
||||
fake_model
|
||||
.send_last_completion_stream_event(LanguageModelCompletionEvent::ToolUse(tool_use.clone()));
|
||||
@@ -738,7 +731,6 @@ async fn test_send_after_tool_use_limit(cx: &mut TestAppContext) {
|
||||
raw_input: "{}".into(),
|
||||
input: serde_json::to_value(&EchoToolInput { text: "def".into() }).unwrap(),
|
||||
is_input_complete: true,
|
||||
thought_signature: None,
|
||||
};
|
||||
let tool_result = LanguageModelToolResult {
|
||||
tool_use_id: "tool_id_1".into(),
|
||||
@@ -1045,7 +1037,6 @@ async fn test_mcp_tools(cx: &mut TestAppContext) {
|
||||
raw_input: json!({"text": "test"}).to_string(),
|
||||
input: json!({"text": "test"}),
|
||||
is_input_complete: true,
|
||||
thought_signature: None,
|
||||
},
|
||||
));
|
||||
fake_model.end_last_completion_stream();
|
||||
@@ -1089,7 +1080,6 @@ async fn test_mcp_tools(cx: &mut TestAppContext) {
|
||||
raw_input: json!({"text": "mcp"}).to_string(),
|
||||
input: json!({"text": "mcp"}),
|
||||
is_input_complete: true,
|
||||
thought_signature: None,
|
||||
},
|
||||
));
|
||||
fake_model.send_last_completion_stream_event(LanguageModelCompletionEvent::ToolUse(
|
||||
@@ -1099,7 +1089,6 @@ async fn test_mcp_tools(cx: &mut TestAppContext) {
|
||||
raw_input: json!({"text": "native"}).to_string(),
|
||||
input: json!({"text": "native"}),
|
||||
is_input_complete: true,
|
||||
thought_signature: None,
|
||||
},
|
||||
));
|
||||
fake_model.end_last_completion_stream();
|
||||
@@ -1799,7 +1788,6 @@ async fn test_building_request_with_pending_tools(cx: &mut TestAppContext) {
|
||||
raw_input: "{}".into(),
|
||||
input: json!({}),
|
||||
is_input_complete: true,
|
||||
thought_signature: None,
|
||||
};
|
||||
let echo_tool_use = LanguageModelToolUse {
|
||||
id: "tool_id_2".into(),
|
||||
@@ -1807,7 +1795,6 @@ async fn test_building_request_with_pending_tools(cx: &mut TestAppContext) {
|
||||
raw_input: json!({"text": "test"}).to_string(),
|
||||
input: json!({"text": "test"}),
|
||||
is_input_complete: true,
|
||||
thought_signature: None,
|
||||
};
|
||||
fake_model.send_last_completion_stream_text_chunk("Hi!");
|
||||
fake_model.send_last_completion_stream_event(LanguageModelCompletionEvent::ToolUse(
|
||||
@@ -2013,7 +2000,6 @@ async fn test_tool_updates_to_completion(cx: &mut TestAppContext) {
|
||||
raw_input: input.to_string(),
|
||||
input,
|
||||
is_input_complete: false,
|
||||
thought_signature: None,
|
||||
},
|
||||
));
|
||||
|
||||
@@ -2026,7 +2012,6 @@ async fn test_tool_updates_to_completion(cx: &mut TestAppContext) {
|
||||
raw_input: input.to_string(),
|
||||
input,
|
||||
is_input_complete: true,
|
||||
thought_signature: None,
|
||||
},
|
||||
));
|
||||
fake_model.end_last_completion_stream();
|
||||
@@ -2229,7 +2214,6 @@ async fn test_send_retry_finishes_tool_calls_on_error(cx: &mut TestAppContext) {
|
||||
raw_input: json!({"text": "test"}).to_string(),
|
||||
input: json!({"text": "test"}),
|
||||
is_input_complete: true,
|
||||
thought_signature: None,
|
||||
};
|
||||
fake_model.send_last_completion_stream_event(LanguageModelCompletionEvent::ToolUse(
|
||||
tool_use_1.clone(),
|
||||
|
||||
@@ -35,7 +35,6 @@ pub struct AcpConnection {
|
||||
auth_methods: Vec<acp::AuthMethod>,
|
||||
agent_capabilities: acp::AgentCapabilities,
|
||||
default_mode: Option<acp::SessionModeId>,
|
||||
default_model: Option<acp::ModelId>,
|
||||
root_dir: PathBuf,
|
||||
// NB: Don't move this into the wait_task, since we need to ensure the process is
|
||||
// killed on drop (setting kill_on_drop on the command seems to not always work).
|
||||
@@ -58,7 +57,6 @@ pub async fn connect(
|
||||
command: AgentServerCommand,
|
||||
root_dir: &Path,
|
||||
default_mode: Option<acp::SessionModeId>,
|
||||
default_model: Option<acp::ModelId>,
|
||||
is_remote: bool,
|
||||
cx: &mut AsyncApp,
|
||||
) -> Result<Rc<dyn AgentConnection>> {
|
||||
@@ -68,7 +66,6 @@ pub async fn connect(
|
||||
command.clone(),
|
||||
root_dir,
|
||||
default_mode,
|
||||
default_model,
|
||||
is_remote,
|
||||
cx,
|
||||
)
|
||||
@@ -85,7 +82,6 @@ impl AcpConnection {
|
||||
command: AgentServerCommand,
|
||||
root_dir: &Path,
|
||||
default_mode: Option<acp::SessionModeId>,
|
||||
default_model: Option<acp::ModelId>,
|
||||
is_remote: bool,
|
||||
cx: &mut AsyncApp,
|
||||
) -> Result<Self> {
|
||||
@@ -211,7 +207,6 @@ impl AcpConnection {
|
||||
sessions,
|
||||
agent_capabilities: response.agent_capabilities,
|
||||
default_mode,
|
||||
default_model,
|
||||
_io_task: io_task,
|
||||
_wait_task: wait_task,
|
||||
_stderr_task: stderr_task,
|
||||
@@ -250,61 +245,39 @@ impl AgentConnection for AcpConnection {
|
||||
let conn = self.connection.clone();
|
||||
let sessions = self.sessions.clone();
|
||||
let default_mode = self.default_mode.clone();
|
||||
let default_model = self.default_model.clone();
|
||||
let cwd = cwd.to_path_buf();
|
||||
let context_server_store = project.read(cx).context_server_store().read(cx);
|
||||
let mcp_servers =
|
||||
if project.read(cx).is_local() {
|
||||
context_server_store
|
||||
.configured_server_ids()
|
||||
.iter()
|
||||
.filter_map(|id| {
|
||||
let configuration = context_server_store.configuration_for_server(id)?;
|
||||
match &*configuration {
|
||||
project::context_server_store::ContextServerConfiguration::Custom {
|
||||
command,
|
||||
..
|
||||
}
|
||||
| project::context_server_store::ContextServerConfiguration::Extension {
|
||||
command,
|
||||
..
|
||||
} => Some(acp::McpServer::Stdio {
|
||||
name: id.0.to_string(),
|
||||
command: command.path.clone(),
|
||||
args: command.args.clone(),
|
||||
env: if let Some(env) = command.env.as_ref() {
|
||||
env.iter()
|
||||
.map(|(name, value)| acp::EnvVariable {
|
||||
name: name.clone(),
|
||||
value: value.clone(),
|
||||
meta: None,
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
}),
|
||||
project::context_server_store::ContextServerConfiguration::Http {
|
||||
url,
|
||||
headers,
|
||||
} => Some(acp::McpServer::Http {
|
||||
name: id.0.to_string(),
|
||||
url: url.to_string(),
|
||||
headers: headers.iter().map(|(name, value)| acp::HttpHeader {
|
||||
name: name.clone(),
|
||||
value: value.clone(),
|
||||
meta: None,
|
||||
}).collect(),
|
||||
}),
|
||||
}
|
||||
let mcp_servers = if project.read(cx).is_local() {
|
||||
context_server_store
|
||||
.configured_server_ids()
|
||||
.iter()
|
||||
.filter_map(|id| {
|
||||
let configuration = context_server_store.configuration_for_server(id)?;
|
||||
let command = configuration.command();
|
||||
Some(acp::McpServer::Stdio {
|
||||
name: id.0.to_string(),
|
||||
command: command.path.clone(),
|
||||
args: command.args.clone(),
|
||||
env: if let Some(env) = command.env.as_ref() {
|
||||
env.iter()
|
||||
.map(|(name, value)| acp::EnvVariable {
|
||||
name: name.clone(),
|
||||
value: value.clone(),
|
||||
meta: None,
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
// In SSH projects, the external agent is running on the remote
|
||||
// machine, and currently we only run MCP servers on the local
|
||||
// machine. So don't pass any MCP servers to the agent in that case.
|
||||
Vec::new()
|
||||
};
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
// In SSH projects, the external agent is running on the remote
|
||||
// machine, and currently we only run MCP servers on the local
|
||||
// machine. So don't pass any MCP servers to the agent in that case.
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
cx.spawn(async move |cx| {
|
||||
let response = conn
|
||||
@@ -339,7 +312,6 @@ impl AgentConnection for AcpConnection {
|
||||
let default_mode = default_mode.clone();
|
||||
let session_id = response.session_id.clone();
|
||||
let modes = modes.clone();
|
||||
let conn = conn.clone();
|
||||
async move |_| {
|
||||
let result = conn.set_session_mode(acp::SetSessionModeRequest {
|
||||
session_id,
|
||||
@@ -374,53 +346,6 @@ impl AgentConnection for AcpConnection {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(default_model) = default_model {
|
||||
if let Some(models) = models.as_ref() {
|
||||
let mut models_ref = models.borrow_mut();
|
||||
let has_model = models_ref.available_models.iter().any(|model| model.model_id == default_model);
|
||||
|
||||
if has_model {
|
||||
let initial_model_id = models_ref.current_model_id.clone();
|
||||
|
||||
cx.spawn({
|
||||
let default_model = default_model.clone();
|
||||
let session_id = response.session_id.clone();
|
||||
let models = models.clone();
|
||||
let conn = conn.clone();
|
||||
async move |_| {
|
||||
let result = conn.set_session_model(acp::SetSessionModelRequest {
|
||||
session_id,
|
||||
model_id: default_model,
|
||||
meta: None,
|
||||
})
|
||||
.await.log_err();
|
||||
|
||||
if result.is_none() {
|
||||
models.borrow_mut().current_model_id = initial_model_id;
|
||||
}
|
||||
}
|
||||
}).detach();
|
||||
|
||||
models_ref.current_model_id = default_model;
|
||||
} else {
|
||||
let available_models = models_ref
|
||||
.available_models
|
||||
.iter()
|
||||
.map(|model| format!("- `{}`: {}", model.model_id, model.name))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
|
||||
log::warn!(
|
||||
"`{default_model}` is not a valid {name} model. Available options:\n{available_models}",
|
||||
);
|
||||
}
|
||||
} else {
|
||||
log::warn!(
|
||||
"`{name}` does not support model selection, but `default_model` was set in settings.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let session_id = response.session_id;
|
||||
let action_log = cx.new(|_| ActionLog::new(project.clone()))?;
|
||||
let thread = cx.new(|cx| {
|
||||
|
||||
@@ -68,18 +68,6 @@ pub trait AgentServer: Send {
|
||||
) {
|
||||
}
|
||||
|
||||
fn default_model(&self, _cx: &mut App) -> Option<agent_client_protocol::ModelId> {
|
||||
None
|
||||
}
|
||||
|
||||
fn set_default_model(
|
||||
&self,
|
||||
_model_id: Option<agent_client_protocol::ModelId>,
|
||||
_fs: Arc<dyn Fs>,
|
||||
_cx: &mut App,
|
||||
) {
|
||||
}
|
||||
|
||||
fn connect(
|
||||
&self,
|
||||
root_dir: Option<&Path>,
|
||||
|
||||
@@ -55,27 +55,6 @@ impl AgentServer for ClaudeCode {
|
||||
});
|
||||
}
|
||||
|
||||
fn default_model(&self, cx: &mut App) -> Option<acp::ModelId> {
|
||||
let settings = cx.read_global(|settings: &SettingsStore, _| {
|
||||
settings.get::<AllAgentServersSettings>(None).claude.clone()
|
||||
});
|
||||
|
||||
settings
|
||||
.as_ref()
|
||||
.and_then(|s| s.default_model.clone().map(|m| acp::ModelId(m.into())))
|
||||
}
|
||||
|
||||
fn set_default_model(&self, model_id: Option<acp::ModelId>, fs: Arc<dyn Fs>, cx: &mut App) {
|
||||
update_settings_file(fs, cx, |settings, _| {
|
||||
settings
|
||||
.agent_servers
|
||||
.get_or_insert_default()
|
||||
.claude
|
||||
.get_or_insert_default()
|
||||
.default_model = model_id.map(|m| m.to_string())
|
||||
});
|
||||
}
|
||||
|
||||
fn connect(
|
||||
&self,
|
||||
root_dir: Option<&Path>,
|
||||
@@ -89,7 +68,6 @@ impl AgentServer for ClaudeCode {
|
||||
let store = delegate.store.downgrade();
|
||||
let extra_env = load_proxy_env(cx);
|
||||
let default_mode = self.default_mode(cx);
|
||||
let default_model = self.default_model(cx);
|
||||
|
||||
cx.spawn(async move |cx| {
|
||||
let (command, root_dir, login) = store
|
||||
@@ -112,7 +90,6 @@ impl AgentServer for ClaudeCode {
|
||||
command,
|
||||
root_dir.as_ref(),
|
||||
default_mode,
|
||||
default_model,
|
||||
is_remote,
|
||||
cx,
|
||||
)
|
||||
|
||||
@@ -56,27 +56,6 @@ impl AgentServer for Codex {
|
||||
});
|
||||
}
|
||||
|
||||
fn default_model(&self, cx: &mut App) -> Option<acp::ModelId> {
|
||||
let settings = cx.read_global(|settings: &SettingsStore, _| {
|
||||
settings.get::<AllAgentServersSettings>(None).codex.clone()
|
||||
});
|
||||
|
||||
settings
|
||||
.as_ref()
|
||||
.and_then(|s| s.default_model.clone().map(|m| acp::ModelId(m.into())))
|
||||
}
|
||||
|
||||
fn set_default_model(&self, model_id: Option<acp::ModelId>, fs: Arc<dyn Fs>, cx: &mut App) {
|
||||
update_settings_file(fs, cx, |settings, _| {
|
||||
settings
|
||||
.agent_servers
|
||||
.get_or_insert_default()
|
||||
.codex
|
||||
.get_or_insert_default()
|
||||
.default_model = model_id.map(|m| m.to_string())
|
||||
});
|
||||
}
|
||||
|
||||
fn connect(
|
||||
&self,
|
||||
root_dir: Option<&Path>,
|
||||
@@ -90,7 +69,6 @@ impl AgentServer for Codex {
|
||||
let store = delegate.store.downgrade();
|
||||
let extra_env = load_proxy_env(cx);
|
||||
let default_mode = self.default_mode(cx);
|
||||
let default_model = self.default_model(cx);
|
||||
|
||||
cx.spawn(async move |cx| {
|
||||
let (command, root_dir, login) = store
|
||||
@@ -114,7 +92,6 @@ impl AgentServer for Codex {
|
||||
command,
|
||||
root_dir.as_ref(),
|
||||
default_mode,
|
||||
default_model,
|
||||
is_remote,
|
||||
cx,
|
||||
)
|
||||
|
||||
@@ -61,34 +61,6 @@ impl crate::AgentServer for CustomAgentServer {
|
||||
});
|
||||
}
|
||||
|
||||
fn default_model(&self, cx: &mut App) -> Option<acp::ModelId> {
|
||||
let settings = cx.read_global(|settings: &SettingsStore, _| {
|
||||
settings
|
||||
.get::<AllAgentServersSettings>(None)
|
||||
.custom
|
||||
.get(&self.name())
|
||||
.cloned()
|
||||
});
|
||||
|
||||
settings
|
||||
.as_ref()
|
||||
.and_then(|s| s.default_model.clone().map(|m| acp::ModelId(m.into())))
|
||||
}
|
||||
|
||||
fn set_default_model(&self, model_id: Option<acp::ModelId>, fs: Arc<dyn Fs>, cx: &mut App) {
|
||||
let name = self.name();
|
||||
update_settings_file(fs, cx, move |settings, _| {
|
||||
if let Some(settings) = settings
|
||||
.agent_servers
|
||||
.get_or_insert_default()
|
||||
.custom
|
||||
.get_mut(&name)
|
||||
{
|
||||
settings.default_model = model_id.map(|m| m.to_string())
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn connect(
|
||||
&self,
|
||||
root_dir: Option<&Path>,
|
||||
@@ -100,7 +72,6 @@ impl crate::AgentServer for CustomAgentServer {
|
||||
let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().into_owned());
|
||||
let is_remote = delegate.project.read(cx).is_via_remote_server();
|
||||
let default_mode = self.default_mode(cx);
|
||||
let default_model = self.default_model(cx);
|
||||
let store = delegate.store.downgrade();
|
||||
let extra_env = load_proxy_env(cx);
|
||||
|
||||
@@ -127,7 +98,6 @@ impl crate::AgentServer for CustomAgentServer {
|
||||
command,
|
||||
root_dir.as_ref(),
|
||||
default_mode,
|
||||
default_model,
|
||||
is_remote,
|
||||
cx,
|
||||
)
|
||||
|
||||
@@ -476,7 +476,6 @@ pub async fn init_test(cx: &mut TestAppContext) -> Arc<FakeFs> {
|
||||
env: None,
|
||||
ignore_system_version: None,
|
||||
default_mode: None,
|
||||
default_model: None,
|
||||
}),
|
||||
gemini: Some(crate::gemini::tests::local_command().into()),
|
||||
codex: Some(BuiltinAgentServerSettings {
|
||||
@@ -485,7 +484,6 @@ pub async fn init_test(cx: &mut TestAppContext) -> Arc<FakeFs> {
|
||||
env: None,
|
||||
ignore_system_version: None,
|
||||
default_mode: None,
|
||||
default_model: None,
|
||||
}),
|
||||
custom: collections::HashMap::default(),
|
||||
},
|
||||
|
||||
@@ -37,7 +37,6 @@ impl AgentServer for Gemini {
|
||||
let store = delegate.store.downgrade();
|
||||
let mut extra_env = load_proxy_env(cx);
|
||||
let default_mode = self.default_mode(cx);
|
||||
let default_model = self.default_model(cx);
|
||||
|
||||
cx.spawn(async move |cx| {
|
||||
extra_env.insert("SURFACE".to_owned(), "zed".to_owned());
|
||||
@@ -70,7 +69,6 @@ impl AgentServer for Gemini {
|
||||
command,
|
||||
root_dir.as_ref(),
|
||||
default_mode,
|
||||
default_model,
|
||||
is_remote,
|
||||
cx,
|
||||
)
|
||||
|
||||
@@ -13,7 +13,7 @@ use collections::{HashMap, HashSet};
|
||||
use editor::{
|
||||
Addon, Anchor, AnchorRangeExt, ContextMenuOptions, ContextMenuPlacement, Editor, EditorElement,
|
||||
EditorEvent, EditorMode, EditorSnapshot, EditorStyle, ExcerptId, FoldPlaceholder, Inlay,
|
||||
MultiBuffer, MultiBufferOffset, ToOffset,
|
||||
MultiBuffer, ToOffset,
|
||||
actions::Paste,
|
||||
code_context_menus::CodeContextMenu,
|
||||
display_map::{Crease, CreaseId, FoldId},
|
||||
@@ -209,7 +209,7 @@ impl MessageEditor {
|
||||
let acp::AvailableCommandInput::Unstructured { mut hint } =
|
||||
available_command.input.clone()?;
|
||||
|
||||
let mut hint_pos = MultiBufferOffset(parsed_command.source_range.end) + 1usize;
|
||||
let mut hint_pos = parsed_command.source_range.end + 1;
|
||||
if hint_pos > snapshot.len() {
|
||||
hint_pos = snapshot.len();
|
||||
hint.insert(0, ' ');
|
||||
@@ -307,9 +307,9 @@ impl MessageEditor {
|
||||
return Task::ready(());
|
||||
};
|
||||
let excerpt_id = start_anchor.excerpt_id;
|
||||
let end_anchor = snapshot.buffer_snapshot().anchor_before(
|
||||
start_anchor.to_offset(&snapshot.buffer_snapshot()) + content_len + 1usize,
|
||||
);
|
||||
let end_anchor = snapshot
|
||||
.buffer_snapshot()
|
||||
.anchor_before(start_anchor.to_offset(&snapshot.buffer_snapshot()) + content_len + 1);
|
||||
|
||||
let crease = if let MentionUri::File { abs_path } = &mention_uri
|
||||
&& let Some(extension) = abs_path.extension()
|
||||
@@ -739,8 +739,8 @@ impl MessageEditor {
|
||||
};
|
||||
|
||||
let crease_range = crease.range().to_offset(&snapshot.buffer_snapshot());
|
||||
if crease_range.start.0 > ix {
|
||||
let chunk = text[ix..crease_range.start.0].into();
|
||||
if crease_range.start > ix {
|
||||
let chunk = text[ix..crease_range.start].into();
|
||||
chunks.push(chunk);
|
||||
}
|
||||
let chunk = match mention {
|
||||
@@ -808,7 +808,7 @@ impl MessageEditor {
|
||||
}),
|
||||
};
|
||||
chunks.push(chunk);
|
||||
ix = crease_range.end.0;
|
||||
ix = crease_range.end;
|
||||
}
|
||||
|
||||
if ix < text.len() {
|
||||
@@ -862,7 +862,7 @@ impl MessageEditor {
|
||||
let snapshot = editor.display_snapshot(cx);
|
||||
let cursor = editor.selections.newest::<text::Point>(&snapshot).head();
|
||||
let offset = cursor.to_offset(&snapshot);
|
||||
if offset.0 > 0 {
|
||||
if offset > 0 {
|
||||
snapshot
|
||||
.buffer_snapshot()
|
||||
.reversed_chars_at(offset)
|
||||
@@ -1132,7 +1132,7 @@ impl MessageEditor {
|
||||
let cursor_anchor = editor.selections.newest_anchor().head();
|
||||
let cursor_offset = cursor_anchor.to_offset(&editor_buffer.snapshot(cx));
|
||||
let anchor = buffer.update(cx, |buffer, _cx| {
|
||||
buffer.anchor_before(cursor_offset.0.min(buffer.len()))
|
||||
buffer.anchor_before(cursor_offset.min(buffer.len()))
|
||||
});
|
||||
let Some(workspace) = self.workspace.upgrade() else {
|
||||
return;
|
||||
@@ -1258,7 +1258,7 @@ impl MessageEditor {
|
||||
});
|
||||
|
||||
for (range, mention_uri, mention) in mentions {
|
||||
let anchor = snapshot.anchor_before(MultiBufferOffset(range.start));
|
||||
let anchor = snapshot.anchor_before(range.start);
|
||||
let Some((crease_id, tx)) = insert_crease_for_mention(
|
||||
anchor.excerpt_id,
|
||||
anchor.text_anchor,
|
||||
@@ -1713,7 +1713,7 @@ mod tests {
|
||||
use agent::{HistoryStore, outline};
|
||||
use agent_client_protocol as acp;
|
||||
use assistant_text_thread::TextThreadStore;
|
||||
use editor::{AnchorRangeExt as _, Editor, EditorMode, MultiBufferOffset};
|
||||
use editor::{AnchorRangeExt as _, Editor, EditorMode};
|
||||
use fs::FakeFs;
|
||||
use futures::StreamExt as _;
|
||||
use gpui::{
|
||||
@@ -2682,7 +2682,7 @@ mod tests {
|
||||
editor.display_map.update(cx, |display_map, cx| {
|
||||
display_map
|
||||
.snapshot(cx)
|
||||
.folds_in_range(MultiBufferOffset(0)..snapshot.len())
|
||||
.folds_in_range(0..snapshot.len())
|
||||
.map(|fold| fold.range.to_point(&snapshot))
|
||||
.collect()
|
||||
})
|
||||
|
||||
@@ -11,7 +11,7 @@ use ui::{
|
||||
PopoverMenu, PopoverMenuHandle, Tooltip, prelude::*,
|
||||
};
|
||||
|
||||
use crate::{CycleModeSelector, ToggleProfileSelector, ui::HoldForDefault};
|
||||
use crate::{CycleModeSelector, ToggleProfileSelector};
|
||||
|
||||
pub struct ModeSelector {
|
||||
connection: Rc<dyn AgentSessionModes>,
|
||||
@@ -56,10 +56,6 @@ impl ModeSelector {
|
||||
self.set_mode(all_modes[next_index].id.clone(), cx);
|
||||
}
|
||||
|
||||
pub fn mode(&self) -> acp::SessionModeId {
|
||||
self.connection.current_mode()
|
||||
}
|
||||
|
||||
pub fn set_mode(&mut self, mode: acp::SessionModeId, cx: &mut Context<Self>) {
|
||||
let task = self.connection.set_mode(mode, cx);
|
||||
self.setting_mode = true;
|
||||
@@ -108,11 +104,36 @@ impl ModeSelector {
|
||||
entry.documentation_aside(side, DocumentationEdge::Bottom, {
|
||||
let description = description.clone();
|
||||
|
||||
move |_| {
|
||||
move |cx| {
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.child(Label::new(description.clone()))
|
||||
.child(HoldForDefault::new(is_default))
|
||||
.child(
|
||||
h_flex()
|
||||
.pt_1()
|
||||
.border_t_1()
|
||||
.border_color(cx.theme().colors().border_variant)
|
||||
.gap_0p5()
|
||||
.text_sm()
|
||||
.text_color(Color::Muted.color(cx))
|
||||
.child("Hold")
|
||||
.child(h_flex().flex_shrink_0().children(
|
||||
ui::render_modifiers(
|
||||
&gpui::Modifiers::secondary_key(),
|
||||
PlatformStyle::platform(),
|
||||
None,
|
||||
Some(ui::TextSize::Default.rems(cx).into()),
|
||||
true,
|
||||
),
|
||||
))
|
||||
.child(div().map(|this| {
|
||||
if is_default {
|
||||
this.child("to also unset as default")
|
||||
} else {
|
||||
this.child("to also set as default")
|
||||
}
|
||||
})),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
use std::{cmp::Reverse, rc::Rc, sync::Arc};
|
||||
|
||||
use acp_thread::{AgentModelInfo, AgentModelList, AgentModelSelector};
|
||||
use agent_servers::AgentServer;
|
||||
use anyhow::Result;
|
||||
use collections::IndexMap;
|
||||
use fs::Fs;
|
||||
use futures::FutureExt;
|
||||
use fuzzy::{StringMatchCandidate, match_strings};
|
||||
use gpui::{AsyncWindowContext, BackgroundExecutor, DismissEvent, Task, WeakEntity};
|
||||
@@ -16,18 +14,14 @@ use ui::{
|
||||
};
|
||||
use util::ResultExt;
|
||||
|
||||
use crate::ui::HoldForDefault;
|
||||
|
||||
pub type AcpModelSelector = Picker<AcpModelPickerDelegate>;
|
||||
|
||||
pub fn acp_model_selector(
|
||||
selector: Rc<dyn AgentModelSelector>,
|
||||
agent_server: Rc<dyn AgentServer>,
|
||||
fs: Arc<dyn Fs>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<AcpModelSelector>,
|
||||
) -> AcpModelSelector {
|
||||
let delegate = AcpModelPickerDelegate::new(selector, agent_server, fs, window, cx);
|
||||
let delegate = AcpModelPickerDelegate::new(selector, window, cx);
|
||||
Picker::list(delegate, window, cx)
|
||||
.show_scrollbar(true)
|
||||
.width(rems(20.))
|
||||
@@ -41,12 +35,10 @@ enum AcpModelPickerEntry {
|
||||
|
||||
pub struct AcpModelPickerDelegate {
|
||||
selector: Rc<dyn AgentModelSelector>,
|
||||
agent_server: Rc<dyn AgentServer>,
|
||||
fs: Arc<dyn Fs>,
|
||||
filtered_entries: Vec<AcpModelPickerEntry>,
|
||||
models: Option<AgentModelList>,
|
||||
selected_index: usize,
|
||||
selected_description: Option<(usize, SharedString, bool)>,
|
||||
selected_description: Option<(usize, SharedString)>,
|
||||
selected_model: Option<AgentModelInfo>,
|
||||
_refresh_models_task: Task<()>,
|
||||
}
|
||||
@@ -54,8 +46,6 @@ pub struct AcpModelPickerDelegate {
|
||||
impl AcpModelPickerDelegate {
|
||||
fn new(
|
||||
selector: Rc<dyn AgentModelSelector>,
|
||||
agent_server: Rc<dyn AgentServer>,
|
||||
fs: Arc<dyn Fs>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<AcpModelSelector>,
|
||||
) -> Self {
|
||||
@@ -96,8 +86,6 @@ impl AcpModelPickerDelegate {
|
||||
|
||||
Self {
|
||||
selector,
|
||||
agent_server,
|
||||
fs,
|
||||
filtered_entries: Vec::new(),
|
||||
models: None,
|
||||
selected_model: None,
|
||||
@@ -193,21 +181,6 @@ impl PickerDelegate for AcpModelPickerDelegate {
|
||||
if let Some(AcpModelPickerEntry::Model(model_info)) =
|
||||
self.filtered_entries.get(self.selected_index)
|
||||
{
|
||||
if window.modifiers().secondary() {
|
||||
let default_model = self.agent_server.default_model(cx);
|
||||
let is_default = default_model.as_ref() == Some(&model_info.id);
|
||||
|
||||
self.agent_server.set_default_model(
|
||||
if is_default {
|
||||
None
|
||||
} else {
|
||||
Some(model_info.id.clone())
|
||||
},
|
||||
self.fs.clone(),
|
||||
cx,
|
||||
);
|
||||
}
|
||||
|
||||
self.selector
|
||||
.select_model(model_info.id.clone(), cx)
|
||||
.detach_and_log_err(cx);
|
||||
@@ -252,8 +225,6 @@ impl PickerDelegate for AcpModelPickerDelegate {
|
||||
),
|
||||
AcpModelPickerEntry::Model(model_info) => {
|
||||
let is_selected = Some(model_info) == self.selected_model.as_ref();
|
||||
let default_model = self.agent_server.default_model(cx);
|
||||
let is_default = default_model.as_ref() == Some(&model_info.id);
|
||||
|
||||
let model_icon_color = if is_selected {
|
||||
Color::Accent
|
||||
@@ -268,8 +239,8 @@ impl PickerDelegate for AcpModelPickerDelegate {
|
||||
this
|
||||
.on_hover(cx.listener(move |menu, hovered, _, cx| {
|
||||
if *hovered {
|
||||
menu.delegate.selected_description = Some((ix, description.clone(), is_default));
|
||||
} else if matches!(menu.delegate.selected_description, Some((id, _, _)) if id == ix) {
|
||||
menu.delegate.selected_description = Some((ix, description.clone()));
|
||||
} else if matches!(menu.delegate.selected_description, Some((id, _)) if id == ix) {
|
||||
menu.delegate.selected_description = None;
|
||||
}
|
||||
cx.notify();
|
||||
@@ -312,24 +283,14 @@ impl PickerDelegate for AcpModelPickerDelegate {
|
||||
_window: &mut Window,
|
||||
_cx: &mut Context<Picker<Self>>,
|
||||
) -> Option<ui::DocumentationAside> {
|
||||
self.selected_description
|
||||
.as_ref()
|
||||
.map(|(_, description, is_default)| {
|
||||
let description = description.clone();
|
||||
let is_default = *is_default;
|
||||
|
||||
DocumentationAside::new(
|
||||
DocumentationSide::Left,
|
||||
DocumentationEdge::Top,
|
||||
Rc::new(move |_| {
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.child(Label::new(description.clone()))
|
||||
.child(HoldForDefault::new(is_default))
|
||||
.into_any_element()
|
||||
}),
|
||||
)
|
||||
})
|
||||
self.selected_description.as_ref().map(|(_, description)| {
|
||||
let description = description.clone();
|
||||
DocumentationAside::new(
|
||||
DocumentationSide::Left,
|
||||
DocumentationEdge::Top,
|
||||
Rc::new(move |_| Label::new(description.clone()).into_any_element()),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use acp_thread::{AgentModelInfo, AgentModelSelector};
|
||||
use agent_servers::AgentServer;
|
||||
use fs::Fs;
|
||||
use gpui::{Entity, FocusHandle};
|
||||
use picker::popover_menu::PickerPopoverMenu;
|
||||
use ui::{
|
||||
@@ -23,15 +20,13 @@ pub struct AcpModelSelectorPopover {
|
||||
impl AcpModelSelectorPopover {
|
||||
pub(crate) fn new(
|
||||
selector: Rc<dyn AgentModelSelector>,
|
||||
agent_server: Rc<dyn AgentServer>,
|
||||
fs: Arc<dyn Fs>,
|
||||
menu_handle: PopoverMenuHandle<AcpModelSelector>,
|
||||
focus_handle: FocusHandle,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
Self {
|
||||
selector: cx.new(move |cx| acp_model_selector(selector, agent_server, fs, window, cx)),
|
||||
selector: cx.new(move |cx| acp_model_selector(selector, window, cx)),
|
||||
menu_handle,
|
||||
focus_handle,
|
||||
}
|
||||
|
||||
@@ -591,13 +591,9 @@ impl AcpThreadView {
|
||||
.connection()
|
||||
.model_selector(thread.read(cx).session_id())
|
||||
.map(|selector| {
|
||||
let agent_server = this.agent.clone();
|
||||
let fs = this.project.read(cx).fs().clone();
|
||||
cx.new(|cx| {
|
||||
AcpModelSelectorPopover::new(
|
||||
selector,
|
||||
agent_server,
|
||||
fs,
|
||||
PopoverMenuHandle::default(),
|
||||
this.focus_handle(cx),
|
||||
window,
|
||||
@@ -1139,7 +1135,6 @@ impl AcpThreadView {
|
||||
|
||||
self.is_loading_contents = true;
|
||||
let model_id = self.current_model_id(cx);
|
||||
let mode_id = self.current_mode_id(cx);
|
||||
let guard = cx.new(|_| ());
|
||||
cx.observe_release(&guard, |this, _guard, cx| {
|
||||
this.is_loading_contents = false;
|
||||
@@ -1174,8 +1169,7 @@ impl AcpThreadView {
|
||||
"Agent Message Sent",
|
||||
agent = agent_telemetry_id,
|
||||
session = session_id,
|
||||
model = model_id,
|
||||
mode = mode_id
|
||||
model = model_id
|
||||
);
|
||||
|
||||
thread.send(contents, cx)
|
||||
@@ -1188,7 +1182,6 @@ impl AcpThreadView {
|
||||
agent = agent_telemetry_id,
|
||||
session = session_id,
|
||||
model = model_id,
|
||||
mode = mode_id,
|
||||
status,
|
||||
turn_time_ms,
|
||||
);
|
||||
@@ -5412,16 +5405,6 @@ impl AcpThreadView {
|
||||
)
|
||||
}
|
||||
|
||||
fn current_mode_id(&self, cx: &App) -> Option<Arc<str>> {
|
||||
if let Some(thread) = self.as_native_thread(cx) {
|
||||
Some(thread.read(cx).profile().0.clone())
|
||||
} else if let Some(mode_selector) = self.mode_selector() {
|
||||
Some(mode_selector.read(cx).mode().0)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn current_model_id(&self, cx: &App) -> Option<String> {
|
||||
self.model_selector
|
||||
.as_ref()
|
||||
@@ -6070,7 +6053,6 @@ pub(crate) mod tests {
|
||||
use acp_thread::StubAgentConnection;
|
||||
use agent_client_protocol::SessionId;
|
||||
use assistant_text_thread::TextThreadStore;
|
||||
use editor::MultiBufferOffset;
|
||||
use fs::FakeFs;
|
||||
use gpui::{EventEmitter, SemanticVersion, TestAppContext, VisualTestContext};
|
||||
use project::Project;
|
||||
@@ -7239,7 +7221,7 @@ pub(crate) mod tests {
|
||||
Editor::for_buffer(buffer.clone(), Some(project.clone()), window, cx);
|
||||
|
||||
editor.change_selections(Default::default(), window, cx, |selections| {
|
||||
selections.select_ranges([MultiBufferOffset(8)..MultiBufferOffset(15)]);
|
||||
selections.select_ranges([8..15]);
|
||||
});
|
||||
|
||||
editor
|
||||
@@ -7301,7 +7283,7 @@ pub(crate) mod tests {
|
||||
Editor::for_buffer(buffer.clone(), Some(project.clone()), window, cx);
|
||||
|
||||
editor.change_selections(Default::default(), window, cx, |selections| {
|
||||
selections.select_ranges([MultiBufferOffset(8)..MultiBufferOffset(15)]);
|
||||
selections.select_ranges([8..15]);
|
||||
});
|
||||
|
||||
editor
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
mod add_llm_provider_modal;
|
||||
pub mod configure_context_server_modal;
|
||||
mod configure_context_server_modal;
|
||||
mod configure_context_server_tools_modal;
|
||||
mod manage_profiles_modal;
|
||||
mod tool_picker;
|
||||
@@ -12,7 +12,7 @@ use client::zed_urls;
|
||||
use cloud_llm_client::{Plan, PlanV1, PlanV2};
|
||||
use collections::HashMap;
|
||||
use context_server::ContextServerId;
|
||||
use editor::{Editor, MultiBufferOffset, SelectionEffects, scroll::Autoscroll};
|
||||
use editor::{Editor, SelectionEffects, scroll::Autoscroll};
|
||||
use extension::ExtensionManifest;
|
||||
use extension_host::ExtensionStore;
|
||||
use fs::Fs;
|
||||
@@ -46,8 +46,9 @@ pub(crate) use configure_context_server_modal::ConfigureContextServerModal;
|
||||
pub(crate) use configure_context_server_tools_modal::ConfigureContextServerToolsModal;
|
||||
pub(crate) use manage_profiles_modal::ManageProfilesModal;
|
||||
|
||||
use crate::agent_configuration::add_llm_provider_modal::{
|
||||
AddLlmProviderModal, LlmCompatibleProvider,
|
||||
use crate::{
|
||||
AddContextServer,
|
||||
agent_configuration::add_llm_provider_modal::{AddLlmProviderModal, LlmCompatibleProvider},
|
||||
};
|
||||
|
||||
pub struct AgentConfiguration {
|
||||
@@ -552,9 +553,7 @@ impl AgentConfiguration {
|
||||
move |window, cx| {
|
||||
Some(ContextMenu::build(window, cx, |menu, _window, _cx| {
|
||||
menu.entry("Add Custom Server", None, {
|
||||
|window, cx| {
|
||||
window.dispatch_action(crate::AddContextServer.boxed_clone(), cx)
|
||||
}
|
||||
|window, cx| window.dispatch_action(AddContextServer.boxed_clone(), cx)
|
||||
})
|
||||
.entry("Install from Extensions", None, {
|
||||
|window, cx| {
|
||||
@@ -652,7 +651,7 @@ impl AgentConfiguration {
|
||||
let is_running = matches!(server_status, ContextServerStatus::Running);
|
||||
let item_id = SharedString::from(context_server_id.0.clone());
|
||||
// Servers without a configuration can only be provided by extensions.
|
||||
let provided_by_extension = server_configuration.as_ref().is_none_or(|config| {
|
||||
let provided_by_extension = server_configuration.is_none_or(|config| {
|
||||
matches!(
|
||||
config.as_ref(),
|
||||
ContextServerConfiguration::Extension { .. }
|
||||
@@ -708,10 +707,7 @@ impl AgentConfiguration {
|
||||
"Server is stopped.",
|
||||
),
|
||||
};
|
||||
let is_remote = server_configuration
|
||||
.as_ref()
|
||||
.map(|config| matches!(config.as_ref(), ContextServerConfiguration::Http { .. }))
|
||||
.unwrap_or(false);
|
||||
|
||||
let context_server_configuration_menu = PopoverMenu::new("context-server-config-menu")
|
||||
.trigger_with_tooltip(
|
||||
IconButton::new("context-server-config-menu", IconName::Settings)
|
||||
@@ -734,25 +730,14 @@ impl AgentConfiguration {
|
||||
let language_registry = language_registry.clone();
|
||||
let workspace = workspace.clone();
|
||||
move |window, cx| {
|
||||
if is_remote {
|
||||
crate::agent_configuration::configure_context_server_modal::ConfigureContextServerModal::show_modal_for_existing_server(
|
||||
context_server_id.clone(),
|
||||
language_registry.clone(),
|
||||
workspace.clone(),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
.detach();
|
||||
} else {
|
||||
ConfigureContextServerModal::show_modal_for_existing_server(
|
||||
context_server_id.clone(),
|
||||
language_registry.clone(),
|
||||
workspace.clone(),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
.detach();
|
||||
}
|
||||
ConfigureContextServerModal::show_modal_for_existing_server(
|
||||
context_server_id.clone(),
|
||||
language_registry.clone(),
|
||||
workspace.clone(),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
}).when(tool_count > 0, |this| this.entry("View Tools", None, {
|
||||
let context_server_id = context_server_id.clone();
|
||||
@@ -1348,7 +1333,6 @@ async fn open_new_agent_servers_entry_in_settings_editor(
|
||||
args: vec![],
|
||||
env: Some(HashMap::default()),
|
||||
default_mode: None,
|
||||
default_model: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1363,15 +1347,7 @@ async fn open_new_agent_servers_entry_in_settings_editor(
|
||||
.map(|(range, _)| range.clone())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
item.edit(
|
||||
edits.into_iter().map(|(range, s)| {
|
||||
(
|
||||
MultiBufferOffset(range.start)..MultiBufferOffset(range.end),
|
||||
s,
|
||||
)
|
||||
}),
|
||||
cx,
|
||||
);
|
||||
item.edit(edits, cx);
|
||||
if let Some((unique_server_name, buffer)) =
|
||||
unique_server_name.zip(item.buffer().read(cx).as_singleton())
|
||||
{
|
||||
@@ -1384,9 +1360,7 @@ async fn open_new_agent_servers_entry_in_settings_editor(
|
||||
window,
|
||||
cx,
|
||||
|selections| {
|
||||
selections.select_ranges(vec![
|
||||
MultiBufferOffset(range.start)..MultiBufferOffset(range.end),
|
||||
]);
|
||||
selections.select_ranges(vec![range]);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,42 +3,16 @@ use std::sync::Arc;
|
||||
use anyhow::Result;
|
||||
use collections::HashSet;
|
||||
use fs::Fs;
|
||||
use gpui::{
|
||||
DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Render, ScrollHandle, Task,
|
||||
};
|
||||
use gpui::{DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Render, Task};
|
||||
use language_model::LanguageModelRegistry;
|
||||
use language_models::provider::open_ai_compatible::{AvailableModel, ModelCapabilities};
|
||||
use settings::{OpenAiCompatibleSettingsContent, update_settings_file};
|
||||
use ui::{
|
||||
Banner, Checkbox, KeyBinding, Modal, ModalFooter, ModalHeader, Section, ToggleState,
|
||||
WithScrollbar, prelude::*,
|
||||
Banner, Checkbox, KeyBinding, Modal, ModalFooter, ModalHeader, Section, ToggleState, prelude::*,
|
||||
};
|
||||
use ui_input::InputField;
|
||||
use workspace::{ModalView, Workspace};
|
||||
|
||||
fn single_line_input(
|
||||
label: impl Into<SharedString>,
|
||||
placeholder: impl Into<SharedString>,
|
||||
text: Option<&str>,
|
||||
tab_index: isize,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> Entity<InputField> {
|
||||
cx.new(|cx| {
|
||||
let input = InputField::new(window, cx, placeholder)
|
||||
.label(label)
|
||||
.tab_index(tab_index)
|
||||
.tab_stop(true);
|
||||
|
||||
if let Some(text) = text {
|
||||
input
|
||||
.editor()
|
||||
.update(cx, |editor, cx| editor.set_text(text, window, cx));
|
||||
}
|
||||
input
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum LlmCompatibleProvider {
|
||||
OpenAi,
|
||||
@@ -67,14 +41,12 @@ struct AddLlmProviderInput {
|
||||
|
||||
impl AddLlmProviderInput {
|
||||
fn new(provider: LlmCompatibleProvider, window: &mut Window, cx: &mut App) -> Self {
|
||||
let provider_name =
|
||||
single_line_input("Provider Name", provider.name(), None, 1, window, cx);
|
||||
let api_url = single_line_input("API URL", provider.api_url(), None, 2, window, cx);
|
||||
let provider_name = single_line_input("Provider Name", provider.name(), None, window, cx);
|
||||
let api_url = single_line_input("API URL", provider.api_url(), None, window, cx);
|
||||
let api_key = single_line_input(
|
||||
"API Key",
|
||||
"000000000000000000000000000000000000000000000000",
|
||||
None,
|
||||
3,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
@@ -83,13 +55,12 @@ impl AddLlmProviderInput {
|
||||
provider_name,
|
||||
api_url,
|
||||
api_key,
|
||||
models: vec![ModelInput::new(0, window, cx)],
|
||||
models: vec![ModelInput::new(window, cx)],
|
||||
}
|
||||
}
|
||||
|
||||
fn add_model(&mut self, window: &mut Window, cx: &mut App) {
|
||||
let model_index = self.models.len();
|
||||
self.models.push(ModelInput::new(model_index, window, cx));
|
||||
self.models.push(ModelInput::new(window, cx));
|
||||
}
|
||||
|
||||
fn remove_model(&mut self, index: usize) {
|
||||
@@ -113,14 +84,11 @@ struct ModelInput {
|
||||
}
|
||||
|
||||
impl ModelInput {
|
||||
fn new(model_index: usize, window: &mut Window, cx: &mut App) -> Self {
|
||||
let base_tab_index = (3 + (model_index * 4)) as isize;
|
||||
|
||||
fn new(window: &mut Window, cx: &mut App) -> Self {
|
||||
let model_name = single_line_input(
|
||||
"Model Name",
|
||||
"e.g. gpt-4o, claude-opus-4, gemini-2.5-pro",
|
||||
None,
|
||||
base_tab_index + 1,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
@@ -128,7 +96,6 @@ impl ModelInput {
|
||||
"Max Completion Tokens",
|
||||
"200000",
|
||||
Some("200000"),
|
||||
base_tab_index + 2,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
@@ -136,26 +103,16 @@ impl ModelInput {
|
||||
"Max Output Tokens",
|
||||
"Max Output Tokens",
|
||||
Some("32000"),
|
||||
base_tab_index + 3,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
let max_tokens = single_line_input(
|
||||
"Max Tokens",
|
||||
"Max Tokens",
|
||||
Some("200000"),
|
||||
base_tab_index + 4,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
|
||||
let max_tokens = single_line_input("Max Tokens", "Max Tokens", Some("200000"), window, cx);
|
||||
let ModelCapabilities {
|
||||
tools,
|
||||
images,
|
||||
parallel_tool_calls,
|
||||
prompt_cache_key,
|
||||
} = ModelCapabilities::default();
|
||||
|
||||
Self {
|
||||
name: model_name,
|
||||
max_completion_tokens,
|
||||
@@ -208,6 +165,24 @@ impl ModelInput {
|
||||
}
|
||||
}
|
||||
|
||||
fn single_line_input(
|
||||
label: impl Into<SharedString>,
|
||||
placeholder: impl Into<SharedString>,
|
||||
text: Option<&str>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> Entity<InputField> {
|
||||
cx.new(|cx| {
|
||||
let input = InputField::new(window, cx, placeholder).label(label);
|
||||
if let Some(text) = text {
|
||||
input
|
||||
.editor()
|
||||
.update(cx, |editor, cx| editor.set_text(text, window, cx));
|
||||
}
|
||||
input
|
||||
})
|
||||
}
|
||||
|
||||
fn save_provider_to_settings(
|
||||
input: &AddLlmProviderInput,
|
||||
cx: &mut App,
|
||||
@@ -283,7 +258,6 @@ fn save_provider_to_settings(
|
||||
pub struct AddLlmProviderModal {
|
||||
provider: LlmCompatibleProvider,
|
||||
input: AddLlmProviderInput,
|
||||
scroll_handle: ScrollHandle,
|
||||
focus_handle: FocusHandle,
|
||||
last_error: Option<SharedString>,
|
||||
}
|
||||
@@ -304,7 +278,6 @@ impl AddLlmProviderModal {
|
||||
provider,
|
||||
last_error: None,
|
||||
focus_handle: cx.focus_handle(),
|
||||
scroll_handle: ScrollHandle::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -445,19 +418,6 @@ impl AddLlmProviderModal {
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn on_tab(&mut self, _: &menu::SelectNext, window: &mut Window, _: &mut Context<Self>) {
|
||||
window.focus_next();
|
||||
}
|
||||
|
||||
fn on_tab_prev(
|
||||
&mut self,
|
||||
_: &menu::SelectPrevious,
|
||||
window: &mut Window,
|
||||
_: &mut Context<Self>,
|
||||
) {
|
||||
window.focus_prev();
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<DismissEvent> for AddLlmProviderModal {}
|
||||
@@ -471,27 +431,15 @@ impl Focusable for AddLlmProviderModal {
|
||||
impl ModalView for AddLlmProviderModal {}
|
||||
|
||||
impl Render for AddLlmProviderModal {
|
||||
fn render(&mut self, window: &mut ui::Window, cx: &mut ui::Context<Self>) -> impl IntoElement {
|
||||
fn render(&mut self, _window: &mut ui::Window, cx: &mut ui::Context<Self>) -> impl IntoElement {
|
||||
let focus_handle = self.focus_handle(cx);
|
||||
|
||||
let window_size = window.viewport_size();
|
||||
let rem_size = window.rem_size();
|
||||
let is_large_window = window_size.height / rem_size > rems_from_px(600.).0;
|
||||
|
||||
let modal_max_height = if is_large_window {
|
||||
rems_from_px(450.)
|
||||
} else {
|
||||
rems_from_px(200.)
|
||||
};
|
||||
|
||||
v_flex()
|
||||
div()
|
||||
.id("add-llm-provider-modal")
|
||||
.key_context("AddLlmProviderModal")
|
||||
.w(rems(34.))
|
||||
.elevation_3(cx)
|
||||
.on_action(cx.listener(Self::cancel))
|
||||
.on_action(cx.listener(Self::on_tab))
|
||||
.on_action(cx.listener(Self::on_tab_prev))
|
||||
.capture_any_mouse_down(cx.listener(|this, _, window, cx| {
|
||||
this.focus_handle(cx).focus(window);
|
||||
}))
|
||||
@@ -514,25 +462,17 @@ impl Render for AddLlmProviderModal {
|
||||
)
|
||||
})
|
||||
.child(
|
||||
div()
|
||||
v_flex()
|
||||
.id("modal_content")
|
||||
.size_full()
|
||||
.vertical_scrollbar_for(self.scroll_handle.clone(), window, cx)
|
||||
.child(
|
||||
v_flex()
|
||||
.id("modal_content")
|
||||
.size_full()
|
||||
.tab_group()
|
||||
.max_h(modal_max_height)
|
||||
.pl_3()
|
||||
.pr_4()
|
||||
.gap_2()
|
||||
.overflow_y_scroll()
|
||||
.track_scroll(&self.scroll_handle)
|
||||
.child(self.input.provider_name.clone())
|
||||
.child(self.input.api_url.clone())
|
||||
.child(self.input.api_key.clone())
|
||||
.child(self.render_model_section(cx)),
|
||||
),
|
||||
.max_h_128()
|
||||
.overflow_y_scroll()
|
||||
.px(DynamicSpacing::Base12.rems(cx))
|
||||
.gap(DynamicSpacing::Base04.rems(cx))
|
||||
.child(self.input.provider_name.clone())
|
||||
.child(self.input.api_url.clone())
|
||||
.child(self.input.api_key.clone())
|
||||
.child(self.render_model_section(cx)),
|
||||
)
|
||||
.footer(
|
||||
ModalFooter::new().end_slot(
|
||||
@@ -702,7 +642,7 @@ mod tests {
|
||||
let cx = setup_test(cx).await;
|
||||
|
||||
cx.update(|window, cx| {
|
||||
let model_input = ModelInput::new(0, window, cx);
|
||||
let model_input = ModelInput::new(window, cx);
|
||||
model_input.name.update(cx, |input, cx| {
|
||||
input.editor().update(cx, |editor, cx| {
|
||||
editor.set_text("somemodel", window, cx);
|
||||
@@ -738,7 +678,7 @@ mod tests {
|
||||
let cx = setup_test(cx).await;
|
||||
|
||||
cx.update(|window, cx| {
|
||||
let mut model_input = ModelInput::new(0, window, cx);
|
||||
let mut model_input = ModelInput::new(window, cx);
|
||||
model_input.name.update(cx, |input, cx| {
|
||||
input.editor().update(cx, |editor, cx| {
|
||||
editor.set_text("somemodel", window, cx);
|
||||
@@ -763,7 +703,7 @@ mod tests {
|
||||
let cx = setup_test(cx).await;
|
||||
|
||||
cx.update(|window, cx| {
|
||||
let mut model_input = ModelInput::new(0, window, cx);
|
||||
let mut model_input = ModelInput::new(window, cx);
|
||||
model_input.name.update(cx, |input, cx| {
|
||||
input.editor().update(cx, |editor, cx| {
|
||||
editor.set_text("somemodel", window, cx);
|
||||
@@ -827,7 +767,7 @@ mod tests {
|
||||
models.iter().enumerate()
|
||||
{
|
||||
if i >= input.models.len() {
|
||||
input.models.push(ModelInput::new(i, window, cx));
|
||||
input.models.push(ModelInput::new(window, cx));
|
||||
}
|
||||
let model = &mut input.models[i];
|
||||
set_text(&model.name, name, window, cx);
|
||||
|
||||
@@ -4,7 +4,6 @@ use std::{
|
||||
};
|
||||
|
||||
use anyhow::{Context as _, Result};
|
||||
use collections::HashMap;
|
||||
use context_server::{ContextServerCommand, ContextServerId};
|
||||
use editor::{Editor, EditorElement, EditorStyle};
|
||||
use gpui::{
|
||||
@@ -21,7 +20,6 @@ use project::{
|
||||
project_settings::{ContextServerSettings, ProjectSettings},
|
||||
worktree_store::WorktreeStore,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use settings::{Settings as _, update_settings_file};
|
||||
use theme::ThemeSettings;
|
||||
use ui::{
|
||||
@@ -39,11 +37,6 @@ enum ConfigurationTarget {
|
||||
id: ContextServerId,
|
||||
command: ContextServerCommand,
|
||||
},
|
||||
ExistingHttp {
|
||||
id: ContextServerId,
|
||||
url: String,
|
||||
headers: HashMap<String, String>,
|
||||
},
|
||||
Extension {
|
||||
id: ContextServerId,
|
||||
repository_url: Option<SharedString>,
|
||||
@@ -54,11 +47,9 @@ enum ConfigurationTarget {
|
||||
enum ConfigurationSource {
|
||||
New {
|
||||
editor: Entity<Editor>,
|
||||
is_http: bool,
|
||||
},
|
||||
Existing {
|
||||
editor: Entity<Editor>,
|
||||
is_http: bool,
|
||||
},
|
||||
Extension {
|
||||
id: ContextServerId,
|
||||
@@ -106,7 +97,6 @@ impl ConfigurationSource {
|
||||
match target {
|
||||
ConfigurationTarget::New => ConfigurationSource::New {
|
||||
editor: create_editor(context_server_input(None), jsonc_language, window, cx),
|
||||
is_http: false,
|
||||
},
|
||||
ConfigurationTarget::Existing { id, command } => ConfigurationSource::Existing {
|
||||
editor: create_editor(
|
||||
@@ -115,20 +105,6 @@ impl ConfigurationSource {
|
||||
window,
|
||||
cx,
|
||||
),
|
||||
is_http: false,
|
||||
},
|
||||
ConfigurationTarget::ExistingHttp {
|
||||
id,
|
||||
url,
|
||||
headers: auth,
|
||||
} => ConfigurationSource::Existing {
|
||||
editor: create_editor(
|
||||
context_server_http_input(Some((id, url, auth))),
|
||||
jsonc_language,
|
||||
window,
|
||||
cx,
|
||||
),
|
||||
is_http: true,
|
||||
},
|
||||
ConfigurationTarget::Extension {
|
||||
id,
|
||||
@@ -165,30 +141,16 @@ impl ConfigurationSource {
|
||||
|
||||
fn output(&self, cx: &mut App) -> Result<(ContextServerId, ContextServerSettings)> {
|
||||
match self {
|
||||
ConfigurationSource::New { editor, is_http }
|
||||
| ConfigurationSource::Existing { editor, is_http } => {
|
||||
if *is_http {
|
||||
parse_http_input(&editor.read(cx).text(cx)).map(|(id, url, auth)| {
|
||||
(
|
||||
id,
|
||||
ContextServerSettings::Http {
|
||||
enabled: true,
|
||||
url,
|
||||
headers: auth,
|
||||
},
|
||||
)
|
||||
})
|
||||
} else {
|
||||
parse_input(&editor.read(cx).text(cx)).map(|(id, command)| {
|
||||
(
|
||||
id,
|
||||
ContextServerSettings::Custom {
|
||||
enabled: true,
|
||||
command,
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
ConfigurationSource::New { editor } | ConfigurationSource::Existing { editor } => {
|
||||
parse_input(&editor.read(cx).text(cx)).map(|(id, command)| {
|
||||
(
|
||||
id,
|
||||
ContextServerSettings::Custom {
|
||||
enabled: true,
|
||||
command,
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
ConfigurationSource::Extension {
|
||||
id,
|
||||
@@ -250,66 +212,6 @@ fn context_server_input(existing: Option<(ContextServerId, ContextServerCommand)
|
||||
)
|
||||
}
|
||||
|
||||
fn context_server_http_input(
|
||||
existing: Option<(ContextServerId, String, HashMap<String, String>)>,
|
||||
) -> String {
|
||||
let (name, url, headers) = match existing {
|
||||
Some((id, url, headers)) => {
|
||||
let header = if headers.is_empty() {
|
||||
r#"// "Authorization": "Bearer <token>"#.to_string()
|
||||
} else {
|
||||
let json = serde_json::to_string_pretty(&headers).unwrap();
|
||||
let mut lines = json.split("\n").collect::<Vec<_>>();
|
||||
if lines.len() > 1 {
|
||||
lines.remove(0);
|
||||
lines.pop();
|
||||
}
|
||||
lines
|
||||
.into_iter()
|
||||
.map(|line| format!(" {}", line))
|
||||
.collect::<String>()
|
||||
};
|
||||
(id.0.to_string(), url, header)
|
||||
}
|
||||
None => (
|
||||
"some-remote-server".to_string(),
|
||||
"https://example.com/mcp".to_string(),
|
||||
r#"// "Authorization": "Bearer <token>"#.to_string(),
|
||||
),
|
||||
};
|
||||
|
||||
format!(
|
||||
r#"{{
|
||||
/// The name of your remote MCP server
|
||||
"{name}": {{
|
||||
/// The URL of the remote MCP server
|
||||
"url": "{url}",
|
||||
"headers": {{
|
||||
/// Any headers to send along
|
||||
{headers}
|
||||
}}
|
||||
}}
|
||||
}}"#
|
||||
)
|
||||
}
|
||||
|
||||
fn parse_http_input(text: &str) -> Result<(ContextServerId, String, HashMap<String, String>)> {
|
||||
#[derive(Deserialize)]
|
||||
struct Temp {
|
||||
url: String,
|
||||
#[serde(default)]
|
||||
headers: HashMap<String, String>,
|
||||
}
|
||||
let value: HashMap<String, Temp> = serde_json_lenient::from_str(text)?;
|
||||
if value.len() != 1 {
|
||||
anyhow::bail!("Expected exactly one context server configuration");
|
||||
}
|
||||
|
||||
let (key, value) = value.into_iter().next().unwrap();
|
||||
|
||||
Ok((ContextServerId(key.into()), value.url, value.headers))
|
||||
}
|
||||
|
||||
fn resolve_context_server_extension(
|
||||
id: ContextServerId,
|
||||
worktree_store: Entity<WorktreeStore>,
|
||||
@@ -410,15 +312,6 @@ impl ConfigureContextServerModal {
|
||||
id: server_id,
|
||||
command,
|
||||
}),
|
||||
ContextServerSettings::Http {
|
||||
enabled: _,
|
||||
url,
|
||||
headers,
|
||||
} => Some(ConfigurationTarget::ExistingHttp {
|
||||
id: server_id,
|
||||
url,
|
||||
headers,
|
||||
}),
|
||||
ContextServerSettings::Extension { .. } => {
|
||||
match workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
@@ -460,7 +353,6 @@ impl ConfigureContextServerModal {
|
||||
state: State::Idle,
|
||||
original_server_id: match &target {
|
||||
ConfigurationTarget::Existing { id, .. } => Some(id.clone()),
|
||||
ConfigurationTarget::ExistingHttp { id, .. } => Some(id.clone()),
|
||||
ConfigurationTarget::Extension { id, .. } => Some(id.clone()),
|
||||
ConfigurationTarget::New => None,
|
||||
},
|
||||
@@ -589,7 +481,7 @@ impl ModalView for ConfigureContextServerModal {}
|
||||
impl Focusable for ConfigureContextServerModal {
|
||||
fn focus_handle(&self, cx: &App) -> FocusHandle {
|
||||
match &self.source {
|
||||
ConfigurationSource::New { editor, .. } => editor.focus_handle(cx),
|
||||
ConfigurationSource::New { editor } => editor.focus_handle(cx),
|
||||
ConfigurationSource::Existing { editor, .. } => editor.focus_handle(cx),
|
||||
ConfigurationSource::Extension { editor, .. } => editor
|
||||
.as_ref()
|
||||
@@ -635,10 +527,9 @@ impl ConfigureContextServerModal {
|
||||
}
|
||||
|
||||
fn render_modal_content(&self, cx: &App) -> AnyElement {
|
||||
// All variants now use single editor approach
|
||||
let editor = match &self.source {
|
||||
ConfigurationSource::New { editor, .. } => editor,
|
||||
ConfigurationSource::Existing { editor, .. } => editor,
|
||||
ConfigurationSource::New { editor } => editor,
|
||||
ConfigurationSource::Existing { editor } => editor,
|
||||
ConfigurationSource::Extension { editor, .. } => {
|
||||
let Some(editor) = editor else {
|
||||
return div().into_any_element();
|
||||
@@ -710,36 +601,6 @@ impl ConfigureContextServerModal {
|
||||
move |_, _, cx| cx.open_url(&repository_url)
|
||||
}),
|
||||
)
|
||||
} else if let ConfigurationSource::New { is_http, .. } = &self.source {
|
||||
let label = if *is_http {
|
||||
"Run command"
|
||||
} else {
|
||||
"Connect via HTTP"
|
||||
};
|
||||
let tooltip = if *is_http {
|
||||
"Configure an MCP serevr that runs on stdin/stdout."
|
||||
} else {
|
||||
"Configure an MCP server that you connect to over HTTP"
|
||||
};
|
||||
|
||||
Some(
|
||||
Button::new("toggle-kind", label)
|
||||
.tooltip(Tooltip::text(tooltip))
|
||||
.on_click(cx.listener(|this, _, window, cx| match &mut this.source {
|
||||
ConfigurationSource::New { editor, is_http } => {
|
||||
*is_http = !*is_http;
|
||||
let new_text = if *is_http {
|
||||
context_server_http_input(None)
|
||||
} else {
|
||||
context_server_input(None)
|
||||
};
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.set_text(new_text, window, cx);
|
||||
})
|
||||
}
|
||||
_ => {}
|
||||
})),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
|
||||
@@ -13,8 +13,8 @@ use editor::{
|
||||
scroll::Autoscroll,
|
||||
};
|
||||
use gpui::{
|
||||
Action, AnyElement, App, AppContext, Empty, Entity, EventEmitter, FocusHandle, Focusable,
|
||||
Global, SharedString, Subscription, Task, WeakEntity, Window, prelude::*,
|
||||
Action, AnyElement, AnyView, App, AppContext, Empty, Entity, EventEmitter, FocusHandle,
|
||||
Focusable, Global, SharedString, Subscription, Task, WeakEntity, Window, prelude::*,
|
||||
};
|
||||
|
||||
use language::{Buffer, Capability, DiskState, OffsetRangeExt, Point};
|
||||
@@ -580,11 +580,11 @@ impl Item for AgentDiffPane {
|
||||
type_id: TypeId,
|
||||
self_handle: &'a Entity<Self>,
|
||||
_: &'a App,
|
||||
) -> Option<gpui::AnyEntity> {
|
||||
) -> Option<AnyView> {
|
||||
if type_id == TypeId::of::<Self>() {
|
||||
Some(self_handle.clone().into())
|
||||
Some(self_handle.to_any())
|
||||
} else if type_id == TypeId::of::<Editor>() {
|
||||
Some(self.editor.clone().into())
|
||||
Some(self.editor.to_any())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -429,12 +429,7 @@ impl CodegenAlternative {
|
||||
|
||||
let prompt = self
|
||||
.builder
|
||||
.generate_inline_transformation_prompt(
|
||||
user_prompt,
|
||||
language_name,
|
||||
buffer,
|
||||
range.start.0..range.end.0,
|
||||
)
|
||||
.generate_inline_transformation_prompt(user_prompt, language_name, buffer, range)
|
||||
.context("generating content prompt")?;
|
||||
|
||||
let context_task = self.context_store.as_ref().and_then(|context_store| {
|
||||
|
||||
@@ -1082,7 +1082,7 @@ impl MentionCompletion {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use editor::{AnchorRangeExt, MultiBufferOffset};
|
||||
use editor::AnchorRangeExt;
|
||||
use gpui::{EventEmitter, FocusHandle, Focusable, TestAppContext, VisualTestContext};
|
||||
use project::{Project, ProjectPath};
|
||||
use serde_json::json;
|
||||
@@ -1677,7 +1677,7 @@ mod tests {
|
||||
editor.display_map.update(cx, |display_map, cx| {
|
||||
display_map
|
||||
.snapshot(cx)
|
||||
.folds_in_range(MultiBufferOffset(0)..snapshot.len())
|
||||
.folds_in_range(0..snapshot.len())
|
||||
.map(|fold| fold.range.to_point(&snapshot))
|
||||
.collect()
|
||||
})
|
||||
|
||||
@@ -16,7 +16,6 @@ use agent_settings::AgentSettings;
|
||||
use anyhow::{Context as _, Result};
|
||||
use client::telemetry::Telemetry;
|
||||
use collections::{HashMap, HashSet, VecDeque, hash_map};
|
||||
use editor::MultiBufferOffset;
|
||||
use editor::RowExt;
|
||||
use editor::SelectionEffects;
|
||||
use editor::scroll::ScrollOffset;
|
||||
@@ -804,7 +803,7 @@ impl InlineAssistant {
|
||||
(
|
||||
editor
|
||||
.selections
|
||||
.newest::<MultiBufferOffset>(&editor.display_snapshot(cx)),
|
||||
.newest::<usize>(&editor.display_snapshot(cx)),
|
||||
editor.buffer().read(cx).snapshot(cx),
|
||||
)
|
||||
});
|
||||
@@ -837,7 +836,7 @@ impl InlineAssistant {
|
||||
(
|
||||
editor
|
||||
.selections
|
||||
.newest::<MultiBufferOffset>(&editor.display_snapshot(cx)),
|
||||
.newest::<usize>(&editor.display_snapshot(cx)),
|
||||
editor.buffer().read(cx).snapshot(cx),
|
||||
)
|
||||
});
|
||||
@@ -854,14 +853,12 @@ impl InlineAssistant {
|
||||
} else {
|
||||
let distance_from_selection = assist_range
|
||||
.start
|
||||
.0
|
||||
.abs_diff(selection.start.0)
|
||||
.min(assist_range.start.0.abs_diff(selection.end.0))
|
||||
.abs_diff(selection.start)
|
||||
.min(assist_range.start.abs_diff(selection.end))
|
||||
+ assist_range
|
||||
.end
|
||||
.0
|
||||
.abs_diff(selection.start.0)
|
||||
.min(assist_range.end.0.abs_diff(selection.end.0));
|
||||
.abs_diff(selection.start)
|
||||
.min(assist_range.end.abs_diff(selection.end));
|
||||
match closest_assist_fallback {
|
||||
Some((_, old_distance)) => {
|
||||
if distance_from_selection < old_distance {
|
||||
@@ -938,7 +935,7 @@ impl InlineAssistant {
|
||||
EditorEvent::Edited { transaction_id } => {
|
||||
let buffer = editor.read(cx).buffer().read(cx);
|
||||
let edited_ranges =
|
||||
buffer.edited_ranges_for_transaction::<MultiBufferOffset>(*transaction_id, cx);
|
||||
buffer.edited_ranges_for_transaction::<usize>(*transaction_id, cx);
|
||||
let snapshot = buffer.snapshot(cx);
|
||||
|
||||
for assist_id in editor_assists.assist_ids.clone() {
|
||||
|
||||
@@ -2,7 +2,7 @@ use agent::HistoryStore;
|
||||
use collections::{HashMap, VecDeque};
|
||||
use editor::actions::Paste;
|
||||
use editor::display_map::{CreaseId, EditorMargins};
|
||||
use editor::{Addon, AnchorRangeExt as _, MultiBufferOffset};
|
||||
use editor::{Addon, AnchorRangeExt as _};
|
||||
use editor::{
|
||||
ContextMenuOptions, Editor, EditorElement, EditorEvent, EditorMode, EditorStyle, MultiBuffer,
|
||||
actions::{MoveDown, MoveUp},
|
||||
@@ -1165,7 +1165,7 @@ impl GenerationMode {
|
||||
/// Stored information that can be used to resurrect a context crease when creating an editor for a past message.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MessageCrease {
|
||||
pub range: Range<MultiBufferOffset>,
|
||||
pub range: Range<usize>,
|
||||
pub icon_path: SharedString,
|
||||
pub label: SharedString,
|
||||
/// None for a deserialized message, Some otherwise.
|
||||
|
||||
@@ -9,8 +9,8 @@ use assistant_slash_commands::{DefaultSlashCommand, FileSlashCommand, selections
|
||||
use client::{proto, zed_urls};
|
||||
use collections::{BTreeSet, HashMap, HashSet, hash_map};
|
||||
use editor::{
|
||||
Anchor, Editor, EditorEvent, MenuEditPredictionsPolicy, MultiBuffer, MultiBufferOffset,
|
||||
MultiBufferSnapshot, RowExt, ToOffset as _, ToPoint,
|
||||
Anchor, Editor, EditorEvent, MenuEditPredictionsPolicy, MultiBuffer, MultiBufferSnapshot,
|
||||
RowExt, ToOffset as _, ToPoint,
|
||||
actions::{MoveToEndOfLine, Newline, ShowCompletions},
|
||||
display_map::{
|
||||
BlockPlacement, BlockProperties, BlockStyle, Crease, CreaseMetadata, CustomBlockId, FoldId,
|
||||
@@ -22,11 +22,11 @@ use editor::{FoldPlaceholder, display_map::CreaseId};
|
||||
use fs::Fs;
|
||||
use futures::FutureExt;
|
||||
use gpui::{
|
||||
Action, Animation, AnimationExt, AnyElement, App, ClipboardEntry, ClipboardItem, Empty, Entity,
|
||||
EventEmitter, FocusHandle, Focusable, FontWeight, Global, InteractiveElement, IntoElement,
|
||||
ParentElement, Pixels, Render, RenderImage, SharedString, Size, StatefulInteractiveElement,
|
||||
Styled, Subscription, Task, WeakEntity, actions, div, img, point, prelude::*,
|
||||
pulsating_between, size,
|
||||
Action, Animation, AnimationExt, AnyElement, AnyView, App, ClipboardEntry, ClipboardItem,
|
||||
Empty, Entity, EventEmitter, FocusHandle, Focusable, FontWeight, Global, InteractiveElement,
|
||||
IntoElement, ParentElement, Pixels, Render, RenderImage, SharedString, Size,
|
||||
StatefulInteractiveElement, Styled, Subscription, Task, WeakEntity, actions, div, img, point,
|
||||
prelude::*, pulsating_between, size,
|
||||
};
|
||||
use language::{
|
||||
BufferSnapshot, LspAdapterDelegate, ToOffset,
|
||||
@@ -66,7 +66,7 @@ use workspace::{
|
||||
};
|
||||
use workspace::{
|
||||
Save, Toast, Workspace,
|
||||
item::{self, FollowableItem, Item},
|
||||
item::{self, FollowableItem, Item, ItemHandle},
|
||||
notifications::NotificationId,
|
||||
pane,
|
||||
searchable::{SearchEvent, SearchableItem},
|
||||
@@ -390,7 +390,7 @@ impl TextThreadEditor {
|
||||
let cursor = user_message
|
||||
.start
|
||||
.to_offset(self.text_thread.read(cx).buffer().read(cx));
|
||||
MultiBufferOffset(cursor)..MultiBufferOffset(cursor)
|
||||
cursor..cursor
|
||||
};
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
editor.change_selections(Default::default(), window, cx, |selections| {
|
||||
@@ -431,7 +431,7 @@ impl TextThreadEditor {
|
||||
let cursors = self.cursors(cx);
|
||||
self.text_thread.update(cx, |text_thread, cx| {
|
||||
let messages = text_thread
|
||||
.messages_for_offsets(cursors.into_iter().map(|cursor| cursor.0), cx)
|
||||
.messages_for_offsets(cursors, cx)
|
||||
.into_iter()
|
||||
.map(|message| message.id)
|
||||
.collect();
|
||||
@@ -439,11 +439,9 @@ impl TextThreadEditor {
|
||||
});
|
||||
}
|
||||
|
||||
fn cursors(&self, cx: &mut App) -> Vec<MultiBufferOffset> {
|
||||
fn cursors(&self, cx: &mut App) -> Vec<usize> {
|
||||
let selections = self.editor.update(cx, |editor, cx| {
|
||||
editor
|
||||
.selections
|
||||
.all::<MultiBufferOffset>(&editor.display_snapshot(cx))
|
||||
editor.selections.all::<usize>(&editor.display_snapshot(cx))
|
||||
});
|
||||
selections
|
||||
.into_iter()
|
||||
@@ -1582,11 +1580,7 @@ impl TextThreadEditor {
|
||||
fn get_clipboard_contents(
|
||||
&mut self,
|
||||
cx: &mut Context<Self>,
|
||||
) -> (
|
||||
String,
|
||||
CopyMetadata,
|
||||
Vec<text::Selection<MultiBufferOffset>>,
|
||||
) {
|
||||
) -> (String, CopyMetadata, Vec<text::Selection<usize>>) {
|
||||
let (mut selection, creases) = self.editor.update(cx, |editor, cx| {
|
||||
let mut selection = editor
|
||||
.selections
|
||||
@@ -1644,26 +1638,30 @@ impl TextThreadEditor {
|
||||
|
||||
// If selection is empty, we want to copy the entire line
|
||||
if selection.range().is_empty() {
|
||||
let snapshot = self.editor.read(cx).buffer().read(cx).snapshot(cx);
|
||||
let snapshot = text_thread.buffer().read(cx).snapshot();
|
||||
let point = snapshot.offset_to_point(selection.range().start);
|
||||
selection.start = snapshot.point_to_offset(Point::new(point.row, 0));
|
||||
selection.end = snapshot
|
||||
.point_to_offset(cmp::min(Point::new(point.row + 1, 0), snapshot.max_point()));
|
||||
for chunk in snapshot.text_for_range(selection.range()) {
|
||||
for chunk in text_thread
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.text_for_range(selection.range())
|
||||
{
|
||||
text.push_str(chunk);
|
||||
}
|
||||
} else {
|
||||
for message in text_thread.messages(cx) {
|
||||
if message.offset_range.start >= selection.range().end.0 {
|
||||
if message.offset_range.start >= selection.range().end {
|
||||
break;
|
||||
} else if message.offset_range.end >= selection.range().start.0 {
|
||||
let range = cmp::max(message.offset_range.start, selection.range().start.0)
|
||||
..cmp::min(message.offset_range.end, selection.range().end.0);
|
||||
} else if message.offset_range.end >= selection.range().start {
|
||||
let range = cmp::max(message.offset_range.start, selection.range().start)
|
||||
..cmp::min(message.offset_range.end, selection.range().end);
|
||||
if !range.is_empty() {
|
||||
for chunk in text_thread.buffer().read(cx).text_for_range(range) {
|
||||
text.push_str(chunk);
|
||||
}
|
||||
if message.offset_range.end < selection.range().end.0 {
|
||||
if message.offset_range.end < selection.range().end {
|
||||
text.push('\n');
|
||||
}
|
||||
}
|
||||
@@ -1745,7 +1743,7 @@ impl TextThreadEditor {
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
let paste_position = editor
|
||||
.selections
|
||||
.newest::<MultiBufferOffset>(&editor.display_snapshot(cx))
|
||||
.newest::<usize>(&editor.display_snapshot(cx))
|
||||
.head();
|
||||
editor.paste(action, window, cx);
|
||||
|
||||
@@ -1793,16 +1791,13 @@ impl TextThreadEditor {
|
||||
editor.transact(window, cx, |editor, _window, cx| {
|
||||
let edits = editor
|
||||
.selections
|
||||
.all::<MultiBufferOffset>(&editor.display_snapshot(cx))
|
||||
.all::<usize>(&editor.display_snapshot(cx))
|
||||
.into_iter()
|
||||
.map(|selection| (selection.start..selection.end, "\n"));
|
||||
editor.edit(edits, cx);
|
||||
|
||||
let snapshot = editor.buffer().read(cx).snapshot(cx);
|
||||
for selection in editor
|
||||
.selections
|
||||
.all::<MultiBufferOffset>(&editor.display_snapshot(cx))
|
||||
{
|
||||
for selection in editor.selections.all::<usize>(&editor.display_snapshot(cx)) {
|
||||
image_positions.push(snapshot.anchor_before(selection.end));
|
||||
}
|
||||
});
|
||||
@@ -1894,7 +1889,7 @@ impl TextThreadEditor {
|
||||
let range = selection
|
||||
.map(|endpoint| endpoint.to_offset(&buffer))
|
||||
.range();
|
||||
text_thread.split_message(range.start.0..range.end.0, cx);
|
||||
text_thread.split_message(range, cx);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2588,11 +2583,11 @@ impl Item for TextThreadEditor {
|
||||
type_id: TypeId,
|
||||
self_handle: &'a Entity<Self>,
|
||||
_: &'a App,
|
||||
) -> Option<gpui::AnyEntity> {
|
||||
) -> Option<AnyView> {
|
||||
if type_id == TypeId::of::<Self>() {
|
||||
Some(self_handle.clone().into())
|
||||
Some(self_handle.to_any())
|
||||
} else if type_id == TypeId::of::<Editor>() {
|
||||
Some(self.editor.clone().into())
|
||||
Some(self.editor.to_any())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -2968,7 +2963,7 @@ pub fn make_lsp_adapter_delegate(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use editor::{MultiBufferOffset, SelectionEffects};
|
||||
use editor::SelectionEffects;
|
||||
use fs::FakeFs;
|
||||
use gpui::{App, TestAppContext, VisualTestContext};
|
||||
use indoc::indoc;
|
||||
@@ -3174,16 +3169,15 @@ mod tests {
|
||||
text_thread: &Entity<TextThread>,
|
||||
message_ix: usize,
|
||||
cx: &mut TestAppContext,
|
||||
) -> Range<MultiBufferOffset> {
|
||||
let range = text_thread.update(cx, |text_thread, cx| {
|
||||
) -> Range<usize> {
|
||||
text_thread.update(cx, |text_thread, cx| {
|
||||
text_thread
|
||||
.messages(cx)
|
||||
.nth(message_ix)
|
||||
.unwrap()
|
||||
.anchor_range
|
||||
.to_offset(&text_thread.buffer().read(cx).snapshot())
|
||||
});
|
||||
MultiBufferOffset(range.start)..MultiBufferOffset(range.end)
|
||||
})
|
||||
}
|
||||
|
||||
fn assert_copy_paste_text_thread_editor<T: editor::ToOffset>(
|
||||
|
||||
@@ -4,7 +4,6 @@ mod burn_mode_tooltip;
|
||||
mod claude_code_onboarding_modal;
|
||||
mod context_pill;
|
||||
mod end_trial_upsell;
|
||||
mod hold_for_default;
|
||||
mod onboarding_modal;
|
||||
mod unavailable_editing_tooltip;
|
||||
mod usage_callout;
|
||||
@@ -15,7 +14,6 @@ pub use burn_mode_tooltip::*;
|
||||
pub use claude_code_onboarding_modal::*;
|
||||
pub use context_pill::*;
|
||||
pub use end_trial_upsell::*;
|
||||
pub use hold_for_default::*;
|
||||
pub use onboarding_modal::*;
|
||||
pub use unavailable_editing_tooltip::*;
|
||||
pub use usage_callout::*;
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
use gpui::{App, IntoElement, Modifiers, RenderOnce, Window};
|
||||
use ui::{prelude::*, render_modifiers};
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct HoldForDefault {
|
||||
is_default: bool,
|
||||
}
|
||||
|
||||
impl HoldForDefault {
|
||||
pub fn new(is_default: bool) -> Self {
|
||||
Self { is_default }
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for HoldForDefault {
|
||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
h_flex()
|
||||
.pt_1()
|
||||
.border_t_1()
|
||||
.border_color(cx.theme().colors().border_variant)
|
||||
.gap_0p5()
|
||||
.text_sm()
|
||||
.text_color(Color::Muted.color(cx))
|
||||
.child("Hold")
|
||||
.child(h_flex().flex_shrink_0().children(render_modifiers(
|
||||
&Modifiers::secondary_key(),
|
||||
PlatformStyle::platform(),
|
||||
None,
|
||||
Some(TextSize::Default.rems(cx).into()),
|
||||
true,
|
||||
)))
|
||||
.child(div().map(|this| {
|
||||
if self.is_default {
|
||||
this.child("to unset as default")
|
||||
} else {
|
||||
this.child("to set as default")
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -250,7 +250,6 @@ impl PasswordProxy {
|
||||
.await
|
||||
.with_context(|| format!("creating askpass script at {askpass_script_path:?}"))?;
|
||||
make_file_executable(&askpass_script_path).await?;
|
||||
// todo(shell): There might be no powershell on the system
|
||||
#[cfg(target_os = "windows")]
|
||||
let askpass_helper = format!(
|
||||
"powershell.exe -ExecutionPolicy Bypass -File {}",
|
||||
|
||||
@@ -667,7 +667,7 @@ pub struct TextThread {
|
||||
buffer: Entity<Buffer>,
|
||||
pub(crate) parsed_slash_commands: Vec<ParsedSlashCommand>,
|
||||
invoked_slash_commands: HashMap<InvokedSlashCommandId, InvokedSlashCommand>,
|
||||
edits_since_last_parse: language::Subscription<usize>,
|
||||
edits_since_last_parse: language::Subscription,
|
||||
slash_commands: Arc<SlashCommandWorkingSet>,
|
||||
pub(crate) slash_command_output_sections: Vec<SlashCommandOutputSection<language::Anchor>>,
|
||||
thought_process_output_sections: Vec<ThoughtProcessOutputSection<language::Anchor>>,
|
||||
|
||||
@@ -123,7 +123,7 @@ impl Render for Breadcrumbs {
|
||||
.upgrade()
|
||||
.zip(zed_actions::outline::TOGGLE_OUTLINE.get())
|
||||
{
|
||||
callback(editor.to_any_view(), window, cx);
|
||||
callback(editor.to_any(), window, cx);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -19,5 +19,4 @@ ordered-float.workspace = true
|
||||
rustc-hash.workspace = true
|
||||
schemars.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
strum.workspace = true
|
||||
|
||||
@@ -40,47 +40,9 @@ pub fn build_prompt(request: predict_edits_v3::PlanContextRetrievalRequest) -> R
|
||||
pub struct SearchToolInput {
|
||||
/// An array of queries to run for gathering context relevant to the next prediction
|
||||
#[schemars(length(max = 3))]
|
||||
#[serde(deserialize_with = "deserialize_queries")]
|
||||
pub queries: Box<[SearchToolQuery]>,
|
||||
}
|
||||
|
||||
fn deserialize_queries<'de, D>(deserializer: D) -> Result<Box<[SearchToolQuery]>, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
use serde::de::Error;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum QueryCollection {
|
||||
Array(Box<[SearchToolQuery]>),
|
||||
DoubleArray(Box<[Box<[SearchToolQuery]>]>),
|
||||
Single(SearchToolQuery),
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum MaybeDoubleEncoded {
|
||||
SingleEncoded(QueryCollection),
|
||||
DoubleEncoded(String),
|
||||
}
|
||||
|
||||
let result = MaybeDoubleEncoded::deserialize(deserializer)?;
|
||||
|
||||
let normalized = match result {
|
||||
MaybeDoubleEncoded::SingleEncoded(value) => value,
|
||||
MaybeDoubleEncoded::DoubleEncoded(value) => {
|
||||
serde_json::from_str(&value).map_err(D::Error::custom)?
|
||||
}
|
||||
};
|
||||
|
||||
Ok(match normalized {
|
||||
QueryCollection::Array(items) => items,
|
||||
QueryCollection::Single(search_tool_query) => Box::new([search_tool_query]),
|
||||
QueryCollection::DoubleArray(double_array) => double_array.into_iter().flatten().collect(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Search for relevant code by path, syntax hierarchy, and content.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Hash)]
|
||||
pub struct SearchToolQuery {
|
||||
@@ -130,115 +92,3 @@ const TOOL_USE_REMINDER: &str = indoc! {"
|
||||
--
|
||||
Analyze the user's intent in one to two sentences, then call the `search` tool.
|
||||
"};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json::json;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_deserialize_queries() {
|
||||
let single_query_json = indoc! {r#"{
|
||||
"queries": {
|
||||
"glob": "**/*.rs",
|
||||
"syntax_node": ["fn test"],
|
||||
"content": "assert"
|
||||
}
|
||||
}"#};
|
||||
|
||||
let flat_input: SearchToolInput = serde_json::from_str(single_query_json).unwrap();
|
||||
assert_eq!(flat_input.queries.len(), 1);
|
||||
assert_eq!(flat_input.queries[0].glob, "**/*.rs");
|
||||
assert_eq!(flat_input.queries[0].syntax_node, vec!["fn test"]);
|
||||
assert_eq!(flat_input.queries[0].content, Some("assert".to_string()));
|
||||
|
||||
let flat_json = indoc! {r#"{
|
||||
"queries": [
|
||||
{
|
||||
"glob": "**/*.rs",
|
||||
"syntax_node": ["fn test"],
|
||||
"content": "assert"
|
||||
},
|
||||
{
|
||||
"glob": "**/*.ts",
|
||||
"syntax_node": [],
|
||||
"content": null
|
||||
}
|
||||
]
|
||||
}"#};
|
||||
|
||||
let flat_input: SearchToolInput = serde_json::from_str(flat_json).unwrap();
|
||||
assert_eq!(flat_input.queries.len(), 2);
|
||||
assert_eq!(flat_input.queries[0].glob, "**/*.rs");
|
||||
assert_eq!(flat_input.queries[0].syntax_node, vec!["fn test"]);
|
||||
assert_eq!(flat_input.queries[0].content, Some("assert".to_string()));
|
||||
assert_eq!(flat_input.queries[1].glob, "**/*.ts");
|
||||
assert_eq!(flat_input.queries[1].syntax_node.len(), 0);
|
||||
assert_eq!(flat_input.queries[1].content, None);
|
||||
|
||||
let nested_json = indoc! {r#"{
|
||||
"queries": [
|
||||
[
|
||||
{
|
||||
"glob": "**/*.rs",
|
||||
"syntax_node": ["fn test"],
|
||||
"content": "assert"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"glob": "**/*.ts",
|
||||
"syntax_node": [],
|
||||
"content": null
|
||||
}
|
||||
]
|
||||
]
|
||||
}"#};
|
||||
|
||||
let nested_input: SearchToolInput = serde_json::from_str(nested_json).unwrap();
|
||||
|
||||
assert_eq!(nested_input.queries.len(), 2);
|
||||
|
||||
assert_eq!(nested_input.queries[0].glob, "**/*.rs");
|
||||
assert_eq!(nested_input.queries[0].syntax_node, vec!["fn test"]);
|
||||
assert_eq!(nested_input.queries[0].content, Some("assert".to_string()));
|
||||
assert_eq!(nested_input.queries[1].glob, "**/*.ts");
|
||||
assert_eq!(nested_input.queries[1].syntax_node.len(), 0);
|
||||
assert_eq!(nested_input.queries[1].content, None);
|
||||
|
||||
let double_encoded_queries = serde_json::to_string(&json!({
|
||||
"queries": serde_json::to_string(&json!([
|
||||
{
|
||||
"glob": "**/*.rs",
|
||||
"syntax_node": ["fn test"],
|
||||
"content": "assert"
|
||||
},
|
||||
{
|
||||
"glob": "**/*.ts",
|
||||
"syntax_node": [],
|
||||
"content": null
|
||||
}
|
||||
])).unwrap()
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
let double_encoded_input: SearchToolInput =
|
||||
serde_json::from_str(&double_encoded_queries).unwrap();
|
||||
|
||||
assert_eq!(double_encoded_input.queries.len(), 2);
|
||||
|
||||
assert_eq!(double_encoded_input.queries[0].glob, "**/*.rs");
|
||||
assert_eq!(double_encoded_input.queries[0].syntax_node, vec!["fn test"]);
|
||||
assert_eq!(
|
||||
double_encoded_input.queries[0].content,
|
||||
Some("assert".to_string())
|
||||
);
|
||||
assert_eq!(double_encoded_input.queries[1].glob, "**/*.ts");
|
||||
assert_eq!(double_encoded_input.queries[1].syntax_node.len(), 0);
|
||||
assert_eq!(double_encoded_input.queries[1].content, None);
|
||||
|
||||
// ### ERROR Switching from var declarations to lexical declarations [RUN 073]
|
||||
// invalid search json {"queries": ["express/lib/response.js", "var\\s+[a-zA-Z_][a-zA-Z0-9_]*\\s*=.*;", "function.*\\(.*\\).*\\{.*\\}"]}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1005,6 +1005,7 @@ impl Database {
|
||||
is_last_update: true,
|
||||
merge_message: db_repository_entry.merge_message,
|
||||
stash_entries: Vec::new(),
|
||||
renamed_paths: Default::default(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -796,6 +796,7 @@ impl Database {
|
||||
is_last_update: true,
|
||||
merge_message: db_repository.merge_message,
|
||||
stash_entries: Vec::new(),
|
||||
renamed_paths: Default::default(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use channel::ACKNOWLEDGE_DEBOUNCE_INTERVAL;
|
||||
use client::{Collaborator, ParticipantIndex, UserId};
|
||||
use collab_ui::channel_view::ChannelView;
|
||||
use collections::HashMap;
|
||||
use editor::{Anchor, Editor, MultiBufferOffset, ToOffset};
|
||||
use editor::{Anchor, Editor, ToOffset};
|
||||
use futures::future;
|
||||
use gpui::{BackgroundExecutor, Context, Entity, TestAppContext, Window};
|
||||
use rpc::{RECEIVE_TIMEOUT, proto::PeerId};
|
||||
@@ -180,7 +180,7 @@ async fn test_channel_notes_participant_indices(
|
||||
notes.editor.update(cx, |editor, cx| {
|
||||
editor.insert("a", window, cx);
|
||||
editor.change_selections(Default::default(), window, cx, |selections| {
|
||||
selections.select_ranges(vec![MultiBufferOffset(0)..MultiBufferOffset(1)]);
|
||||
selections.select_ranges(vec![0..1]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -190,7 +190,7 @@ async fn test_channel_notes_participant_indices(
|
||||
editor.move_down(&Default::default(), window, cx);
|
||||
editor.insert("b", window, cx);
|
||||
editor.change_selections(Default::default(), window, cx, |selections| {
|
||||
selections.select_ranges(vec![MultiBufferOffset(1)..MultiBufferOffset(2)]);
|
||||
selections.select_ranges(vec![1..2]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -200,7 +200,7 @@ async fn test_channel_notes_participant_indices(
|
||||
editor.move_down(&Default::default(), window, cx);
|
||||
editor.insert("c", window, cx);
|
||||
editor.change_selections(Default::default(), window, cx, |selections| {
|
||||
selections.select_ranges(vec![MultiBufferOffset(2)..MultiBufferOffset(3)]);
|
||||
selections.select_ranges(vec![2..3]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -287,12 +287,12 @@ async fn test_channel_notes_participant_indices(
|
||||
|
||||
editor_a.update_in(cx_a, |editor, window, cx| {
|
||||
editor.change_selections(Default::default(), window, cx, |selections| {
|
||||
selections.select_ranges(vec![MultiBufferOffset(0)..MultiBufferOffset(1)]);
|
||||
selections.select_ranges(vec![0..1]);
|
||||
});
|
||||
});
|
||||
editor_b.update_in(cx_b, |editor, window, cx| {
|
||||
editor.change_selections(Default::default(), window, cx, |selections| {
|
||||
selections.select_ranges(vec![MultiBufferOffset(2)..MultiBufferOffset(3)]);
|
||||
selections.select_ranges(vec![2..3]);
|
||||
});
|
||||
});
|
||||
executor.run_until_parked();
|
||||
@@ -327,7 +327,7 @@ fn assert_remote_selections(
|
||||
let end = s.selection.end.to_offset(snapshot.buffer_snapshot());
|
||||
let user_id = collaborators.get(&peer_id).unwrap().user_id;
|
||||
let participant_index = hub.user_participant_indices(cx).get(&user_id).copied();
|
||||
(participant_index, start.0..end.0)
|
||||
(participant_index, start..end)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
|
||||
@@ -4,8 +4,7 @@ use crate::{
|
||||
};
|
||||
use call::ActiveCall;
|
||||
use editor::{
|
||||
DocumentColorsRenderMode, Editor, FETCH_COLORS_DEBOUNCE_TIMEOUT, MultiBufferOffset, RowInfo,
|
||||
SelectionEffects,
|
||||
DocumentColorsRenderMode, Editor, FETCH_COLORS_DEBOUNCE_TIMEOUT, RowInfo, SelectionEffects,
|
||||
actions::{
|
||||
ConfirmCodeAction, ConfirmCompletion, ConfirmRename, ContextMenuFirst,
|
||||
ExpandMacroRecursively, MoveToEnd, Redo, Rename, SelectAll, ToggleCodeActions, Undo,
|
||||
@@ -382,7 +381,7 @@ async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mu
|
||||
// Type a completion trigger character as the guest.
|
||||
editor_b.update_in(cx_b, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(13)..MultiBufferOffset(13)])
|
||||
s.select_ranges([13..13])
|
||||
});
|
||||
editor.handle_input(".", window, cx);
|
||||
});
|
||||
@@ -504,7 +503,7 @@ async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mu
|
||||
// resolved
|
||||
editor_b.update_in(cx_b, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(46)..MultiBufferOffset(46)])
|
||||
s.select_ranges([46..46])
|
||||
});
|
||||
editor.handle_input("; a", window, cx);
|
||||
editor.handle_input(".", window, cx);
|
||||
@@ -602,7 +601,7 @@ async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mu
|
||||
// Add another completion trigger to test the second language server
|
||||
editor_b.update_in(cx_b, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(68)..MultiBufferOffset(68)])
|
||||
s.select_ranges([68..68])
|
||||
});
|
||||
editor.handle_input("; b", window, cx);
|
||||
editor.handle_input(".", window, cx);
|
||||
@@ -951,7 +950,7 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T
|
||||
// Move cursor to a location that can be renamed.
|
||||
let prepare_rename = editor_b.update_in(cx_b, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(7)..MultiBufferOffset(7)])
|
||||
s.select_ranges([7..7])
|
||||
});
|
||||
editor.rename(&Rename, window, cx).unwrap()
|
||||
});
|
||||
@@ -978,17 +977,17 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T
|
||||
let buffer = editor.buffer().read(cx).snapshot(cx);
|
||||
assert_eq!(
|
||||
rename.range.start.to_offset(&buffer)..rename.range.end.to_offset(&buffer),
|
||||
MultiBufferOffset(6)..MultiBufferOffset(9)
|
||||
6..9
|
||||
);
|
||||
rename.editor.update(cx, |rename_editor, cx| {
|
||||
let rename_selection = rename_editor.selections.newest::<MultiBufferOffset>(&rename_editor.display_snapshot(cx));
|
||||
let rename_selection = rename_editor.selections.newest::<usize>(&rename_editor.display_snapshot(cx));
|
||||
assert_eq!(
|
||||
rename_selection.range(),
|
||||
MultiBufferOffset(0)..MultiBufferOffset(3),
|
||||
0..3,
|
||||
"Rename that was triggered from zero selection caret, should propose the whole word."
|
||||
);
|
||||
rename_editor.buffer().update(cx, |rename_buffer, cx| {
|
||||
rename_buffer.edit([(MultiBufferOffset(0)..MultiBufferOffset(3), "THREE")], None, cx);
|
||||
rename_buffer.edit([(0..3, "THREE")], None, cx);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -999,7 +998,7 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T
|
||||
});
|
||||
let prepare_rename = editor_b.update_in(cx_b, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(7)..MultiBufferOffset(8)])
|
||||
s.select_ranges([7..8])
|
||||
});
|
||||
editor.rename(&Rename, window, cx).unwrap()
|
||||
});
|
||||
@@ -1026,16 +1025,16 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T
|
||||
let buffer = editor.buffer().read(cx).snapshot(cx);
|
||||
let lsp_rename_start = rename.range.start.to_offset(&buffer);
|
||||
let lsp_rename_end = rename.range.end.to_offset(&buffer);
|
||||
assert_eq!(lsp_rename_start..lsp_rename_end, MultiBufferOffset(6)..MultiBufferOffset(9));
|
||||
assert_eq!(lsp_rename_start..lsp_rename_end, 6..9);
|
||||
rename.editor.update(cx, |rename_editor, cx| {
|
||||
let rename_selection = rename_editor.selections.newest::<MultiBufferOffset>(&rename_editor.display_snapshot(cx));
|
||||
let rename_selection = rename_editor.selections.newest::<usize>(&rename_editor.display_snapshot(cx));
|
||||
assert_eq!(
|
||||
rename_selection.range(),
|
||||
MultiBufferOffset(1)..MultiBufferOffset(2),
|
||||
1..2,
|
||||
"Rename that was triggered from a selection, should have the same selection range in the rename proposal"
|
||||
);
|
||||
rename_editor.buffer().update(cx, |rename_buffer, cx| {
|
||||
rename_buffer.edit([(MultiBufferOffset(0)..MultiBufferOffset(lsp_rename_end - lsp_rename_start), "THREE")], None, cx);
|
||||
rename_buffer.edit([(0..lsp_rename_end - lsp_rename_start, "THREE")], None, cx);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1238,7 +1237,7 @@ async fn test_slow_lsp_server(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte
|
||||
// Move cursor to a location, this should trigger the code lens call.
|
||||
editor_b.update_in(cx_b, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(7)..MultiBufferOffset(7)])
|
||||
s.select_ranges([7..7])
|
||||
});
|
||||
});
|
||||
let () = request_started_rx.next().await.unwrap();
|
||||
@@ -1260,7 +1259,7 @@ async fn test_slow_lsp_server(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte
|
||||
|
||||
editor_b.update_in(cx_b, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(1)..MultiBufferOffset(1)])
|
||||
s.select_ranges([1..1])
|
||||
});
|
||||
});
|
||||
let () = request_started_rx.next().await.unwrap();
|
||||
@@ -1282,7 +1281,7 @@ async fn test_slow_lsp_server(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte
|
||||
|
||||
editor_b.update_in(cx_b, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(2)..MultiBufferOffset(2)])
|
||||
s.select_ranges([2..2])
|
||||
});
|
||||
});
|
||||
let () = request_started_rx.next().await.unwrap();
|
||||
@@ -1720,7 +1719,7 @@ async fn test_on_input_format_from_host_to_guest(
|
||||
cx_a.focus(&editor_a);
|
||||
editor_a.update_in(cx_a, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(13)..MultiBufferOffset(13)])
|
||||
s.select_ranges([13..13])
|
||||
});
|
||||
editor.handle_input(">", window, cx);
|
||||
});
|
||||
@@ -1829,7 +1828,7 @@ async fn test_on_input_format_from_guest_to_host(
|
||||
cx_b.focus(&editor_b);
|
||||
editor_b.update_in(cx_b, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(13)..MultiBufferOffset(13)])
|
||||
s.select_ranges([13..13])
|
||||
});
|
||||
editor.handle_input(":", window, cx);
|
||||
});
|
||||
@@ -2057,7 +2056,7 @@ async fn test_mutual_editor_inlay_hint_cache_update(
|
||||
let after_client_edit = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
|
||||
editor_b.update_in(cx_b, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(13)..MultiBufferOffset(13)].clone())
|
||||
s.select_ranges([13..13].clone())
|
||||
});
|
||||
editor.handle_input(":", window, cx);
|
||||
});
|
||||
@@ -2081,7 +2080,7 @@ async fn test_mutual_editor_inlay_hint_cache_update(
|
||||
let after_host_edit = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
|
||||
editor_a.update_in(cx_a, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(13)..MultiBufferOffset(13)])
|
||||
s.select_ranges([13..13])
|
||||
});
|
||||
editor.handle_input("a change to increment both buffers' versions", window, cx);
|
||||
});
|
||||
@@ -2521,7 +2520,7 @@ async fn test_lsp_document_color(cx_a: &mut TestAppContext, cx_b: &mut TestAppCo
|
||||
|
||||
editor_a.update_in(cx_a, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(13)..MultiBufferOffset(13)].clone())
|
||||
s.select_ranges([13..13].clone())
|
||||
});
|
||||
editor.handle_input(":", window, cx);
|
||||
});
|
||||
@@ -2958,7 +2957,7 @@ async fn test_lsp_pull_diagnostics(
|
||||
editor_a_main.update(cx_a, |editor, cx| {
|
||||
let snapshot = editor.buffer().read(cx).snapshot(cx);
|
||||
let all_diagnostics = snapshot
|
||||
.diagnostics_in_range(MultiBufferOffset(0)..snapshot.len())
|
||||
.diagnostics_in_range(0..snapshot.len())
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
all_diagnostics.len(),
|
||||
@@ -3087,7 +3086,7 @@ async fn test_lsp_pull_diagnostics(
|
||||
editor_a_main.update(cx_a, |editor, cx| {
|
||||
let snapshot = editor.buffer().read(cx).snapshot(cx);
|
||||
let all_diagnostics = snapshot
|
||||
.diagnostics_in_range(MultiBufferOffset(0)..snapshot.len())
|
||||
.diagnostics_in_range(0..snapshot.len())
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
all_diagnostics.len(),
|
||||
@@ -3134,7 +3133,7 @@ async fn test_lsp_pull_diagnostics(
|
||||
editor_b_main.update(cx_b, |editor, cx| {
|
||||
let snapshot = editor.buffer().read(cx).snapshot(cx);
|
||||
let all_diagnostics = snapshot
|
||||
.diagnostics_in_range(MultiBufferOffset(0)..snapshot.len())
|
||||
.diagnostics_in_range(0..snapshot.len())
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
all_diagnostics.len(),
|
||||
@@ -3181,7 +3180,7 @@ async fn test_lsp_pull_diagnostics(
|
||||
editor_b_lib.update(cx_b, |editor, cx| {
|
||||
let snapshot = editor.buffer().read(cx).snapshot(cx);
|
||||
let all_diagnostics = snapshot
|
||||
.diagnostics_in_range(MultiBufferOffset(0)..snapshot.len())
|
||||
.diagnostics_in_range(0..snapshot.len())
|
||||
.collect::<Vec<_>>();
|
||||
let expected_messages = [
|
||||
expected_pull_diagnostic_lib_message,
|
||||
@@ -3248,7 +3247,7 @@ async fn test_lsp_pull_diagnostics(
|
||||
editor_b_lib.update(cx_b, |editor, cx| {
|
||||
let snapshot = editor.buffer().read(cx).snapshot(cx);
|
||||
let all_diagnostics = snapshot
|
||||
.diagnostics_in_range(MultiBufferOffset(0)..snapshot.len())
|
||||
.diagnostics_in_range(0..snapshot.len())
|
||||
.collect::<Vec<_>>();
|
||||
let expected_messages = [
|
||||
expected_workspace_pull_diagnostics_lib_message,
|
||||
@@ -3383,7 +3382,7 @@ async fn test_lsp_pull_diagnostics(
|
||||
editor_b_lib.update(cx_b, |editor, cx| {
|
||||
let snapshot = editor.buffer().read(cx).snapshot(cx);
|
||||
let all_diagnostics = snapshot
|
||||
.diagnostics_in_range(MultiBufferOffset(0)..snapshot.len())
|
||||
.diagnostics_in_range(0..snapshot.len())
|
||||
.collect::<Vec<_>>();
|
||||
let expected_messages = [
|
||||
expected_workspace_pull_diagnostics_lib_message,
|
||||
@@ -3401,7 +3400,7 @@ async fn test_lsp_pull_diagnostics(
|
||||
editor_b_main.update(cx_b, |editor, cx| {
|
||||
let snapshot = editor.buffer().read(cx).snapshot(cx);
|
||||
let all_diagnostics = snapshot
|
||||
.diagnostics_in_range(MultiBufferOffset(0)..snapshot.len())
|
||||
.diagnostics_in_range(0..snapshot.len())
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(all_diagnostics.len(), 2);
|
||||
|
||||
@@ -3420,7 +3419,7 @@ async fn test_lsp_pull_diagnostics(
|
||||
editor_a_main.update(cx_a, |editor, cx| {
|
||||
let snapshot = editor.buffer().read(cx).snapshot(cx);
|
||||
let all_diagnostics = snapshot
|
||||
.diagnostics_in_range(MultiBufferOffset(0)..snapshot.len())
|
||||
.diagnostics_in_range(0..snapshot.len())
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(all_diagnostics.len(), 2);
|
||||
let expected_messages = [
|
||||
|
||||
@@ -6,7 +6,7 @@ use collab_ui::{
|
||||
channel_view::ChannelView,
|
||||
notifications::project_shared_notification::ProjectSharedNotification,
|
||||
};
|
||||
use editor::{Editor, MultiBuffer, MultiBufferOffset, PathKey, SelectionEffects};
|
||||
use editor::{Editor, MultiBuffer, PathKey, SelectionEffects};
|
||||
use gpui::{
|
||||
AppContext as _, BackgroundExecutor, BorrowAppContext, Entity, SharedString, TestAppContext,
|
||||
VisualContext, VisualTestContext, point,
|
||||
@@ -124,7 +124,7 @@ async fn test_basic_following(
|
||||
editor.select_left(&Default::default(), window, cx);
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
vec![MultiBufferOffset(3)..MultiBufferOffset(2)]
|
||||
vec![3..2]
|
||||
);
|
||||
});
|
||||
editor_a2.update_in(cx_a, |editor, window, cx| {
|
||||
@@ -133,7 +133,7 @@ async fn test_basic_following(
|
||||
editor.select_left(&Default::default(), window, cx);
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
vec![MultiBufferOffset(2)..MultiBufferOffset(1)]
|
||||
vec![2..1]
|
||||
);
|
||||
});
|
||||
|
||||
@@ -158,13 +158,13 @@ async fn test_basic_following(
|
||||
editor_b2.update(cx_b, |editor, cx| editor
|
||||
.selections
|
||||
.ranges(&editor.display_snapshot(cx))),
|
||||
vec![MultiBufferOffset(2)..MultiBufferOffset(1)]
|
||||
vec![2..1]
|
||||
);
|
||||
assert_eq!(
|
||||
editor_b1.update(cx_b, |editor, cx| editor
|
||||
.selections
|
||||
.ranges(&editor.display_snapshot(cx))),
|
||||
vec![MultiBufferOffset(3)..MultiBufferOffset(3)]
|
||||
vec![3..3]
|
||||
);
|
||||
|
||||
executor.run_until_parked();
|
||||
@@ -386,10 +386,7 @@ async fn test_basic_following(
|
||||
// Changes to client A's editor are reflected on client B.
|
||||
editor_a1.update_in(cx_a, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([
|
||||
MultiBufferOffset(1)..MultiBufferOffset(1),
|
||||
MultiBufferOffset(2)..MultiBufferOffset(2),
|
||||
])
|
||||
s.select_ranges([1..1, 2..2])
|
||||
});
|
||||
});
|
||||
executor.advance_clock(workspace::item::LEADER_UPDATE_THROTTLE);
|
||||
@@ -399,10 +396,7 @@ async fn test_basic_following(
|
||||
editor_b1.update(cx_b, |editor, cx| {
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
&[
|
||||
MultiBufferOffset(1)..MultiBufferOffset(1),
|
||||
MultiBufferOffset(2)..MultiBufferOffset(2)
|
||||
]
|
||||
&[1..1, 2..2]
|
||||
);
|
||||
});
|
||||
|
||||
@@ -414,7 +408,7 @@ async fn test_basic_following(
|
||||
|
||||
editor_a1.update_in(cx_a, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(3)..MultiBufferOffset(3)])
|
||||
s.select_ranges([3..3])
|
||||
});
|
||||
editor.set_scroll_position(point(0., 100.), window, cx);
|
||||
});
|
||||
@@ -423,7 +417,7 @@ async fn test_basic_following(
|
||||
editor_b1.update(cx_b, |editor, cx| {
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
&[MultiBufferOffset(3)..MultiBufferOffset(3)]
|
||||
&[3..3]
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1700,7 +1694,7 @@ async fn test_following_stops_on_unshare(cx_a: &mut TestAppContext, cx_b: &mut T
|
||||
// b should follow a to position 1
|
||||
editor_a.update_in(cx_a, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(1)..MultiBufferOffset(1)])
|
||||
s.select_ranges([1..1])
|
||||
})
|
||||
});
|
||||
cx_a.executor()
|
||||
@@ -1709,7 +1703,7 @@ async fn test_following_stops_on_unshare(cx_a: &mut TestAppContext, cx_b: &mut T
|
||||
editor_b.update(cx_b, |editor, cx| {
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
vec![MultiBufferOffset(1)..MultiBufferOffset(1)]
|
||||
vec![1..1]
|
||||
)
|
||||
});
|
||||
|
||||
@@ -1725,7 +1719,7 @@ async fn test_following_stops_on_unshare(cx_a: &mut TestAppContext, cx_b: &mut T
|
||||
// b should not follow a to position 2
|
||||
editor_a.update_in(cx_a, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(2)..MultiBufferOffset(2)])
|
||||
s.select_ranges([2..2])
|
||||
})
|
||||
});
|
||||
cx_a.executor()
|
||||
@@ -1734,7 +1728,7 @@ async fn test_following_stops_on_unshare(cx_a: &mut TestAppContext, cx_b: &mut T
|
||||
editor_b.update(cx_b, |editor, cx| {
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
vec![MultiBufferOffset(1)..MultiBufferOffset(1)]
|
||||
vec![1..1]
|
||||
)
|
||||
});
|
||||
cx_b.update(|_, cx| {
|
||||
@@ -1835,7 +1829,7 @@ async fn test_following_into_excluded_file(
|
||||
editor.select_left(&Default::default(), window, cx);
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
vec![MultiBufferOffset(3)..MultiBufferOffset(2)]
|
||||
vec![3..2]
|
||||
);
|
||||
});
|
||||
editor_for_excluded_a.update_in(cx_a, |editor, window, cx| {
|
||||
@@ -1844,7 +1838,7 @@ async fn test_following_into_excluded_file(
|
||||
editor.select_left(&Default::default(), window, cx);
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
vec![MultiBufferOffset(18)..MultiBufferOffset(17)]
|
||||
vec![18..17]
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1870,7 +1864,7 @@ async fn test_following_into_excluded_file(
|
||||
editor_for_excluded_b.update(cx_b, |editor, cx| editor
|
||||
.selections
|
||||
.ranges(&editor.display_snapshot(cx))),
|
||||
vec![MultiBufferOffset(18)..MultiBufferOffset(17)]
|
||||
vec![18..17]
|
||||
);
|
||||
|
||||
editor_for_excluded_a.update_in(cx_a, |editor, window, cx| {
|
||||
@@ -2046,7 +2040,7 @@ async fn test_following_to_channel_notes_without_a_shared_project(
|
||||
notes.editor.update(cx, |editor, cx| {
|
||||
editor.insert("Hello from A.", window, cx);
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
|
||||
selections.select_ranges(vec![MultiBufferOffset(3)..MultiBufferOffset(4)]);
|
||||
selections.select_ranges(vec![3..4]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -2082,8 +2076,8 @@ async fn test_following_to_channel_notes_without_a_shared_project(
|
||||
assert_eq!(
|
||||
editor
|
||||
.selections
|
||||
.ranges::<MultiBufferOffset>(&editor.display_snapshot(cx)),
|
||||
&[MultiBufferOffset(3)..MultiBufferOffset(4)]
|
||||
.ranges::<usize>(&editor.display_snapshot(cx)),
|
||||
&[3..4]
|
||||
);
|
||||
})
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@ use editor::{
|
||||
display_map::ToDisplayPoint, scroll::Autoscroll,
|
||||
};
|
||||
use gpui::{
|
||||
App, ClipboardItem, Context, Entity, EventEmitter, Focusable, Pixels, Point, Render,
|
||||
AnyView, App, ClipboardItem, Context, Entity, EventEmitter, Focusable, Pixels, Point, Render,
|
||||
Subscription, Task, VisualContext as _, WeakEntity, Window, actions,
|
||||
};
|
||||
use project::Project;
|
||||
@@ -25,7 +25,7 @@ use util::ResultExt;
|
||||
use workspace::{CollaboratorId, item::TabContentParams};
|
||||
use workspace::{
|
||||
ItemNavHistory, Pane, SaveIntent, Toast, ViewId, Workspace, WorkspaceId,
|
||||
item::{FollowableItem, Item, ItemEvent},
|
||||
item::{FollowableItem, Item, ItemEvent, ItemHandle},
|
||||
searchable::SearchableItemHandle,
|
||||
};
|
||||
use workspace::{item::Dedup, notifications::NotificationId};
|
||||
@@ -441,11 +441,11 @@ impl Item for ChannelView {
|
||||
type_id: TypeId,
|
||||
self_handle: &'a Entity<Self>,
|
||||
_: &'a App,
|
||||
) -> Option<gpui::AnyEntity> {
|
||||
) -> Option<AnyView> {
|
||||
if type_id == TypeId::of::<Self>() {
|
||||
Some(self_handle.clone().into())
|
||||
Some(self_handle.to_any())
|
||||
} else if type_id == TypeId::of::<Editor>() {
|
||||
Some(self.editor.clone().into())
|
||||
Some(self.editor.to_any())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -1496,7 +1496,7 @@ impl CollabPanel {
|
||||
|
||||
fn reset_filter_editor_text(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
|
||||
self.filter_editor.update(cx, |editor, cx| {
|
||||
if editor.buffer().read(cx).len(cx).0 > 0 {
|
||||
if editor.buffer().read(cx).len(cx) > 0 {
|
||||
editor.set_text("", window, cx);
|
||||
true
|
||||
} else {
|
||||
|
||||
@@ -12,7 +12,7 @@ workspace = true
|
||||
path = "src/context_server.rs"
|
||||
|
||||
[features]
|
||||
test-support = ["gpui/test-support"]
|
||||
test-support = []
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
@@ -20,7 +20,6 @@ async-trait.workspace = true
|
||||
collections.workspace = true
|
||||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
http_client = { workspace = true, features = ["test-support"] }
|
||||
log.workspace = true
|
||||
net.workspace = true
|
||||
parking_lot.workspace = true
|
||||
@@ -33,6 +32,3 @@ smol.workspace = true
|
||||
tempfile.workspace = true
|
||||
url = { workspace = true, features = ["serde"] }
|
||||
util.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
gpui = { workspace = true, features = ["test-support"] }
|
||||
|
||||
@@ -6,8 +6,6 @@ pub mod test;
|
||||
pub mod transport;
|
||||
pub mod types;
|
||||
|
||||
use collections::HashMap;
|
||||
use http_client::HttpClient;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use std::{fmt::Display, path::PathBuf};
|
||||
@@ -17,9 +15,6 @@ use client::Client;
|
||||
use gpui::AsyncApp;
|
||||
use parking_lot::RwLock;
|
||||
pub use settings::ContextServerCommand;
|
||||
use url::Url;
|
||||
|
||||
use crate::transport::HttpTransport;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ContextServerId(pub Arc<str>);
|
||||
@@ -57,25 +52,6 @@ impl ContextServer {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn http(
|
||||
id: ContextServerId,
|
||||
endpoint: &Url,
|
||||
headers: HashMap<String, String>,
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
executor: gpui::BackgroundExecutor,
|
||||
) -> Result<Self> {
|
||||
let transport = match endpoint.scheme() {
|
||||
"http" | "https" => {
|
||||
log::info!("Using HTTP transport for {}", endpoint);
|
||||
let transport =
|
||||
HttpTransport::new(http_client, endpoint.to_string(), headers, executor);
|
||||
Arc::new(transport) as _
|
||||
}
|
||||
_ => anyhow::bail!("unsupported MCP url scheme {}", endpoint.scheme()),
|
||||
};
|
||||
Ok(Self::new(id, transport))
|
||||
}
|
||||
|
||||
pub fn new(id: ContextServerId, transport: Arc<dyn crate::transport::Transport>) -> Self {
|
||||
Self {
|
||||
id,
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
pub mod http;
|
||||
mod stdio_transport;
|
||||
|
||||
use std::pin::Pin;
|
||||
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use futures::Stream;
|
||||
use std::pin::Pin;
|
||||
|
||||
pub use http::*;
|
||||
pub use stdio_transport::*;
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@@ -1,259 +0,0 @@
|
||||
use anyhow::{Result, anyhow};
|
||||
use async_trait::async_trait;
|
||||
use collections::HashMap;
|
||||
use futures::{Stream, StreamExt};
|
||||
use gpui::BackgroundExecutor;
|
||||
use http_client::{AsyncBody, HttpClient, Request, Response, http::Method};
|
||||
use parking_lot::Mutex as SyncMutex;
|
||||
use smol::channel;
|
||||
use std::{pin::Pin, sync::Arc};
|
||||
|
||||
use crate::transport::Transport;
|
||||
|
||||
// Constants from MCP spec
|
||||
const HEADER_SESSION_ID: &str = "Mcp-Session-Id";
|
||||
const EVENT_STREAM_MIME_TYPE: &str = "text/event-stream";
|
||||
const JSON_MIME_TYPE: &str = "application/json";
|
||||
|
||||
/// HTTP Transport with session management and SSE support
|
||||
pub struct HttpTransport {
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
endpoint: String,
|
||||
session_id: Arc<SyncMutex<Option<String>>>,
|
||||
executor: BackgroundExecutor,
|
||||
response_tx: channel::Sender<String>,
|
||||
response_rx: channel::Receiver<String>,
|
||||
error_tx: channel::Sender<String>,
|
||||
error_rx: channel::Receiver<String>,
|
||||
// Authentication headers to include in requests
|
||||
headers: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl HttpTransport {
|
||||
pub fn new(
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
endpoint: String,
|
||||
headers: HashMap<String, String>,
|
||||
executor: BackgroundExecutor,
|
||||
) -> Self {
|
||||
let (response_tx, response_rx) = channel::unbounded();
|
||||
let (error_tx, error_rx) = channel::unbounded();
|
||||
|
||||
Self {
|
||||
http_client,
|
||||
executor,
|
||||
endpoint,
|
||||
session_id: Arc::new(SyncMutex::new(None)),
|
||||
response_tx,
|
||||
response_rx,
|
||||
error_tx,
|
||||
error_rx,
|
||||
headers,
|
||||
}
|
||||
}
|
||||
|
||||
/// Send a message and handle the response based on content type
|
||||
async fn send_message(&self, message: String) -> Result<()> {
|
||||
let is_notification =
|
||||
!message.contains("\"id\":") || message.contains("notifications/initialized");
|
||||
|
||||
let mut request_builder = Request::builder()
|
||||
.method(Method::POST)
|
||||
.uri(&self.endpoint)
|
||||
.header("Content-Type", JSON_MIME_TYPE)
|
||||
.header(
|
||||
"Accept",
|
||||
format!("{}, {}", JSON_MIME_TYPE, EVENT_STREAM_MIME_TYPE),
|
||||
);
|
||||
|
||||
for (key, value) in &self.headers {
|
||||
request_builder = request_builder.header(key.as_str(), value.as_str());
|
||||
}
|
||||
|
||||
// Add session ID if we have one (except for initialize)
|
||||
if let Some(ref session_id) = *self.session_id.lock() {
|
||||
request_builder = request_builder.header(HEADER_SESSION_ID, session_id.as_str());
|
||||
}
|
||||
|
||||
let request = request_builder.body(AsyncBody::from(message.into_bytes()))?;
|
||||
let mut response = self.http_client.send(request).await?;
|
||||
|
||||
// Handle different response types based on status and content-type
|
||||
match response.status() {
|
||||
status if status.is_success() => {
|
||||
// Check content type
|
||||
let content_type = response
|
||||
.headers()
|
||||
.get("content-type")
|
||||
.and_then(|v| v.to_str().ok());
|
||||
|
||||
// Extract session ID from response headers if present
|
||||
if let Some(session_id) = response
|
||||
.headers()
|
||||
.get(HEADER_SESSION_ID)
|
||||
.and_then(|v| v.to_str().ok())
|
||||
{
|
||||
*self.session_id.lock() = Some(session_id.to_string());
|
||||
log::debug!("Session ID set: {}", session_id);
|
||||
}
|
||||
|
||||
match content_type {
|
||||
Some(ct) if ct.starts_with(JSON_MIME_TYPE) => {
|
||||
// JSON response - read and forward immediately
|
||||
let mut body = String::new();
|
||||
futures::AsyncReadExt::read_to_string(response.body_mut(), &mut body)
|
||||
.await?;
|
||||
|
||||
// Only send non-empty responses
|
||||
if !body.is_empty() {
|
||||
self.response_tx
|
||||
.send(body)
|
||||
.await
|
||||
.map_err(|_| anyhow!("Failed to send JSON response"))?;
|
||||
}
|
||||
}
|
||||
Some(ct) if ct.starts_with(EVENT_STREAM_MIME_TYPE) => {
|
||||
// SSE stream - set up streaming
|
||||
self.setup_sse_stream(response).await?;
|
||||
}
|
||||
_ => {
|
||||
// For notifications, 202 Accepted with no content type is ok
|
||||
if is_notification && status.as_u16() == 202 {
|
||||
log::debug!("Notification accepted");
|
||||
} else {
|
||||
return Err(anyhow!("Unexpected content type: {:?}", content_type));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
status if status.as_u16() == 202 => {
|
||||
// Accepted - notification acknowledged, no response needed
|
||||
log::debug!("Notification accepted");
|
||||
}
|
||||
_ => {
|
||||
let mut error_body = String::new();
|
||||
futures::AsyncReadExt::read_to_string(response.body_mut(), &mut error_body).await?;
|
||||
|
||||
self.error_tx
|
||||
.send(format!("HTTP {}: {}", response.status(), error_body))
|
||||
.await
|
||||
.map_err(|_| anyhow!("Failed to send error"))?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set up SSE streaming from the response
|
||||
async fn setup_sse_stream(&self, mut response: Response<AsyncBody>) -> Result<()> {
|
||||
let response_tx = self.response_tx.clone();
|
||||
let error_tx = self.error_tx.clone();
|
||||
|
||||
// Spawn a task to handle the SSE stream
|
||||
smol::spawn(async move {
|
||||
let reader = futures::io::BufReader::new(response.body_mut());
|
||||
let mut lines = futures::AsyncBufReadExt::lines(reader);
|
||||
|
||||
let mut data_buffer = Vec::new();
|
||||
let mut in_message = false;
|
||||
|
||||
while let Some(line_result) = lines.next().await {
|
||||
match line_result {
|
||||
Ok(line) => {
|
||||
if line.is_empty() {
|
||||
// Empty line signals end of event
|
||||
if !data_buffer.is_empty() {
|
||||
let message = data_buffer.join("\n");
|
||||
|
||||
// Filter out ping messages and empty data
|
||||
if !message.trim().is_empty() && message != "ping" {
|
||||
if let Err(e) = response_tx.send(message).await {
|
||||
log::error!("Failed to send SSE message: {}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
data_buffer.clear();
|
||||
}
|
||||
in_message = false;
|
||||
} else if let Some(data) = line.strip_prefix("data: ") {
|
||||
// Handle data lines
|
||||
let data = data.trim();
|
||||
if !data.is_empty() {
|
||||
// Check if this is a ping message
|
||||
if data == "ping" {
|
||||
log::trace!("Received SSE ping");
|
||||
continue;
|
||||
}
|
||||
data_buffer.push(data.to_string());
|
||||
in_message = true;
|
||||
}
|
||||
} else if line.starts_with("event:")
|
||||
|| line.starts_with("id:")
|
||||
|| line.starts_with("retry:")
|
||||
{
|
||||
// Ignore other SSE fields
|
||||
continue;
|
||||
} else if in_message {
|
||||
// Continuation of data
|
||||
data_buffer.push(line);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = error_tx.send(format!("SSE stream error: {}", e)).await;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Transport for HttpTransport {
|
||||
async fn send(&self, message: String) -> Result<()> {
|
||||
self.send_message(message).await
|
||||
}
|
||||
|
||||
fn receive(&self) -> Pin<Box<dyn Stream<Item = String> + Send>> {
|
||||
Box::pin(self.response_rx.clone())
|
||||
}
|
||||
|
||||
fn receive_err(&self) -> Pin<Box<dyn Stream<Item = String> + Send>> {
|
||||
Box::pin(self.error_rx.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for HttpTransport {
|
||||
fn drop(&mut self) {
|
||||
// Try to cleanup session on drop
|
||||
let http_client = self.http_client.clone();
|
||||
let endpoint = self.endpoint.clone();
|
||||
let session_id = self.session_id.lock().clone();
|
||||
let headers = self.headers.clone();
|
||||
|
||||
if let Some(session_id) = session_id {
|
||||
self.executor
|
||||
.spawn(async move {
|
||||
let mut request_builder = Request::builder()
|
||||
.method(Method::DELETE)
|
||||
.uri(&endpoint)
|
||||
.header(HEADER_SESSION_ID, &session_id);
|
||||
|
||||
// Add authentication headers if present
|
||||
for (key, value) in headers {
|
||||
request_builder = request_builder.header(key.as_str(), value.as_str());
|
||||
}
|
||||
|
||||
let request = request_builder.body(AsyncBody::empty());
|
||||
|
||||
if let Ok(request) = request {
|
||||
let _ = http_client.send(request).await;
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -270,7 +270,7 @@ fn common_prefix<T1: Iterator<Item = char>, T2: Iterator<Item = char>>(a: T1, b:
|
||||
mod tests {
|
||||
use super::*;
|
||||
use editor::{
|
||||
Editor, ExcerptRange, MultiBuffer, MultiBufferOffset, SelectionEffects,
|
||||
Editor, ExcerptRange, MultiBuffer, SelectionEffects,
|
||||
test::editor_lsp_test_context::EditorLspTestContext,
|
||||
};
|
||||
use fs::FakeFs;
|
||||
@@ -1081,9 +1081,8 @@ mod tests {
|
||||
vec![complete_from_marker, replace_range_marker.clone()],
|
||||
);
|
||||
|
||||
let range = marked_ranges.remove(&replace_range_marker).unwrap()[0].clone();
|
||||
let replace_range =
|
||||
cx.to_lsp_range(MultiBufferOffset(range.start)..MultiBufferOffset(range.end));
|
||||
cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
|
||||
|
||||
let mut request =
|
||||
cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
|
||||
|
||||
@@ -289,11 +289,15 @@ impl minidumper::ServerHandler for CrashServer {
|
||||
pub fn panic_hook(info: &PanicHookInfo) {
|
||||
// Don't handle a panic on threads that are not relevant to the main execution.
|
||||
if extension_host::wasm_host::IS_WASM_THREAD.with(|v| v.load(Ordering::Acquire)) {
|
||||
log::error!("wasm thread panicked!");
|
||||
return;
|
||||
}
|
||||
|
||||
let message = info.payload_as_str().unwrap_or("Box<Any>").to_owned();
|
||||
let message = info
|
||||
.payload()
|
||||
.downcast_ref::<&str>()
|
||||
.map(|s| s.to_string())
|
||||
.or_else(|| info.payload().downcast_ref::<String>().cloned())
|
||||
.unwrap_or_else(|| "Box<Any>".to_string());
|
||||
|
||||
let span = info
|
||||
.location()
|
||||
|
||||
@@ -324,7 +324,6 @@ pub async fn download_adapter_from_github(
|
||||
extract_zip(&version_path, file)
|
||||
.await
|
||||
// we cannot check the status as some adapter include files with names that trigger `Illegal byte sequence`
|
||||
.inspect_err(|e| log::warn!("ZIP extraction error: {}. Ignoring...", e))
|
||||
.ok();
|
||||
|
||||
util::fs::remove_matching(&adapter_path, |entry| {
|
||||
|
||||
@@ -14,12 +14,13 @@ use collections::IndexMap;
|
||||
use dap::adapters::DebugAdapterName;
|
||||
use dap::{DapRegistry, StartDebuggingRequestArguments};
|
||||
use dap::{client::SessionId, debugger_settings::DebuggerSettings};
|
||||
use editor::{Editor, MultiBufferOffset, ToPoint};
|
||||
use editor::Editor;
|
||||
use gpui::{
|
||||
Action, App, AsyncWindowContext, ClipboardItem, Context, DismissEvent, Entity, EntityId,
|
||||
EventEmitter, FocusHandle, Focusable, MouseButton, MouseDownEvent, Point, Subscription, Task,
|
||||
WeakEntity, anchored, deferred,
|
||||
};
|
||||
use text::ToPoint as _;
|
||||
|
||||
use itertools::Itertools as _;
|
||||
use language::Buffer;
|
||||
@@ -1215,11 +1216,11 @@ impl DebugPanel {
|
||||
let mut last_offset = None;
|
||||
while let Some(mat) = matches.next() {
|
||||
if let Some(pos) = mat.captures.first().map(|m| m.node.byte_range().end) {
|
||||
last_offset = Some(MultiBufferOffset(pos))
|
||||
last_offset = Some(pos)
|
||||
}
|
||||
}
|
||||
let mut edits = Vec::new();
|
||||
let mut cursor_position = MultiBufferOffset(0);
|
||||
let mut cursor_position = 0;
|
||||
|
||||
if let Some(pos) = last_offset {
|
||||
edits.push((pos..pos, format!(",\n{new_scenario}")));
|
||||
@@ -1233,25 +1234,24 @@ impl DebugPanel {
|
||||
|
||||
if let Some(mat) = matches.next() {
|
||||
if let Some(pos) = mat.captures.first().map(|m| m.node.byte_range().end - 1) {
|
||||
edits.push((
|
||||
MultiBufferOffset(pos)..MultiBufferOffset(pos),
|
||||
format!("\n{new_scenario}\n"),
|
||||
));
|
||||
cursor_position = MultiBufferOffset(pos) + "\n ".len();
|
||||
edits.push((pos..pos, format!("\n{new_scenario}\n")));
|
||||
cursor_position = pos + "\n ".len();
|
||||
}
|
||||
} else {
|
||||
edits.push((
|
||||
MultiBufferOffset(0)..MultiBufferOffset(0),
|
||||
format!("[\n{}\n]", new_scenario),
|
||||
));
|
||||
cursor_position = MultiBufferOffset("[\n ".len());
|
||||
edits.push((0..0, format!("[\n{}\n]", new_scenario)));
|
||||
cursor_position = "[\n ".len();
|
||||
}
|
||||
}
|
||||
editor.transact(window, cx, |editor, window, cx| {
|
||||
editor.edit(edits, cx);
|
||||
let snapshot = editor.buffer().read(cx).read(cx);
|
||||
let snapshot = editor
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.as_singleton()
|
||||
.unwrap()
|
||||
.read(cx)
|
||||
.snapshot();
|
||||
let point = cursor_position.to_point(&snapshot);
|
||||
drop(snapshot);
|
||||
editor.go_to_singleton_buffer_point(point, window, cx);
|
||||
});
|
||||
Ok(editor.save(SaveOptions::default(), project, window, cx))
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::any::TypeId;
|
||||
|
||||
use debugger_panel::DebugPanel;
|
||||
use editor::{Editor, MultiBufferOffsetUtf16};
|
||||
use editor::Editor;
|
||||
use gpui::{Action, App, DispatchPhase, EntityInputHandler, actions};
|
||||
use new_process_modal::{NewProcessModal, NewProcessMode};
|
||||
use onboarding_modal::DebuggerOnboardingModal;
|
||||
@@ -390,14 +390,11 @@ pub fn init(cx: &mut App) {
|
||||
maybe!({
|
||||
let text = editor
|
||||
.update(cx, |editor, cx| {
|
||||
let range = editor
|
||||
.selections
|
||||
.newest::<MultiBufferOffsetUtf16>(
|
||||
&editor.display_snapshot(cx),
|
||||
)
|
||||
.range();
|
||||
editor.text_for_range(
|
||||
range.start.0.0..range.end.0.0,
|
||||
editor
|
||||
.selections
|
||||
.newest(&editor.display_snapshot(cx))
|
||||
.range(),
|
||||
&mut None,
|
||||
window,
|
||||
cx,
|
||||
|
||||
@@ -25,9 +25,12 @@ use settings::Settings;
|
||||
use task::{DebugScenario, RevealTarget, VariableName, ZedDebugConfig};
|
||||
use theme::ThemeSettings;
|
||||
use ui::{
|
||||
CheckboxWithLabel, ContextMenu, DropdownMenu, FluentBuilder, IconWithIndicator, Indicator,
|
||||
KeyBinding, ListItem, ListItemSpacing, ToggleButtonGroup, ToggleButtonSimple, ToggleState,
|
||||
Tooltip, prelude::*,
|
||||
ActiveTheme, Button, ButtonCommon, ButtonSize, CheckboxWithLabel, Clickable, Color, Context,
|
||||
ContextMenu, Disableable, DropdownMenu, FluentBuilder, Icon, IconName, IconSize,
|
||||
IconWithIndicator, Indicator, InteractiveElement, IntoElement, KeyBinding, Label,
|
||||
LabelCommon as _, LabelSize, ListItem, ListItemSpacing, ParentElement, RenderOnce,
|
||||
SharedString, Styled, StyledExt, ToggleButton, ToggleState, Toggleable, Tooltip, Window, div,
|
||||
h_flex, relative, rems, v_flex,
|
||||
};
|
||||
use util::{ResultExt, rel_path::RelPath, shell::ShellKind};
|
||||
use workspace::{ModalView, Workspace, notifications::DetachAndPromptErr, pane};
|
||||
@@ -617,64 +620,72 @@ impl Render for NewProcessModal {
|
||||
.border_b_1()
|
||||
.border_color(cx.theme().colors().border_variant)
|
||||
.child(
|
||||
ToggleButtonGroup::single_row(
|
||||
"debugger-mode-buttons",
|
||||
[
|
||||
ToggleButtonSimple::new(
|
||||
NewProcessMode::Task.to_string(),
|
||||
cx.listener(|this, _, window, cx| {
|
||||
this.mode = NewProcessMode::Task;
|
||||
this.mode_focus_handle(cx).focus(window);
|
||||
cx.notify();
|
||||
}),
|
||||
)
|
||||
.tooltip(Tooltip::text("Run predefined task")),
|
||||
ToggleButtonSimple::new(
|
||||
NewProcessMode::Debug.to_string(),
|
||||
cx.listener(|this, _, window, cx| {
|
||||
this.mode = NewProcessMode::Debug;
|
||||
this.mode_focus_handle(cx).focus(window);
|
||||
cx.notify();
|
||||
}),
|
||||
)
|
||||
.tooltip(Tooltip::text("Start a predefined debug scenario")),
|
||||
ToggleButtonSimple::new(
|
||||
NewProcessMode::Attach.to_string(),
|
||||
cx.listener(|this, _, window, cx| {
|
||||
this.mode = NewProcessMode::Attach;
|
||||
|
||||
if let Some(debugger) = this.debugger.as_ref() {
|
||||
Self::update_attach_picker(
|
||||
&this.attach_mode,
|
||||
debugger,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
this.mode_focus_handle(cx).focus(window);
|
||||
cx.notify();
|
||||
}),
|
||||
)
|
||||
.tooltip(Tooltip::text("Attach the debugger to a running process")),
|
||||
ToggleButtonSimple::new(
|
||||
NewProcessMode::Launch.to_string(),
|
||||
cx.listener(|this, _, window, cx| {
|
||||
this.mode = NewProcessMode::Launch;
|
||||
this.mode_focus_handle(cx).focus(window);
|
||||
cx.notify();
|
||||
}),
|
||||
)
|
||||
.tooltip(Tooltip::text("Launch a new process with a debugger")),
|
||||
],
|
||||
ToggleButton::new(
|
||||
"debugger-session-ui-tasks-button",
|
||||
NewProcessMode::Task.to_string(),
|
||||
)
|
||||
.label_size(LabelSize::Default)
|
||||
.auto_width()
|
||||
.selected_index(match self.mode {
|
||||
NewProcessMode::Task => 0,
|
||||
NewProcessMode::Debug => 1,
|
||||
NewProcessMode::Attach => 2,
|
||||
NewProcessMode::Launch => 3,
|
||||
}),
|
||||
.size(ButtonSize::Default)
|
||||
.toggle_state(matches!(self.mode, NewProcessMode::Task))
|
||||
.style(ui::ButtonStyle::Subtle)
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
this.mode = NewProcessMode::Task;
|
||||
this.mode_focus_handle(cx).focus(window);
|
||||
cx.notify();
|
||||
}))
|
||||
.tooltip(Tooltip::text("Run predefined task"))
|
||||
.first(),
|
||||
)
|
||||
.child(
|
||||
ToggleButton::new(
|
||||
"debugger-session-ui-launch-button",
|
||||
NewProcessMode::Debug.to_string(),
|
||||
)
|
||||
.size(ButtonSize::Default)
|
||||
.style(ui::ButtonStyle::Subtle)
|
||||
.toggle_state(matches!(self.mode, NewProcessMode::Debug))
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
this.mode = NewProcessMode::Debug;
|
||||
this.mode_focus_handle(cx).focus(window);
|
||||
cx.notify();
|
||||
}))
|
||||
.tooltip(Tooltip::text("Start a predefined debug scenario"))
|
||||
.middle(),
|
||||
)
|
||||
.child(
|
||||
ToggleButton::new(
|
||||
"debugger-session-ui-attach-button",
|
||||
NewProcessMode::Attach.to_string(),
|
||||
)
|
||||
.size(ButtonSize::Default)
|
||||
.toggle_state(matches!(self.mode, NewProcessMode::Attach))
|
||||
.style(ui::ButtonStyle::Subtle)
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
this.mode = NewProcessMode::Attach;
|
||||
|
||||
if let Some(debugger) = this.debugger.as_ref() {
|
||||
Self::update_attach_picker(&this.attach_mode, debugger, window, cx);
|
||||
}
|
||||
this.mode_focus_handle(cx).focus(window);
|
||||
cx.notify();
|
||||
}))
|
||||
.tooltip(Tooltip::text("Attach the debugger to a running process"))
|
||||
.middle(),
|
||||
)
|
||||
.child(
|
||||
ToggleButton::new(
|
||||
"debugger-session-ui-custom-button",
|
||||
NewProcessMode::Launch.to_string(),
|
||||
)
|
||||
.size(ButtonSize::Default)
|
||||
.toggle_state(matches!(self.mode, NewProcessMode::Launch))
|
||||
.style(ui::ButtonStyle::Subtle)
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
this.mode = NewProcessMode::Launch;
|
||||
this.mode_focus_handle(cx).focus(window);
|
||||
cx.notify();
|
||||
}))
|
||||
.tooltip(Tooltip::text("Launch a new process with a debugger"))
|
||||
.last(),
|
||||
),
|
||||
)
|
||||
.child(v_flex().child(self.render_mode(window, cx)))
|
||||
|
||||
@@ -8,7 +8,7 @@ use collections::HashMap;
|
||||
use dap::{CompletionItem, CompletionItemType, OutputEvent};
|
||||
use editor::{
|
||||
Bias, CompletionProvider, Editor, EditorElement, EditorMode, EditorStyle, ExcerptId,
|
||||
MultiBufferOffset, SizingBehavior,
|
||||
SizingBehavior,
|
||||
};
|
||||
use fuzzy::StringMatchCandidate;
|
||||
use gpui::{
|
||||
@@ -161,9 +161,7 @@ impl Console {
|
||||
) -> Task<Result<()>> {
|
||||
self.console.update(cx, |_, cx| {
|
||||
cx.spawn_in(window, async move |console, cx| {
|
||||
let mut len = console
|
||||
.update(cx, |this, cx| this.buffer().read(cx).len(cx))?
|
||||
.0;
|
||||
let mut len = console.update(cx, |this, cx| this.buffer().read(cx).len(cx))?;
|
||||
let (output, spans, background_spans) = cx
|
||||
.background_spawn(async move {
|
||||
let mut all_spans = Vec::new();
|
||||
@@ -229,8 +227,8 @@ impl Console {
|
||||
for (range, color) in spans {
|
||||
let Some(color) = color else { continue };
|
||||
let start_offset = range.start;
|
||||
let range = buffer.anchor_after(MultiBufferOffset(range.start))
|
||||
..buffer.anchor_before(MultiBufferOffset(range.end));
|
||||
let range =
|
||||
buffer.anchor_after(range.start)..buffer.anchor_before(range.end);
|
||||
let style = HighlightStyle {
|
||||
color: Some(terminal_view::terminal_element::convert_color(
|
||||
&color,
|
||||
@@ -249,8 +247,8 @@ impl Console {
|
||||
for (range, color) in background_spans {
|
||||
let Some(color) = color else { continue };
|
||||
let start_offset = range.start;
|
||||
let range = buffer.anchor_after(MultiBufferOffset(range.start))
|
||||
..buffer.anchor_before(MultiBufferOffset(range.end));
|
||||
let range =
|
||||
buffer.anchor_after(range.start)..buffer.anchor_before(range.end);
|
||||
console.highlight_background_key::<ConsoleAnsiHighlight>(
|
||||
start_offset,
|
||||
&[range],
|
||||
@@ -963,7 +961,7 @@ fn color_fetcher(color: ansi::Color) -> fn(&Theme) -> Hsla {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::tests::init_test;
|
||||
use editor::{MultiBufferOffset, test::editor_test_context::EditorTestContext};
|
||||
use editor::test::editor_test_context::EditorTestContext;
|
||||
use gpui::TestAppContext;
|
||||
use language::Point;
|
||||
|
||||
@@ -995,8 +993,8 @@ mod tests {
|
||||
cx.update_editor(|editor, _, cx| {
|
||||
editor.edit(
|
||||
vec![(
|
||||
MultiBufferOffset(snapshot.offset_for_anchor(&replace_range.start))
|
||||
..MultiBufferOffset(snapshot.offset_for_anchor(&replace_range.end)),
|
||||
snapshot.offset_for_anchor(&replace_range.start)
|
||||
..snapshot.offset_for_anchor(&replace_range.end),
|
||||
replacement,
|
||||
)],
|
||||
cx,
|
||||
|
||||
@@ -7,7 +7,7 @@ use editor::{
|
||||
RowHighlightOptions, SelectionEffects, ToPoint, scroll::Autoscroll,
|
||||
};
|
||||
use gpui::{
|
||||
App, AppContext, Entity, EventEmitter, Focusable, IntoElement, Render, SharedString,
|
||||
AnyView, App, AppContext, Entity, EventEmitter, Focusable, IntoElement, Render, SharedString,
|
||||
Subscription, Task, WeakEntity, Window,
|
||||
};
|
||||
use language::{BufferSnapshot, Capability, Point, Selection, SelectionGoal, TreeSitterOptions};
|
||||
@@ -418,11 +418,11 @@ impl Item for StackTraceView {
|
||||
type_id: TypeId,
|
||||
self_handle: &'a Entity<Self>,
|
||||
_: &'a App,
|
||||
) -> Option<gpui::AnyEntity> {
|
||||
) -> Option<AnyView> {
|
||||
if type_id == TypeId::of::<Self>() {
|
||||
Some(self_handle.clone().into())
|
||||
Some(self_handle.to_any())
|
||||
} else if type_id == TypeId::of::<Editor>() {
|
||||
Some(self.editor.clone().into())
|
||||
Some(self.editor.to_any())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -680,11 +680,11 @@ impl Item for BufferDiagnosticsEditor {
|
||||
type_id: std::any::TypeId,
|
||||
self_handle: &'a Entity<Self>,
|
||||
_: &'a App,
|
||||
) -> Option<gpui::AnyEntity> {
|
||||
) -> Option<gpui::AnyView> {
|
||||
if type_id == TypeId::of::<Self>() {
|
||||
Some(self_handle.clone().into())
|
||||
Some(self_handle.to_any())
|
||||
} else if type_id == TypeId::of::<Editor>() {
|
||||
Some(self.editor.clone().into())
|
||||
Some(self.editor.to_any())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ use editor::{
|
||||
multibuffer_context_lines,
|
||||
};
|
||||
use gpui::{
|
||||
AnyElement, App, AsyncApp, Context, Entity, EventEmitter, FocusHandle, FocusOutEvent,
|
||||
AnyElement, AnyView, App, AsyncApp, Context, Entity, EventEmitter, FocusHandle, FocusOutEvent,
|
||||
Focusable, Global, InteractiveElement, IntoElement, ParentElement, Render, SharedString,
|
||||
Styled, Subscription, Task, WeakEntity, Window, actions, div,
|
||||
};
|
||||
@@ -880,11 +880,11 @@ impl Item for ProjectDiagnosticsEditor {
|
||||
type_id: TypeId,
|
||||
self_handle: &'a Entity<Self>,
|
||||
_: &'a App,
|
||||
) -> Option<gpui::AnyEntity> {
|
||||
) -> Option<AnyView> {
|
||||
if type_id == TypeId::of::<Self>() {
|
||||
Some(self_handle.clone().into())
|
||||
Some(self_handle.to_any())
|
||||
} else if type_id == TypeId::of::<Editor>() {
|
||||
Some(self.editor.clone().into())
|
||||
Some(self.editor.to_any())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::*;
|
||||
use collections::{HashMap, HashSet};
|
||||
use editor::{
|
||||
DisplayPoint, EditorSettings, Inlay, MultiBufferOffset,
|
||||
DisplayPoint, EditorSettings, Inlay,
|
||||
actions::{GoToDiagnostic, GoToPreviousDiagnostic, Hover, MoveToBeginning},
|
||||
display_map::DisplayRow,
|
||||
test::{
|
||||
@@ -878,8 +878,7 @@ async fn test_random_diagnostics_with_inlays(cx: &mut TestAppContext, mut rng: S
|
||||
diagnostics.editor.update(cx, |editor, cx| {
|
||||
let snapshot = editor.snapshot(window, cx);
|
||||
if !snapshot.buffer_snapshot().is_empty() {
|
||||
let position = rng
|
||||
.random_range(MultiBufferOffset(0)..snapshot.buffer_snapshot().len());
|
||||
let position = rng.random_range(0..snapshot.buffer_snapshot().len());
|
||||
let position = snapshot.buffer_snapshot().clip_offset(position, Bias::Left);
|
||||
log::info!(
|
||||
"adding inlay at {position}/{}: {:?}",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use editor::{Editor, MultiBufferOffset};
|
||||
use editor::Editor;
|
||||
use gpui::{
|
||||
Context, Entity, EventEmitter, IntoElement, ParentElement, Render, Styled, Subscription, Task,
|
||||
WeakEntity, Window,
|
||||
@@ -171,19 +171,14 @@ impl DiagnosticIndicator {
|
||||
let buffer = editor.buffer().read(cx).snapshot(cx);
|
||||
let cursor_position = editor
|
||||
.selections
|
||||
.newest::<MultiBufferOffset>(&editor.display_snapshot(cx))
|
||||
.newest::<usize>(&editor.display_snapshot(cx))
|
||||
.head();
|
||||
(buffer, cursor_position)
|
||||
});
|
||||
let new_diagnostic = buffer
|
||||
.diagnostics_in_range::<MultiBufferOffset>(cursor_position..cursor_position)
|
||||
.diagnostics_in_range::<usize>(cursor_position..cursor_position)
|
||||
.filter(|entry| !entry.range.is_empty())
|
||||
.min_by_key(|entry| {
|
||||
(
|
||||
entry.diagnostic.severity,
|
||||
entry.range.end - entry.range.start,
|
||||
)
|
||||
})
|
||||
.min_by_key(|entry| (entry.diagnostic.severity, entry.range.len()))
|
||||
.map(|entry| entry.diagnostic);
|
||||
if new_diagnostic != self.current_diagnostic.as_ref() {
|
||||
let new_diagnostic = new_diagnostic.cloned();
|
||||
|
||||
@@ -30,12 +30,12 @@ project.workspace = true
|
||||
regex.workspace = true
|
||||
settings.workspace = true
|
||||
supermaven.workspace = true
|
||||
sweep_ai.workspace = true
|
||||
telemetry.workspace = true
|
||||
ui.workspace = true
|
||||
workspace.workspace = true
|
||||
zed_actions.workspace = true
|
||||
zeta.workspace = true
|
||||
zeta2.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
copilot = { workspace = true, features = ["test-support"] }
|
||||
|
||||
@@ -3,9 +3,7 @@ use client::{Client, UserStore, zed_urls};
|
||||
use cloud_llm_client::UsageLimit;
|
||||
use codestral::CodestralCompletionProvider;
|
||||
use copilot::{Copilot, Status};
|
||||
use editor::{
|
||||
Editor, MultiBufferOffset, SelectionEffects, actions::ShowEditPrediction, scroll::Autoscroll,
|
||||
};
|
||||
use editor::{Editor, SelectionEffects, actions::ShowEditPrediction, scroll::Autoscroll};
|
||||
use feature_flags::{FeatureFlagAppExt, PredictEditsRateCompletionsFeatureFlag};
|
||||
use fs::Fs;
|
||||
use gpui::{
|
||||
@@ -28,6 +26,7 @@ use std::{
|
||||
time::Duration,
|
||||
};
|
||||
use supermaven::{AccountStatus, Supermaven};
|
||||
use sweep_ai::SweepFeatureFlag;
|
||||
use ui::{
|
||||
Clickable, ContextMenu, ContextMenuEntry, DocumentationEdge, DocumentationSide, IconButton,
|
||||
IconButtonShape, Indicator, PopoverMenu, PopoverMenuHandle, ProgressBar, Tooltip, prelude::*,
|
||||
@@ -38,7 +37,6 @@ use workspace::{
|
||||
};
|
||||
use zed_actions::OpenBrowser;
|
||||
use zeta::RateCompletions;
|
||||
use zeta2::SweepFeatureFlag;
|
||||
|
||||
actions!(
|
||||
edit_prediction,
|
||||
@@ -1109,12 +1107,7 @@ async fn open_disabled_globs_setting_in_editor(
|
||||
});
|
||||
|
||||
if !edits.is_empty() {
|
||||
item.edit(
|
||||
edits
|
||||
.into_iter()
|
||||
.map(|(r, s)| (MultiBufferOffset(r.start)..MultiBufferOffset(r.end), s)),
|
||||
cx,
|
||||
);
|
||||
item.edit(edits, cx);
|
||||
}
|
||||
|
||||
let text = item.buffer().read(cx).snapshot(cx).text();
|
||||
@@ -1129,7 +1122,6 @@ async fn open_disabled_globs_setting_in_editor(
|
||||
.map(|inner_match| inner_match.start()..inner_match.end())
|
||||
});
|
||||
if let Some(range) = range {
|
||||
let range = MultiBufferOffset(range.start)..MultiBufferOffset(range.end);
|
||||
item.change_selections(
|
||||
SelectionEffects::scroll(Autoscroll::newest()),
|
||||
window,
|
||||
|
||||
@@ -2,7 +2,6 @@ use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
|
||||
use editor::MultiBuffer;
|
||||
use gpui::TestDispatcher;
|
||||
use itertools::Itertools;
|
||||
use multi_buffer::MultiBufferOffset;
|
||||
use rand::{Rng, SeedableRng, rngs::StdRng};
|
||||
use std::num::NonZeroU32;
|
||||
use text::Bias;
|
||||
@@ -25,9 +24,7 @@ fn to_tab_point_benchmark(c: &mut Criterion) {
|
||||
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
|
||||
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot.clone());
|
||||
let fold_point = fold_snapshot.to_fold_point(
|
||||
inlay_snapshot.to_point(InlayOffset(
|
||||
rng.random_range(MultiBufferOffset(0)..MultiBufferOffset(length)),
|
||||
)),
|
||||
inlay_snapshot.to_point(InlayOffset(rng.random_range(0..length))),
|
||||
Bias::Left,
|
||||
);
|
||||
let (_, snapshot) = TabMap::new(fold_snapshot, NonZeroU32::new(4).unwrap());
|
||||
@@ -72,9 +69,7 @@ fn to_fold_point_benchmark(c: &mut Criterion) {
|
||||
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot.clone());
|
||||
|
||||
let fold_point = fold_snapshot.to_fold_point(
|
||||
inlay_snapshot.to_point(InlayOffset(
|
||||
rng.random_range(MultiBufferOffset(0)..MultiBufferOffset(length)),
|
||||
)),
|
||||
inlay_snapshot.to_point(InlayOffset(rng.random_range(0..length))),
|
||||
Bias::Left,
|
||||
);
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ use gpui::{
|
||||
use itertools::Itertools;
|
||||
use language::CodeLabel;
|
||||
use language::{Buffer, LanguageName, LanguageRegistry};
|
||||
use lsp::CompletionItemTag;
|
||||
use markdown::{Markdown, MarkdownElement};
|
||||
use multi_buffer::{Anchor, ExcerptId};
|
||||
use ordered_float::OrderedFloat;
|
||||
@@ -841,16 +840,7 @@ impl CompletionsMenu {
|
||||
if completion
|
||||
.source
|
||||
.lsp_completion(false)
|
||||
.and_then(|lsp_completion| {
|
||||
match (lsp_completion.deprecated, &lsp_completion.tags)
|
||||
{
|
||||
(Some(true), _) => Some(true),
|
||||
(_, Some(tags)) => Some(
|
||||
tags.contains(&CompletionItemTag::DEPRECATED),
|
||||
),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.and_then(|lsp_completion| lsp_completion.deprecated)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
highlight.strikethrough = Some(StrikethroughStyle {
|
||||
|
||||
@@ -44,10 +44,12 @@ pub use invisibles::{is_invisible, replacement};
|
||||
|
||||
use collections::{HashMap, HashSet};
|
||||
use gpui::{App, Context, Entity, Font, HighlightStyle, LineLayout, Pixels, UnderlineStyle};
|
||||
use language::{Point, Subscription as BufferSubscription, language_settings::language_settings};
|
||||
use language::{
|
||||
OffsetUtf16, Point, Subscription as BufferSubscription, language_settings::language_settings,
|
||||
};
|
||||
use multi_buffer::{
|
||||
Anchor, AnchorRangeExt, MultiBuffer, MultiBufferOffset, MultiBufferOffsetUtf16,
|
||||
MultiBufferPoint, MultiBufferRow, MultiBufferSnapshot, RowInfo, ToOffset, ToPoint,
|
||||
Anchor, AnchorRangeExt, MultiBuffer, MultiBufferPoint, MultiBufferRow, MultiBufferSnapshot,
|
||||
RowInfo, ToOffset, ToPoint,
|
||||
};
|
||||
use project::InlayId;
|
||||
use project::project_settings::DiagnosticSeverity;
|
||||
@@ -102,7 +104,7 @@ type InlayHighlights = TreeMap<TypeId, TreeMap<InlayId, (HighlightStyle, InlayHi
|
||||
pub struct DisplayMap {
|
||||
/// The buffer that we are displaying.
|
||||
buffer: Entity<MultiBuffer>,
|
||||
buffer_subscription: BufferSubscription<MultiBufferOffset>,
|
||||
buffer_subscription: BufferSubscription,
|
||||
/// Decides where the [`Inlay`]s should be displayed.
|
||||
inlay_map: InlayMap,
|
||||
/// Decides where the fold indicators should be and tracks parts of a source file that are currently folded.
|
||||
@@ -196,7 +198,7 @@ impl DisplayMap {
|
||||
pub fn set_state(&mut self, other: &DisplaySnapshot, cx: &mut Context<Self>) {
|
||||
self.fold(
|
||||
other
|
||||
.folds_in_range(MultiBufferOffset(0)..other.buffer_snapshot().len())
|
||||
.folds_in_range(0..other.buffer_snapshot().len())
|
||||
.map(|fold| {
|
||||
Crease::simple(
|
||||
fold.range.to_offset(other.buffer_snapshot()),
|
||||
@@ -792,7 +794,7 @@ impl DisplaySnapshot {
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.buffer_snapshot().len() == MultiBufferOffset(0)
|
||||
self.buffer_snapshot().len() == 0
|
||||
}
|
||||
|
||||
pub fn row_infos(&self, start_row: DisplayRow) -> impl Iterator<Item = RowInfo> + '_ {
|
||||
@@ -1131,10 +1133,7 @@ impl DisplaySnapshot {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn buffer_chars_at(
|
||||
&self,
|
||||
mut offset: MultiBufferOffset,
|
||||
) -> impl Iterator<Item = (char, MultiBufferOffset)> + '_ {
|
||||
pub fn buffer_chars_at(&self, mut offset: usize) -> impl Iterator<Item = (char, usize)> + '_ {
|
||||
self.buffer_snapshot().chars_at(offset).map(move |ch| {
|
||||
let ret = (ch, offset);
|
||||
offset += ch.len_utf8();
|
||||
@@ -1144,8 +1143,8 @@ impl DisplaySnapshot {
|
||||
|
||||
pub fn reverse_buffer_chars_at(
|
||||
&self,
|
||||
mut offset: MultiBufferOffset,
|
||||
) -> impl Iterator<Item = (char, MultiBufferOffset)> + '_ {
|
||||
mut offset: usize,
|
||||
) -> impl Iterator<Item = (char, usize)> + '_ {
|
||||
self.buffer_snapshot()
|
||||
.reversed_chars_at(offset)
|
||||
.map(move |ch| {
|
||||
@@ -1527,7 +1526,7 @@ impl DisplayPoint {
|
||||
map.display_point_to_point(self, Bias::Left)
|
||||
}
|
||||
|
||||
pub fn to_offset(self, map: &DisplaySnapshot, bias: Bias) -> MultiBufferOffset {
|
||||
pub fn to_offset(self, map: &DisplaySnapshot, bias: Bias) -> usize {
|
||||
let wrap_point = map.block_snapshot.to_wrap_point(self.0, bias);
|
||||
let tab_point = map.wrap_snapshot().to_tab_point(wrap_point);
|
||||
let fold_point = map.tab_snapshot().to_fold_point(tab_point, bias).0;
|
||||
@@ -1537,13 +1536,13 @@ impl DisplayPoint {
|
||||
}
|
||||
}
|
||||
|
||||
impl ToDisplayPoint for MultiBufferOffset {
|
||||
impl ToDisplayPoint for usize {
|
||||
fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint {
|
||||
map.point_to_display_point(self.to_point(map.buffer_snapshot()), Bias::Left)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToDisplayPoint for MultiBufferOffsetUtf16 {
|
||||
impl ToDisplayPoint for OffsetUtf16 {
|
||||
fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint {
|
||||
self.to_offset(map.buffer_snapshot()).to_display_point(map)
|
||||
}
|
||||
@@ -1686,7 +1685,7 @@ pub mod tests {
|
||||
let block_properties = (0..rng.random_range(1..=1))
|
||||
.map(|_| {
|
||||
let position = buffer.anchor_after(buffer.clip_offset(
|
||||
rng.random_range(MultiBufferOffset(0)..=buffer.len()),
|
||||
rng.random_range(0..=buffer.len()),
|
||||
Bias::Left,
|
||||
));
|
||||
|
||||
@@ -1728,12 +1727,8 @@ pub mod tests {
|
||||
for _ in 0..rng.random_range(1..=3) {
|
||||
buffer.read_with(cx, |buffer, cx| {
|
||||
let buffer = buffer.read(cx);
|
||||
let end = buffer.clip_offset(
|
||||
rng.random_range(MultiBufferOffset(0)..=buffer.len()),
|
||||
Right,
|
||||
);
|
||||
let start = buffer
|
||||
.clip_offset(rng.random_range(MultiBufferOffset(0)..=end), Left);
|
||||
let end = buffer.clip_offset(rng.random_range(0..=buffer.len()), Right);
|
||||
let start = buffer.clip_offset(rng.random_range(0..=end), Left);
|
||||
ranges.push(start..end);
|
||||
});
|
||||
}
|
||||
@@ -1959,7 +1954,7 @@ pub mod tests {
|
||||
)
|
||||
);
|
||||
|
||||
let ix = MultiBufferOffset(snapshot.buffer_snapshot().text().find("seven").unwrap());
|
||||
let ix = snapshot.buffer_snapshot().text().find("seven").unwrap();
|
||||
buffer.update(cx, |buffer, cx| {
|
||||
buffer.edit([(ix..ix, "and ")], None, cx);
|
||||
});
|
||||
@@ -2088,7 +2083,7 @@ pub mod tests {
|
||||
&[],
|
||||
vec![Inlay::edit_prediction(
|
||||
0,
|
||||
buffer_snapshot.anchor_after(MultiBufferOffset(0)),
|
||||
buffer_snapshot.anchor_after(0),
|
||||
"\n",
|
||||
)],
|
||||
cx,
|
||||
@@ -2099,11 +2094,7 @@ pub mod tests {
|
||||
// Regression test: updating the display map does not crash when a
|
||||
// block is immediately followed by a multi-line inlay.
|
||||
buffer.update(cx, |buffer, cx| {
|
||||
buffer.edit(
|
||||
[(MultiBufferOffset(1)..MultiBufferOffset(1), "b")],
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
buffer.edit([(1..1, "b")], None, cx);
|
||||
});
|
||||
map.update(cx, |m, cx| assert_eq!(m.snapshot(cx).text(), "\n\n\nab"));
|
||||
}
|
||||
@@ -2703,7 +2694,6 @@ pub mod tests {
|
||||
HighlightKey::Type(TypeId::of::<MyType>()),
|
||||
highlighted_ranges
|
||||
.into_iter()
|
||||
.map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end))
|
||||
.map(|range| {
|
||||
buffer_snapshot.anchor_before(range.start)
|
||||
..buffer_snapshot.anchor_before(range.end)
|
||||
|
||||
@@ -11,8 +11,8 @@ use collections::{Bound, HashMap, HashSet};
|
||||
use gpui::{AnyElement, App, EntityId, Pixels, Window};
|
||||
use language::{Patch, Point};
|
||||
use multi_buffer::{
|
||||
Anchor, ExcerptId, ExcerptInfo, MultiBuffer, MultiBufferOffset, MultiBufferRow,
|
||||
MultiBufferSnapshot, RowInfo, ToOffset, ToPoint as _,
|
||||
Anchor, ExcerptId, ExcerptInfo, MultiBuffer, MultiBufferRow, MultiBufferSnapshot, RowInfo,
|
||||
ToOffset, ToPoint as _,
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
use std::{
|
||||
@@ -1208,7 +1208,7 @@ impl BlockMapWriter<'_> {
|
||||
|
||||
pub fn remove_intersecting_replace_blocks(
|
||||
&mut self,
|
||||
ranges: impl IntoIterator<Item = Range<MultiBufferOffset>>,
|
||||
ranges: impl IntoIterator<Item = Range<usize>>,
|
||||
inclusive: bool,
|
||||
) {
|
||||
let wrap_snapshot = self.0.wrap_snapshot.borrow();
|
||||
@@ -1283,7 +1283,7 @@ impl BlockMapWriter<'_> {
|
||||
|
||||
fn blocks_intersecting_buffer_range(
|
||||
&self,
|
||||
range: Range<MultiBufferOffset>,
|
||||
range: Range<usize>,
|
||||
inclusive: bool,
|
||||
) -> &[Arc<CustomBlock>] {
|
||||
if range.is_empty() && !inclusive {
|
||||
@@ -3043,10 +3043,8 @@ mod tests {
|
||||
let block_properties = (0..block_count)
|
||||
.map(|_| {
|
||||
let buffer = cx.update(|cx| buffer.read(cx).read(cx).clone());
|
||||
let offset = buffer.clip_offset(
|
||||
rng.random_range(MultiBufferOffset(0)..=buffer.len()),
|
||||
Bias::Left,
|
||||
);
|
||||
let offset =
|
||||
buffer.clip_offset(rng.random_range(0..=buffer.len()), Bias::Left);
|
||||
let mut min_height = 0;
|
||||
let placement = match rng.random_range(0..3) {
|
||||
0 => {
|
||||
@@ -3246,7 +3244,7 @@ mod tests {
|
||||
// Note that this needs to be synced with the related section in BlockMap::sync
|
||||
expected_blocks.extend(block_map.header_and_footer_blocks(
|
||||
&buffer_snapshot,
|
||||
MultiBufferOffset(0)..,
|
||||
0..,
|
||||
&wraps_snapshot,
|
||||
));
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use collections::BTreeMap;
|
||||
use gpui::HighlightStyle;
|
||||
use language::Chunk;
|
||||
use multi_buffer::{MultiBufferChunks, MultiBufferOffset, MultiBufferSnapshot, ToOffset as _};
|
||||
use multi_buffer::{MultiBufferChunks, MultiBufferSnapshot, ToOffset as _};
|
||||
use std::{
|
||||
cmp,
|
||||
iter::{self, Peekable},
|
||||
@@ -14,7 +14,7 @@ use crate::display_map::{HighlightKey, TextHighlights};
|
||||
pub struct CustomHighlightsChunks<'a> {
|
||||
buffer_chunks: MultiBufferChunks<'a>,
|
||||
buffer_chunk: Option<Chunk<'a>>,
|
||||
offset: MultiBufferOffset,
|
||||
offset: usize,
|
||||
multibuffer_snapshot: &'a MultiBufferSnapshot,
|
||||
|
||||
highlight_endpoints: Peekable<vec::IntoIter<HighlightEndpoint>>,
|
||||
@@ -24,14 +24,14 @@ pub struct CustomHighlightsChunks<'a> {
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
struct HighlightEndpoint {
|
||||
offset: MultiBufferOffset,
|
||||
offset: usize,
|
||||
tag: HighlightKey,
|
||||
style: Option<HighlightStyle>,
|
||||
}
|
||||
|
||||
impl<'a> CustomHighlightsChunks<'a> {
|
||||
pub fn new(
|
||||
range: Range<MultiBufferOffset>,
|
||||
range: Range<usize>,
|
||||
language_aware: bool,
|
||||
text_highlights: Option<&'a TextHighlights>,
|
||||
multibuffer_snapshot: &'a MultiBufferSnapshot,
|
||||
@@ -52,7 +52,7 @@ impl<'a> CustomHighlightsChunks<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn seek(&mut self, new_range: Range<MultiBufferOffset>) {
|
||||
pub fn seek(&mut self, new_range: Range<usize>) {
|
||||
self.highlight_endpoints =
|
||||
create_highlight_endpoints(&new_range, self.text_highlights, self.multibuffer_snapshot);
|
||||
self.offset = new_range.start;
|
||||
@@ -63,7 +63,7 @@ impl<'a> CustomHighlightsChunks<'a> {
|
||||
}
|
||||
|
||||
fn create_highlight_endpoints(
|
||||
range: &Range<MultiBufferOffset>,
|
||||
range: &Range<usize>,
|
||||
text_highlights: Option<&TextHighlights>,
|
||||
buffer: &MultiBufferSnapshot,
|
||||
) -> iter::Peekable<vec::IntoIter<HighlightEndpoint>> {
|
||||
@@ -117,7 +117,7 @@ impl<'a> Iterator for CustomHighlightsChunks<'a> {
|
||||
type Item = Chunk<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut next_highlight_endpoint = MultiBufferOffset(usize::MAX);
|
||||
let mut next_highlight_endpoint = usize::MAX;
|
||||
while let Some(endpoint) = self.highlight_endpoints.peek().copied() {
|
||||
if endpoint.offset <= self.offset {
|
||||
if let Some(style) = endpoint.style {
|
||||
@@ -224,22 +224,20 @@ mod tests {
|
||||
let range_count = rng.random_range(1..10);
|
||||
let text = buffer_snapshot.text();
|
||||
for _ in 0..range_count {
|
||||
if buffer_snapshot.len() == MultiBufferOffset(0) {
|
||||
if buffer_snapshot.len() == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut start = rng.random_range(
|
||||
MultiBufferOffset(0)..=buffer_snapshot.len().saturating_sub_usize(10),
|
||||
);
|
||||
let mut start = rng.random_range(0..=buffer_snapshot.len().saturating_sub(10));
|
||||
|
||||
while !text.is_char_boundary(start.0) {
|
||||
start = start.saturating_sub_usize(1);
|
||||
while !text.is_char_boundary(start) {
|
||||
start = start.saturating_sub(1);
|
||||
}
|
||||
|
||||
let end_end = buffer_snapshot.len().min(start + 100usize);
|
||||
let end_end = buffer_snapshot.len().min(start + 100);
|
||||
let mut end = rng.random_range(start..=end_end);
|
||||
while !text.is_char_boundary(end.0) {
|
||||
end = end.saturating_sub_usize(1);
|
||||
while !text.is_char_boundary(end) {
|
||||
end = end.saturating_sub(1);
|
||||
}
|
||||
|
||||
if start < end {
|
||||
@@ -255,12 +253,8 @@ mod tests {
|
||||
}
|
||||
|
||||
// Get all chunks and verify their bitmaps
|
||||
let chunks = CustomHighlightsChunks::new(
|
||||
MultiBufferOffset(0)..buffer_snapshot.len(),
|
||||
false,
|
||||
None,
|
||||
&buffer_snapshot,
|
||||
);
|
||||
let chunks =
|
||||
CustomHighlightsChunks::new(0..buffer_snapshot.len(), false, None, &buffer_snapshot);
|
||||
|
||||
for chunk in chunks {
|
||||
let chunk_text = chunk.text;
|
||||
|
||||
@@ -5,17 +5,16 @@ use super::{
|
||||
inlay_map::{InlayBufferRows, InlayChunks, InlayEdit, InlayOffset, InlayPoint, InlaySnapshot},
|
||||
};
|
||||
use gpui::{AnyElement, App, ElementId, HighlightStyle, Pixels, Window};
|
||||
use language::{Edit, HighlightId, Point};
|
||||
use language::{Edit, HighlightId, Point, TextSummary};
|
||||
use multi_buffer::{
|
||||
Anchor, AnchorRangeExt, MBTextSummary, MultiBufferOffset, MultiBufferRow, MultiBufferSnapshot,
|
||||
RowInfo, ToOffset,
|
||||
Anchor, AnchorRangeExt, MultiBufferRow, MultiBufferSnapshot, RowInfo, ToOffset,
|
||||
};
|
||||
use project::InlayId;
|
||||
use std::{
|
||||
any::TypeId,
|
||||
cmp::{self, Ordering},
|
||||
fmt, iter,
|
||||
ops::{Add, AddAssign, Deref, DerefMut, Range, Sub, SubAssign},
|
||||
ops::{Add, AddAssign, Deref, DerefMut, Range, Sub},
|
||||
sync::Arc,
|
||||
usize,
|
||||
};
|
||||
@@ -262,7 +261,7 @@ impl FoldMapWriter<'_> {
|
||||
fold_ixs_to_delete.dedup();
|
||||
|
||||
self.0.snapshot.folds = {
|
||||
let mut cursor = self.0.snapshot.folds.cursor::<MultiBufferOffset>(buffer);
|
||||
let mut cursor = self.0.snapshot.folds.cursor::<usize>(buffer);
|
||||
let mut folds = SumTree::new(buffer);
|
||||
for fold_ix in fold_ixs_to_delete {
|
||||
folds.append(cursor.slice(&fold_ix, Bias::Right), buffer);
|
||||
@@ -414,7 +413,7 @@ impl FoldMap {
|
||||
|
||||
let mut new_transforms = SumTree::<Transform>::default();
|
||||
let mut cursor = self.snapshot.transforms.cursor::<InlayOffset>(());
|
||||
cursor.seek(&InlayOffset(MultiBufferOffset(0)), Bias::Right);
|
||||
cursor.seek(&InlayOffset(0), Bias::Right);
|
||||
|
||||
while let Some(mut edit) = inlay_edits_iter.next() {
|
||||
if let Some(item) = cursor.item()
|
||||
@@ -437,7 +436,7 @@ impl FoldMap {
|
||||
cursor.seek(&edit.old.end, Bias::Right);
|
||||
cursor.next();
|
||||
|
||||
let mut delta = edit.new_len() as isize - edit.old_len() as isize;
|
||||
let mut delta = edit.new_len().0 as isize - edit.old_len().0 as isize;
|
||||
loop {
|
||||
edit.old.end = *cursor.start();
|
||||
|
||||
@@ -447,7 +446,7 @@ impl FoldMap {
|
||||
}
|
||||
|
||||
let next_edit = inlay_edits_iter.next().unwrap();
|
||||
delta += next_edit.new_len() as isize - next_edit.old_len() as isize;
|
||||
delta += next_edit.new_len().0 as isize - next_edit.old_len().0 as isize;
|
||||
|
||||
if next_edit.old.end >= edit.old.end {
|
||||
edit.old.end = next_edit.old.end;
|
||||
@@ -459,9 +458,8 @@ impl FoldMap {
|
||||
}
|
||||
}
|
||||
|
||||
edit.new.end = InlayOffset(MultiBufferOffset(
|
||||
((edit.new.start + edit.old_len()).0.0 as isize + delta) as usize,
|
||||
));
|
||||
edit.new.end =
|
||||
InlayOffset(((edit.new.start + edit.old_len()).0 as isize + delta) as usize);
|
||||
|
||||
let anchor = inlay_snapshot
|
||||
.buffer
|
||||
@@ -524,7 +522,7 @@ impl FoldMap {
|
||||
new_transforms.push(
|
||||
Transform {
|
||||
summary: TransformSummary {
|
||||
output: MBTextSummary::from(ELLIPSIS),
|
||||
output: TextSummary::from(ELLIPSIS),
|
||||
input: inlay_snapshot
|
||||
.text_summary_for_range(fold_range.start..fold_range.end),
|
||||
},
|
||||
@@ -581,7 +579,7 @@ impl FoldMap {
|
||||
edit.old.start = old_transforms.start().0;
|
||||
}
|
||||
let old_start =
|
||||
old_transforms.start().1.0 + (edit.old.start - old_transforms.start().0);
|
||||
old_transforms.start().1.0 + (edit.old.start - old_transforms.start().0).0;
|
||||
|
||||
old_transforms.seek_forward(&edit.old.end, Bias::Right);
|
||||
if old_transforms.item().is_some_and(|t| t.is_fold()) {
|
||||
@@ -589,14 +587,14 @@ impl FoldMap {
|
||||
edit.old.end = old_transforms.start().0;
|
||||
}
|
||||
let old_end =
|
||||
old_transforms.start().1.0 + (edit.old.end - old_transforms.start().0);
|
||||
old_transforms.start().1.0 + (edit.old.end - old_transforms.start().0).0;
|
||||
|
||||
new_transforms.seek(&edit.new.start, Bias::Left);
|
||||
if new_transforms.item().is_some_and(|t| t.is_fold()) {
|
||||
edit.new.start = new_transforms.start().0;
|
||||
}
|
||||
let new_start =
|
||||
new_transforms.start().1.0 + (edit.new.start - new_transforms.start().0);
|
||||
new_transforms.start().1.0 + (edit.new.start - new_transforms.start().0).0;
|
||||
|
||||
new_transforms.seek_forward(&edit.new.end, Bias::Right);
|
||||
if new_transforms.item().is_some_and(|t| t.is_fold()) {
|
||||
@@ -604,7 +602,7 @@ impl FoldMap {
|
||||
edit.new.end = new_transforms.start().0;
|
||||
}
|
||||
let new_end =
|
||||
new_transforms.start().1.0 + (edit.new.end - new_transforms.start().0);
|
||||
new_transforms.start().1.0 + (edit.new.end - new_transforms.start().0).0;
|
||||
|
||||
fold_edits.push(FoldEdit {
|
||||
old: FoldOffset(old_start)..FoldOffset(old_end),
|
||||
@@ -651,13 +649,9 @@ impl FoldSnapshot {
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn text(&self) -> String {
|
||||
self.chunks(
|
||||
FoldOffset(MultiBufferOffset(0))..self.len(),
|
||||
false,
|
||||
Highlights::default(),
|
||||
)
|
||||
.map(|c| c.text)
|
||||
.collect()
|
||||
self.chunks(FoldOffset(0)..self.len(), false, Highlights::default())
|
||||
.map(|c| c.text)
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -665,8 +659,8 @@ impl FoldSnapshot {
|
||||
self.folds.items(&self.inlay_snapshot.buffer).len()
|
||||
}
|
||||
|
||||
pub fn text_summary_for_range(&self, range: Range<FoldPoint>) -> MBTextSummary {
|
||||
let mut summary = MBTextSummary::default();
|
||||
pub fn text_summary_for_range(&self, range: Range<FoldPoint>) -> TextSummary {
|
||||
let mut summary = TextSummary::default();
|
||||
|
||||
let mut cursor = self
|
||||
.transforms
|
||||
@@ -676,7 +670,7 @@ impl FoldSnapshot {
|
||||
let start_in_transform = range.start.0 - cursor.start().0.0;
|
||||
let end_in_transform = cmp::min(range.end, cursor.end().0).0 - cursor.start().0.0;
|
||||
if let Some(placeholder) = transform.placeholder.as_ref() {
|
||||
summary = MBTextSummary::from(
|
||||
summary = TextSummary::from(
|
||||
&placeholder.text
|
||||
[start_in_transform.column as usize..end_in_transform.column as usize],
|
||||
);
|
||||
@@ -695,14 +689,14 @@ impl FoldSnapshot {
|
||||
|
||||
if range.end > cursor.end().0 {
|
||||
cursor.next();
|
||||
summary += cursor
|
||||
summary += &cursor
|
||||
.summary::<_, TransformSummary>(&range.end, Bias::Right)
|
||||
.output;
|
||||
if let Some(transform) = cursor.item() {
|
||||
let end_in_transform = range.end.0 - cursor.start().0.0;
|
||||
if let Some(placeholder) = transform.placeholder.as_ref() {
|
||||
summary +=
|
||||
MBTextSummary::from(&placeholder.text[..end_in_transform.column as usize]);
|
||||
TextSummary::from(&placeholder.text[..end_in_transform.column as usize]);
|
||||
} else {
|
||||
let inlay_start = self.inlay_snapshot.to_offset(cursor.start().1);
|
||||
let inlay_end = self
|
||||
@@ -845,8 +839,8 @@ impl FoldSnapshot {
|
||||
transform_cursor.seek(&range.start, Bias::Right);
|
||||
|
||||
let inlay_start = {
|
||||
let overshoot = range.start - transform_cursor.start().0;
|
||||
transform_cursor.start().1 + overshoot
|
||||
let overshoot = range.start.0 - transform_cursor.start().0.0;
|
||||
transform_cursor.start().1 + InlayOffset(overshoot)
|
||||
};
|
||||
|
||||
let transform_end = transform_cursor.end();
|
||||
@@ -857,8 +851,8 @@ impl FoldSnapshot {
|
||||
{
|
||||
inlay_start
|
||||
} else if range.end < transform_end.0 {
|
||||
let overshoot = range.end - transform_cursor.start().0;
|
||||
transform_cursor.start().1 + overshoot
|
||||
let overshoot = range.end.0 - transform_cursor.start().0.0;
|
||||
transform_cursor.start().1 + InlayOffset(overshoot)
|
||||
} else {
|
||||
transform_end.1
|
||||
};
|
||||
@@ -927,7 +921,7 @@ impl FoldSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
fn push_isomorphic(transforms: &mut SumTree<Transform>, summary: MBTextSummary) {
|
||||
fn push_isomorphic(transforms: &mut SumTree<Transform>, summary: TextSummary) {
|
||||
let mut did_merge = false;
|
||||
transforms.update_last(
|
||||
|last| {
|
||||
@@ -956,13 +950,13 @@ fn push_isomorphic(transforms: &mut SumTree<Transform>, summary: MBTextSummary)
|
||||
fn intersecting_folds<'a>(
|
||||
inlay_snapshot: &'a InlaySnapshot,
|
||||
folds: &'a SumTree<Fold>,
|
||||
range: Range<MultiBufferOffset>,
|
||||
range: Range<usize>,
|
||||
inclusive: bool,
|
||||
) -> FilterCursor<'a, 'a, impl 'a + FnMut(&FoldSummary) -> bool, Fold, MultiBufferOffset> {
|
||||
) -> FilterCursor<'a, 'a, impl 'a + FnMut(&FoldSummary) -> bool, Fold, usize> {
|
||||
let buffer = &inlay_snapshot.buffer;
|
||||
let start = buffer.anchor_before(range.start.to_offset(buffer));
|
||||
let end = buffer.anchor_after(range.end.to_offset(buffer));
|
||||
let mut cursor = folds.filter::<_, MultiBufferOffset>(buffer, move |summary| {
|
||||
let mut cursor = folds.filter::<_, usize>(buffer, move |summary| {
|
||||
let start_cmp = start.cmp(&summary.max_end, buffer);
|
||||
let end_cmp = end.cmp(&summary.min_start, buffer);
|
||||
|
||||
@@ -1067,8 +1061,8 @@ impl Transform {
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
struct TransformSummary {
|
||||
output: MBTextSummary,
|
||||
input: MBTextSummary,
|
||||
output: TextSummary,
|
||||
input: TextSummary,
|
||||
}
|
||||
|
||||
impl sum_tree::Item for Transform {
|
||||
@@ -1085,8 +1079,8 @@ impl sum_tree::ContextLessSummary for TransformSummary {
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, other: &Self) {
|
||||
self.input += other.input;
|
||||
self.output += other.output;
|
||||
self.input += &other.input;
|
||||
self.output += &other.output;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1217,7 +1211,7 @@ impl sum_tree::SeekTarget<'_, FoldSummary, FoldRange> for FoldRange {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, FoldSummary> for MultiBufferOffset {
|
||||
impl<'a> sum_tree::Dimension<'a, FoldSummary> for usize {
|
||||
fn zero(_cx: &MultiBufferSnapshot) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
@@ -1363,8 +1357,8 @@ impl FoldChunks<'_> {
|
||||
self.transform_cursor.seek(&range.start, Bias::Right);
|
||||
|
||||
let inlay_start = {
|
||||
let overshoot = range.start - self.transform_cursor.start().0;
|
||||
self.transform_cursor.start().1 + overshoot
|
||||
let overshoot = range.start.0 - self.transform_cursor.start().0.0;
|
||||
self.transform_cursor.start().1 + InlayOffset(overshoot)
|
||||
};
|
||||
|
||||
let transform_end = self.transform_cursor.end();
|
||||
@@ -1376,8 +1370,8 @@ impl FoldChunks<'_> {
|
||||
{
|
||||
inlay_start
|
||||
} else if range.end < transform_end.0 {
|
||||
let overshoot = range.end - self.transform_cursor.start().0;
|
||||
self.transform_cursor.start().1 + overshoot
|
||||
let overshoot = range.end.0 - self.transform_cursor.start().0.0;
|
||||
self.transform_cursor.start().1 + InlayOffset(overshoot)
|
||||
} else {
|
||||
transform_end.1
|
||||
};
|
||||
@@ -1429,8 +1423,8 @@ impl<'a> Iterator for FoldChunks<'a> {
|
||||
let transform_start = self.transform_cursor.start();
|
||||
let transform_end = self.transform_cursor.end();
|
||||
let inlay_end = if self.max_output_offset < transform_end.0 {
|
||||
let overshoot = self.max_output_offset - transform_start.0;
|
||||
transform_start.1 + overshoot
|
||||
let overshoot = self.max_output_offset.0 - transform_start.0.0;
|
||||
transform_start.1 + InlayOffset(overshoot)
|
||||
} else {
|
||||
transform_end.1
|
||||
};
|
||||
@@ -1447,15 +1441,15 @@ impl<'a> Iterator for FoldChunks<'a> {
|
||||
// Otherwise, take a chunk from the buffer's text.
|
||||
if let Some((buffer_chunk_start, mut inlay_chunk)) = self.inlay_chunk.clone() {
|
||||
let chunk = &mut inlay_chunk.chunk;
|
||||
let buffer_chunk_end = buffer_chunk_start + chunk.text.len();
|
||||
let buffer_chunk_end = buffer_chunk_start + InlayOffset(chunk.text.len());
|
||||
let transform_end = self.transform_cursor.end().1;
|
||||
let chunk_end = buffer_chunk_end.min(transform_end);
|
||||
|
||||
let bit_start = self.inlay_offset - buffer_chunk_start;
|
||||
let bit_end = chunk_end - buffer_chunk_start;
|
||||
let bit_start = (self.inlay_offset - buffer_chunk_start).0;
|
||||
let bit_end = (chunk_end - buffer_chunk_start).0;
|
||||
chunk.text = &chunk.text[bit_start..bit_end];
|
||||
|
||||
let bit_end = chunk_end - buffer_chunk_start;
|
||||
let bit_end = (chunk_end - buffer_chunk_start).0;
|
||||
let mask = 1u128.unbounded_shl(bit_end as u32).wrapping_sub(1);
|
||||
|
||||
chunk.tabs = (chunk.tabs >> bit_start) & mask;
|
||||
@@ -1489,7 +1483,7 @@ impl<'a> Iterator for FoldChunks<'a> {
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
|
||||
pub struct FoldOffset(pub MultiBufferOffset);
|
||||
pub struct FoldOffset(pub usize);
|
||||
|
||||
impl FoldOffset {
|
||||
pub fn to_point(self, snapshot: &FoldSnapshot) -> FoldPoint {
|
||||
@@ -1499,7 +1493,7 @@ impl FoldOffset {
|
||||
let overshoot = if item.is_none_or(|t| t.is_fold()) {
|
||||
Point::new(0, (self.0 - start.0.0) as u32)
|
||||
} else {
|
||||
let inlay_offset = start.1.input.len + (self - start.0);
|
||||
let inlay_offset = start.1.input.len + self.0 - start.0.0;
|
||||
let inlay_point = snapshot.inlay_snapshot.to_point(InlayOffset(inlay_offset));
|
||||
inlay_point.0 - start.1.input.lines
|
||||
};
|
||||
@@ -1511,7 +1505,7 @@ impl FoldOffset {
|
||||
let (start, _, _) = snapshot
|
||||
.transforms
|
||||
.find::<Dimensions<FoldOffset, InlayOffset>, _>((), &self, Bias::Right);
|
||||
let overshoot = self - start.0;
|
||||
let overshoot = self.0 - start.0.0;
|
||||
InlayOffset(start.1.0 + overshoot)
|
||||
}
|
||||
}
|
||||
@@ -1524,46 +1518,17 @@ impl Add for FoldOffset {
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for FoldOffset {
|
||||
type Output = <MultiBufferOffset as Sub>::Output;
|
||||
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
self.0 - rhs.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SubAssign<T> for FoldOffset
|
||||
where
|
||||
MultiBufferOffset: SubAssign<T>,
|
||||
{
|
||||
fn sub_assign(&mut self, rhs: T) {
|
||||
self.0 -= rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Add<T> for FoldOffset
|
||||
where
|
||||
MultiBufferOffset: Add<T, Output = MultiBufferOffset>,
|
||||
{
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: T) -> Self::Output {
|
||||
Self(self.0 + rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for FoldOffset {
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
self.0 += rhs.0;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AddAssign<T> for FoldOffset
|
||||
where
|
||||
MultiBufferOffset: AddAssign<T>,
|
||||
{
|
||||
fn add_assign(&mut self, rhs: T) {
|
||||
self.0 += rhs;
|
||||
impl Sub for FoldOffset {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
Self(self.0 - rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1573,7 +1538,7 @@ impl<'a> sum_tree::Dimension<'a, TransformSummary> for FoldOffset {
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, summary: &'a TransformSummary, _: ()) {
|
||||
self.0 += summary.output.len;
|
||||
self.0 += &summary.output.len;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1593,7 +1558,7 @@ impl<'a> sum_tree::Dimension<'a, TransformSummary> for InlayOffset {
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, summary: &'a TransformSummary, _: ()) {
|
||||
self.0 += summary.input.len;
|
||||
self.0 += &summary.input.len;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1631,12 +1596,12 @@ mod tests {
|
||||
edits,
|
||||
&[
|
||||
FoldEdit {
|
||||
old: FoldOffset(MultiBufferOffset(2))..FoldOffset(MultiBufferOffset(16)),
|
||||
new: FoldOffset(MultiBufferOffset(2))..FoldOffset(MultiBufferOffset(5)),
|
||||
old: FoldOffset(2)..FoldOffset(16),
|
||||
new: FoldOffset(2)..FoldOffset(5),
|
||||
},
|
||||
FoldEdit {
|
||||
old: FoldOffset(MultiBufferOffset(18))..FoldOffset(MultiBufferOffset(29)),
|
||||
new: FoldOffset(MultiBufferOffset(7))..FoldOffset(MultiBufferOffset(10)),
|
||||
old: FoldOffset(18)..FoldOffset(29),
|
||||
new: FoldOffset(7)..FoldOffset(10)
|
||||
},
|
||||
]
|
||||
);
|
||||
@@ -1661,12 +1626,12 @@ mod tests {
|
||||
edits,
|
||||
&[
|
||||
FoldEdit {
|
||||
old: FoldOffset(MultiBufferOffset(0))..FoldOffset(MultiBufferOffset(1)),
|
||||
new: FoldOffset(MultiBufferOffset(0))..FoldOffset(MultiBufferOffset(3)),
|
||||
old: FoldOffset(0)..FoldOffset(1),
|
||||
new: FoldOffset(0)..FoldOffset(3),
|
||||
},
|
||||
FoldEdit {
|
||||
old: FoldOffset(MultiBufferOffset(6))..FoldOffset(MultiBufferOffset(6)),
|
||||
new: FoldOffset(MultiBufferOffset(8))..FoldOffset(MultiBufferOffset(11)),
|
||||
old: FoldOffset(6)..FoldOffset(6),
|
||||
new: FoldOffset(8)..FoldOffset(11),
|
||||
},
|
||||
]
|
||||
);
|
||||
@@ -1703,24 +1668,15 @@ mod tests {
|
||||
let mut map = FoldMap::new(inlay_snapshot.clone()).0;
|
||||
|
||||
let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]);
|
||||
writer.fold(vec![(
|
||||
MultiBufferOffset(5)..MultiBufferOffset(8),
|
||||
FoldPlaceholder::test(),
|
||||
)]);
|
||||
writer.fold(vec![(5..8, FoldPlaceholder::test())]);
|
||||
let (snapshot, _) = map.read(inlay_snapshot.clone(), vec![]);
|
||||
assert_eq!(snapshot.text(), "abcde⋯ijkl");
|
||||
|
||||
// Create an fold adjacent to the start of the first fold.
|
||||
let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]);
|
||||
writer.fold(vec![
|
||||
(
|
||||
MultiBufferOffset(0)..MultiBufferOffset(1),
|
||||
FoldPlaceholder::test(),
|
||||
),
|
||||
(
|
||||
MultiBufferOffset(2)..MultiBufferOffset(5),
|
||||
FoldPlaceholder::test(),
|
||||
),
|
||||
(0..1, FoldPlaceholder::test()),
|
||||
(2..5, FoldPlaceholder::test()),
|
||||
]);
|
||||
let (snapshot, _) = map.read(inlay_snapshot.clone(), vec![]);
|
||||
assert_eq!(snapshot.text(), "⋯b⋯ijkl");
|
||||
@@ -1728,14 +1684,8 @@ mod tests {
|
||||
// Create an fold adjacent to the end of the first fold.
|
||||
let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]);
|
||||
writer.fold(vec![
|
||||
(
|
||||
MultiBufferOffset(11)..MultiBufferOffset(11),
|
||||
FoldPlaceholder::test(),
|
||||
),
|
||||
(
|
||||
MultiBufferOffset(8)..MultiBufferOffset(10),
|
||||
FoldPlaceholder::test(),
|
||||
),
|
||||
(11..11, FoldPlaceholder::test()),
|
||||
(8..10, FoldPlaceholder::test()),
|
||||
]);
|
||||
let (snapshot, _) = map.read(inlay_snapshot.clone(), vec![]);
|
||||
assert_eq!(snapshot.text(), "⋯b⋯kl");
|
||||
@@ -1747,25 +1697,15 @@ mod tests {
|
||||
// Create two adjacent folds.
|
||||
let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]);
|
||||
writer.fold(vec![
|
||||
(
|
||||
MultiBufferOffset(0)..MultiBufferOffset(2),
|
||||
FoldPlaceholder::test(),
|
||||
),
|
||||
(
|
||||
MultiBufferOffset(2)..MultiBufferOffset(5),
|
||||
FoldPlaceholder::test(),
|
||||
),
|
||||
(0..2, FoldPlaceholder::test()),
|
||||
(2..5, FoldPlaceholder::test()),
|
||||
]);
|
||||
let (snapshot, _) = map.read(inlay_snapshot, vec![]);
|
||||
assert_eq!(snapshot.text(), "⋯fghijkl");
|
||||
|
||||
// Edit within one of the folds.
|
||||
let buffer_snapshot = buffer.update(cx, |buffer, cx| {
|
||||
buffer.edit(
|
||||
[(MultiBufferOffset(0)..MultiBufferOffset(1), "12345")],
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
buffer.edit([(0..1, "12345")], None, cx);
|
||||
buffer.snapshot(cx)
|
||||
});
|
||||
let (inlay_snapshot, inlay_edits) =
|
||||
@@ -1909,7 +1849,7 @@ mod tests {
|
||||
for fold_range in map.merged_folds().into_iter().rev() {
|
||||
let fold_inlay_start = inlay_snapshot.to_inlay_offset(fold_range.start);
|
||||
let fold_inlay_end = inlay_snapshot.to_inlay_offset(fold_range.end);
|
||||
expected_text.replace_range(fold_inlay_start.0.0..fold_inlay_end.0.0, "⋯");
|
||||
expected_text.replace_range(fold_inlay_start.0..fold_inlay_end.0, "⋯");
|
||||
}
|
||||
|
||||
assert_eq!(snapshot.text(), expected_text);
|
||||
@@ -1958,7 +1898,7 @@ mod tests {
|
||||
.chars()
|
||||
.count();
|
||||
let mut fold_point = FoldPoint::new(0, 0);
|
||||
let mut fold_offset = FoldOffset(MultiBufferOffset(0));
|
||||
let mut fold_offset = FoldOffset(0);
|
||||
let mut char_column = 0;
|
||||
for c in expected_text.chars() {
|
||||
let inlay_point = fold_point.to_inlay_point(&snapshot);
|
||||
@@ -2004,18 +1944,18 @@ mod tests {
|
||||
|
||||
for _ in 0..5 {
|
||||
let mut start = snapshot.clip_offset(
|
||||
FoldOffset(rng.random_range(MultiBufferOffset(0)..=snapshot.len().0)),
|
||||
FoldOffset(rng.random_range(0..=snapshot.len().0)),
|
||||
Bias::Left,
|
||||
);
|
||||
let mut end = snapshot.clip_offset(
|
||||
FoldOffset(rng.random_range(MultiBufferOffset(0)..=snapshot.len().0)),
|
||||
FoldOffset(rng.random_range(0..=snapshot.len().0)),
|
||||
Bias::Right,
|
||||
);
|
||||
if start > end {
|
||||
mem::swap(&mut start, &mut end);
|
||||
}
|
||||
|
||||
let text = &expected_text[start.0.0..end.0.0];
|
||||
let text = &expected_text[start.0..end.0];
|
||||
assert_eq!(
|
||||
snapshot
|
||||
.chunks(start..end, false, Highlights::default())
|
||||
@@ -2064,12 +2004,9 @@ mod tests {
|
||||
}
|
||||
|
||||
for _ in 0..5 {
|
||||
let end = buffer_snapshot.clip_offset(
|
||||
rng.random_range(MultiBufferOffset(0)..=buffer_snapshot.len()),
|
||||
Right,
|
||||
);
|
||||
let start =
|
||||
buffer_snapshot.clip_offset(rng.random_range(MultiBufferOffset(0)..=end), Left);
|
||||
let end =
|
||||
buffer_snapshot.clip_offset(rng.random_range(0..=buffer_snapshot.len()), Right);
|
||||
let start = buffer_snapshot.clip_offset(rng.random_range(0..=end), Left);
|
||||
let expected_folds = map
|
||||
.snapshot
|
||||
.folds
|
||||
@@ -2109,7 +2046,7 @@ mod tests {
|
||||
let bytes = start.to_offset(&snapshot)..end.to_offset(&snapshot);
|
||||
assert_eq!(
|
||||
snapshot.text_summary_for_range(lines),
|
||||
MBTextSummary::from(&text[bytes.start.0.0..bytes.end.0.0])
|
||||
TextSummary::from(&text[bytes.start.0..bytes.end.0])
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2117,8 +2054,8 @@ mod tests {
|
||||
for (snapshot, edits) in snapshot_edits.drain(..) {
|
||||
let new_text = snapshot.text();
|
||||
for edit in edits {
|
||||
let old_bytes = edit.new.start.0.0..edit.new.start.0.0 + edit.old_len();
|
||||
let new_bytes = edit.new.start.0.0..edit.new.end.0.0;
|
||||
let old_bytes = edit.new.start.0..edit.new.start.0 + edit.old_len().0;
|
||||
let new_bytes = edit.new.start.0..edit.new.end.0;
|
||||
text.replace_range(old_bytes, &new_text[new_bytes]);
|
||||
}
|
||||
|
||||
@@ -2189,7 +2126,7 @@ mod tests {
|
||||
|
||||
// Get all chunks and verify their bitmaps
|
||||
let chunks = snapshot.chunks(
|
||||
FoldOffset(MultiBufferOffset(0))..FoldOffset(snapshot.len().0),
|
||||
FoldOffset(0)..FoldOffset(snapshot.len().0),
|
||||
false,
|
||||
Highlights::default(),
|
||||
);
|
||||
@@ -2258,7 +2195,7 @@ mod tests {
|
||||
}
|
||||
|
||||
impl FoldMap {
|
||||
fn merged_folds(&self) -> Vec<Range<MultiBufferOffset>> {
|
||||
fn merged_folds(&self) -> Vec<Range<usize>> {
|
||||
let inlay_snapshot = self.snapshot.inlay_snapshot.clone();
|
||||
let buffer = &inlay_snapshot.buffer;
|
||||
let mut folds = self.snapshot.folds.items(buffer);
|
||||
@@ -2299,12 +2236,8 @@ mod tests {
|
||||
let buffer = &inlay_snapshot.buffer;
|
||||
let mut to_unfold = Vec::new();
|
||||
for _ in 0..rng.random_range(1..=3) {
|
||||
let end = buffer.clip_offset(
|
||||
rng.random_range(MultiBufferOffset(0)..=buffer.len()),
|
||||
Right,
|
||||
);
|
||||
let start =
|
||||
buffer.clip_offset(rng.random_range(MultiBufferOffset(0)..=end), Left);
|
||||
let end = buffer.clip_offset(rng.random_range(0..=buffer.len()), Right);
|
||||
let start = buffer.clip_offset(rng.random_range(0..=end), Left);
|
||||
to_unfold.push(start..end);
|
||||
}
|
||||
let inclusive = rng.random();
|
||||
@@ -2319,12 +2252,8 @@ mod tests {
|
||||
let buffer = &inlay_snapshot.buffer;
|
||||
let mut to_fold = Vec::new();
|
||||
for _ in 0..rng.random_range(1..=2) {
|
||||
let end = buffer.clip_offset(
|
||||
rng.random_range(MultiBufferOffset(0)..=buffer.len()),
|
||||
Right,
|
||||
);
|
||||
let start =
|
||||
buffer.clip_offset(rng.random_range(MultiBufferOffset(0)..=end), Left);
|
||||
let end = buffer.clip_offset(rng.random_range(0..=buffer.len()), Right);
|
||||
let start = buffer.clip_offset(rng.random_range(0..=end), Left);
|
||||
to_fold.push((start..end, FoldPlaceholder::test()));
|
||||
}
|
||||
log::info!("folding {:?}", to_fold);
|
||||
|
||||
@@ -4,10 +4,7 @@ use crate::{
|
||||
};
|
||||
use collections::BTreeSet;
|
||||
use language::{Chunk, Edit, Point, TextSummary};
|
||||
use multi_buffer::{
|
||||
MBTextSummary, MultiBufferOffset, MultiBufferRow, MultiBufferRows, MultiBufferSnapshot,
|
||||
RowInfo, ToOffset,
|
||||
};
|
||||
use multi_buffer::{MultiBufferRow, MultiBufferRows, MultiBufferSnapshot, RowInfo, ToOffset};
|
||||
use project::InlayId;
|
||||
use std::{
|
||||
cmp,
|
||||
@@ -45,7 +42,7 @@ impl std::ops::Deref for InlaySnapshot {
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum Transform {
|
||||
Isomorphic(MBTextSummary),
|
||||
Isomorphic(TextSummary),
|
||||
Inlay(Inlay),
|
||||
}
|
||||
|
||||
@@ -59,8 +56,8 @@ impl sum_tree::Item for Transform {
|
||||
output: *summary,
|
||||
},
|
||||
Transform::Inlay(inlay) => TransformSummary {
|
||||
input: MBTextSummary::default(),
|
||||
output: MBTextSummary::from(inlay.text().summary()),
|
||||
input: TextSummary::default(),
|
||||
output: inlay.text().summary(),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -68,8 +65,8 @@ impl sum_tree::Item for Transform {
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct TransformSummary {
|
||||
input: MBTextSummary,
|
||||
output: MBTextSummary,
|
||||
input: TextSummary,
|
||||
output: TextSummary,
|
||||
}
|
||||
|
||||
impl sum_tree::ContextLessSummary for TransformSummary {
|
||||
@@ -78,15 +75,15 @@ impl sum_tree::ContextLessSummary for TransformSummary {
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, other: &Self) {
|
||||
self.input += other.input;
|
||||
self.output += other.output;
|
||||
self.input += &other.input;
|
||||
self.output += &other.output;
|
||||
}
|
||||
}
|
||||
|
||||
pub type InlayEdit = Edit<InlayOffset>;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
|
||||
pub struct InlayOffset(pub MultiBufferOffset);
|
||||
pub struct InlayOffset(pub usize);
|
||||
|
||||
impl Add for InlayOffset {
|
||||
type Output = Self;
|
||||
@@ -97,30 +94,10 @@ impl Add for InlayOffset {
|
||||
}
|
||||
|
||||
impl Sub for InlayOffset {
|
||||
type Output = <MultiBufferOffset as Sub>::Output;
|
||||
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
self.0 - rhs.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SubAssign<T> for InlayOffset
|
||||
where
|
||||
MultiBufferOffset: SubAssign<T>,
|
||||
{
|
||||
fn sub_assign(&mut self, rhs: T) {
|
||||
self.0 -= rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Add<T> for InlayOffset
|
||||
where
|
||||
MultiBufferOffset: Add<T, Output = MultiBufferOffset>,
|
||||
{
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: T) -> Self::Output {
|
||||
Self(self.0 + rhs)
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
Self(self.0 - rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,12 +107,9 @@ impl AddAssign for InlayOffset {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AddAssign<T> for InlayOffset
|
||||
where
|
||||
MultiBufferOffset: AddAssign<T>,
|
||||
{
|
||||
fn add_assign(&mut self, rhs: T) {
|
||||
self.0 += rhs;
|
||||
impl SubAssign for InlayOffset {
|
||||
fn sub_assign(&mut self, rhs: Self) {
|
||||
self.0 -= rhs.0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,7 +119,7 @@ impl<'a> sum_tree::Dimension<'a, TransformSummary> for InlayOffset {
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, summary: &'a TransformSummary, _: ()) {
|
||||
self.0 += summary.output.len;
|
||||
self.0 += &summary.output.len;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,13 +152,13 @@ impl<'a> sum_tree::Dimension<'a, TransformSummary> for InlayPoint {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, TransformSummary> for MultiBufferOffset {
|
||||
impl<'a> sum_tree::Dimension<'a, TransformSummary> for usize {
|
||||
fn zero(_cx: ()) -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_summary(&mut self, summary: &'a TransformSummary, _: ()) {
|
||||
*self += summary.input.len;
|
||||
*self += &summary.input.len;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,7 +181,7 @@ pub struct InlayBufferRows<'a> {
|
||||
}
|
||||
|
||||
pub struct InlayChunks<'a> {
|
||||
transforms: Cursor<'a, 'static, Transform, Dimensions<InlayOffset, MultiBufferOffset>>,
|
||||
transforms: Cursor<'a, 'static, Transform, Dimensions<InlayOffset, usize>>,
|
||||
buffer_chunks: CustomHighlightsChunks<'a>,
|
||||
buffer_chunk: Option<Chunk<'a>>,
|
||||
inlay_chunks: Option<text::ChunkWithBitmaps<'a>>,
|
||||
@@ -358,12 +332,12 @@ impl<'a> Iterator for InlayChunks<'a> {
|
||||
let offset_in_inlay = self.output_offset - self.transforms.start().0;
|
||||
if let Some((style, highlight)) = inlay_style_and_highlight {
|
||||
let range = &highlight.range;
|
||||
if offset_in_inlay < range.start {
|
||||
next_inlay_highlight_endpoint = range.start - offset_in_inlay;
|
||||
} else if offset_in_inlay >= range.end {
|
||||
if offset_in_inlay.0 < range.start {
|
||||
next_inlay_highlight_endpoint = range.start - offset_in_inlay.0;
|
||||
} else if offset_in_inlay.0 >= range.end {
|
||||
next_inlay_highlight_endpoint = usize::MAX;
|
||||
} else {
|
||||
next_inlay_highlight_endpoint = range.end - offset_in_inlay;
|
||||
next_inlay_highlight_endpoint = range.end - offset_in_inlay.0;
|
||||
highlight_style = highlight_style
|
||||
.map(|highlight| highlight.highlight(*style))
|
||||
.or_else(|| Some(*style));
|
||||
@@ -376,7 +350,7 @@ impl<'a> Iterator for InlayChunks<'a> {
|
||||
let start = offset_in_inlay;
|
||||
let end = cmp::min(self.max_output_offset, self.transforms.end().0)
|
||||
- self.transforms.start().0;
|
||||
let chunks = inlay.text().chunks_in_range(start..end);
|
||||
let chunks = inlay.text().chunks_in_range(start.0..end.0);
|
||||
text::ChunkWithBitmaps(chunks)
|
||||
});
|
||||
let ChunkBitmaps {
|
||||
@@ -514,7 +488,7 @@ impl InlayMap {
|
||||
pub fn sync(
|
||||
&mut self,
|
||||
buffer_snapshot: MultiBufferSnapshot,
|
||||
mut buffer_edits: Vec<text::Edit<MultiBufferOffset>>,
|
||||
mut buffer_edits: Vec<text::Edit<usize>>,
|
||||
) -> (InlaySnapshot, Vec<InlayEdit>) {
|
||||
let snapshot = &mut self.snapshot;
|
||||
|
||||
@@ -545,7 +519,7 @@ impl InlayMap {
|
||||
let mut new_transforms = SumTree::default();
|
||||
let mut cursor = snapshot
|
||||
.transforms
|
||||
.cursor::<Dimensions<MultiBufferOffset, InlayOffset>>(());
|
||||
.cursor::<Dimensions<usize, InlayOffset>>(());
|
||||
let mut buffer_edits_iter = buffer_edits.iter().peekable();
|
||||
while let Some(buffer_edit) = buffer_edits_iter.next() {
|
||||
new_transforms.append(cursor.slice(&buffer_edit.old.start, Bias::Left), ());
|
||||
@@ -557,9 +531,11 @@ impl InlayMap {
|
||||
}
|
||||
|
||||
// Remove all the inlays and transforms contained by the edit.
|
||||
let old_start = cursor.start().1 + (buffer_edit.old.start - cursor.start().0);
|
||||
let old_start =
|
||||
cursor.start().1 + InlayOffset(buffer_edit.old.start - cursor.start().0);
|
||||
cursor.seek(&buffer_edit.old.end, Bias::Right);
|
||||
let old_end = cursor.start().1 + (buffer_edit.old.end - cursor.start().0);
|
||||
let old_end =
|
||||
cursor.start().1 + InlayOffset(buffer_edit.old.end - cursor.start().0);
|
||||
|
||||
// Push the unchanged prefix.
|
||||
let prefix_start = new_transforms.summary().input.len;
|
||||
@@ -711,10 +687,7 @@ impl InlayMap {
|
||||
let snapshot = &mut self.snapshot;
|
||||
for i in 0..rng.random_range(1..=5) {
|
||||
if self.inlays.is_empty() || rng.random() {
|
||||
let position = snapshot
|
||||
.buffer
|
||||
.random_byte_range(MultiBufferOffset(0), rng)
|
||||
.start;
|
||||
let position = snapshot.buffer.random_byte_range(0, rng).start;
|
||||
let bias = if rng.random() {
|
||||
Bias::Left
|
||||
} else {
|
||||
@@ -767,11 +740,9 @@ impl InlayMap {
|
||||
|
||||
impl InlaySnapshot {
|
||||
pub fn to_point(&self, offset: InlayOffset) -> InlayPoint {
|
||||
let (start, _, item) = self.transforms.find::<Dimensions<
|
||||
InlayOffset,
|
||||
InlayPoint,
|
||||
MultiBufferOffset,
|
||||
>, _>((), &offset, Bias::Right);
|
||||
let (start, _, item) = self
|
||||
.transforms
|
||||
.find::<Dimensions<InlayOffset, InlayPoint, usize>, _>((), &offset, Bias::Right);
|
||||
let overshoot = offset.0 - start.0.0;
|
||||
match item {
|
||||
Some(Transform::Isomorphic(_)) => {
|
||||
@@ -830,24 +801,22 @@ impl InlaySnapshot {
|
||||
None => self.buffer.max_point(),
|
||||
}
|
||||
}
|
||||
pub fn to_buffer_offset(&self, offset: InlayOffset) -> MultiBufferOffset {
|
||||
let (start, _, item) = self
|
||||
.transforms
|
||||
.find::<Dimensions<InlayOffset, MultiBufferOffset>, _>((), &offset, Bias::Right);
|
||||
pub fn to_buffer_offset(&self, offset: InlayOffset) -> usize {
|
||||
let (start, _, item) =
|
||||
self.transforms
|
||||
.find::<Dimensions<InlayOffset, usize>, _>((), &offset, Bias::Right);
|
||||
match item {
|
||||
Some(Transform::Isomorphic(_)) => {
|
||||
let overshoot = offset - start.0;
|
||||
start.1 + overshoot
|
||||
start.1 + overshoot.0
|
||||
}
|
||||
Some(Transform::Inlay(_)) => start.1,
|
||||
None => self.buffer.len(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_inlay_offset(&self, offset: MultiBufferOffset) -> InlayOffset {
|
||||
let mut cursor = self
|
||||
.transforms
|
||||
.cursor::<Dimensions<MultiBufferOffset, InlayOffset>>(());
|
||||
pub fn to_inlay_offset(&self, offset: usize) -> InlayOffset {
|
||||
let mut cursor = self.transforms.cursor::<Dimensions<usize, InlayOffset>>(());
|
||||
cursor.seek(&offset, Bias::Left);
|
||||
loop {
|
||||
match cursor.item() {
|
||||
@@ -1004,16 +973,14 @@ impl InlaySnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn text_summary(&self) -> MBTextSummary {
|
||||
pub fn text_summary(&self) -> TextSummary {
|
||||
self.transforms.summary().output
|
||||
}
|
||||
|
||||
pub fn text_summary_for_range(&self, range: Range<InlayOffset>) -> MBTextSummary {
|
||||
let mut summary = MBTextSummary::default();
|
||||
pub fn text_summary_for_range(&self, range: Range<InlayOffset>) -> TextSummary {
|
||||
let mut summary = TextSummary::default();
|
||||
|
||||
let mut cursor = self
|
||||
.transforms
|
||||
.cursor::<Dimensions<InlayOffset, MultiBufferOffset>>(());
|
||||
let mut cursor = self.transforms.cursor::<Dimensions<InlayOffset, usize>>(());
|
||||
cursor.seek(&range.start, Bias::Right);
|
||||
|
||||
let overshoot = range.start.0 - cursor.start().0.0;
|
||||
@@ -1029,12 +996,7 @@ impl InlaySnapshot {
|
||||
Some(Transform::Inlay(inlay)) => {
|
||||
let suffix_start = overshoot;
|
||||
let suffix_end = cmp::min(cursor.end().0, range.end).0 - cursor.start().0.0;
|
||||
summary = MBTextSummary::from(
|
||||
inlay
|
||||
.text()
|
||||
.cursor(suffix_start)
|
||||
.summary::<TextSummary>(suffix_end),
|
||||
);
|
||||
summary = inlay.text().cursor(suffix_start).summary(suffix_end);
|
||||
cursor.next();
|
||||
}
|
||||
None => {}
|
||||
@@ -1052,7 +1014,7 @@ impl InlaySnapshot {
|
||||
let prefix_end = prefix_start + overshoot;
|
||||
summary += self
|
||||
.buffer
|
||||
.text_summary_for_range::<MBTextSummary, _>(prefix_start..prefix_end);
|
||||
.text_summary_for_range::<TextSummary, _>(prefix_start..prefix_end);
|
||||
}
|
||||
Some(Transform::Inlay(inlay)) => {
|
||||
let prefix_end = overshoot;
|
||||
@@ -1108,9 +1070,7 @@ impl InlaySnapshot {
|
||||
language_aware: bool,
|
||||
highlights: Highlights<'a>,
|
||||
) -> InlayChunks<'a> {
|
||||
let mut cursor = self
|
||||
.transforms
|
||||
.cursor::<Dimensions<InlayOffset, MultiBufferOffset>>(());
|
||||
let mut cursor = self.transforms.cursor::<Dimensions<InlayOffset, usize>>(());
|
||||
cursor.seek(&range.start, Bias::Right);
|
||||
|
||||
let buffer_range = self.to_buffer_offset(range.start)..self.to_buffer_offset(range.end);
|
||||
@@ -1162,8 +1122,8 @@ impl InlaySnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
fn push_isomorphic(sum_tree: &mut SumTree<Transform>, summary: MBTextSummary) {
|
||||
if summary.len == MultiBufferOffset(0) {
|
||||
fn push_isomorphic(sum_tree: &mut SumTree<Transform>, summary: TextSummary) {
|
||||
if summary.len == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1319,10 +1279,7 @@ mod tests {
|
||||
&[],
|
||||
vec![Inlay::mock_hint(
|
||||
post_inc(&mut next_inlay_id),
|
||||
buffer
|
||||
.read(cx)
|
||||
.snapshot(cx)
|
||||
.anchor_after(MultiBufferOffset(3)),
|
||||
buffer.read(cx).snapshot(cx).anchor_after(3),
|
||||
"|123|",
|
||||
)],
|
||||
);
|
||||
@@ -1378,15 +1335,7 @@ mod tests {
|
||||
|
||||
// Edits before or after the inlay should not affect it.
|
||||
buffer.update(cx, |buffer, cx| {
|
||||
buffer.edit(
|
||||
[
|
||||
(MultiBufferOffset(2)..MultiBufferOffset(3), "x"),
|
||||
(MultiBufferOffset(3)..MultiBufferOffset(3), "y"),
|
||||
(MultiBufferOffset(4)..MultiBufferOffset(4), "z"),
|
||||
],
|
||||
None,
|
||||
cx,
|
||||
)
|
||||
buffer.edit([(2..3, "x"), (3..3, "y"), (4..4, "z")], None, cx)
|
||||
});
|
||||
let (inlay_snapshot, _) = inlay_map.sync(
|
||||
buffer.read(cx).snapshot(cx),
|
||||
@@ -1395,13 +1344,7 @@ mod tests {
|
||||
assert_eq!(inlay_snapshot.text(), "abxy|123|dzefghi");
|
||||
|
||||
// An edit surrounding the inlay should invalidate it.
|
||||
buffer.update(cx, |buffer, cx| {
|
||||
buffer.edit(
|
||||
[(MultiBufferOffset(4)..MultiBufferOffset(5), "D")],
|
||||
None,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
buffer.update(cx, |buffer, cx| buffer.edit([(4..5, "D")], None, cx));
|
||||
let (inlay_snapshot, _) = inlay_map.sync(
|
||||
buffer.read(cx).snapshot(cx),
|
||||
buffer_edits.consume().into_inner(),
|
||||
@@ -1413,18 +1356,12 @@ mod tests {
|
||||
vec![
|
||||
Inlay::mock_hint(
|
||||
post_inc(&mut next_inlay_id),
|
||||
buffer
|
||||
.read(cx)
|
||||
.snapshot(cx)
|
||||
.anchor_before(MultiBufferOffset(3)),
|
||||
buffer.read(cx).snapshot(cx).anchor_before(3),
|
||||
"|123|",
|
||||
),
|
||||
Inlay::edit_prediction(
|
||||
post_inc(&mut next_inlay_id),
|
||||
buffer
|
||||
.read(cx)
|
||||
.snapshot(cx)
|
||||
.anchor_after(MultiBufferOffset(3)),
|
||||
buffer.read(cx).snapshot(cx).anchor_after(3),
|
||||
"|456|",
|
||||
),
|
||||
],
|
||||
@@ -1432,13 +1369,7 @@ mod tests {
|
||||
assert_eq!(inlay_snapshot.text(), "abx|123||456|yDzefghi");
|
||||
|
||||
// Edits ending where the inlay starts should not move it if it has a left bias.
|
||||
buffer.update(cx, |buffer, cx| {
|
||||
buffer.edit(
|
||||
[(MultiBufferOffset(3)..MultiBufferOffset(3), "JKL")],
|
||||
None,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
buffer.update(cx, |buffer, cx| buffer.edit([(3..3, "JKL")], None, cx));
|
||||
let (inlay_snapshot, _) = inlay_map.sync(
|
||||
buffer.read(cx).snapshot(cx),
|
||||
buffer_edits.consume().into_inner(),
|
||||
@@ -1640,26 +1571,17 @@ mod tests {
|
||||
vec![
|
||||
Inlay::mock_hint(
|
||||
post_inc(&mut next_inlay_id),
|
||||
buffer
|
||||
.read(cx)
|
||||
.snapshot(cx)
|
||||
.anchor_before(MultiBufferOffset(0)),
|
||||
buffer.read(cx).snapshot(cx).anchor_before(0),
|
||||
"|123|\n",
|
||||
),
|
||||
Inlay::mock_hint(
|
||||
post_inc(&mut next_inlay_id),
|
||||
buffer
|
||||
.read(cx)
|
||||
.snapshot(cx)
|
||||
.anchor_before(MultiBufferOffset(4)),
|
||||
buffer.read(cx).snapshot(cx).anchor_before(4),
|
||||
"|456|",
|
||||
),
|
||||
Inlay::edit_prediction(
|
||||
post_inc(&mut next_inlay_id),
|
||||
buffer
|
||||
.read(cx)
|
||||
.snapshot(cx)
|
||||
.anchor_before(MultiBufferOffset(7)),
|
||||
buffer.read(cx).snapshot(cx).anchor_before(7),
|
||||
"\n|567|\n",
|
||||
),
|
||||
],
|
||||
@@ -1736,7 +1658,7 @@ mod tests {
|
||||
.collect::<Vec<_>>();
|
||||
let mut expected_text = Rope::from(&buffer_snapshot.text());
|
||||
for (offset, inlay) in inlays.iter().rev() {
|
||||
expected_text.replace(offset.0..offset.0, &inlay.text().to_string());
|
||||
expected_text.replace(*offset..*offset, &inlay.text().to_string());
|
||||
}
|
||||
assert_eq!(inlay_snapshot.text(), expected_text.to_string());
|
||||
|
||||
@@ -1759,7 +1681,7 @@ mod tests {
|
||||
let mut text_highlights = TextHighlights::default();
|
||||
let text_highlight_count = rng.random_range(0_usize..10);
|
||||
let mut text_highlight_ranges = (0..text_highlight_count)
|
||||
.map(|_| buffer_snapshot.random_byte_range(MultiBufferOffset(0), &mut rng))
|
||||
.map(|_| buffer_snapshot.random_byte_range(0, &mut rng))
|
||||
.collect::<Vec<_>>();
|
||||
text_highlight_ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
|
||||
log::info!("highlighting text ranges {text_highlight_ranges:?}");
|
||||
@@ -1822,13 +1744,12 @@ mod tests {
|
||||
}
|
||||
|
||||
for _ in 0..5 {
|
||||
let mut end = rng.random_range(0..=inlay_snapshot.len().0.0);
|
||||
let mut end = rng.random_range(0..=inlay_snapshot.len().0);
|
||||
end = expected_text.clip_offset(end, Bias::Right);
|
||||
let mut start = rng.random_range(0..=end);
|
||||
start = expected_text.clip_offset(start, Bias::Right);
|
||||
|
||||
let range =
|
||||
InlayOffset(MultiBufferOffset(start))..InlayOffset(MultiBufferOffset(end));
|
||||
let range = InlayOffset(start)..InlayOffset(end);
|
||||
log::info!("calling inlay_snapshot.chunks({range:?})");
|
||||
let actual_text = inlay_snapshot
|
||||
.chunks(
|
||||
@@ -1850,27 +1771,25 @@ mod tests {
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
inlay_snapshot.text_summary_for_range(
|
||||
InlayOffset(MultiBufferOffset(start))..InlayOffset(MultiBufferOffset(end))
|
||||
),
|
||||
MBTextSummary::from(expected_text.slice(start..end).summary())
|
||||
inlay_snapshot.text_summary_for_range(InlayOffset(start)..InlayOffset(end)),
|
||||
expected_text.slice(start..end).summary()
|
||||
);
|
||||
}
|
||||
|
||||
for edit in inlay_edits {
|
||||
prev_inlay_text.replace_range(
|
||||
edit.new.start.0.0..edit.new.start.0.0 + edit.old_len(),
|
||||
&inlay_snapshot.text()[edit.new.start.0.0..edit.new.end.0.0],
|
||||
edit.new.start.0..edit.new.start.0 + edit.old_len().0,
|
||||
&inlay_snapshot.text()[edit.new.start.0..edit.new.end.0],
|
||||
);
|
||||
}
|
||||
assert_eq!(prev_inlay_text, inlay_snapshot.text());
|
||||
|
||||
assert_eq!(expected_text.max_point(), inlay_snapshot.max_point().0);
|
||||
assert_eq!(expected_text.len(), inlay_snapshot.len().0.0);
|
||||
assert_eq!(expected_text.len(), inlay_snapshot.len().0);
|
||||
|
||||
let mut buffer_point = Point::default();
|
||||
let mut inlay_point = inlay_snapshot.to_inlay_point(buffer_point);
|
||||
let mut buffer_chars = buffer_snapshot.chars_at(MultiBufferOffset(0));
|
||||
let mut buffer_chars = buffer_snapshot.chars_at(0);
|
||||
loop {
|
||||
// Ensure conversion from buffer coordinates to inlay coordinates
|
||||
// is consistent.
|
||||
@@ -2011,7 +1930,7 @@ mod tests {
|
||||
|
||||
// Get all chunks and verify their bitmaps
|
||||
let chunks = snapshot.chunks(
|
||||
InlayOffset(MultiBufferOffset(0))..snapshot.len(),
|
||||
InlayOffset(0)..InlayOffset(snapshot.len().0),
|
||||
false,
|
||||
Highlights::default(),
|
||||
);
|
||||
@@ -2145,7 +2064,7 @@ mod tests {
|
||||
// Collect chunks - this previously would panic
|
||||
let chunks: Vec<_> = inlay_snapshot
|
||||
.chunks(
|
||||
InlayOffset(MultiBufferOffset(0))..inlay_snapshot.len(),
|
||||
InlayOffset(0)..InlayOffset(inlay_snapshot.len().0),
|
||||
false,
|
||||
highlights,
|
||||
)
|
||||
@@ -2259,7 +2178,7 @@ mod tests {
|
||||
|
||||
let chunks: Vec<_> = inlay_snapshot
|
||||
.chunks(
|
||||
InlayOffset(MultiBufferOffset(0))..inlay_snapshot.len(),
|
||||
InlayOffset(0)..InlayOffset(inlay_snapshot.len().0),
|
||||
false,
|
||||
highlights,
|
||||
)
|
||||
|
||||
@@ -648,7 +648,6 @@ mod tests {
|
||||
inlay_map::InlayMap,
|
||||
},
|
||||
};
|
||||
use multi_buffer::MultiBufferOffset;
|
||||
use rand::{Rng, prelude::StdRng};
|
||||
use util;
|
||||
|
||||
@@ -1157,7 +1156,7 @@ mod tests {
|
||||
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
|
||||
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
|
||||
let chunks = fold_snapshot.chunks(
|
||||
FoldOffset(MultiBufferOffset(0))..fold_snapshot.len(),
|
||||
FoldOffset(0)..fold_snapshot.len(),
|
||||
false,
|
||||
Default::default(),
|
||||
);
|
||||
@@ -1319,7 +1318,7 @@ mod tests {
|
||||
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
|
||||
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
|
||||
let chunks = fold_snapshot.chunks(
|
||||
FoldOffset(MultiBufferOffset(0))..fold_snapshot.len(),
|
||||
FoldOffset(0)..fold_snapshot.len(),
|
||||
false,
|
||||
Default::default(),
|
||||
);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -35,7 +35,7 @@ use language_settings::Formatter;
|
||||
use languages::markdown_lang;
|
||||
use languages::rust_lang;
|
||||
use lsp::CompletionParams;
|
||||
use multi_buffer::{IndentGuide, MultiBufferOffset, MultiBufferOffsetUtf16, PathKey};
|
||||
use multi_buffer::{IndentGuide, PathKey};
|
||||
use parking_lot::Mutex;
|
||||
use pretty_assertions::{assert_eq, assert_ne};
|
||||
use project::{
|
||||
@@ -197,7 +197,7 @@ fn test_edit_events(cx: &mut TestAppContext) {
|
||||
// No event is emitted when the mutation is a no-op.
|
||||
_ = editor2.update(cx, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(0)..MultiBufferOffset(0)])
|
||||
s.select_ranges([0..0])
|
||||
});
|
||||
|
||||
editor.backspace(&Backspace, window, cx);
|
||||
@@ -222,7 +222,7 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
|
||||
_ = editor.update(cx, |editor, window, cx| {
|
||||
editor.start_transaction_at(now, window, cx);
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(2)..MultiBufferOffset(4)])
|
||||
s.select_ranges([2..4])
|
||||
});
|
||||
|
||||
editor.insert("cd", window, cx);
|
||||
@@ -230,46 +230,38 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
|
||||
assert_eq!(editor.text(cx), "12cd56");
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
vec![MultiBufferOffset(4)..MultiBufferOffset(4)]
|
||||
vec![4..4]
|
||||
);
|
||||
|
||||
editor.start_transaction_at(now, window, cx);
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(4)..MultiBufferOffset(5)])
|
||||
s.select_ranges([4..5])
|
||||
});
|
||||
editor.insert("e", window, cx);
|
||||
editor.end_transaction_at(now, cx);
|
||||
assert_eq!(editor.text(cx), "12cde6");
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
vec![MultiBufferOffset(5)..MultiBufferOffset(5)]
|
||||
vec![5..5]
|
||||
);
|
||||
|
||||
now += group_interval + Duration::from_millis(1);
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(2)..MultiBufferOffset(2)])
|
||||
s.select_ranges([2..2])
|
||||
});
|
||||
|
||||
// Simulate an edit in another editor
|
||||
buffer.update(cx, |buffer, cx| {
|
||||
buffer.start_transaction_at(now, cx);
|
||||
buffer.edit(
|
||||
[(MultiBufferOffset(0)..MultiBufferOffset(1), "a")],
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
buffer.edit(
|
||||
[(MultiBufferOffset(1)..MultiBufferOffset(1), "b")],
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
buffer.edit([(0..1, "a")], None, cx);
|
||||
buffer.edit([(1..1, "b")], None, cx);
|
||||
buffer.end_transaction_at(now, cx);
|
||||
});
|
||||
|
||||
assert_eq!(editor.text(cx), "ab2cde6");
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
vec![MultiBufferOffset(3)..MultiBufferOffset(3)]
|
||||
vec![3..3]
|
||||
);
|
||||
|
||||
// Last transaction happened past the group interval in a different editor.
|
||||
@@ -278,7 +270,7 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
|
||||
assert_eq!(editor.text(cx), "12cde6");
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
vec![MultiBufferOffset(2)..MultiBufferOffset(2)]
|
||||
vec![2..2]
|
||||
);
|
||||
|
||||
// First two transactions happened within the group interval in this editor.
|
||||
@@ -288,7 +280,7 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
|
||||
assert_eq!(editor.text(cx), "123456");
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
vec![MultiBufferOffset(0)..MultiBufferOffset(0)]
|
||||
vec![0..0]
|
||||
);
|
||||
|
||||
// Redo the first two transactions together.
|
||||
@@ -296,7 +288,7 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
|
||||
assert_eq!(editor.text(cx), "12cde6");
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
vec![MultiBufferOffset(5)..MultiBufferOffset(5)]
|
||||
vec![5..5]
|
||||
);
|
||||
|
||||
// Redo the last transaction on its own.
|
||||
@@ -304,7 +296,7 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
|
||||
assert_eq!(editor.text(cx), "ab2cde6");
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
vec![MultiBufferOffset(6)..MultiBufferOffset(6)]
|
||||
vec![6..6]
|
||||
);
|
||||
|
||||
// Test empty transactions.
|
||||
@@ -337,9 +329,7 @@ fn test_ime_composition(cx: &mut TestAppContext) {
|
||||
assert_eq!(editor.text(cx), "äbcde");
|
||||
assert_eq!(
|
||||
editor.marked_text_ranges(cx),
|
||||
Some(vec![
|
||||
MultiBufferOffsetUtf16(OffsetUtf16(0))..MultiBufferOffsetUtf16(OffsetUtf16(1))
|
||||
])
|
||||
Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
|
||||
);
|
||||
|
||||
// Finalize IME composition.
|
||||
@@ -359,9 +349,7 @@ fn test_ime_composition(cx: &mut TestAppContext) {
|
||||
editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
|
||||
assert_eq!(
|
||||
editor.marked_text_ranges(cx),
|
||||
Some(vec![
|
||||
MultiBufferOffsetUtf16(OffsetUtf16(0))..MultiBufferOffsetUtf16(OffsetUtf16(1))
|
||||
])
|
||||
Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
|
||||
);
|
||||
|
||||
// Undoing during an IME composition cancels it.
|
||||
@@ -374,9 +362,7 @@ fn test_ime_composition(cx: &mut TestAppContext) {
|
||||
assert_eq!(editor.text(cx), "ābcdè");
|
||||
assert_eq!(
|
||||
editor.marked_text_ranges(cx),
|
||||
Some(vec![
|
||||
MultiBufferOffsetUtf16(OffsetUtf16(4))..MultiBufferOffsetUtf16(OffsetUtf16(5))
|
||||
])
|
||||
Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
|
||||
);
|
||||
|
||||
// Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
|
||||
@@ -387,9 +373,9 @@ fn test_ime_composition(cx: &mut TestAppContext) {
|
||||
// Start a new IME composition with multiple cursors.
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([
|
||||
MultiBufferOffsetUtf16(OffsetUtf16(1))..MultiBufferOffsetUtf16(OffsetUtf16(1)),
|
||||
MultiBufferOffsetUtf16(OffsetUtf16(3))..MultiBufferOffsetUtf16(OffsetUtf16(3)),
|
||||
MultiBufferOffsetUtf16(OffsetUtf16(5))..MultiBufferOffsetUtf16(OffsetUtf16(5)),
|
||||
OffsetUtf16(1)..OffsetUtf16(1),
|
||||
OffsetUtf16(3)..OffsetUtf16(3),
|
||||
OffsetUtf16(5)..OffsetUtf16(5),
|
||||
])
|
||||
});
|
||||
editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);
|
||||
@@ -397,9 +383,9 @@ fn test_ime_composition(cx: &mut TestAppContext) {
|
||||
assert_eq!(
|
||||
editor.marked_text_ranges(cx),
|
||||
Some(vec![
|
||||
MultiBufferOffsetUtf16(OffsetUtf16(0))..MultiBufferOffsetUtf16(OffsetUtf16(3)),
|
||||
MultiBufferOffsetUtf16(OffsetUtf16(4))..MultiBufferOffsetUtf16(OffsetUtf16(7)),
|
||||
MultiBufferOffsetUtf16(OffsetUtf16(8))..MultiBufferOffsetUtf16(OffsetUtf16(11))
|
||||
OffsetUtf16(0)..OffsetUtf16(3),
|
||||
OffsetUtf16(4)..OffsetUtf16(7),
|
||||
OffsetUtf16(8)..OffsetUtf16(11)
|
||||
])
|
||||
);
|
||||
|
||||
@@ -409,9 +395,9 @@ fn test_ime_composition(cx: &mut TestAppContext) {
|
||||
assert_eq!(
|
||||
editor.marked_text_ranges(cx),
|
||||
Some(vec![
|
||||
MultiBufferOffsetUtf16(OffsetUtf16(1))..MultiBufferOffsetUtf16(OffsetUtf16(2)),
|
||||
MultiBufferOffsetUtf16(OffsetUtf16(5))..MultiBufferOffsetUtf16(OffsetUtf16(6)),
|
||||
MultiBufferOffsetUtf16(OffsetUtf16(9))..MultiBufferOffsetUtf16(OffsetUtf16(10))
|
||||
OffsetUtf16(1)..OffsetUtf16(2),
|
||||
OffsetUtf16(5)..OffsetUtf16(6),
|
||||
OffsetUtf16(9)..OffsetUtf16(10)
|
||||
])
|
||||
);
|
||||
|
||||
@@ -771,11 +757,7 @@ fn test_clone(cx: &mut TestAppContext) {
|
||||
|
||||
_ = editor.update(cx, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges(
|
||||
selection_ranges
|
||||
.iter()
|
||||
.map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end)),
|
||||
)
|
||||
s.select_ranges(selection_ranges.clone())
|
||||
});
|
||||
editor.fold_creases(
|
||||
vec![
|
||||
@@ -812,11 +794,9 @@ fn test_clone(cx: &mut TestAppContext) {
|
||||
);
|
||||
assert_eq!(
|
||||
cloned_snapshot
|
||||
.folds_in_range(MultiBufferOffset(0)..MultiBufferOffset(text.len()))
|
||||
.collect::<Vec<_>>(),
|
||||
snapshot
|
||||
.folds_in_range(MultiBufferOffset(0)..MultiBufferOffset(text.len()))
|
||||
.folds_in_range(0..text.len())
|
||||
.collect::<Vec<_>>(),
|
||||
snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
|
||||
);
|
||||
assert_set_eq!(
|
||||
cloned_editor
|
||||
@@ -1438,11 +1418,7 @@ fn test_fold_at_level(cx: &mut TestAppContext) {
|
||||
);
|
||||
|
||||
editor.change_selections(SelectionEffects::default(), window, cx, |s| {
|
||||
s.select_ranges(
|
||||
positions
|
||||
.iter()
|
||||
.map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end)),
|
||||
)
|
||||
s.select_ranges(positions)
|
||||
});
|
||||
|
||||
editor.fold_at_level(&FoldAtLevel(2), window, cx);
|
||||
@@ -3724,11 +3700,7 @@ fn test_insert_with_old_selections(cx: &mut TestAppContext) {
|
||||
let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
|
||||
let mut editor = build_editor(buffer, window, cx);
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([
|
||||
MultiBufferOffset(3)..MultiBufferOffset(4),
|
||||
MultiBufferOffset(11)..MultiBufferOffset(12),
|
||||
MultiBufferOffset(19)..MultiBufferOffset(20),
|
||||
])
|
||||
s.select_ranges([3..4, 11..12, 19..20])
|
||||
});
|
||||
editor
|
||||
});
|
||||
@@ -3736,24 +3708,12 @@ fn test_insert_with_old_selections(cx: &mut TestAppContext) {
|
||||
_ = editor.update(cx, |editor, window, cx| {
|
||||
// Edit the buffer directly, deleting ranges surrounding the editor's selections
|
||||
editor.buffer.update(cx, |buffer, cx| {
|
||||
buffer.edit(
|
||||
[
|
||||
(MultiBufferOffset(2)..MultiBufferOffset(5), ""),
|
||||
(MultiBufferOffset(10)..MultiBufferOffset(13), ""),
|
||||
(MultiBufferOffset(18)..MultiBufferOffset(21), ""),
|
||||
],
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
|
||||
assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
|
||||
});
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
&[
|
||||
MultiBufferOffset(2)..MultiBufferOffset(2),
|
||||
MultiBufferOffset(7)..MultiBufferOffset(7),
|
||||
MultiBufferOffset(12)..MultiBufferOffset(12)
|
||||
],
|
||||
&[2..2, 7..7, 12..12],
|
||||
);
|
||||
|
||||
editor.insert("Z", window, cx);
|
||||
@@ -3762,11 +3722,7 @@ fn test_insert_with_old_selections(cx: &mut TestAppContext) {
|
||||
// The selections are moved after the inserted characters
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
&[
|
||||
MultiBufferOffset(3)..MultiBufferOffset(3),
|
||||
MultiBufferOffset(9)..MultiBufferOffset(9),
|
||||
MultiBufferOffset(15)..MultiBufferOffset(15)
|
||||
],
|
||||
&[3..3, 9..9, 15..15],
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -4736,7 +4692,7 @@ async fn test_custom_newlines_cause_no_false_positive_diffs(
|
||||
assert_eq!(
|
||||
snapshot
|
||||
.buffer_snapshot()
|
||||
.diff_hunks_in_range(MultiBufferOffset(0)..snapshot.buffer_snapshot().len())
|
||||
.diff_hunks_in_range(0..snapshot.buffer_snapshot().len())
|
||||
.collect::<Vec<_>>(),
|
||||
Vec::new(),
|
||||
"Should not have any diffs for files with custom newlines"
|
||||
@@ -6008,27 +5964,27 @@ fn test_transpose(cx: &mut TestAppContext) {
|
||||
let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
|
||||
editor.set_style(EditorStyle::default(), window, cx);
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(1)..MultiBufferOffset(1)])
|
||||
s.select_ranges([1..1])
|
||||
});
|
||||
editor.transpose(&Default::default(), window, cx);
|
||||
assert_eq!(editor.text(cx), "bac");
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
[MultiBufferOffset(2)..MultiBufferOffset(2)]
|
||||
[2..2]
|
||||
);
|
||||
|
||||
editor.transpose(&Default::default(), window, cx);
|
||||
assert_eq!(editor.text(cx), "bca");
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
[MultiBufferOffset(3)..MultiBufferOffset(3)]
|
||||
[3..3]
|
||||
);
|
||||
|
||||
editor.transpose(&Default::default(), window, cx);
|
||||
assert_eq!(editor.text(cx), "bac");
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
[MultiBufferOffset(3)..MultiBufferOffset(3)]
|
||||
[3..3]
|
||||
);
|
||||
|
||||
editor
|
||||
@@ -6038,37 +5994,37 @@ fn test_transpose(cx: &mut TestAppContext) {
|
||||
let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
|
||||
editor.set_style(EditorStyle::default(), window, cx);
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(3)..MultiBufferOffset(3)])
|
||||
s.select_ranges([3..3])
|
||||
});
|
||||
editor.transpose(&Default::default(), window, cx);
|
||||
assert_eq!(editor.text(cx), "acb\nde");
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
[MultiBufferOffset(3)..MultiBufferOffset(3)]
|
||||
[3..3]
|
||||
);
|
||||
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(4)..MultiBufferOffset(4)])
|
||||
s.select_ranges([4..4])
|
||||
});
|
||||
editor.transpose(&Default::default(), window, cx);
|
||||
assert_eq!(editor.text(cx), "acbd\ne");
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
[MultiBufferOffset(5)..MultiBufferOffset(5)]
|
||||
[5..5]
|
||||
);
|
||||
|
||||
editor.transpose(&Default::default(), window, cx);
|
||||
assert_eq!(editor.text(cx), "acbde\n");
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
[MultiBufferOffset(6)..MultiBufferOffset(6)]
|
||||
[6..6]
|
||||
);
|
||||
|
||||
editor.transpose(&Default::default(), window, cx);
|
||||
assert_eq!(editor.text(cx), "acbd\ne");
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
[MultiBufferOffset(6)..MultiBufferOffset(6)]
|
||||
[6..6]
|
||||
);
|
||||
|
||||
editor
|
||||
@@ -6078,62 +6034,41 @@ fn test_transpose(cx: &mut TestAppContext) {
|
||||
let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
|
||||
editor.set_style(EditorStyle::default(), window, cx);
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([
|
||||
MultiBufferOffset(1)..MultiBufferOffset(1),
|
||||
MultiBufferOffset(2)..MultiBufferOffset(2),
|
||||
MultiBufferOffset(4)..MultiBufferOffset(4),
|
||||
])
|
||||
s.select_ranges([1..1, 2..2, 4..4])
|
||||
});
|
||||
editor.transpose(&Default::default(), window, cx);
|
||||
assert_eq!(editor.text(cx), "bacd\ne");
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
[
|
||||
MultiBufferOffset(2)..MultiBufferOffset(2),
|
||||
MultiBufferOffset(3)..MultiBufferOffset(3),
|
||||
MultiBufferOffset(5)..MultiBufferOffset(5)
|
||||
]
|
||||
[2..2, 3..3, 5..5]
|
||||
);
|
||||
|
||||
editor.transpose(&Default::default(), window, cx);
|
||||
assert_eq!(editor.text(cx), "bcade\n");
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
[
|
||||
MultiBufferOffset(3)..MultiBufferOffset(3),
|
||||
MultiBufferOffset(4)..MultiBufferOffset(4),
|
||||
MultiBufferOffset(6)..MultiBufferOffset(6)
|
||||
]
|
||||
[3..3, 4..4, 6..6]
|
||||
);
|
||||
|
||||
editor.transpose(&Default::default(), window, cx);
|
||||
assert_eq!(editor.text(cx), "bcda\ne");
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
[
|
||||
MultiBufferOffset(4)..MultiBufferOffset(4),
|
||||
MultiBufferOffset(6)..MultiBufferOffset(6)
|
||||
]
|
||||
[4..4, 6..6]
|
||||
);
|
||||
|
||||
editor.transpose(&Default::default(), window, cx);
|
||||
assert_eq!(editor.text(cx), "bcade\n");
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
[
|
||||
MultiBufferOffset(4)..MultiBufferOffset(4),
|
||||
MultiBufferOffset(6)..MultiBufferOffset(6)
|
||||
]
|
||||
[4..4, 6..6]
|
||||
);
|
||||
|
||||
editor.transpose(&Default::default(), window, cx);
|
||||
assert_eq!(editor.text(cx), "bcaed\n");
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
[
|
||||
MultiBufferOffset(5)..MultiBufferOffset(5),
|
||||
MultiBufferOffset(6)..MultiBufferOffset(6)
|
||||
]
|
||||
[5..5, 6..6]
|
||||
);
|
||||
|
||||
editor
|
||||
@@ -6143,27 +6078,27 @@ fn test_transpose(cx: &mut TestAppContext) {
|
||||
let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
|
||||
editor.set_style(EditorStyle::default(), window, cx);
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(4)..MultiBufferOffset(4)])
|
||||
s.select_ranges([4..4])
|
||||
});
|
||||
editor.transpose(&Default::default(), window, cx);
|
||||
assert_eq!(editor.text(cx), "🏀🍐✋");
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
[MultiBufferOffset(8)..MultiBufferOffset(8)]
|
||||
[8..8]
|
||||
);
|
||||
|
||||
editor.transpose(&Default::default(), window, cx);
|
||||
assert_eq!(editor.text(cx), "🏀✋🍐");
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
[MultiBufferOffset(11)..MultiBufferOffset(11)]
|
||||
[11..11]
|
||||
);
|
||||
|
||||
editor.transpose(&Default::default(), window, cx);
|
||||
assert_eq!(editor.text(cx), "🏀🍐✋");
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
[MultiBufferOffset(11)..MultiBufferOffset(11)]
|
||||
[11..11]
|
||||
);
|
||||
|
||||
editor
|
||||
@@ -9796,11 +9731,7 @@ async fn test_autoindent(cx: &mut TestAppContext) {
|
||||
|
||||
editor.update_in(cx, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([
|
||||
MultiBufferOffset(5)..MultiBufferOffset(5),
|
||||
MultiBufferOffset(8)..MultiBufferOffset(8),
|
||||
MultiBufferOffset(9)..MultiBufferOffset(9),
|
||||
])
|
||||
s.select_ranges([5..5, 8..8, 9..9])
|
||||
});
|
||||
editor.newline(&Newline, window, cx);
|
||||
assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n");
|
||||
@@ -9865,11 +9796,7 @@ async fn test_autoindent_disabled(cx: &mut TestAppContext) {
|
||||
|
||||
editor.update_in(cx, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([
|
||||
MultiBufferOffset(5)..MultiBufferOffset(5),
|
||||
MultiBufferOffset(8)..MultiBufferOffset(8),
|
||||
MultiBufferOffset(9)..MultiBufferOffset(9),
|
||||
])
|
||||
s.select_ranges([5..5, 8..8, 9..9])
|
||||
});
|
||||
editor.newline(&Newline, window, cx);
|
||||
assert_eq!(
|
||||
@@ -10526,7 +10453,7 @@ async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
|
||||
let snapshot = editor.snapshot(window, cx);
|
||||
let cursors = editor
|
||||
.selections
|
||||
.ranges::<MultiBufferOffset>(&editor.display_snapshot(cx));
|
||||
.ranges::<usize>(&editor.display_snapshot(cx));
|
||||
let languages = cursors
|
||||
.iter()
|
||||
.map(|c| snapshot.language_at(c.start).unwrap().name())
|
||||
@@ -11216,26 +11143,17 @@ async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
|
||||
let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
|
||||
|
||||
editor
|
||||
.insert_snippet(
|
||||
&insertion_ranges
|
||||
.iter()
|
||||
.map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end))
|
||||
.collect::<Vec<_>>(),
|
||||
snippet,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
.insert_snippet(&insertion_ranges, snippet, window, cx)
|
||||
.unwrap();
|
||||
|
||||
fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
|
||||
let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
|
||||
assert_eq!(editor.text(cx), expected_text);
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
editor
|
||||
.selections
|
||||
.ranges::<usize>(&editor.display_snapshot(cx)),
|
||||
selection_ranges
|
||||
.iter()
|
||||
.map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end))
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11259,11 +11177,10 @@ async fn test_snippet_tabstop_navigation_with_placeholders(cx: &mut TestAppConte
|
||||
let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
|
||||
assert_eq!(editor.text(cx), expected_text);
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
editor
|
||||
.selections
|
||||
.ranges::<usize>(&editor.display_snapshot(cx)),
|
||||
selection_ranges
|
||||
.iter()
|
||||
.map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end))
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11281,15 +11198,7 @@ async fn test_snippet_tabstop_navigation_with_placeholders(cx: &mut TestAppConte
|
||||
let snippet = Snippet::parse("type ${1|,i32,u32|} = $2; $3").unwrap();
|
||||
|
||||
editor
|
||||
.insert_snippet(
|
||||
&insertion_ranges
|
||||
.iter()
|
||||
.map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end))
|
||||
.collect::<Vec<_>>(),
|
||||
snippet,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
.insert_snippet(&insertion_ranges, snippet, window, cx)
|
||||
.unwrap();
|
||||
|
||||
assert_state(
|
||||
@@ -11737,7 +11646,7 @@ async fn test_redo_after_noop_format(cx: &mut TestAppContext) {
|
||||
});
|
||||
editor.update_in(cx, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::default(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(0)..MultiBufferOffset(0)])
|
||||
s.select_ranges([0..0])
|
||||
});
|
||||
});
|
||||
assert!(!cx.read(|cx| editor.is_dirty(cx)));
|
||||
@@ -11903,7 +11812,7 @@ async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
|
||||
SelectionEffects::scroll(Autoscroll::Next),
|
||||
window,
|
||||
cx,
|
||||
|s| s.select_ranges(Some(MultiBufferOffset(1)..MultiBufferOffset(2))),
|
||||
|s| s.select_ranges(Some(1..2)),
|
||||
);
|
||||
editor.insert("|one|two|three|", window, cx);
|
||||
});
|
||||
@@ -11913,7 +11822,7 @@ async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
|
||||
SelectionEffects::scroll(Autoscroll::Next),
|
||||
window,
|
||||
cx,
|
||||
|s| s.select_ranges(Some(MultiBufferOffset(60)..MultiBufferOffset(70))),
|
||||
|s| s.select_ranges(Some(60..70)),
|
||||
);
|
||||
editor.insert("|four|five|six|", window, cx);
|
||||
});
|
||||
@@ -12081,7 +11990,7 @@ async fn test_autosave_with_dirty_buffers(cx: &mut TestAppContext) {
|
||||
SelectionEffects::scroll(Autoscroll::Next),
|
||||
window,
|
||||
cx,
|
||||
|s| s.select_ranges(Some(MultiBufferOffset(10)..MultiBufferOffset(10))),
|
||||
|s| s.select_ranges(Some(10..10)),
|
||||
);
|
||||
editor.insert("// edited", window, cx);
|
||||
});
|
||||
@@ -13519,7 +13428,7 @@ async fn test_signature_help(cx: &mut TestAppContext) {
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(0)..MultiBufferOffset(0)])
|
||||
s.select_ranges([0..0])
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16504,11 +16413,7 @@ fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
|
||||
);
|
||||
assert_eq!(editor.text(cx), expected_text);
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges(
|
||||
selection_ranges
|
||||
.iter()
|
||||
.map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end)),
|
||||
)
|
||||
s.select_ranges(selection_ranges)
|
||||
});
|
||||
|
||||
editor.handle_input("X", window, cx);
|
||||
@@ -16526,9 +16431,6 @@ fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
expected_selections
|
||||
.iter()
|
||||
.map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end))
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
editor.newline(&Newline, window, cx);
|
||||
@@ -16549,9 +16451,6 @@ fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
expected_selections
|
||||
.iter()
|
||||
.map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end))
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -16927,7 +16826,7 @@ async fn test_following(cx: &mut TestAppContext) {
|
||||
// Update the selections only
|
||||
_ = leader.update(cx, |leader, window, cx| {
|
||||
leader.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(1)..MultiBufferOffset(1)])
|
||||
s.select_ranges([1..1])
|
||||
});
|
||||
});
|
||||
follower
|
||||
@@ -16945,7 +16844,7 @@ async fn test_following(cx: &mut TestAppContext) {
|
||||
_ = follower.update(cx, |follower, _, cx| {
|
||||
assert_eq!(
|
||||
follower.selections.ranges(&follower.display_snapshot(cx)),
|
||||
vec![MultiBufferOffset(1)..MultiBufferOffset(1)]
|
||||
vec![1..1]
|
||||
);
|
||||
});
|
||||
assert!(*is_still_following.borrow());
|
||||
@@ -16980,7 +16879,7 @@ async fn test_following(cx: &mut TestAppContext) {
|
||||
// via autoscroll, not via the leader's exact scroll position.
|
||||
_ = leader.update(cx, |leader, window, cx| {
|
||||
leader.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(0)..MultiBufferOffset(0)])
|
||||
s.select_ranges([0..0])
|
||||
});
|
||||
leader.request_autoscroll(Autoscroll::newest(), cx);
|
||||
leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
|
||||
@@ -17001,7 +16900,7 @@ async fn test_following(cx: &mut TestAppContext) {
|
||||
assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
|
||||
assert_eq!(
|
||||
follower.selections.ranges(&follower.display_snapshot(cx)),
|
||||
vec![MultiBufferOffset(0)..MultiBufferOffset(0)]
|
||||
vec![0..0]
|
||||
);
|
||||
});
|
||||
assert!(*is_still_following.borrow());
|
||||
@@ -17009,7 +16908,7 @@ async fn test_following(cx: &mut TestAppContext) {
|
||||
// Creating a pending selection that precedes another selection
|
||||
_ = leader.update(cx, |leader, window, cx| {
|
||||
leader.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(1)..MultiBufferOffset(1)])
|
||||
s.select_ranges([1..1])
|
||||
});
|
||||
leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
|
||||
});
|
||||
@@ -17028,10 +16927,7 @@ async fn test_following(cx: &mut TestAppContext) {
|
||||
_ = follower.update(cx, |follower, _, cx| {
|
||||
assert_eq!(
|
||||
follower.selections.ranges(&follower.display_snapshot(cx)),
|
||||
vec![
|
||||
MultiBufferOffset(0)..MultiBufferOffset(0),
|
||||
MultiBufferOffset(1)..MultiBufferOffset(1)
|
||||
]
|
||||
vec![0..0, 1..1]
|
||||
);
|
||||
});
|
||||
assert!(*is_still_following.borrow());
|
||||
@@ -17055,17 +16951,13 @@ async fn test_following(cx: &mut TestAppContext) {
|
||||
_ = follower.update(cx, |follower, _, cx| {
|
||||
assert_eq!(
|
||||
follower.selections.ranges(&follower.display_snapshot(cx)),
|
||||
vec![MultiBufferOffset(0)..MultiBufferOffset(2)]
|
||||
vec![0..2]
|
||||
);
|
||||
});
|
||||
|
||||
// Scrolling locally breaks the follow
|
||||
_ = follower.update(cx, |follower, window, cx| {
|
||||
let top_anchor = follower
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.read(cx)
|
||||
.anchor_after(MultiBufferOffset(0));
|
||||
let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
|
||||
follower.set_scroll_anchor(
|
||||
ScrollAnchor {
|
||||
anchor: top_anchor,
|
||||
@@ -19559,7 +19451,7 @@ async fn test_multibuffer_in_navigation_history(cx: &mut TestAppContext) {
|
||||
SelectionEffects::scroll(Autoscroll::Next),
|
||||
window,
|
||||
cx,
|
||||
|s| s.select_ranges(Some(MultiBufferOffset(1)..MultiBufferOffset(2))),
|
||||
|s| s.select_ranges(Some(1..2)),
|
||||
);
|
||||
editor.open_excerpts(&OpenExcerpts, window, cx);
|
||||
});
|
||||
@@ -19615,7 +19507,7 @@ async fn test_multibuffer_in_navigation_history(cx: &mut TestAppContext) {
|
||||
SelectionEffects::scroll(Autoscroll::Next),
|
||||
window,
|
||||
cx,
|
||||
|s| s.select_ranges(Some(MultiBufferOffset(39)..MultiBufferOffset(40))),
|
||||
|s| s.select_ranges(Some(39..40)),
|
||||
);
|
||||
editor.open_excerpts(&OpenExcerpts, window, cx);
|
||||
});
|
||||
@@ -19675,7 +19567,7 @@ async fn test_multibuffer_in_navigation_history(cx: &mut TestAppContext) {
|
||||
SelectionEffects::scroll(Autoscroll::Next),
|
||||
window,
|
||||
cx,
|
||||
|s| s.select_ranges(Some(MultiBufferOffset(70)..MultiBufferOffset(70))),
|
||||
|s| s.select_ranges(Some(70..70)),
|
||||
);
|
||||
editor.open_excerpts(&OpenExcerpts, window, cx);
|
||||
});
|
||||
@@ -22465,7 +22357,7 @@ async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
|
||||
(buffer.read(cx).remote_id(), 3),
|
||||
RunnableTasks {
|
||||
templates: vec![],
|
||||
offset: snapshot.anchor_before(MultiBufferOffset(43)),
|
||||
offset: snapshot.anchor_before(43),
|
||||
column: 0,
|
||||
extra_variables: HashMap::default(),
|
||||
context_range: BufferOffset(43)..BufferOffset(85),
|
||||
@@ -22475,7 +22367,7 @@ async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
|
||||
(buffer.read(cx).remote_id(), 8),
|
||||
RunnableTasks {
|
||||
templates: vec![],
|
||||
offset: snapshot.anchor_before(MultiBufferOffset(86)),
|
||||
offset: snapshot.anchor_before(86),
|
||||
column: 0,
|
||||
extra_variables: HashMap::default(),
|
||||
context_range: BufferOffset(86)..BufferOffset(191),
|
||||
@@ -25857,10 +25749,7 @@ fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Cont
|
||||
assert_eq!(editor.text(cx), text);
|
||||
assert_eq!(
|
||||
editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
ranges
|
||||
.iter()
|
||||
.map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end))
|
||||
.collect::<Vec<_>>(),
|
||||
ranges,
|
||||
"Assert selections are {}",
|
||||
marked_text
|
||||
);
|
||||
@@ -26106,12 +25995,10 @@ pub fn handle_completion_request(
|
||||
vec![complete_from_marker.clone(), replace_range_marker.clone()],
|
||||
);
|
||||
|
||||
let complete_from_position = cx.to_lsp(MultiBufferOffset(
|
||||
marked_ranges.remove(&complete_from_marker).unwrap()[0].start,
|
||||
));
|
||||
let range = marked_ranges.remove(&replace_range_marker).unwrap()[0].clone();
|
||||
let complete_from_position =
|
||||
cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
|
||||
let replace_range =
|
||||
cx.to_lsp_range(MultiBufferOffset(range.start)..MultiBufferOffset(range.end));
|
||||
cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
|
||||
|
||||
let mut request =
|
||||
cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
|
||||
@@ -26172,18 +26059,13 @@ pub fn handle_completion_request_with_insert_and_replace(
|
||||
],
|
||||
);
|
||||
|
||||
let complete_from_position = cx.to_lsp(MultiBufferOffset(
|
||||
marked_ranges.remove(&complete_from_marker).unwrap()[0].start,
|
||||
));
|
||||
let range = marked_ranges.remove(&replace_range_marker).unwrap()[0].clone();
|
||||
let complete_from_position =
|
||||
cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
|
||||
let replace_range =
|
||||
cx.to_lsp_range(MultiBufferOffset(range.start)..MultiBufferOffset(range.end));
|
||||
cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
|
||||
|
||||
let insert_range = match marked_ranges.remove(&insert_range_marker) {
|
||||
Some(ranges) if !ranges.is_empty() => {
|
||||
let range1 = ranges[0].clone();
|
||||
cx.to_lsp_range(MultiBufferOffset(range1.start)..MultiBufferOffset(range1.end))
|
||||
}
|
||||
Some(ranges) if !ranges.is_empty() => cx.to_lsp_range(ranges[0].clone()),
|
||||
_ => lsp::Range {
|
||||
start: replace_range.start,
|
||||
end: complete_from_position,
|
||||
@@ -26233,10 +26115,7 @@ fn handle_resolve_completion_request(
|
||||
.iter()
|
||||
.map(|(marked_string, new_text)| {
|
||||
let (_, marked_ranges) = marked_text_ranges(marked_string, false);
|
||||
let replace_range = cx.to_lsp_range(
|
||||
MultiBufferOffset(marked_ranges[0].start)
|
||||
..MultiBufferOffset(marked_ranges[0].end),
|
||||
);
|
||||
let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
|
||||
lsp::TextEdit::new(replace_range, new_text.to_string())
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
@@ -26320,7 +26199,7 @@ fn assert_hunk_revert(
|
||||
let snapshot = editor.snapshot(window, cx);
|
||||
let reverted_hunk_statuses = snapshot
|
||||
.buffer_snapshot()
|
||||
.diff_hunks_in_range(MultiBufferOffset(0)..snapshot.buffer_snapshot().len())
|
||||
.diff_hunks_in_range(0..snapshot.buffer_snapshot().len())
|
||||
.map(|hunk| hunk.status().kind)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@@ -26944,9 +26823,7 @@ async fn test_newline_replacement_in_single_line(cx: &mut TestAppContext) {
|
||||
editor.update(cx, |editor, cx| {
|
||||
assert_eq!(editor.display_text(cx), "oops⋯⋯wow⋯");
|
||||
});
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.edit([(MultiBufferOffset(3)..MultiBufferOffset(5), "")], cx)
|
||||
});
|
||||
editor.update(cx, |editor, cx| editor.edit([(3..5, "")], cx));
|
||||
cx.run_until_parked();
|
||||
editor.update(cx, |editor, cx| {
|
||||
assert_eq!(editor.display_text(cx), "oop⋯wow⋯");
|
||||
@@ -26983,7 +26860,7 @@ async fn test_non_utf_8_opens(cx: &mut TestAppContext) {
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
handle.to_any_view().entity_type(),
|
||||
handle.to_any().entity_type(),
|
||||
TypeId::of::<InvalidItemView>()
|
||||
);
|
||||
}
|
||||
@@ -28018,7 +27895,7 @@ async fn test_multibuffer_selections_with_folding(cx: &mut TestAppContext) {
|
||||
// Scenario 1: Unfolded buffers, position cursor on "2", select all matches, then insert
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.change_selections(None.into(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(2)..MultiBufferOffset(3)]);
|
||||
s.select_ranges([2..3]);
|
||||
});
|
||||
});
|
||||
cx.assert_excerpts_with_selections(indoc! {"
|
||||
@@ -28075,7 +27952,7 @@ async fn test_multibuffer_selections_with_folding(cx: &mut TestAppContext) {
|
||||
// Select "2" and select all matches
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.change_selections(None.into(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(2)..MultiBufferOffset(3)]);
|
||||
s.select_ranges([2..3]);
|
||||
});
|
||||
editor
|
||||
.select_all_matches(&SelectAllMatches, window, cx)
|
||||
@@ -28126,7 +28003,7 @@ async fn test_multibuffer_selections_with_folding(cx: &mut TestAppContext) {
|
||||
// Select "2" and select all matches
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.change_selections(None.into(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(2)..MultiBufferOffset(3)]);
|
||||
s.select_ranges([2..3]);
|
||||
});
|
||||
editor
|
||||
.select_all_matches(&SelectAllMatches, window, cx)
|
||||
|
||||
@@ -74,7 +74,6 @@ use smallvec::{SmallVec, smallvec};
|
||||
use std::{
|
||||
any::TypeId,
|
||||
borrow::Cow,
|
||||
cell::Cell,
|
||||
cmp::{self, Ordering},
|
||||
fmt::{self, Write},
|
||||
iter, mem,
|
||||
@@ -186,13 +185,6 @@ impl SelectionLayout {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct RenderBlocksOutput {
|
||||
blocks: Vec<BlockLayout>,
|
||||
row_block_types: HashMap<DisplayRow, bool>,
|
||||
resized_blocks: Option<HashMap<CustomBlockId, u32>>,
|
||||
}
|
||||
|
||||
pub struct EditorElement {
|
||||
editor: Entity<Editor>,
|
||||
style: EditorStyle,
|
||||
@@ -3675,7 +3667,6 @@ impl EditorElement {
|
||||
latest_selection_anchors: &HashMap<BufferId, Anchor>,
|
||||
is_row_soft_wrapped: impl Copy + Fn(usize) -> bool,
|
||||
sticky_header_excerpt_id: Option<ExcerptId>,
|
||||
block_resize_offset: &mut i32,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> Option<(AnyElement, Size<Pixels>, DisplayRow, Pixels)> {
|
||||
@@ -3829,10 +3820,7 @@ impl EditorElement {
|
||||
};
|
||||
let mut element_height_in_lines = ((final_size.height / line_height).ceil() as u32).max(1);
|
||||
|
||||
let effective_row_start = block_row_start.0 as i32 + *block_resize_offset;
|
||||
debug_assert!(effective_row_start >= 0);
|
||||
let mut row = DisplayRow(effective_row_start.max(0) as u32);
|
||||
|
||||
let mut row = block_row_start;
|
||||
let mut x_offset = px(0.);
|
||||
let mut is_block = true;
|
||||
|
||||
@@ -3862,7 +3850,6 @@ impl EditorElement {
|
||||
}
|
||||
};
|
||||
if element_height_in_lines != block.height() {
|
||||
*block_resize_offset += element_height_in_lines as i32 - block.height() as i32;
|
||||
resized_blocks.insert(custom_block_id, element_height_in_lines);
|
||||
}
|
||||
}
|
||||
@@ -4267,7 +4254,7 @@ impl EditorElement {
|
||||
sticky_header_excerpt_id: Option<ExcerptId>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> RenderBlocksOutput {
|
||||
) -> Result<(Vec<BlockLayout>, HashMap<DisplayRow, bool>), HashMap<CustomBlockId, u32>> {
|
||||
let (fixed_blocks, non_fixed_blocks) = snapshot
|
||||
.blocks_in_range(rows.clone())
|
||||
.partition::<Vec<_>, _>(|(_, block)| block.style() == BlockStyle::Fixed);
|
||||
@@ -4279,7 +4266,6 @@ impl EditorElement {
|
||||
let mut blocks = Vec::new();
|
||||
let mut resized_blocks = HashMap::default();
|
||||
let mut row_block_types = HashMap::default();
|
||||
let mut block_resize_offset: i32 = 0;
|
||||
|
||||
for (row, block) in fixed_blocks {
|
||||
let block_id = block.id();
|
||||
@@ -4310,7 +4296,6 @@ impl EditorElement {
|
||||
latest_selection_anchors,
|
||||
is_row_soft_wrapped,
|
||||
sticky_header_excerpt_id,
|
||||
&mut block_resize_offset,
|
||||
window,
|
||||
cx,
|
||||
) {
|
||||
@@ -4369,7 +4354,6 @@ impl EditorElement {
|
||||
latest_selection_anchors,
|
||||
is_row_soft_wrapped,
|
||||
sticky_header_excerpt_id,
|
||||
&mut block_resize_offset,
|
||||
window,
|
||||
cx,
|
||||
) {
|
||||
@@ -4426,7 +4410,6 @@ impl EditorElement {
|
||||
latest_selection_anchors,
|
||||
is_row_soft_wrapped,
|
||||
sticky_header_excerpt_id,
|
||||
&mut block_resize_offset,
|
||||
window,
|
||||
cx,
|
||||
) {
|
||||
@@ -4446,12 +4429,9 @@ impl EditorElement {
|
||||
if resized_blocks.is_empty() {
|
||||
*scroll_width =
|
||||
(*scroll_width).max(fixed_block_max_width - editor_margins.gutter.width);
|
||||
}
|
||||
|
||||
RenderBlocksOutput {
|
||||
blocks,
|
||||
row_block_types,
|
||||
resized_blocks: (!resized_blocks.is_empty()).then_some(resized_blocks),
|
||||
Ok((blocks, row_block_types))
|
||||
} else {
|
||||
Err(resized_blocks)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8792,48 +8772,8 @@ impl EditorElement {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct EditorRequestLayoutState {
|
||||
// We use prepaint depth to limit the number of times prepaint is
|
||||
// called recursively. We need this so that we can update stale
|
||||
// data for e.g. block heights in block map.
|
||||
prepaint_depth: Rc<Cell<usize>>,
|
||||
}
|
||||
|
||||
impl EditorRequestLayoutState {
|
||||
// In ideal conditions we only need one more subsequent prepaint call for resize to take effect.
|
||||
// i.e. MAX_PREPAINT_DEPTH = 2, but since moving blocks inline (place_near), more lines from
|
||||
// below get exposed, and we end up querying blocks for those lines too in subsequent renders.
|
||||
// Setting MAX_PREPAINT_DEPTH = 3, passes all tests. Just to be on the safe side we set it to 5, so
|
||||
// that subsequent shrinking does not lead to incorrect block placing.
|
||||
const MAX_PREPAINT_DEPTH: usize = 5;
|
||||
|
||||
fn increment_prepaint_depth(&self) -> EditorPrepaintGuard {
|
||||
let depth = self.prepaint_depth.get();
|
||||
self.prepaint_depth.set(depth + 1);
|
||||
EditorPrepaintGuard {
|
||||
prepaint_depth: self.prepaint_depth.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn can_prepaint(&self) -> bool {
|
||||
self.prepaint_depth.get() < Self::MAX_PREPAINT_DEPTH
|
||||
}
|
||||
}
|
||||
|
||||
struct EditorPrepaintGuard {
|
||||
prepaint_depth: Rc<Cell<usize>>,
|
||||
}
|
||||
|
||||
impl Drop for EditorPrepaintGuard {
|
||||
fn drop(&mut self) {
|
||||
let depth = self.prepaint_depth.get();
|
||||
self.prepaint_depth.set(depth.saturating_sub(1));
|
||||
}
|
||||
}
|
||||
|
||||
impl Element for EditorElement {
|
||||
type RequestLayoutState = EditorRequestLayoutState;
|
||||
type RequestLayoutState = ();
|
||||
type PrepaintState = EditorLayout;
|
||||
|
||||
fn id(&self) -> Option<ElementId> {
|
||||
@@ -8850,7 +8790,7 @@ impl Element for EditorElement {
|
||||
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> (gpui::LayoutId, Self::RequestLayoutState) {
|
||||
) -> (gpui::LayoutId, ()) {
|
||||
let rem_size = self.rem_size(cx);
|
||||
window.with_rem_size(rem_size, |window| {
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
@@ -8917,7 +8857,7 @@ impl Element for EditorElement {
|
||||
}
|
||||
};
|
||||
|
||||
(layout_id, EditorRequestLayoutState::default())
|
||||
(layout_id, ())
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -8927,11 +8867,10 @@ impl Element for EditorElement {
|
||||
_: Option<&GlobalElementId>,
|
||||
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||
bounds: Bounds<Pixels>,
|
||||
request_layout: &mut Self::RequestLayoutState,
|
||||
_: &mut Self::RequestLayoutState,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> Self::PrepaintState {
|
||||
let _prepaint_depth_guard = request_layout.increment_prepaint_depth();
|
||||
let text_style = TextStyleRefinement {
|
||||
font_size: Some(self.style.text.font_size),
|
||||
line_height: Some(self.style.text.line_height),
|
||||
@@ -9116,6 +9055,9 @@ impl Element for EditorElement {
|
||||
)
|
||||
});
|
||||
|
||||
if snapshot.scroll_near_end() {
|
||||
dbg!("near end!");
|
||||
}
|
||||
let mut scroll_position = snapshot.scroll_position();
|
||||
// The scroll position is a fractional point, the whole number of which represents
|
||||
// the top of the window in terms of display rows.
|
||||
@@ -9455,20 +9397,7 @@ impl Element for EditorElement {
|
||||
// If the fold widths have changed, we need to prepaint
|
||||
// the element again to account for any changes in
|
||||
// wrapping.
|
||||
if request_layout.can_prepaint() {
|
||||
return self.prepaint(
|
||||
None,
|
||||
_inspector_id,
|
||||
bounds,
|
||||
request_layout,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
} else {
|
||||
debug_panic!(
|
||||
"skipping recursive prepaint at max depth. renderer widths may be stale."
|
||||
);
|
||||
}
|
||||
return self.prepaint(None, _inspector_id, bounds, &mut (), window, cx);
|
||||
}
|
||||
|
||||
let longest_line_blame_width = self
|
||||
@@ -9555,35 +9484,20 @@ impl Element for EditorElement {
|
||||
)
|
||||
})
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let RenderBlocksOutput {
|
||||
mut blocks,
|
||||
row_block_types,
|
||||
resized_blocks,
|
||||
} = blocks;
|
||||
if let Some(resized_blocks) = resized_blocks {
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
editor.resize_blocks(
|
||||
resized_blocks,
|
||||
autoscroll_request.map(|(autoscroll, _)| autoscroll),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
if request_layout.can_prepaint() {
|
||||
return self.prepaint(
|
||||
None,
|
||||
_inspector_id,
|
||||
bounds,
|
||||
request_layout,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
} else {
|
||||
debug_panic!(
|
||||
"skipping recursive prepaint at max depth. block layout may be stale."
|
||||
);
|
||||
.unwrap_or_else(|| Ok((Vec::default(), HashMap::default())));
|
||||
let (mut blocks, row_block_types) = match blocks {
|
||||
Ok(blocks) => blocks,
|
||||
Err(resized_blocks) => {
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
editor.resize_blocks(
|
||||
resized_blocks,
|
||||
autoscroll_request.map(|(autoscroll, _)| autoscroll),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
return self.prepaint(None, _inspector_id, bounds, &mut (), window, cx);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let sticky_buffer_header = sticky_header_excerpt.map(|sticky_header_excerpt| {
|
||||
window.with_element_namespace("blocks", |window| {
|
||||
|
||||
@@ -67,7 +67,7 @@ impl<'a> sum_tree::Dimension<'a, GitBlameEntrySummary> for u32 {
|
||||
struct GitBlameBuffer {
|
||||
entries: SumTree<GitBlameEntry>,
|
||||
buffer_snapshot: BufferSnapshot,
|
||||
buffer_edits: text::Subscription<usize>,
|
||||
buffer_edits: text::Subscription,
|
||||
commit_details: HashMap<Oid, ParsedCommitMessage>,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use crate::{Editor, RangeToAnchorExt};
|
||||
use gpui::{Context, HighlightStyle, Window};
|
||||
use language::CursorShape;
|
||||
use multi_buffer::MultiBufferOffset;
|
||||
use theme::ActiveTheme;
|
||||
|
||||
enum MatchingBracketHighlight {}
|
||||
@@ -16,7 +15,7 @@ impl Editor {
|
||||
|
||||
let snapshot = self.snapshot(window, cx);
|
||||
let buffer_snapshot = snapshot.buffer_snapshot();
|
||||
let newest_selection = self.selections.newest::<MultiBufferOffset>(&snapshot);
|
||||
let newest_selection = self.selections.newest::<usize>(&snapshot);
|
||||
// Don't highlight brackets if the selection isn't empty
|
||||
if !newest_selection.is_empty() {
|
||||
return;
|
||||
|
||||
@@ -738,7 +738,6 @@ mod tests {
|
||||
use gpui::Modifiers;
|
||||
use indoc::indoc;
|
||||
use lsp::request::{GotoDefinition, GotoTypeDefinition};
|
||||
use multi_buffer::MultiBufferOffset;
|
||||
use settings::InlayHintSettingsContent;
|
||||
use util::{assert_set_eq, path};
|
||||
use workspace::item::Item;
|
||||
@@ -1068,8 +1067,8 @@ mod tests {
|
||||
.clone();
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
let snapshot = editor.buffer().read(cx).snapshot(cx);
|
||||
let anchor_range = snapshot.anchor_before(MultiBufferOffset(selection_range.start))
|
||||
..snapshot.anchor_after(MultiBufferOffset(selection_range.end));
|
||||
let anchor_range = snapshot.anchor_before(selection_range.start)
|
||||
..snapshot.anchor_after(selection_range.end);
|
||||
editor.change_selections(Default::default(), window, cx, |s| {
|
||||
s.set_pending_anchor_range(anchor_range, crate::SelectMode::Character)
|
||||
});
|
||||
@@ -1123,7 +1122,7 @@ mod tests {
|
||||
}
|
||||
"})[0]
|
||||
.start;
|
||||
let hint_position = cx.to_lsp(MultiBufferOffset(hint_start_offset));
|
||||
let hint_position = cx.to_lsp(hint_start_offset);
|
||||
let target_range = cx.lsp_range(indoc! {"
|
||||
struct «TestStruct»;
|
||||
|
||||
@@ -1180,8 +1179,8 @@ mod tests {
|
||||
.unwrap();
|
||||
let midpoint = cx.update_editor(|editor, window, cx| {
|
||||
let snapshot = editor.snapshot(window, cx);
|
||||
let previous_valid = MultiBufferOffset(inlay_range.start).to_display_point(&snapshot);
|
||||
let next_valid = MultiBufferOffset(inlay_range.end).to_display_point(&snapshot);
|
||||
let previous_valid = inlay_range.start.to_display_point(&snapshot);
|
||||
let next_valid = inlay_range.end.to_display_point(&snapshot);
|
||||
assert_eq!(previous_valid.row(), next_valid.row());
|
||||
assert!(previous_valid.column() < next_valid.column());
|
||||
DisplayPoint::new(
|
||||
@@ -1204,7 +1203,7 @@ mod tests {
|
||||
let buffer_snapshot = editor.buffer().update(cx, |buffer, cx| buffer.snapshot(cx));
|
||||
let expected_highlight = InlayHighlight {
|
||||
inlay: InlayId::Hint(0),
|
||||
inlay_position: buffer_snapshot.anchor_after(MultiBufferOffset(inlay_range.start)),
|
||||
inlay_position: buffer_snapshot.anchor_after(inlay_range.start),
|
||||
range: 0..hint_label.len(),
|
||||
};
|
||||
assert_set_eq!(actual_highlights, vec![&expected_highlight]);
|
||||
|
||||
@@ -17,7 +17,7 @@ use itertools::Itertools;
|
||||
use language::{DiagnosticEntry, Language, LanguageRegistry};
|
||||
use lsp::DiagnosticSeverity;
|
||||
use markdown::{Markdown, MarkdownElement, MarkdownStyle};
|
||||
use multi_buffer::{MultiBufferOffset, ToOffset, ToPoint};
|
||||
use multi_buffer::{ToOffset, ToPoint};
|
||||
use project::{HoverBlock, HoverBlockKind, InlayHintLabelPart};
|
||||
use settings::Settings;
|
||||
use std::{borrow::Cow, cell::RefCell};
|
||||
@@ -106,7 +106,7 @@ pub fn find_hovered_hint_part(
|
||||
hovered_offset: InlayOffset,
|
||||
) -> Option<(InlayHintLabelPart, Range<InlayOffset>)> {
|
||||
if hovered_offset >= hint_start {
|
||||
let mut hovered_character = hovered_offset - hint_start;
|
||||
let mut hovered_character = (hovered_offset - hint_start).0;
|
||||
let mut part_start = hint_start;
|
||||
for part in label_parts {
|
||||
let part_len = part.value.chars().count();
|
||||
@@ -316,12 +316,12 @@ fn show_hover(
|
||||
} else {
|
||||
snapshot
|
||||
.buffer_snapshot()
|
||||
.diagnostics_with_buffer_ids_in_range::<MultiBufferOffset>(offset..offset)
|
||||
.diagnostics_with_buffer_ids_in_range::<usize>(offset..offset)
|
||||
.filter(|(_, diagnostic)| {
|
||||
Some(diagnostic.diagnostic.group_id) != active_group_id
|
||||
})
|
||||
// Find the entry with the most specific range
|
||||
.min_by_key(|(_, entry)| entry.range.end - entry.range.start)
|
||||
.min_by_key(|(_, entry)| entry.range.len())
|
||||
};
|
||||
|
||||
let diagnostic_popover = if let Some((buffer_id, local_diagnostic)) = local_diagnostic {
|
||||
@@ -893,6 +893,7 @@ impl InfoPopover {
|
||||
*keyboard_grace = false;
|
||||
cx.stop_propagation();
|
||||
})
|
||||
.p_2()
|
||||
.when_some(self.parsed_content.clone(), |this, markdown| {
|
||||
this.child(
|
||||
div()
|
||||
@@ -908,8 +909,7 @@ impl InfoPopover {
|
||||
copy_button_on_hover: false,
|
||||
border: false,
|
||||
})
|
||||
.on_url_click(open_markdown_url)
|
||||
.p_2(),
|
||||
.on_url_click(open_markdown_url),
|
||||
),
|
||||
)
|
||||
.custom_scrollbars(
|
||||
@@ -1633,7 +1633,7 @@ mod tests {
|
||||
}
|
||||
"})[0]
|
||||
.start;
|
||||
let hint_position = cx.to_lsp(MultiBufferOffset(hint_start_offset));
|
||||
let hint_position = cx.to_lsp(hint_start_offset);
|
||||
let new_type_target_range = cx.lsp_range(indoc! {"
|
||||
struct TestStruct;
|
||||
|
||||
@@ -1708,8 +1708,8 @@ mod tests {
|
||||
.unwrap();
|
||||
let new_type_hint_part_hover_position = cx.update_editor(|editor, window, cx| {
|
||||
let snapshot = editor.snapshot(window, cx);
|
||||
let previous_valid = MultiBufferOffset(inlay_range.start).to_display_point(&snapshot);
|
||||
let next_valid = MultiBufferOffset(inlay_range.end).to_display_point(&snapshot);
|
||||
let previous_valid = inlay_range.start.to_display_point(&snapshot);
|
||||
let next_valid = inlay_range.end.to_display_point(&snapshot);
|
||||
assert_eq!(previous_valid.row(), next_valid.row());
|
||||
assert!(previous_valid.column() < next_valid.column());
|
||||
let exact_unclipped = DisplayPoint::new(
|
||||
@@ -1819,8 +1819,7 @@ mod tests {
|
||||
popover.symbol_range,
|
||||
RangeInEditor::Inlay(InlayHighlight {
|
||||
inlay: InlayId::Hint(0),
|
||||
inlay_position: buffer_snapshot
|
||||
.anchor_after(MultiBufferOffset(inlay_range.start)),
|
||||
inlay_position: buffer_snapshot.anchor_after(inlay_range.start),
|
||||
range: ": ".len()..": ".len() + new_type_label.len(),
|
||||
}),
|
||||
"Popover range should match the new type label part"
|
||||
@@ -1833,8 +1832,8 @@ mod tests {
|
||||
|
||||
let struct_hint_part_hover_position = cx.update_editor(|editor, window, cx| {
|
||||
let snapshot = editor.snapshot(window, cx);
|
||||
let previous_valid = MultiBufferOffset(inlay_range.start).to_display_point(&snapshot);
|
||||
let next_valid = MultiBufferOffset(inlay_range.end).to_display_point(&snapshot);
|
||||
let previous_valid = inlay_range.start.to_display_point(&snapshot);
|
||||
let next_valid = inlay_range.end.to_display_point(&snapshot);
|
||||
assert_eq!(previous_valid.row(), next_valid.row());
|
||||
assert!(previous_valid.column() < next_valid.column());
|
||||
let exact_unclipped = DisplayPoint::new(
|
||||
@@ -1874,8 +1873,7 @@ mod tests {
|
||||
popover.symbol_range,
|
||||
RangeInEditor::Inlay(InlayHighlight {
|
||||
inlay: InlayId::Hint(0),
|
||||
inlay_position: buffer_snapshot
|
||||
.anchor_after(MultiBufferOffset(inlay_range.start)),
|
||||
inlay_position: buffer_snapshot.anchor_after(inlay_range.start),
|
||||
range: ": ".len() + new_type_label.len() + "<".len()
|
||||
..": ".len() + new_type_label.len() + "<".len() + struct_label.len(),
|
||||
}),
|
||||
|
||||
@@ -645,9 +645,9 @@ impl Editor {
|
||||
)
|
||||
{
|
||||
let highlight_start =
|
||||
(part_range.start - hint_start) + extra_shift_left;
|
||||
(part_range.start - hint_start).0 + extra_shift_left;
|
||||
let highlight_end =
|
||||
(part_range.end - hint_start) + extra_shift_right;
|
||||
(part_range.end - hint_start).0 + extra_shift_right;
|
||||
let highlight = InlayHighlight {
|
||||
inlay: hovered_hint.id,
|
||||
inlay_position: hovered_hint.position,
|
||||
@@ -948,7 +948,7 @@ pub mod tests {
|
||||
use language::{Language, LanguageConfig, LanguageMatcher};
|
||||
use languages::rust_lang;
|
||||
use lsp::FakeLanguageServer;
|
||||
use multi_buffer::{MultiBuffer, MultiBufferOffset};
|
||||
use multi_buffer::MultiBuffer;
|
||||
use parking_lot::Mutex;
|
||||
use pretty_assertions::assert_eq;
|
||||
use project::{FakeFs, Project};
|
||||
@@ -1029,7 +1029,7 @@ pub mod tests {
|
||||
editor
|
||||
.update(cx, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(13)..MultiBufferOffset(13)])
|
||||
s.select_ranges([13..13])
|
||||
});
|
||||
editor.handle_input("some change", window, cx);
|
||||
})
|
||||
@@ -1429,7 +1429,7 @@ pub mod tests {
|
||||
rs_editor
|
||||
.update(cx, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(13)..MultiBufferOffset(13)])
|
||||
s.select_ranges([13..13])
|
||||
});
|
||||
editor.handle_input("some rs change", window, cx);
|
||||
})
|
||||
@@ -1461,7 +1461,7 @@ pub mod tests {
|
||||
md_editor
|
||||
.update(cx, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(13)..MultiBufferOffset(13)])
|
||||
s.select_ranges([13..13])
|
||||
});
|
||||
editor.handle_input("some md change", window, cx);
|
||||
})
|
||||
@@ -1909,7 +1909,7 @@ pub mod tests {
|
||||
editor
|
||||
.update(cx, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(13)..MultiBufferOffset(13)])
|
||||
s.select_ranges([13..13])
|
||||
});
|
||||
editor.handle_input(change_after_opening, window, cx);
|
||||
})
|
||||
@@ -1955,7 +1955,7 @@ pub mod tests {
|
||||
task_editor
|
||||
.update(&mut cx, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(13)..MultiBufferOffset(13)])
|
||||
s.select_ranges([13..13])
|
||||
});
|
||||
editor.handle_input(async_later_change, window, cx);
|
||||
})
|
||||
@@ -2706,7 +2706,7 @@ let c = 3;"#
|
||||
let mut editor =
|
||||
Editor::for_multibuffer(multi_buffer, Some(project.clone()), window, cx);
|
||||
editor.change_selections(SelectionEffects::default(), window, cx, |s| {
|
||||
s.select_ranges([MultiBufferOffset(0)..MultiBufferOffset(0)])
|
||||
s.select_ranges([0..0])
|
||||
});
|
||||
editor
|
||||
});
|
||||
|
||||
@@ -21,7 +21,6 @@ use language::{
|
||||
SelectionGoal, proto::serialize_anchor as serialize_text_anchor,
|
||||
};
|
||||
use lsp::DiagnosticSeverity;
|
||||
use multi_buffer::MultiBufferOffset;
|
||||
use project::{
|
||||
Project, ProjectItem as _, ProjectPath, lsp_store::FormatTrigger,
|
||||
project_settings::ProjectSettings, search::SearchQuery,
|
||||
@@ -588,21 +587,6 @@ fn deserialize_anchor(buffer: &MultiBufferSnapshot, anchor: proto::EditorAnchor)
|
||||
impl Item for Editor {
|
||||
type Event = EditorEvent;
|
||||
|
||||
fn act_as_type<'a>(
|
||||
&'a self,
|
||||
type_id: TypeId,
|
||||
self_handle: &'a Entity<Self>,
|
||||
cx: &'a App,
|
||||
) -> Option<gpui::AnyEntity> {
|
||||
if TypeId::of::<Self>() == type_id {
|
||||
Some(self_handle.clone().into())
|
||||
} else if TypeId::of::<MultiBuffer>() == type_id {
|
||||
Some(self_handle.read(cx).buffer.clone().into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn navigate(
|
||||
&mut self,
|
||||
data: Box<dyn std::any::Any>,
|
||||
@@ -957,7 +941,7 @@ impl Item for Editor {
|
||||
|
||||
fn breadcrumbs(&self, variant: &Theme, cx: &App) -> Option<Vec<BreadcrumbText>> {
|
||||
let cursor = self.selections.newest_anchor().head();
|
||||
let multibuffer = self.buffer().read(cx);
|
||||
let multibuffer = &self.buffer().read(cx);
|
||||
let (buffer_id, symbols) = multibuffer
|
||||
.read(cx)
|
||||
.symbols_containing(cursor, Some(variant.syntax()))?;
|
||||
@@ -1751,7 +1735,7 @@ impl SearchableItem for Editor {
|
||||
let mut ranges = Vec::new();
|
||||
|
||||
let search_within_ranges = if search_within_ranges.is_empty() {
|
||||
vec![buffer.anchor_before(MultiBufferOffset(0))..buffer.anchor_after(buffer.len())]
|
||||
vec![buffer.anchor_before(0)..buffer.anchor_after(buffer.len())]
|
||||
} else {
|
||||
search_within_ranges
|
||||
};
|
||||
@@ -1762,10 +1746,7 @@ impl SearchableItem for Editor {
|
||||
{
|
||||
ranges.extend(
|
||||
query
|
||||
.search(
|
||||
search_buffer,
|
||||
Some(search_range.start.0..search_range.end.0),
|
||||
)
|
||||
.search(search_buffer, Some(search_range.clone()))
|
||||
.await
|
||||
.into_iter()
|
||||
.map(|match_range| {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use anyhow::{Context as _, Result, anyhow};
|
||||
use collections::HashMap;
|
||||
use gpui::{Context, Entity, Window};
|
||||
use multi_buffer::{BufferOffset, MultiBuffer, ToOffset};
|
||||
use multi_buffer::{MultiBuffer, ToOffset};
|
||||
use std::ops::Range;
|
||||
use util::ResultExt as _;
|
||||
|
||||
@@ -546,10 +546,9 @@ pub(crate) fn handle_from(
|
||||
if edit_range_offset.start != edit_range_offset.end {
|
||||
continue;
|
||||
}
|
||||
if let Some(selection) = buffer_selection_map.get_mut(&(
|
||||
BufferOffset(edit_range_offset.start),
|
||||
BufferOffset(edit_range_offset.end),
|
||||
)) {
|
||||
if let Some(selection) =
|
||||
buffer_selection_map.get_mut(&(edit_range_offset.start, edit_range_offset.end))
|
||||
{
|
||||
if selection.0.head().bias() != text::Bias::Right
|
||||
|| selection.0.tail().bias() != text::Bias::Right
|
||||
{
|
||||
@@ -622,7 +621,7 @@ mod jsx_tag_autoclose_tests {
|
||||
use super::*;
|
||||
use gpui::{AppContext as _, TestAppContext};
|
||||
use languages::language;
|
||||
use multi_buffer::{ExcerptRange, MultiBufferOffset};
|
||||
use multi_buffer::ExcerptRange;
|
||||
use text::Selection;
|
||||
|
||||
async fn test_setup(cx: &mut TestAppContext) -> EditorTestContext {
|
||||
@@ -843,9 +842,9 @@ mod jsx_tag_autoclose_tests {
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
|
||||
selections.select(vec![
|
||||
Selection::from_offset(MultiBufferOffset(4)),
|
||||
Selection::from_offset(MultiBufferOffset(9)),
|
||||
Selection::from_offset(MultiBufferOffset(15)),
|
||||
Selection::from_offset(4),
|
||||
Selection::from_offset(9),
|
||||
Selection::from_offset(15),
|
||||
])
|
||||
})
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use collections::HashMap;
|
||||
use gpui::{AppContext, Context, Window};
|
||||
use itertools::Itertools;
|
||||
use multi_buffer::MultiBufferOffset;
|
||||
use std::{ops::Range, time::Duration};
|
||||
use text::{AnchorRangeExt, BufferId, ToPoint};
|
||||
use util::ResultExt;
|
||||
@@ -61,9 +60,7 @@ pub(super) fn refresh_linked_ranges(
|
||||
editor
|
||||
.update(cx, |editor, cx| {
|
||||
let display_snapshot = editor.display_snapshot(cx);
|
||||
let selections = editor
|
||||
.selections
|
||||
.all::<MultiBufferOffset>(&display_snapshot);
|
||||
let selections = editor.selections.all::<usize>(&display_snapshot);
|
||||
let snapshot = display_snapshot.buffer_snapshot();
|
||||
let buffer = editor.buffer.read(cx);
|
||||
for selection in selections {
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::{
|
||||
};
|
||||
use gpui::{Pixels, WindowTextSystem};
|
||||
use language::{CharClassifier, Point};
|
||||
use multi_buffer::{MultiBufferOffset, MultiBufferRow, MultiBufferSnapshot};
|
||||
use multi_buffer::{MultiBufferRow, MultiBufferSnapshot};
|
||||
use serde::Deserialize;
|
||||
use workspace::searchable::Direction;
|
||||
|
||||
@@ -358,28 +358,28 @@ pub fn adjust_greedy_deletion(
|
||||
|
||||
let mut whitespace_sequences = Vec::new();
|
||||
let mut current_offset = trimmed_delete_range.start;
|
||||
let mut whitespace_sequence_length = MultiBufferOffset(0);
|
||||
let mut whitespace_sequence_start = MultiBufferOffset(0);
|
||||
let mut whitespace_sequence_length = 0;
|
||||
let mut whitespace_sequence_start = 0;
|
||||
for ch in map
|
||||
.buffer_snapshot()
|
||||
.text_for_range(trimmed_delete_range.clone())
|
||||
.flat_map(str::chars)
|
||||
{
|
||||
if ch.is_whitespace() {
|
||||
if whitespace_sequence_length == MultiBufferOffset(0) {
|
||||
if whitespace_sequence_length == 0 {
|
||||
whitespace_sequence_start = current_offset;
|
||||
}
|
||||
whitespace_sequence_length += 1;
|
||||
} else {
|
||||
if whitespace_sequence_length >= MultiBufferOffset(2) {
|
||||
if whitespace_sequence_length >= 2 {
|
||||
whitespace_sequences.push((whitespace_sequence_start, current_offset));
|
||||
}
|
||||
whitespace_sequence_start = MultiBufferOffset(0);
|
||||
whitespace_sequence_length = MultiBufferOffset(0);
|
||||
whitespace_sequence_start = 0;
|
||||
whitespace_sequence_length = 0;
|
||||
}
|
||||
current_offset += ch.len_utf8();
|
||||
}
|
||||
if whitespace_sequence_length >= MultiBufferOffset(2) {
|
||||
if whitespace_sequence_length >= 2 {
|
||||
whitespace_sequences.push((whitespace_sequence_start, current_offset));
|
||||
}
|
||||
|
||||
@@ -731,7 +731,7 @@ pub fn find_preceding_boundary_trail(
|
||||
}
|
||||
|
||||
let trail = trail_offset
|
||||
.map(|trail_offset| map.clip_point(trail_offset.to_display_point(map), Bias::Left));
|
||||
.map(|trail_offset: usize| map.clip_point(trail_offset.to_display_point(map), Bias::Left));
|
||||
|
||||
(
|
||||
trail,
|
||||
@@ -779,7 +779,7 @@ pub fn find_boundary_trail(
|
||||
}
|
||||
|
||||
let trail = trail_offset
|
||||
.map(|trail_offset| map.clip_point(trail_offset.to_display_point(map), Bias::Right));
|
||||
.map(|trail_offset: usize| map.clip_point(trail_offset.to_display_point(map), Bias::Right));
|
||||
|
||||
(
|
||||
trail,
|
||||
@@ -810,8 +810,8 @@ pub fn find_boundary_exclusive(
|
||||
/// the [`DisplaySnapshot`]. The offsets are relative to the start of a buffer.
|
||||
pub fn chars_after(
|
||||
map: &DisplaySnapshot,
|
||||
mut offset: MultiBufferOffset,
|
||||
) -> impl Iterator<Item = (char, Range<MultiBufferOffset>)> + '_ {
|
||||
mut offset: usize,
|
||||
) -> impl Iterator<Item = (char, Range<usize>)> + '_ {
|
||||
map.buffer_snapshot().chars_at(offset).map(move |ch| {
|
||||
let before = offset;
|
||||
offset += ch.len_utf8();
|
||||
@@ -824,8 +824,8 @@ pub fn chars_after(
|
||||
/// the [`DisplaySnapshot`]. The offsets are relative to the start of a buffer.
|
||||
pub fn chars_before(
|
||||
map: &DisplaySnapshot,
|
||||
mut offset: MultiBufferOffset,
|
||||
) -> impl Iterator<Item = (char, Range<MultiBufferOffset>)> + '_ {
|
||||
mut offset: usize,
|
||||
) -> impl Iterator<Item = (char, Range<usize>)> + '_ {
|
||||
map.buffer_snapshot()
|
||||
.reversed_chars_at(offset)
|
||||
.map(move |ch| {
|
||||
@@ -1018,9 +1018,8 @@ mod tests {
|
||||
|
||||
// add all kinds of inlays between two word boundaries: we should be able to cross them all, when looking for another boundary
|
||||
let mut id = 0;
|
||||
let inlays = (0..buffer_snapshot.len().0)
|
||||
let inlays = (0..buffer_snapshot.len())
|
||||
.flat_map(|offset| {
|
||||
let offset = MultiBufferOffset(offset);
|
||||
[
|
||||
Inlay::edit_prediction(
|
||||
post_inc(&mut id),
|
||||
@@ -1059,7 +1058,7 @@ mod tests {
|
||||
),
|
||||
snapshot
|
||||
.buffer_snapshot()
|
||||
.offset_to_point(MultiBufferOffset(5))
|
||||
.offset_to_point(5)
|
||||
.to_display_point(&snapshot),
|
||||
"Should not stop at inlays when looking for boundaries"
|
||||
);
|
||||
|
||||
@@ -46,12 +46,20 @@ impl ScrollAnchor {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn near_end(&self, snapshot: &DisplaySnapshot) -> bool {
|
||||
let editor_length = snapshot.max_point().row().as_f64();
|
||||
let scroll_top = self.anchor.to_display_point(snapshot).row().as_f64();
|
||||
(scroll_top - editor_length).abs() < 300.0
|
||||
}
|
||||
|
||||
pub fn scroll_position(&self, snapshot: &DisplaySnapshot) -> gpui::Point<ScrollOffset> {
|
||||
self.offset.apply_along(Axis::Vertical, |offset| {
|
||||
if self.anchor == Anchor::min() {
|
||||
0.
|
||||
} else {
|
||||
dbg!(snapshot.max_point().row().as_f64());
|
||||
let scroll_top = self.anchor.to_display_point(snapshot).row().as_f64();
|
||||
dbg!(scroll_top, offset);
|
||||
(offset + scroll_top).max(0.)
|
||||
}
|
||||
})
|
||||
@@ -243,6 +251,11 @@ impl ScrollManager {
|
||||
}
|
||||
}
|
||||
};
|
||||
let near_end = self.anchor.near_end(map);
|
||||
// // TODO call load more here
|
||||
// if near_end {
|
||||
// cx.read();
|
||||
// }
|
||||
|
||||
let scroll_top_row = DisplayRow(scroll_top as u32);
|
||||
let scroll_top_buffer_point = map
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
use std::{
|
||||
cmp, fmt, iter, mem,
|
||||
ops::{AddAssign, Deref, DerefMut, Range, Sub},
|
||||
ops::{Deref, DerefMut, Range, Sub},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use collections::HashMap;
|
||||
use gpui::Pixels;
|
||||
use itertools::Itertools as _;
|
||||
use language::{Bias, Point, Selection, SelectionGoal};
|
||||
use multi_buffer::{MultiBufferDimension, MultiBufferOffset};
|
||||
use language::{Bias, Point, Selection, SelectionGoal, TextDimension};
|
||||
use util::post_inc;
|
||||
|
||||
use crate::{
|
||||
Anchor, DisplayPoint, DisplayRow, ExcerptId, MultiBufferSnapshot, SelectMode, ToOffset,
|
||||
ToPoint,
|
||||
display_map::{DisplaySnapshot, ToDisplayPoint},
|
||||
movement::TextLayoutDetails,
|
||||
};
|
||||
@@ -97,7 +97,7 @@ impl SelectionsCollection {
|
||||
if self.pending.is_none() {
|
||||
self.disjoint_anchors_arc()
|
||||
} else {
|
||||
let all_offset_selections = self.all::<MultiBufferOffset>(snapshot);
|
||||
let all_offset_selections = self.all::<usize>(snapshot);
|
||||
all_offset_selections
|
||||
.into_iter()
|
||||
.map(|selection| selection_to_anchor_selection(selection, snapshot))
|
||||
@@ -113,10 +113,10 @@ impl SelectionsCollection {
|
||||
self.pending.as_mut().map(|pending| &mut pending.selection)
|
||||
}
|
||||
|
||||
pub fn pending<D>(&self, snapshot: &DisplaySnapshot) -> Option<Selection<D>>
|
||||
where
|
||||
D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord,
|
||||
{
|
||||
pub fn pending<D: TextDimension + Ord + Sub<D, Output = D>>(
|
||||
&self,
|
||||
snapshot: &DisplaySnapshot,
|
||||
) -> Option<Selection<D>> {
|
||||
resolve_selections_wrapping_blocks(self.pending_anchor(), &snapshot).next()
|
||||
}
|
||||
|
||||
@@ -124,9 +124,9 @@ impl SelectionsCollection {
|
||||
self.pending.as_ref().map(|pending| pending.mode.clone())
|
||||
}
|
||||
|
||||
pub fn all<D>(&self, snapshot: &DisplaySnapshot) -> Vec<Selection<D>>
|
||||
pub fn all<'a, D>(&self, snapshot: &DisplaySnapshot) -> Vec<Selection<D>>
|
||||
where
|
||||
D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord,
|
||||
D: 'a + TextDimension + Ord + Sub<D, Output = D>,
|
||||
{
|
||||
let disjoint_anchors = &self.disjoint;
|
||||
let mut disjoint =
|
||||
@@ -204,13 +204,13 @@ impl SelectionsCollection {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn disjoint_in_range<D>(
|
||||
pub fn disjoint_in_range<'a, D>(
|
||||
&self,
|
||||
range: Range<Anchor>,
|
||||
snapshot: &DisplaySnapshot,
|
||||
) -> Vec<Selection<D>>
|
||||
where
|
||||
D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord + std::fmt::Debug,
|
||||
D: 'a + TextDimension + Ord + Sub<D, Output = D> + std::fmt::Debug,
|
||||
{
|
||||
let start_ix = match self
|
||||
.disjoint
|
||||
@@ -267,10 +267,10 @@ impl SelectionsCollection {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn newest<D>(&self, snapshot: &DisplaySnapshot) -> Selection<D>
|
||||
where
|
||||
D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord,
|
||||
{
|
||||
pub fn newest<D: TextDimension + Ord + Sub<D, Output = D>>(
|
||||
&self,
|
||||
snapshot: &DisplaySnapshot,
|
||||
) -> Selection<D> {
|
||||
resolve_selections_wrapping_blocks([self.newest_anchor()], &snapshot)
|
||||
.next()
|
||||
.unwrap()
|
||||
@@ -290,10 +290,10 @@ impl SelectionsCollection {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn oldest<D>(&self, snapshot: &DisplaySnapshot) -> Selection<D>
|
||||
where
|
||||
D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord,
|
||||
{
|
||||
pub fn oldest<D: TextDimension + Ord + Sub<D, Output = D>>(
|
||||
&self,
|
||||
snapshot: &DisplaySnapshot,
|
||||
) -> Selection<D> {
|
||||
resolve_selections_wrapping_blocks([self.oldest_anchor()], &snapshot)
|
||||
.next()
|
||||
.unwrap()
|
||||
@@ -306,27 +306,27 @@ impl SelectionsCollection {
|
||||
.unwrap_or_else(|| self.disjoint.first().cloned().unwrap())
|
||||
}
|
||||
|
||||
pub fn first<D>(&self, snapshot: &DisplaySnapshot) -> Selection<D>
|
||||
where
|
||||
D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord,
|
||||
{
|
||||
pub fn first<D: TextDimension + Ord + Sub<D, Output = D>>(
|
||||
&self,
|
||||
snapshot: &DisplaySnapshot,
|
||||
) -> Selection<D> {
|
||||
self.all(snapshot).first().unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn last<D>(&self, snapshot: &DisplaySnapshot) -> Selection<D>
|
||||
where
|
||||
D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord,
|
||||
{
|
||||
pub fn last<D: TextDimension + Ord + Sub<D, Output = D>>(
|
||||
&self,
|
||||
snapshot: &DisplaySnapshot,
|
||||
) -> Selection<D> {
|
||||
self.all(snapshot).last().unwrap().clone()
|
||||
}
|
||||
|
||||
/// Returns a list of (potentially backwards!) ranges representing the selections.
|
||||
/// Useful for test assertions, but prefer `.all()` instead.
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn ranges<D>(&self, snapshot: &DisplaySnapshot) -> Vec<Range<D>>
|
||||
where
|
||||
D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord,
|
||||
{
|
||||
pub fn ranges<D: TextDimension + Ord + Sub<D, Output = D>>(
|
||||
&self,
|
||||
snapshot: &DisplaySnapshot,
|
||||
) -> Vec<Range<D>> {
|
||||
self.all::<D>(snapshot)
|
||||
.iter()
|
||||
.map(|s| {
|
||||
@@ -509,7 +509,7 @@ impl<'snap, 'a> MutableSelectionsCollection<'snap, 'a> {
|
||||
};
|
||||
|
||||
if filtered_selections.is_empty() {
|
||||
let default_anchor = self.snapshot.anchor_before(MultiBufferOffset(0));
|
||||
let default_anchor = self.snapshot.anchor_before(0);
|
||||
self.collection.disjoint = Arc::from([Selection {
|
||||
id: post_inc(&mut self.collection.next_selection_id),
|
||||
start: default_anchor,
|
||||
@@ -590,7 +590,7 @@ impl<'snap, 'a> MutableSelectionsCollection<'snap, 'a> {
|
||||
|
||||
pub fn insert_range<T>(&mut self, range: Range<T>)
|
||||
where
|
||||
T: ToOffset,
|
||||
T: 'a + ToOffset + ToPoint + TextDimension + Ord + Sub<T, Output = T> + std::marker::Copy,
|
||||
{
|
||||
let display_map = self.display_snapshot();
|
||||
let mut selections = self.collection.all(&display_map);
|
||||
@@ -656,8 +656,7 @@ impl<'snap, 'a> MutableSelectionsCollection<'snap, 'a> {
|
||||
pub fn select_anchors(&mut self, selections: Vec<Selection<Anchor>>) {
|
||||
let map = self.display_snapshot();
|
||||
let resolved_selections =
|
||||
resolve_selections_wrapping_blocks::<MultiBufferOffset, _>(&selections, &map)
|
||||
.collect::<Vec<_>>();
|
||||
resolve_selections_wrapping_blocks::<usize, _>(&selections, &map).collect::<Vec<_>>();
|
||||
self.select(resolved_selections);
|
||||
}
|
||||
|
||||
@@ -674,7 +673,7 @@ impl<'snap, 'a> MutableSelectionsCollection<'snap, 'a> {
|
||||
|
||||
fn select_offset_ranges<I>(&mut self, ranges: I)
|
||||
where
|
||||
I: IntoIterator<Item = Range<MultiBufferOffset>>,
|
||||
I: IntoIterator<Item = Range<usize>>,
|
||||
{
|
||||
let selections = ranges
|
||||
.into_iter()
|
||||
@@ -809,13 +808,13 @@ impl<'snap, 'a> MutableSelectionsCollection<'snap, 'a> {
|
||||
|
||||
pub fn move_offsets_with(
|
||||
&mut self,
|
||||
mut move_selection: impl FnMut(&MultiBufferSnapshot, &mut Selection<MultiBufferOffset>),
|
||||
mut move_selection: impl FnMut(&MultiBufferSnapshot, &mut Selection<usize>),
|
||||
) {
|
||||
let mut changed = false;
|
||||
let display_map = self.display_snapshot();
|
||||
let selections = self
|
||||
.collection
|
||||
.all::<MultiBufferOffset>(&display_map)
|
||||
.all::<usize>(&display_map)
|
||||
.into_iter()
|
||||
.map(|selection| {
|
||||
let mut moved_selection = selection.clone();
|
||||
@@ -939,7 +938,7 @@ impl<'snap, 'a> MutableSelectionsCollection<'snap, 'a> {
|
||||
let map = self.display_snapshot();
|
||||
let resolved_selections =
|
||||
resolve_selections_wrapping_blocks(adjusted_disjoint.iter(), &map).collect();
|
||||
self.select::<MultiBufferOffset>(resolved_selections);
|
||||
self.select::<usize>(resolved_selections);
|
||||
}
|
||||
|
||||
if let Some(pending) = pending.as_mut() {
|
||||
@@ -982,7 +981,7 @@ impl DerefMut for MutableSelectionsCollection<'_, '_> {
|
||||
}
|
||||
|
||||
fn selection_to_anchor_selection(
|
||||
selection: Selection<MultiBufferOffset>,
|
||||
selection: Selection<usize>,
|
||||
buffer: &MultiBufferSnapshot,
|
||||
) -> Selection<Anchor> {
|
||||
let end_bias = if selection.start == selection.end {
|
||||
@@ -1055,7 +1054,7 @@ fn resolve_selections_display<'a>(
|
||||
coalesce_selections(selections)
|
||||
}
|
||||
|
||||
/// Resolves the passed in anchors to [`MultiBufferDimension`]s `D`
|
||||
/// Resolves the passed in anchors to [`TextDimension`]s `D`
|
||||
/// wrapping around blocks inbetween.
|
||||
///
|
||||
/// # Panics
|
||||
@@ -1066,7 +1065,7 @@ pub(crate) fn resolve_selections_wrapping_blocks<'a, D, I>(
|
||||
map: &'a DisplaySnapshot,
|
||||
) -> impl 'a + Iterator<Item = Selection<D>>
|
||||
where
|
||||
D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord,
|
||||
D: TextDimension + Ord + Sub<D, Output = D>,
|
||||
I: 'a + IntoIterator<Item = &'a Selection<Anchor>>,
|
||||
{
|
||||
// Transforms `Anchor -> DisplayPoint -> Point -> DisplayPoint -> D`
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use crate::actions::ShowSignatureHelp;
|
||||
use crate::hover_popover::open_markdown_url;
|
||||
use crate::{BufferOffset, Editor, EditorSettings, ToggleAutoSignatureHelp, hover_markdown_style};
|
||||
use crate::{Editor, EditorSettings, ToggleAutoSignatureHelp, hover_markdown_style};
|
||||
use gpui::{
|
||||
App, Context, Entity, HighlightStyle, MouseButton, ScrollHandle, Size, StyledText, Task,
|
||||
TextStyle, Window, combine_highlights,
|
||||
};
|
||||
use language::BufferSnapshot;
|
||||
use markdown::{Markdown, MarkdownElement};
|
||||
use multi_buffer::{Anchor, MultiBufferOffset, ToOffset};
|
||||
use multi_buffer::{Anchor, ToOffset};
|
||||
use settings::Settings;
|
||||
use std::ops::Range;
|
||||
use text::Rope;
|
||||
@@ -82,9 +82,7 @@ impl Editor {
|
||||
if !(self.signature_help_state.is_shown() || self.auto_signature_help_enabled(cx)) {
|
||||
return false;
|
||||
}
|
||||
let newest_selection = self
|
||||
.selections
|
||||
.newest::<MultiBufferOffset>(&self.display_snapshot(cx));
|
||||
let newest_selection = self.selections.newest::<usize>(&self.display_snapshot(cx));
|
||||
let head = newest_selection.head();
|
||||
|
||||
if !newest_selection.is_empty() && head != newest_selection.tail() {
|
||||
@@ -94,14 +92,14 @@ impl Editor {
|
||||
}
|
||||
|
||||
let buffer_snapshot = self.buffer().read(cx).snapshot(cx);
|
||||
let bracket_range = |position: MultiBufferOffset| match (position, position + 1usize) {
|
||||
(MultiBufferOffset(0), b) if b <= buffer_snapshot.len() => MultiBufferOffset(0)..b,
|
||||
(MultiBufferOffset(0), b) => MultiBufferOffset(0)..b - 1,
|
||||
let bracket_range = |position: usize| match (position, position + 1) {
|
||||
(0, b) if b <= buffer_snapshot.len() => 0..b,
|
||||
(0, b) => 0..b - 1,
|
||||
(a, b) if b <= buffer_snapshot.len() => a - 1..b,
|
||||
(a, b) => a - 1..b - 1,
|
||||
};
|
||||
let not_quote_like_brackets =
|
||||
|buffer: &BufferSnapshot, start: Range<BufferOffset>, end: Range<BufferOffset>| {
|
||||
|buffer: &BufferSnapshot, start: Range<usize>, end: Range<usize>| {
|
||||
let text_start = buffer.text_for_range(start).collect::<String>();
|
||||
let text_end = buffer.text_for_range(end).collect::<String>();
|
||||
QUOTE_PAIRS
|
||||
|
||||
@@ -16,7 +16,7 @@ use gpui::{
|
||||
AppContext as _, Context, Entity, EntityId, Font, FontFeatures, FontStyle, FontWeight, Pixels,
|
||||
VisualTestContext, Window, font, size,
|
||||
};
|
||||
use multi_buffer::{MultiBufferOffset, ToPoint};
|
||||
use multi_buffer::ToPoint;
|
||||
use pretty_assertions::assert_eq;
|
||||
use project::{Project, project_settings::DiagnosticSeverity};
|
||||
use ui::{App, BorrowAppContext, px};
|
||||
@@ -78,7 +78,7 @@ pub fn marked_display_snapshot(
|
||||
let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||
let markers = markers
|
||||
.into_iter()
|
||||
.map(|offset| MultiBufferOffset(offset).to_display_point(&snapshot))
|
||||
.map(|offset| offset.to_display_point(&snapshot))
|
||||
.collect();
|
||||
|
||||
(snapshot, markers)
|
||||
@@ -94,11 +94,7 @@ pub fn select_ranges(
|
||||
let (unmarked_text, text_ranges) = marked_text_ranges(marked_text, true);
|
||||
assert_eq!(editor.text(cx), unmarked_text);
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges(
|
||||
text_ranges
|
||||
.into_iter()
|
||||
.map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end)),
|
||||
)
|
||||
s.select_ranges(text_ranges)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -112,12 +108,7 @@ pub fn assert_text_with_selections(
|
||||
assert_eq!(editor.text(cx), unmarked_text, "text doesn't match");
|
||||
let actual = generate_marked_text(
|
||||
&editor.text(cx),
|
||||
&editor
|
||||
.selections
|
||||
.ranges::<MultiBufferOffset>(&editor.display_snapshot(cx))
|
||||
.into_iter()
|
||||
.map(|range| range.start.0..range.end.0)
|
||||
.collect::<Vec<_>>(),
|
||||
&editor.selections.ranges(&editor.display_snapshot(cx)),
|
||||
marked_text.contains("«"),
|
||||
);
|
||||
assert_eq!(actual, marked_text, "Selections don't match");
|
||||
|
||||
@@ -7,7 +7,6 @@ use std::{
|
||||
|
||||
use anyhow::Result;
|
||||
use language::{markdown_lang, rust_lang};
|
||||
use multi_buffer::MultiBufferOffset;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{Editor, ToPoint};
|
||||
@@ -334,38 +333,50 @@ impl EditorLspTestContext {
|
||||
#[track_caller]
|
||||
pub fn lsp_range(&mut self, marked_text: &str) -> lsp::Range {
|
||||
let ranges = self.ranges(marked_text);
|
||||
self.to_lsp_range(MultiBufferOffset(ranges[0].start)..MultiBufferOffset(ranges[0].end))
|
||||
self.to_lsp_range(ranges[0].clone())
|
||||
}
|
||||
|
||||
#[expect(clippy::wrong_self_convention, reason = "This is test code")]
|
||||
pub fn to_lsp_range(&mut self, range: Range<MultiBufferOffset>) -> lsp::Range {
|
||||
use language::ToPointUtf16;
|
||||
pub fn to_lsp_range(&mut self, range: Range<usize>) -> lsp::Range {
|
||||
let snapshot = self.update_editor(|editor, window, cx| editor.snapshot(window, cx));
|
||||
let start_point = range.start.to_point(&snapshot.buffer_snapshot());
|
||||
let end_point = range.end.to_point(&snapshot.buffer_snapshot());
|
||||
|
||||
self.editor(|editor, _, cx| {
|
||||
let buffer = editor.buffer().read(cx);
|
||||
let (start_buffer, start_offset) =
|
||||
buffer.point_to_buffer_offset(start_point, cx).unwrap();
|
||||
let start = point_to_lsp(start_offset.to_point_utf16(&start_buffer.read(cx)));
|
||||
let (end_buffer, end_offset) = buffer.point_to_buffer_offset(end_point, cx).unwrap();
|
||||
let end = point_to_lsp(end_offset.to_point_utf16(&end_buffer.read(cx)));
|
||||
let start = point_to_lsp(
|
||||
buffer
|
||||
.point_to_buffer_offset(start_point, cx)
|
||||
.unwrap()
|
||||
.1
|
||||
.to_point_utf16(&buffer.read(cx)),
|
||||
);
|
||||
let end = point_to_lsp(
|
||||
buffer
|
||||
.point_to_buffer_offset(end_point, cx)
|
||||
.unwrap()
|
||||
.1
|
||||
.to_point_utf16(&buffer.read(cx)),
|
||||
);
|
||||
|
||||
lsp::Range { start, end }
|
||||
})
|
||||
}
|
||||
|
||||
#[expect(clippy::wrong_self_convention, reason = "This is test code")]
|
||||
pub fn to_lsp(&mut self, offset: MultiBufferOffset) -> lsp::Position {
|
||||
use language::ToPointUtf16;
|
||||
|
||||
pub fn to_lsp(&mut self, offset: usize) -> lsp::Position {
|
||||
let snapshot = self.update_editor(|editor, window, cx| editor.snapshot(window, cx));
|
||||
let point = offset.to_point(&snapshot.buffer_snapshot());
|
||||
|
||||
self.editor(|editor, _, cx| {
|
||||
let buffer = editor.buffer().read(cx);
|
||||
let (buffer, offset) = buffer.point_to_buffer_offset(point, cx).unwrap();
|
||||
point_to_lsp(offset.to_point_utf16(&buffer.read(cx)))
|
||||
point_to_lsp(
|
||||
buffer
|
||||
.point_to_buffer_offset(point, cx)
|
||||
.unwrap()
|
||||
.1
|
||||
.to_point_utf16(&buffer.read(cx)),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ use gpui::{
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use language::{Buffer, BufferSnapshot, LanguageRegistry};
|
||||
use multi_buffer::{Anchor, ExcerptRange, MultiBufferOffset, MultiBufferRow};
|
||||
use multi_buffer::{Anchor, ExcerptRange, MultiBufferRow};
|
||||
use parking_lot::RwLock;
|
||||
use project::{FakeFs, Project};
|
||||
use std::{
|
||||
@@ -267,7 +267,7 @@ impl EditorTestContext {
|
||||
let snapshot = self.editor.update_in(&mut self.cx, |editor, window, cx| {
|
||||
editor.snapshot(window, cx)
|
||||
});
|
||||
MultiBufferOffset(ranges[0].start).to_display_point(&snapshot)
|
||||
ranges[0].start.to_display_point(&snapshot)
|
||||
}
|
||||
|
||||
pub fn pixel_position(&mut self, marked_text: &str) -> Point<Pixels> {
|
||||
@@ -373,11 +373,7 @@ impl EditorTestContext {
|
||||
self.editor.update_in(&mut self.cx, |editor, window, cx| {
|
||||
editor.set_text(unmarked_text, window, cx);
|
||||
editor.change_selections(Default::default(), window, cx, |s| {
|
||||
s.select_ranges(
|
||||
selection_ranges
|
||||
.into_iter()
|
||||
.map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end)),
|
||||
)
|
||||
s.select_ranges(selection_ranges)
|
||||
})
|
||||
});
|
||||
state_context
|
||||
@@ -394,11 +390,7 @@ impl EditorTestContext {
|
||||
self.editor.update_in(&mut self.cx, |editor, window, cx| {
|
||||
assert_eq!(editor.text(cx), unmarked_text);
|
||||
editor.change_selections(Default::default(), window, cx, |s| {
|
||||
s.select_ranges(
|
||||
selection_ranges
|
||||
.into_iter()
|
||||
.map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end)),
|
||||
)
|
||||
s.select_ranges(selection_ranges)
|
||||
})
|
||||
});
|
||||
state_context
|
||||
@@ -584,7 +576,6 @@ impl EditorTestContext {
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.map(|range| range.to_offset(&snapshot.buffer_snapshot()))
|
||||
.map(|range| range.start.0..range.end.0)
|
||||
.collect()
|
||||
});
|
||||
assert_set_eq!(actual_ranges, expected_ranges);
|
||||
@@ -600,7 +591,6 @@ impl EditorTestContext {
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.map(|range| range.to_offset(&snapshot.buffer_snapshot()))
|
||||
.map(|range| range.start.0..range.end.0)
|
||||
.collect();
|
||||
assert_set_eq!(actual_ranges, expected_ranges);
|
||||
}
|
||||
@@ -618,16 +608,14 @@ impl EditorTestContext {
|
||||
fn editor_selections(&mut self) -> Vec<Range<usize>> {
|
||||
self.editor
|
||||
.update(&mut self.cx, |editor, cx| {
|
||||
editor
|
||||
.selections
|
||||
.all::<MultiBufferOffset>(&editor.display_snapshot(cx))
|
||||
editor.selections.all::<usize>(&editor.display_snapshot(cx))
|
||||
})
|
||||
.into_iter()
|
||||
.map(|s| {
|
||||
if s.reversed {
|
||||
s.end.0..s.start.0
|
||||
s.end..s.start
|
||||
} else {
|
||||
s.start.0..s.end.0
|
||||
s.start..s.end
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
@@ -723,10 +711,7 @@ pub fn assert_state_with_diff(
|
||||
snapshot.buffer_snapshot().clone(),
|
||||
editor
|
||||
.selections
|
||||
.ranges::<MultiBufferOffset>(&snapshot.display_snapshot)
|
||||
.into_iter()
|
||||
.map(|range| range.start.0..range.end.0)
|
||||
.collect::<Vec<_>>(),
|
||||
.ranges::<usize>(&snapshot.display_snapshot),
|
||||
)
|
||||
});
|
||||
|
||||
|
||||
@@ -1128,7 +1128,6 @@ impl ExtensionStore {
|
||||
}
|
||||
|
||||
if extensions_to_load.is_empty() && extensions_to_unload.is_empty() {
|
||||
self.reload_complete_senders.clear();
|
||||
return Task::ready(());
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user