Compare commits

..

17 Commits

Author SHA1 Message Date
David
9fc72f136d adds rough sketch for loader task 2025-11-27 11:11:03 +01:00
David
f213251e2c comment out unoptimized functions 2025-11-26 11:41:13 +01:00
David
8e79ab6f6a lazy scroll start 2025-11-25 12:36:24 +01:00
David
ecddbd470f load only on click and first n items 2025-11-24 11:21:44 +01:00
David
42787904b0 do not greedly load multibuff if paths > 100 2025-11-21 12:58:42 +01:00
David
2bdc385fdf some notes where to add thigns 2025-11-21 12:40:39 +01:00
David
615803646f many much wip 2025-11-19 18:11:22 +01:00
David
101bbebe52 unfreezes diff panel at multibuffer loading speed cost 2025-11-19 11:27:04 +01:00
David
d6af4d3cdd restore more things 2025-11-18 17:32:39 +01:00
David
a32319374d cant type anymore? 2025-11-18 17:26:14 +01:00
David
716937c9c9 Merge branch 'main' into perf/project-diff2 2025-11-18 16:12:50 +01:00
David
6ee35cb43e rewrote project diff buffer loading. Now responsive on start get slow after a while as the mb loads full 2025-11-18 13:29:45 +01:00
David
d76c326ff5 somewhat responsive! omg 2025-11-17 14:43:01 +01:00
David
6effe1f48e sleepy time 2025-11-12 12:18:19 +01:00
David
8d3153abd4 ugly code protoyping only dont get this into prod 2025-11-12 11:30:52 +01:00
Jakub Konka
3a301afbc6 Enable all debug info for easier profiling 2025-11-10 22:38:40 +01:00
Jakub Konka
78add792c7 project_diff: Load buffers in the background 2025-11-10 22:01:38 +01:00
270 changed files with 7081 additions and 8511 deletions

View File

@@ -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"]

View File

@@ -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",

View File

@@ -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

View File

@@ -1,7 +1,7 @@
name: "Close Stale Issues"
on:
schedule:
- cron: "0 8 31 DEC *"
- cron: "0 7,9,11 * * 3"
workflow_dispatch:
jobs:

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
View File

@@ -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",

View File

@@ -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

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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:

View File

@@ -150,7 +150,6 @@ impl DbThread {
.unwrap_or_default(),
input: tool_use.input,
is_input_complete: true,
thought_signature: None,
},
));
}

View File

@@ -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,
})
}

View File

@@ -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(),

View File

@@ -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| {

View File

@@ -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>,

View File

@@ -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,
)

View File

@@ -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,
)

View File

@@ -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,
)

View File

@@ -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(),
},

View File

@@ -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,
)

View File

@@ -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()
})

View File

@@ -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()
}
})

View File

@@ -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()),
)
})
}
}

View File

@@ -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,
}

View File

@@ -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

View File

@@ -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]);
},
);
}

View File

@@ -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);

View File

@@ -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
},

View File

@@ -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
}

View File

@@ -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| {

View File

@@ -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()
})

View File

@@ -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() {

View File

@@ -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.

View File

@@ -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>(

View File

@@ -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::*;

View File

@@ -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")
}
}))
}
}

View File

@@ -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 {}",

View 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>>,

View File

@@ -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);
}
}
})

View File

@@ -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

View File

@@ -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.*\\(.*\\).*\\{.*\\}"]}
}
}

View File

@@ -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(),
});
}
}

View File

@@ -796,6 +796,7 @@ impl Database {
is_last_update: true,
merge_message: db_repository.merge_message,
stash_entries: Vec::new(),
renamed_paths: Default::default(),
});
}
}

View File

@@ -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!(

View File

@@ -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 = [

View File

@@ -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]
);
})
});

View File

@@ -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
}

View File

@@ -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 {

View File

@@ -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"] }

View File

@@ -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,

View File

@@ -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]

View File

@@ -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();
}
}
}

View File

@@ -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, _| {

View File

@@ -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()

View File

@@ -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| {

View File

@@ -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))

View File

@@ -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,

View File

@@ -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)))

View File

@@ -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,

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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}/{}: {:?}",

View File

@@ -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();

View File

@@ -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"] }

View File

@@ -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,

View File

@@ -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,
);

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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,
));

View File

@@ -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;

View File

@@ -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);

View File

@@ -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,
)

View File

@@ -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

View File

@@ -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)

View File

@@ -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| {

View File

@@ -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>,
}

View File

@@ -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;

View File

@@ -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]);

View File

@@ -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(),
}),

View File

@@ -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
});

View File

@@ -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| {

View File

@@ -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),
])
})
});

View File

@@ -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 {

View File

@@ -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"
);

View File

@@ -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

View File

@@ -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`

View File

@@ -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

View File

@@ -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");

View File

@@ -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)),
)
})
}

View File

@@ -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),
)
});

View File

@@ -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