Use replace blocks for patches (#20605)

Antonio Scandurra , Max , and Richard created

Release Notes:

- N/A

---------

Co-authored-by: Max <max@zed.dev>
Co-authored-by: Richard <richard@zed.dev>

Change summary

Cargo.lock                                  | 386 ++++++----------------
crates/assistant/src/assistant_panel.rs     | 243 ++++++-------
crates/assistant/src/inline_assistant.rs    |   8 
crates/diagnostics/src/diagnostics.rs       |   4 
crates/editor/src/display_map.rs            | 193 +++++++++--
crates/editor/src/display_map/block_map.rs  | 192 ++++++++--
crates/editor/src/display_map/crease_map.rs | 159 ++++++--
crates/editor/src/editor.rs                 | 240 +++++++------
crates/editor/src/editor_tests.rs           |  47 +-
crates/editor/src/element.rs                |  64 +--
crates/editor/src/hunk_diff.rs              |   6 
crates/multi_buffer/src/multi_buffer.rs     |   2 
crates/repl/src/session.rs                  |   7 
13 files changed, 835 insertions(+), 716 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -263,9 +263,9 @@ checksum = "34cd60c5e3152cef0a592f1b296f1cc93715d89d2551d85315828c3a09575ff4"
 
 [[package]]
 name = "anyhow"
-version = "1.0.93"
+version = "1.0.91"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
+checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8"
 
 [[package]]
 name = "approx"
@@ -290,7 +290,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -652,7 +652,7 @@ dependencies = [
  "futures-lite 2.3.0",
  "parking",
  "polling 3.7.3",
- "rustix 0.38.39",
+ "rustix 0.38.35",
  "slab",
  "tracing",
  "windows-sys 0.59.0",
@@ -734,7 +734,7 @@ dependencies = [
  "cfg-if",
  "event-listener 3.1.0",
  "futures-lite 1.13.0",
- "rustix 0.38.39",
+ "rustix 0.38.35",
  "windows-sys 0.48.0",
 ]
 
@@ -753,7 +753,7 @@ dependencies = [
  "cfg-if",
  "event-listener 5.3.1",
  "futures-lite 2.3.0",
- "rustix 0.38.39",
+ "rustix 0.38.35",
  "tracing",
  "windows-sys 0.59.0",
 ]
@@ -777,7 +777,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -792,7 +792,7 @@ dependencies = [
  "cfg-if",
  "futures-core",
  "futures-io",
- "rustix 0.38.39",
+ "rustix 0.38.35",
  "signal-hook-registry",
  "slab",
  "windows-sys 0.59.0",
@@ -845,7 +845,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -909,7 +909,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -1596,7 +1596,7 @@ dependencies = [
  "regex",
  "rustc-hash 1.1.0",
  "shlex",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -1616,7 +1616,7 @@ dependencies = [
  "regex",
  "rustc-hash 1.1.0",
  "shlex",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -1740,7 +1740,7 @@ source = "git+https://github.com/kvark/blade?rev=e142a3a5e678eb6a13e642ad8401b1f
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -1824,7 +1824,7 @@ dependencies = [
  "proc-macro-crate",
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
  "syn_derive",
 ]
 
@@ -1909,7 +1909,7 @@ checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -1983,7 +1983,7 @@ dependencies = [
  "bitflags 2.6.0",
  "log",
  "polling 3.7.3",
- "rustix 0.38.39",
+ "rustix 0.38.35",
  "slab",
  "thiserror",
 ]
@@ -1995,7 +1995,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20"
 dependencies = [
  "calloop",
- "rustix 0.38.39",
+ "rustix 0.38.35",
  "wayland-backend",
  "wayland-client",
 ]
@@ -2011,9 +2011,9 @@ dependencies = [
 
 [[package]]
 name = "cap-fs-ext"
-version = "3.4.1"
+version = "3.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e16619ada836f12897a72011fe99b03f0025b87a8dbbea4f3c9f89b458a23bf3"
+checksum = "eb23061fc1c4ead4e45ca713080fe768e6234e959f5a5c399c39eb41aa34e56e"
 dependencies = [
  "cap-primitives",
  "cap-std",
@@ -2023,21 +2023,21 @@ dependencies = [
 
 [[package]]
 name = "cap-net-ext"
-version = "3.4.1"
+version = "3.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "710b0eb776410a22c89a98f2f80b2187c2ac3a8206b99f3412332e63c9b09de0"
+checksum = "f83ae11f116bcbafc5327c6af250341db96b5930046732e1905f7dc65887e0e1"
 dependencies = [
  "cap-primitives",
  "cap-std",
- "rustix 0.38.39",
+ "rustix 0.38.35",
  "smallvec",
 ]
 
 [[package]]
 name = "cap-primitives"
-version = "3.4.1"
+version = "3.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82fa6c3f9773feab88d844aa50035a33fb6e7e7426105d2f4bb7aadc42a5f89a"
+checksum = "6d00bd8d26c4270d950eaaa837387964a2089a1c3c349a690a1fa03221d29531"
 dependencies = [
  "ambient-authority",
  "fs-set-times",
@@ -2045,16 +2045,16 @@ dependencies = [
  "io-lifetimes 2.0.3",
  "ipnet",
  "maybe-owned",
- "rustix 0.38.39",
+ "rustix 0.38.35",
  "windows-sys 0.52.0",
  "winx",
 ]
 
 [[package]]
 name = "cap-rand"
-version = "3.4.1"
+version = "3.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53774d49369892b70184f8312e50c1b87edccb376691de4485b0ff554b27c36c"
+checksum = "dbcb16a619d8b8211ed61f42bd290d2a1ac71277a69cf8417ec0996fa92f5211"
 dependencies = [
  "ambient-authority",
  "rand 0.8.5",
@@ -2062,27 +2062,27 @@ dependencies = [
 
 [[package]]
 name = "cap-std"
-version = "3.4.1"
+version = "3.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f71b70818556b4fe2a10c7c30baac3f5f45e973f49fc2673d7c75c39d0baf5b"
+checksum = "19eb8e3d71996828751c1ed3908a439639752ac6bdc874e41469ef7fc15fbd7f"
 dependencies = [
  "cap-primitives",
  "io-extras",
  "io-lifetimes 2.0.3",
- "rustix 0.38.39",
+ "rustix 0.38.35",
 ]
 
 [[package]]
 name = "cap-time-ext"
-version = "3.4.1"
+version = "3.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "69dd48afa2363f746c93f961c211f6f099fb594a3446b8097bc5f79db51b6816"
+checksum = "61142dc51e25b7acc970ca578ce2c3695eac22bbba46c1073f5f583e78957725"
 dependencies = [
  "ambient-authority",
  "cap-primitives",
  "iana-time-zone",
  "once_cell",
- "rustix 0.38.39",
+ "rustix 0.38.35",
  "winx",
 ]
 
@@ -2147,7 +2147,7 @@ dependencies = [
  "quote",
  "serde",
  "serde_json",
- "syn 2.0.87",
+ "syn 2.0.76",
  "tempfile",
  "toml 0.8.19",
 ]
@@ -2340,7 +2340,7 @@ dependencies = [
  "heck 0.5.0",
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -3068,18 +3068,18 @@ dependencies = [
 
 [[package]]
 name = "cranelift-bforest"
-version = "0.111.2"
+version = "0.111.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f823c6662ea77699089ec8b6b4b8a23c1e1a9c6526a6420ede7ac957274a7ab4"
+checksum = "32d69b774780246008783a75edfb943eccc2487b6a43808503a07cd563f2ffde"
 dependencies = [
  "cranelift-entity",
 ]
 
 [[package]]
 name = "cranelift-bitset"
-version = "0.111.2"
+version = "0.111.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fcbb4187005097204458a8e4309bb9e737933477e47b4609f81b07a5b4cdd25"
+checksum = "a7d8d71c6b32c1a7cff254c5e5d7359872c1e5e610fbe963472afcddbd9cf303"
 dependencies = [
  "serde",
  "serde_derive",
@@ -3087,9 +3087,9 @@ dependencies = [
 
 [[package]]
 name = "cranelift-codegen"
-version = "0.111.2"
+version = "0.111.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8cd1aaf8e88339f4f95afffd60d22033546ec7da4d79e805b85260a16668f78f"
+checksum = "3ad3a906f2a3f3590ad9798d59a46959a8593258eb985af722f634723c063a2c"
 dependencies = [
  "bumpalo",
  "cranelift-bforest",
@@ -3110,33 +3110,33 @@ dependencies = [
 
 [[package]]
 name = "cranelift-codegen-meta"
-version = "0.111.2"
+version = "0.111.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e541b0418bbba3ce82040a445bd9a83bf3e0da604a95178d9e949dc8a7840af"
+checksum = "cd5e4ee12262a135efbef3ced4ab2153adafe4adc55f36af94f9d73be0f7505d"
 dependencies = [
  "cranelift-codegen-shared",
 ]
 
 [[package]]
 name = "cranelift-codegen-shared"
-version = "0.111.2"
+version = "0.111.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91fc96a709a30be39d53ecf89dbfe4edcc5adba528d4b65f7e58dc867ba70fab"
+checksum = "5b9374a2a5f060f72e3080fe1c87c9ff4bef2cbe798faae60daf276fb1a13968"
 
 [[package]]
 name = "cranelift-control"
-version = "0.111.2"
+version = "0.111.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c3bfcb035e0a501323896bb7ea3d7a5dd1fac3e92dda458ccd23960fde12c88"
+checksum = "fba3ca2f344bb22d265a928e7c3f5f46e1a2eb41f1393bd53538d07b6ffb5293"
 dependencies = [
  "arbitrary",
 ]
 
 [[package]]
 name = "cranelift-entity"
-version = "0.111.2"
+version = "0.111.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2f00b4eba51d73a8c343c45cfdeeffa1f74f423bba0e6b8e290e646777c2b81"
+checksum = "a6aef77dfb018eed09d92d4244abe3c1c060cbbd900c24f75ddde7d75d0e781e"
 dependencies = [
  "cranelift-bitset",
  "serde",
@@ -3145,9 +3145,9 @@ dependencies = [
 
 [[package]]
 name = "cranelift-frontend"
-version = "0.111.2"
+version = "0.111.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52d5e18bf04660bb716dacf45809e2d4c85e7111701e27dbdb75b4634504ad8f"
+checksum = "7b1d6954f03d63df1cb95d66153c97df0201862220861349bbd5f583754b1917"
 dependencies = [
  "cranelift-codegen",
  "log",
@@ -3157,15 +3157,15 @@ dependencies = [
 
 [[package]]
 name = "cranelift-isle"
-version = "0.111.2"
+version = "0.111.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31f9901807b6d0fde1205f0e4db9d96dcf7ddfc1894c69eb2ff93c47ebf2439f"
+checksum = "f8b9b7e088b784796ea8aa5947c1cc12034c1b076a077ec2a5a287da717fa746"
 
 [[package]]
 name = "cranelift-native"
-version = "0.111.2"
+version = "0.111.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "967d65a4077726a9afc3f4694e037f34b992cbe2b6c48ce519b714a0b0558f97"
+checksum = "4cab7424083d070669ff3fdeea7c5b4b5013a055aa1ee0532703f17a5f62af64"
 dependencies = [
  "cranelift-codegen",
  "libc",
@@ -3174,9 +3174,9 @@ dependencies = [
 
 [[package]]
 name = "cranelift-wasm"
-version = "0.111.2"
+version = "0.111.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4899fd1ef6b1fe1df30f26ef864bd6e45040b8cf9f3cb3905d3e973c25698579"
+checksum = "81a9f6d0495984eef1d753ec8748de0b216b37ade16d219f1c0f27d8188d7f77"
 dependencies = [
  "cranelift-codegen",
  "cranelift-entity",
@@ -3346,7 +3346,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f"
 dependencies = [
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -3485,7 +3485,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "rustc_version",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -3598,17 +3598,6 @@ dependencies = [
  "windows-sys 0.48.0",
 ]
 
-[[package]]
-name = "displaydoc"
-version = "0.2.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.87",
-]
-
 [[package]]
 name = "dlib"
 version = "0.5.2"
@@ -3761,7 +3750,7 @@ dependencies = [
  "enum-ordinalize",
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -3878,7 +3867,7 @@ checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -3899,7 +3888,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -4308,7 +4297,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947"
 dependencies = [
  "cfg-if",
- "rustix 0.38.39",
+ "rustix 0.38.35",
  "windows-sys 0.52.0",
 ]
 
@@ -4562,7 +4551,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -4644,7 +4633,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "033b337d725b97690d86893f9de22b67b80dcc4e9ad815f348254c38119db8fb"
 dependencies = [
  "io-lifetimes 2.0.3",
- "rustix 0.38.39",
+ "rustix 0.38.35",
  "windows-sys 0.52.0",
 ]
 
@@ -4805,7 +4794,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -5282,12 +5271,11 @@ dependencies = [
 
 [[package]]
 name = "handlebars"
-version = "6.2.0"
+version = "5.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd4ccde012831f9a071a637b0d4e31df31c0f6c525784b35ae76a9ac6bc1e315"
+checksum = "d08485b96a0e6393e9e4d1b8d48cf74ad6c063cd905eb33f42c1ce3f0377539b"
 dependencies = [
  "log",
- "num-order",
  "pest",
  "pest_derive",
  "serde",
@@ -5507,7 +5495,7 @@ dependencies = [
  "markup5ever",
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -5777,124 +5765,6 @@ dependencies = [
  "cc",
 ]
 
-[[package]]
-name = "icu_collections"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
-dependencies = [
- "displaydoc",
- "yoke",
- "zerofrom",
- "zerovec",
-]
-
-[[package]]
-name = "icu_locid"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
-dependencies = [
- "displaydoc",
- "litemap",
- "tinystr",
- "writeable",
- "zerovec",
-]
-
-[[package]]
-name = "icu_locid_transform"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
-dependencies = [
- "displaydoc",
- "icu_locid",
- "icu_locid_transform_data",
- "icu_provider",
- "tinystr",
- "zerovec",
-]
-
-[[package]]
-name = "icu_locid_transform_data"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
-
-[[package]]
-name = "icu_normalizer"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
-dependencies = [
- "displaydoc",
- "icu_collections",
- "icu_normalizer_data",
- "icu_properties",
- "icu_provider",
- "smallvec",
- "utf16_iter",
- "utf8_iter",
- "write16",
- "zerovec",
-]
-
-[[package]]
-name = "icu_normalizer_data"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
-
-[[package]]
-name = "icu_properties"
-version = "1.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
-dependencies = [
- "displaydoc",
- "icu_collections",
- "icu_locid_transform",
- "icu_properties_data",
- "icu_provider",
- "tinystr",
- "zerovec",
-]
-
-[[package]]
-name = "icu_properties_data"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
-
-[[package]]
-name = "icu_provider"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
-dependencies = [
- "displaydoc",
- "icu_locid",
- "icu_provider_macros",
- "stable_deref_trait",
- "tinystr",
- "writeable",
- "yoke",
- "zerofrom",
- "zerovec",
-]
-
-[[package]]
-name = "icu_provider_macros"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.87",
-]
-
 [[package]]
 name = "id-arena"
 version = "2.2.1"
@@ -5903,23 +5773,12 @@ checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
 
 [[package]]
 name = "idna"
-version = "1.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
-dependencies = [
- "idna_adapter",
- "smallvec",
- "utf8_iter",
-]
-
-[[package]]
-name = "idna_adapter"
-version = "1.2.0"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
+checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
 dependencies = [
- "icu_normalizer",
- "icu_properties",
+ "unicode-bidi",
+ "unicode-normalization",
 ]
 
 [[package]]
@@ -6068,7 +5927,7 @@ checksum = "0122b7114117e64a63ac49f752a5ca4624d534c7b1c7de796ac196381cd2d947"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -6153,14 +6012,14 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
 name = "io-extras"
-version = "0.18.3"
+version = "0.18.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d45fd7584f9b67ac37bc041212d06bfac0700b36456b05890d36a3b626260eb"
+checksum = "c9f046b9af244f13b3bd939f55d16830ac3a201e8a9ba9661bfcb03e2be72b9b"
 dependencies = [
  "io-lifetimes 2.0.3",
  "windows-sys 0.52.0",
@@ -6450,7 +6309,7 @@ dependencies = [
  "parking_lot",
  "postage",
  "pretty_assertions",
- "pulldown-cmark 0.12.2",
+ "pulldown-cmark 0.12.1",
  "rand 0.8.5",
  "regex",
  "rpc",
@@ -6778,7 +6637,7 @@ checksum = "b01f197a15988fb5b2ec0a5a9800c97e70771499c456ad757d63b3c5e9b96e75"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -6793,12 +6652,6 @@ version = "0.4.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
 
-[[package]]
-name = "litemap"
-version = "0.7.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704"
-
 [[package]]
 name = "live_kit_client"
 version = "0.1.0"
@@ -6986,7 +6839,7 @@ dependencies = [
  "linkify",
  "log",
  "node_runtime",
- "pulldown-cmark 0.12.2",
+ "pulldown-cmark 0.12.1",
  "settings",
  "theme",
  "ui",
@@ -7006,7 +6859,7 @@ dependencies = [
  "linkify",
  "log",
  "pretty_assertions",
- "pulldown-cmark 0.12.2",
+ "pulldown-cmark 0.12.1",
  "settings",
  "theme",
  "ui",
@@ -7081,9 +6934,9 @@ dependencies = [
 
 [[package]]
 name = "mdbook"
-version = "0.4.41"
+version = "0.4.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c57953fbe900c241a7879c8fd81c5a6bebc583dc8a4338582c63b58b49014d7"
+checksum = "b45a38e19bd200220ef07c892b0157ad3d2365e5b5a267ca01ad12182491eea5"
 dependencies = [
  "ammonia",
  "anyhow",
@@ -7093,7 +6946,7 @@ dependencies = [
  "elasticlunr-rs",
  "env_logger 0.11.5",
  "futures-util",
- "handlebars 6.2.0",
+ "handlebars 5.1.2",
  "ignore",
  "log",
  "memchr",
@@ -7139,7 +6992,7 @@ version = "0.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64"
 dependencies = [
- "rustix 0.38.39",
+ "rustix 0.38.35",
 ]
 
 [[package]]
@@ -7614,7 +7467,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -7647,21 +7500,6 @@ dependencies = [
  "num-traits",
 ]
 
-[[package]]
-name = "num-modular"
-version = "0.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17bb261bf36fa7d83f4c294f834e91256769097b3cb505d44831e0a179ac647f"
-
-[[package]]
-name = "num-order"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "537b596b97c40fcf8056d153049eb22f481c17ebce72a513ec9286e4986d1bb6"
-dependencies = [
- "num-modular",
-]
-
 [[package]]
 name = "num-rational"
 version = "0.4.2"
@@ -7711,7 +7549,7 @@ dependencies = [
  "proc-macro-crate",
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -7899,7 +7737,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -7985,7 +7823,7 @@ dependencies = [
  "proc-macro-error",
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -8098,7 +7936,7 @@ dependencies = [
  "by_address",
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -8268,7 +8106,7 @@ dependencies = [
  "pest_meta",
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -8702,7 +8540,7 @@ dependencies = [
  "phf_shared 0.11.2",
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -8762,7 +8600,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -8905,7 +8743,7 @@ dependencies = [
  "concurrent-queue",
  "hermit-abi 0.4.0",
  "pin-project-lite",
- "rustix 0.38.39",
+ "rustix 0.38.35",
  "tracing",
  "windows-sys 0.59.0",
 ]
@@ -9002,7 +8840,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba"
 dependencies = [
  "proc-macro2",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -9063,7 +8901,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30"
 dependencies = [
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -9307,9 +9145,9 @@ dependencies = [
 
 [[package]]
 name = "pulldown-cmark"
-version = "0.12.2"
+version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f86ba2052aebccc42cbbb3ed234b8b13ce76f75c3551a303cb2bcffcff12bb14"
+checksum = "666f0f59e259aea2d72e6012290c09877a780935cc3c18b1ceded41f3890d59c"
 dependencies = [
  "bitflags 2.6.0",
  "memchr",
@@ -10033,7 +9871,7 @@ dependencies = [
  "gpui",
  "language",
  "linkify",
- "pulldown-cmark 0.12.2",
+ "pulldown-cmark 0.12.1",
  "theme",
  "ui",
  "util",
@@ -10231,7 +10069,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "rust-embed-utils",
- "syn 2.0.87",
+ "syn 2.0.76",
  "walkdir",
 ]
 
@@ -10305,9 +10143,9 @@ dependencies = [
 
 [[package]]
 name = "rustix"
-version = "0.38.39"
+version = "0.38.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "375116bee2be9ed569afe2154ea6a99dfdffd257f533f187498c2a8f5feaf4ee"
+checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f"
 dependencies = [
  "bitflags 2.6.0",
  "errno 0.3.9",
@@ -10326,7 +10164,7 @@ checksum = "a25c3aad9fc1424eb82c88087789a7d938e1829724f3e4043163baf0d13cfc12"
 dependencies = [
  "errno 0.3.9",
  "libc",
- "rustix 0.38.39",
+ "rustix 0.38.35",
 ]
 
 [[package]]
@@ -10504,7 +10342,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "serde_derive_internals 0.29.1",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -10551,7 +10389,7 @@ dependencies = [
  "proc-macro-error",
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -10592,7 +10430,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "sea-bae",
- "syn 2.0.87",
+ "syn 2.0.76",
  "unicode-ident",
 ]
 
@@ -10791,7 +10629,7 @@ checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -10813,7 +10651,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -10827,9 +10665,9 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.132"
+version = "1.0.128"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
+checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
 dependencies = [
  "indexmap 2.4.0",
  "itoa",
@@ -10890,7 +10728,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -11449,7 +11287,7 @@ dependencies = [
  "quote",
  "sqlx-core",
  "sqlx-macros-core",
- "syn 2.0.87",
+ "syn 2.0.76",
 ]
 
 [[package]]

crates/assistant/src/assistant_panel.rs 🔗

@@ -1480,7 +1480,6 @@ struct ScrollPosition {
 }
 
 struct PatchViewState {
-    footer_block_id: CustomBlockId,
     crease_id: CreaseId,
     editor: Option<PatchEditorState>,
     update_task: Option<Task<()>>,
@@ -1934,7 +1933,7 @@ impl ContextEditor {
                                 );
                             });
 
-                            Crease::new(
+                            Crease::inline(
                                 start..end,
                                 placeholder,
                                 fold_toggle("tool-use"),
@@ -2032,7 +2031,7 @@ impl ContextEditor {
                             let end = buffer
                                 .anchor_in_excerpt(excerpt_id, command.source_range.end)
                                 .unwrap();
-                            Crease::new(start..end, placeholder, render_toggle, render_trailer)
+                            Crease::inline(start..end, placeholder, render_toggle, render_trailer)
                         }),
                         cx,
                     );
@@ -2124,7 +2123,7 @@ impl ContextEditor {
 
                     let buffer_row = MultiBufferRow(start.to_point(&buffer).row);
 
-                    let crease = Crease::new(
+                    let crease = Crease::inline(
                         start..end,
                         placeholder,
                         fold_toggle("tool-use"),
@@ -2192,18 +2191,14 @@ impl ContextEditor {
                     let crease_end = buffer
                         .anchor_in_excerpt(excerpt_id, invoked_slash_command.range.end)
                         .unwrap();
-                    let fold_placeholder =
-                        invoked_slash_command_fold_placeholder(command_id, context);
-                    let crease_ids = editor.insert_creases(
-                        [Crease::new(
-                            crease_start..crease_end,
-                            fold_placeholder.clone(),
-                            fold_toggle("invoked-slash-command"),
-                            |_row, _folded, _cx| Empty.into_any(),
-                        )],
-                        cx,
+                    let crease = Crease::inline(
+                        crease_start..crease_end,
+                        invoked_slash_command_fold_placeholder(command_id, context),
+                        fold_toggle("invoked-slash-command"),
+                        |_row, _folded, _cx| Empty.into_any(),
                     );
-                    editor.fold_ranges([(crease_start..crease_end, fold_placeholder)], false, cx);
+                    let crease_ids = editor.insert_creases([crease.clone()], cx);
+                    editor.fold_creases(vec![crease], false, cx);
                     entry.insert(crease_ids[0]);
                 } else {
                     cx.notify()
@@ -2225,23 +2220,32 @@ impl ContextEditor {
         cx: &mut ViewContext<ContextEditor>,
     ) {
         let this = cx.view().downgrade();
-        let mut removed_crease_ids = Vec::new();
-        let mut removed_block_ids = HashSet::default();
         let mut editors_to_close = Vec::new();
-        for range in removed {
-            if let Some(state) = self.patches.remove(range) {
-                editors_to_close.extend(state.editor.and_then(|state| state.editor.upgrade()));
-                removed_block_ids.insert(state.footer_block_id);
-                removed_crease_ids.push(state.crease_id);
-            }
-        }
 
         self.editor.update(cx, |editor, cx| {
             let snapshot = editor.snapshot(cx);
             let multibuffer = &snapshot.buffer_snapshot;
             let (&excerpt_id, _, _) = multibuffer.as_singleton().unwrap();
 
-            let mut replaced_blocks = HashMap::default();
+            let mut removed_crease_ids = Vec::new();
+            let mut ranges_to_unfold: Vec<Range<Anchor>> = Vec::new();
+            for range in removed {
+                if let Some(state) = self.patches.remove(range) {
+                    let patch_start = multibuffer
+                        .anchor_in_excerpt(excerpt_id, range.start)
+                        .unwrap();
+                    let patch_end = multibuffer
+                        .anchor_in_excerpt(excerpt_id, range.end)
+                        .unwrap();
+
+                    editors_to_close.extend(state.editor.and_then(|state| state.editor.upgrade()));
+                    ranges_to_unfold.push(patch_start..patch_end);
+                    removed_crease_ids.push(state.crease_id);
+                }
+            }
+            editor.unfold_ranges(&ranges_to_unfold, true, false, cx);
+            editor.remove_creases(removed_crease_ids, cx);
+
             for range in updated {
                 let Some(patch) = self.context.read(cx).patch_for_range(&range, cx).cloned() else {
                     continue;
@@ -2254,19 +2258,21 @@ impl ContextEditor {
                 let patch_end = multibuffer
                     .anchor_in_excerpt(excerpt_id, patch.range.end)
                     .unwrap();
-                let render_block: RenderBlock = Box::new({
+                let render_block: RenderBlock = Arc::new({
                     let this = this.clone();
                     let patch_range = range.clone();
                     move |cx: &mut BlockContext<'_, '_>| {
                         let max_width = cx.max_width;
                         let gutter_width = cx.gutter_dimensions.full_width();
                         let block_id = cx.block_id;
+                        let selected = cx.selected;
                         this.update(&mut **cx, |this, cx| {
-                            this.render_patch_footer(
+                            this.render_patch_block(
                                 patch_range.clone(),
                                 max_width,
                                 gutter_width,
                                 block_id,
+                                selected,
                                 cx,
                             )
                         })
@@ -2276,25 +2282,16 @@ impl ContextEditor {
                     }
                 });
 
-                let header_placeholder = FoldPlaceholder {
-                    render: {
-                        let this = this.clone();
-                        let patch_range = range.clone();
-                        Arc::new(move |fold_id, _range, cx| {
-                            this.update(cx, |this, cx| {
-                                this.render_patch_header(patch_range.clone(), fold_id, cx)
-                            })
-                            .ok()
-                            .flatten()
-                            .unwrap_or_else(|| Empty.into_any())
-                        })
-                    },
-                    ..Default::default()
-                };
+                let height = path_count as u32 + 1;
+                let crease = Crease::block(
+                    patch_start..patch_end,
+                    height,
+                    BlockStyle::Flex,
+                    render_block.clone(),
+                );
 
                 let should_refold;
                 if let Some(state) = self.patches.get_mut(&range) {
-                    replaced_blocks.insert(state.footer_block_id, render_block);
                     if let Some(editor_state) = &state.editor {
                         if editor_state.opened_patch != patch {
                             state.update_task = Some({
@@ -2311,33 +2308,11 @@ impl ContextEditor {
                     should_refold =
                         snapshot.intersects_fold(patch_start.to_offset(&snapshot.buffer_snapshot));
                 } else {
-                    let block_ids = editor.insert_blocks(
-                        [BlockProperties {
-                            height: path_count as u32 + 1,
-                            style: BlockStyle::Flex,
-                            render: render_block,
-                            placement: BlockPlacement::Below(patch_start),
-                            priority: 0,
-                        }],
-                        None,
-                        cx,
-                    );
-
-                    let new_crease_ids = editor.insert_creases(
-                        [Crease::new(
-                            patch_start..patch_end,
-                            header_placeholder.clone(),
-                            fold_toggle("patch-header"),
-                            |_, _, _| Empty.into_any_element(),
-                        )],
-                        cx,
-                    );
-
+                    let crease_id = editor.insert_creases([crease.clone()], cx)[0];
                     self.patches.insert(
                         range.clone(),
                         PatchViewState {
-                            footer_block_id: block_ids[0],
-                            crease_id: new_crease_ids[0],
+                            crease_id,
                             editor: None,
                             update_task: None,
                         },
@@ -2348,13 +2323,9 @@ impl ContextEditor {
 
                 if should_refold {
                     editor.unfold_ranges(&[patch_start..patch_end], true, false, cx);
-                    editor.fold_ranges([(patch_start..patch_end, header_placeholder)], false, cx);
+                    editor.fold_creases(vec![crease], false, cx);
                 }
             }
-
-            editor.remove_creases(removed_crease_ids, cx);
-            editor.remove_blocks(removed_block_ids, None, cx);
-            editor.replace_blocks(replaced_blocks, None, cx);
         });
 
         for editor in editors_to_close {
@@ -2385,7 +2356,7 @@ impl ContextEditor {
                 let buffer_row = MultiBufferRow(start.to_point(&buffer).row);
                 buffer_rows_to_fold.insert(buffer_row);
                 creases.push(
-                    Crease::new(
+                    Crease::inline(
                         start..end,
                         FoldPlaceholder {
                             render: render_fold_icon_button(
@@ -2674,7 +2645,7 @@ impl ContextEditor {
             let mut blocks_to_replace: HashMap<_, RenderBlock> = Default::default();
 
             let render_block = |message: MessageMetadata| -> RenderBlock {
-                Box::new({
+                Arc::new({
                     let context = self.context.clone();
 
                     move |cx| {
@@ -3127,7 +3098,7 @@ impl ContextEditor {
                                     crease_title,
                                     cx.view().downgrade(),
                                 );
-                                let crease = Crease::new(
+                                let crease = Crease::inline(
                                     anchor_before..anchor_after,
                                     fold_placeholder,
                                     render_quote_selection_output_toggle,
@@ -3217,31 +3188,29 @@ impl ContextEditor {
                             &snapshot,
                         )
                         .filter_map(|crease| {
-                            if let Some(metadata) = &crease.metadata {
-                                let start = crease
-                                    .range
+                            if let Crease::Inline {
+                                range, metadata, ..
+                            } = &crease
+                            {
+                                let metadata = metadata.as_ref()?;
+                                let start = range
                                     .start
                                     .to_offset(&snapshot)
                                     .saturating_sub(selection_start);
-                                let end = crease
-                                    .range
+                                let end = range
                                     .end
                                     .to_offset(&snapshot)
                                     .saturating_sub(selection_start);
 
                                 let range_relative_to_selection = start..end;
-
-                                if range_relative_to_selection.is_empty() {
-                                    None
-                                } else {
-                                    Some(SelectedCreaseMetadata {
+                                if !range_relative_to_selection.is_empty() {
+                                    return Some(SelectedCreaseMetadata {
                                         range_relative_to_selection,
                                         crease: metadata.clone(),
-                                    })
+                                    });
                                 }
-                            } else {
-                                None
                             }
+                            None
                         })
                         .collect::<Vec<_>>()
                 }),
@@ -3322,7 +3291,7 @@ impl ContextEditor {
 
                             let buffer_row = MultiBufferRow(start.to_point(&buffer).row);
                             buffer_rows_to_fold.insert(buffer_row);
-                            Crease::new(
+                            Crease::inline(
                                 start..end,
                                 FoldPlaceholder {
                                     render: render_fold_icon_button(
@@ -3415,7 +3384,7 @@ impl ContextEditor {
                         placement: BlockPlacement::Above(anchor),
                         height: MAX_HEIGHT_IN_LINES,
                         style: BlockStyle::Sticky,
-                        render: Box::new(move |cx| {
+                        render: Arc::new(move |cx| {
                             let image_size = size_for_image(
                                 &image,
                                 size(
@@ -3472,33 +3441,13 @@ impl ContextEditor {
             .unwrap_or_else(|| Cow::Borrowed(DEFAULT_TAB_TITLE))
     }
 
-    fn render_patch_header(
-        &self,
-        range: Range<text::Anchor>,
-        _id: FoldId,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<AnyElement> {
-        let patch = self.context.read(cx).patch_for_range(&range, cx)?;
-        let theme = cx.theme().clone();
-        Some(
-            h_flex()
-                .px_1()
-                .py_0p5()
-                .border_b_1()
-                .border_color(theme.status().info_border)
-                .gap_1()
-                .child(Icon::new(IconName::Diff).size(IconSize::Small))
-                .child(Label::new(patch.title.clone()).size(LabelSize::Small))
-                .into_any(),
-        )
-    }
-
-    fn render_patch_footer(
+    fn render_patch_block(
         &mut self,
         range: Range<text::Anchor>,
         max_width: Pixels,
         gutter_width: Pixels,
         id: BlockId,
+        selected: bool,
         cx: &mut ViewContext<Self>,
     ) -> Option<AnyElement> {
         let snapshot = self.editor.update(cx, |editor, cx| editor.snapshot(cx));
@@ -3509,10 +3458,7 @@ impl ContextEditor {
             .anchor_in_excerpt(excerpt_id, range.start)
             .unwrap();
 
-        if !snapshot.intersects_fold(anchor) {
-            return None;
-        }
-
+        let theme = cx.theme().clone();
         let patch = self.context.read(cx).patch_for_range(&range, cx)?;
         let paths = patch
             .paths()
@@ -3522,9 +3468,17 @@ impl ContextEditor {
         Some(
             v_flex()
                 .id(id)
-                .pl(gutter_width)
-                .w(max_width)
-                .py_2()
+                .bg(theme.colors().editor_background)
+                .ml(gutter_width)
+                .w(max_width - gutter_width)
+                .rounded_md()
+                .border_1()
+                .border_color(theme.colors().border)
+                .hover(|style| style.border_color(theme.colors().text_accent))
+                .when(selected, |this| {
+                    this.border_color(theme.colors().text_accent)
+                })
+                .pb_1()
                 .cursor(CursorStyle::PointingHand)
                 .on_click(cx.listener(move |this, _, cx| {
                     this.editor.update(cx, |editor, cx| {
@@ -3534,24 +3488,55 @@ impl ContextEditor {
                     });
                     this.focus_active_patch(cx);
                 }))
+                .child(
+                    h_flex()
+                        .rounded_t_md()
+                        .bg(theme.colors().element_background)
+                        .px_2()
+                        .py_1()
+                        .child(Label::new(patch.title.clone()).size(LabelSize::Small))
+                        .border_b_1()
+                        .border_color(theme.colors().border),
+                )
                 .children(paths.into_iter().map(|path| {
                     h_flex()
-                        .pl_1()
+                        .px_2()
+                        .pt_1()
                         .gap_1()
                         .child(Icon::new(IconName::File).size(IconSize::Small))
                         .child(Label::new(path).size(LabelSize::Small))
                 }))
                 .when(patch.status == AssistantPatchStatus::Pending, |div| {
                     div.child(
-                        Label::new("Generating")
-                            .color(Color::Muted)
-                            .size(LabelSize::Small)
-                            .with_animation(
-                                "pulsating-label",
-                                Animation::new(Duration::from_secs(2))
-                                    .repeat()
-                                    .with_easing(pulsating_between(0.4, 1.)),
-                                |label, delta| label.alpha(delta),
+                        h_flex()
+                            .pt_1()
+                            .px_2()
+                            .gap_1()
+                            .child(
+                                Icon::new(IconName::ArrowCircle)
+                                    .size(IconSize::XSmall)
+                                    .color(Color::Info)
+                                    .with_animation(
+                                        "arrow-circle",
+                                        Animation::new(Duration::from_secs(2)).repeat(),
+                                        |icon, delta| {
+                                            icon.transform(Transformation::rotate(percentage(
+                                                delta,
+                                            )))
+                                        },
+                                    ),
+                            )
+                            .child(
+                                Label::new("Generating")
+                                    .color(Color::Muted)
+                                    .size(LabelSize::Small)
+                                    .with_animation(
+                                        "pulsating-label",
+                                        Animation::new(Duration::from_secs(2))
+                                            .repeat()
+                                            .with_easing(pulsating_between(0.4, 1.)),
+                                        |label, delta| label.alpha(delta),
+                                    ),
                             ),
                     )
                 })

crates/assistant/src/inline_assistant.rs 🔗

@@ -460,7 +460,7 @@ impl InlineAssistant {
                 style: BlockStyle::Sticky,
                 placement: BlockPlacement::Below(range.end),
                 height: 0,
-                render: Box::new(|cx| {
+                render: Arc::new(|cx| {
                     v_flex()
                         .h_full()
                         .w_full()
@@ -1197,8 +1197,9 @@ impl InlineAssistant {
                     placement: BlockPlacement::Above(new_row),
                     height,
                     style: BlockStyle::Flex,
-                    render: Box::new(move |cx| {
+                    render: Arc::new(move |cx| {
                         div()
+                            .occlude()
                             .bg(cx.theme().status().deleted_background)
                             .size_full()
                             .h(height as f32 * cx.line_height())
@@ -1317,7 +1318,7 @@ impl InlineAssistGroup {
 
 fn build_assist_editor_renderer(editor: &View<PromptEditor>) -> RenderBlock {
     let editor = editor.clone();
-    Box::new(move |cx: &mut BlockContext| {
+    Arc::new(move |cx: &mut BlockContext| {
         *editor.read(cx).gutter_dimensions.lock() = *cx.gutter_dimensions;
         editor.clone().into_any_element()
     })
@@ -1480,6 +1481,7 @@ impl Render for PromptEditor {
         h_flex()
             .key_context("PromptEditor")
             .bg(cx.theme().colors().editor_background)
+            .occlude()
             .border_y_1()
             .border_color(cx.theme().status().info_border)
             .size_full()

crates/diagnostics/src/diagnostics.rs 🔗

@@ -32,6 +32,7 @@ use std::{
     cmp::Ordering,
     mem,
     ops::Range,
+    sync::Arc,
 };
 use theme::ActiveTheme;
 pub use toolbar_controls::ToolbarControls;
@@ -790,10 +791,11 @@ const DIAGNOSTIC_HEADER: &str = "diagnostic header";
 fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
     let (message, code_ranges) = highlight_diagnostic_message(&diagnostic, None);
     let message: SharedString = message;
-    Box::new(move |cx| {
+    Arc::new(move |cx| {
         let highlight_style: HighlightStyle = cx.theme().colors().text_accent.into();
         h_flex()
             .id(DIAGNOSTIC_HEADER)
+            .occlude()
             .h(2. * cx.line_height())
             .pl_10()
             .pr_5()

crates/editor/src/display_map.rs 🔗

@@ -36,7 +36,7 @@ use block_map::{BlockRow, BlockSnapshot};
 use collections::{HashMap, HashSet};
 pub use crease_map::*;
 pub use fold_map::{Fold, FoldId, FoldPlaceholder, FoldPoint};
-use fold_map::{FoldMap, FoldMapWriter, FoldOffset, FoldSnapshot};
+use fold_map::{FoldMap, FoldSnapshot};
 use gpui::{
     AnyElement, Font, HighlightStyle, LineLayout, Model, ModelContext, Pixels, UnderlineStyle,
 };
@@ -65,7 +65,7 @@ use std::{
 };
 use sum_tree::{Bias, TreeMap};
 use tab_map::{TabMap, TabSnapshot};
-use text::{Edit, LineIndent};
+use text::LineIndent;
 use ui::{div, px, IntoElement, ParentElement, SharedString, Styled, WindowContext};
 use unicode_segmentation::UnicodeSegmentation;
 use wrap_map::{WrapMap, WrapSnapshot};
@@ -197,22 +197,86 @@ impl DisplayMap {
             other
                 .folds_in_range(0..other.buffer_snapshot.len())
                 .map(|fold| {
-                    (
+                    Crease::simple(
                         fold.range.to_offset(&other.buffer_snapshot),
                         fold.placeholder.clone(),
                     )
-                }),
+                })
+                .collect(),
             cx,
         );
     }
 
-    /// Creates folds for the given ranges.
-    pub fn fold<T: ToOffset>(
+    /// Creates folds for the given creases.
+    pub fn fold<T: Clone + ToOffset>(
         &mut self,
-        ranges: impl IntoIterator<Item = (Range<T>, FoldPlaceholder)>,
+        creases: Vec<Crease<T>>,
         cx: &mut ModelContext<Self>,
     ) {
-        self.update_fold_map(cx, |fold_map| fold_map.fold(ranges))
+        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
+        let edits = self.buffer_subscription.consume().into_inner();
+        let tab_size = Self::tab_size(&self.buffer, cx);
+        let (snapshot, edits) = self.inlay_map.sync(buffer_snapshot.clone(), edits);
+        let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
+        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
+        let (snapshot, edits) = self
+            .wrap_map
+            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
+        self.block_map.read(snapshot, edits);
+
+        let inline = creases.iter().filter_map(|crease| {
+            if let Crease::Inline {
+                range, placeholder, ..
+            } = crease
+            {
+                Some((range.clone(), placeholder.clone()))
+            } else {
+                None
+            }
+        });
+        let (snapshot, edits) = fold_map.fold(inline);
+
+        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
+        let (snapshot, edits) = self
+            .wrap_map
+            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
+        let mut block_map = self.block_map.write(snapshot, edits);
+        let blocks = creases.into_iter().filter_map(|crease| {
+            if let Crease::Block {
+                range,
+                block_height,
+                render_block,
+                block_style,
+                block_priority,
+                ..
+            } = crease
+            {
+                Some((
+                    range,
+                    render_block,
+                    block_height,
+                    block_style,
+                    block_priority,
+                ))
+            } else {
+                None
+            }
+        });
+        block_map.insert(
+            blocks
+                .into_iter()
+                .map(|(range, render, height, style, priority)| {
+                    let start = buffer_snapshot.anchor_before(range.start);
+                    let end = buffer_snapshot.anchor_after(range.end);
+                    BlockProperties {
+                        placement: BlockPlacement::Replace(start..end),
+                        render,
+                        height,
+                        style,
+                        priority,
+                    }
+                }),
+        );
     }
 
     /// Removes any folds with the given ranges.
@@ -222,7 +286,22 @@ impl DisplayMap {
         type_id: TypeId,
         cx: &mut ModelContext<Self>,
     ) {
-        self.update_fold_map(cx, |fold_map| fold_map.remove_folds(ranges, type_id))
+        let snapshot = self.buffer.read(cx).snapshot(cx);
+        let edits = self.buffer_subscription.consume().into_inner();
+        let tab_size = Self::tab_size(&self.buffer, cx);
+        let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
+        let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
+        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
+        let (snapshot, edits) = self
+            .wrap_map
+            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
+        self.block_map.read(snapshot, edits);
+        let (snapshot, edits) = fold_map.remove_folds(ranges, type_id);
+        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
+        let (snapshot, edits) = self
+            .wrap_map
+            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
+        self.block_map.write(snapshot, edits);
     }
 
     /// Removes any folds whose ranges intersect any of the given ranges.
@@ -231,18 +310,12 @@ impl DisplayMap {
         ranges: impl IntoIterator<Item = Range<T>>,
         inclusive: bool,
         cx: &mut ModelContext<Self>,
-    ) {
-        self.update_fold_map(cx, |fold_map| {
-            fold_map.unfold_intersecting(ranges, inclusive)
-        })
-    }
-
-    fn update_fold_map(
-        &mut self,
-        cx: &mut ModelContext<Self>,
-        callback: impl FnOnce(&mut FoldMapWriter) -> (FoldSnapshot, Vec<Edit<FoldOffset>>),
     ) {
         let snapshot = self.buffer.read(cx).snapshot(cx);
+        let offset_ranges = ranges
+            .into_iter()
+            .map(|range| range.start.to_offset(&snapshot)..range.end.to_offset(&snapshot))
+            .collect::<Vec<_>>();
         let edits = self.buffer_subscription.consume().into_inner();
         let tab_size = Self::tab_size(&self.buffer, cx);
         let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
@@ -252,17 +325,20 @@ impl DisplayMap {
             .wrap_map
             .update(cx, |map, cx| map.sync(snapshot, edits, cx));
         self.block_map.read(snapshot, edits);
-        let (snapshot, edits) = callback(&mut fold_map);
+
+        let (snapshot, edits) =
+            fold_map.unfold_intersecting(offset_ranges.iter().cloned(), inclusive);
         let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
         let (snapshot, edits) = self
             .wrap_map
             .update(cx, |map, cx| map.sync(snapshot, edits, cx));
-        self.block_map.read(snapshot, edits);
+        let mut block_map = self.block_map.write(snapshot, edits);
+        block_map.remove_intersecting_replace_blocks(offset_ranges, inclusive);
     }
 
     pub fn insert_creases(
         &mut self,
-        creases: impl IntoIterator<Item = Crease>,
+        creases: impl IntoIterator<Item = Crease<Anchor>>,
         cx: &mut ModelContext<Self>,
     ) -> Vec<CreaseId> {
         let snapshot = self.buffer.read(cx).snapshot(cx);
@@ -596,7 +672,7 @@ impl DisplaySnapshot {
     ) -> impl Iterator<Item = Option<MultiBufferRow>> + '_ {
         self.block_snapshot
             .buffer_rows(BlockRow(start_row.0))
-            .map(|row| row.map(|row| MultiBufferRow(row.0)))
+            .map(|row| row.map(MultiBufferRow))
     }
 
     pub fn max_buffer_row(&self) -> MultiBufferRow {
@@ -987,7 +1063,12 @@ impl DisplaySnapshot {
     }
 
     pub fn is_line_folded(&self, buffer_row: MultiBufferRow) -> bool {
-        self.fold_snapshot.is_line_folded(buffer_row)
+        self.block_snapshot.is_line_replaced(buffer_row)
+            || self.fold_snapshot.is_line_folded(buffer_row)
+    }
+
+    pub fn is_line_replaced(&self, buffer_row: MultiBufferRow) -> bool {
+        self.block_snapshot.is_line_replaced(buffer_row)
     }
 
     pub fn is_block_line(&self, display_row: DisplayRow) -> bool {
@@ -1061,19 +1142,42 @@ impl DisplaySnapshot {
             .unwrap_or(false)
     }
 
-    pub fn foldable_range(
-        &self,
-        buffer_row: MultiBufferRow,
-    ) -> Option<(Range<Point>, FoldPlaceholder)> {
+    pub fn crease_for_buffer_row(&self, buffer_row: MultiBufferRow) -> Option<Crease<Point>> {
         let start = MultiBufferPoint::new(buffer_row.0, self.buffer_snapshot.line_len(buffer_row));
         if let Some(crease) = self
             .crease_snapshot
             .query_row(buffer_row, &self.buffer_snapshot)
         {
-            Some((
-                crease.range.to_point(&self.buffer_snapshot),
-                crease.placeholder.clone(),
-            ))
+            match crease {
+                Crease::Inline {
+                    range,
+                    placeholder,
+                    render_toggle,
+                    render_trailer,
+                    metadata,
+                } => Some(Crease::Inline {
+                    range: range.to_point(&self.buffer_snapshot),
+                    placeholder: placeholder.clone(),
+                    render_toggle: render_toggle.clone(),
+                    render_trailer: render_trailer.clone(),
+                    metadata: metadata.clone(),
+                }),
+                Crease::Block {
+                    range,
+                    block_height,
+                    block_style,
+                    render_block,
+                    block_priority,
+                    render_toggle,
+                } => Some(Crease::Block {
+                    range: range.to_point(&self.buffer_snapshot),
+                    block_height: *block_height,
+                    block_style: *block_style,
+                    render_block: render_block.clone(),
+                    block_priority: *block_priority,
+                    render_toggle: render_toggle.clone(),
+                }),
+            }
         } else if self.starts_indent(MultiBufferRow(start.row))
             && !self.is_line_folded(MultiBufferRow(start.row))
         {
@@ -1110,7 +1214,13 @@ impl DisplaySnapshot {
                     .line_len(MultiBufferRow(row_before_line_breaks.row)),
             );
 
-            Some((start..row_before_line_breaks, self.fold_placeholder.clone()))
+            Some(Crease::Inline {
+                range: start..row_before_line_breaks,
+                placeholder: self.fold_placeholder.clone(),
+                render_toggle: None,
+                render_trailer: None,
+                metadata: None,
+            })
         } else {
             None
         }
@@ -1418,7 +1528,7 @@ pub mod tests {
                                         placement,
                                         style: BlockStyle::Fixed,
                                         height,
-                                        render: Box::new(|_| div().into_any()),
+                                        render: Arc::new(|_| div().into_any()),
                                         priority,
                                     }
                                 })
@@ -1457,7 +1567,8 @@ pub mod tests {
                             map.fold(
                                 ranges
                                     .into_iter()
-                                    .map(|range| (range, FoldPlaceholder::test())),
+                                    .map(|range| Crease::simple(range, FoldPlaceholder::test()))
+                                    .collect(),
                                 cx,
                             );
                         });
@@ -1832,7 +1943,7 @@ pub mod tests {
 
         map.update(cx, |map, cx| {
             map.fold(
-                vec![(
+                vec![Crease::simple(
                     MultiBufferPoint::new(0, 6)..MultiBufferPoint::new(3, 2),
                     FoldPlaceholder::test(),
                 )],
@@ -1922,7 +2033,7 @@ pub mod tests {
                     ),
                     height: 1,
                     style: BlockStyle::Sticky,
-                    render: Box::new(|_| div().into_any()),
+                    render: Arc::new(|_| div().into_any()),
                     priority: 0,
                 }],
                 cx,
@@ -2028,7 +2139,7 @@ pub mod tests {
                     ),
                     height: 1,
                     style: BlockStyle::Sticky,
-                    render: Box::new(|_| div().into_any()),
+                    render: Arc::new(|_| div().into_any()),
                     priority: 0,
                 }],
                 cx,
@@ -2104,7 +2215,7 @@ pub mod tests {
                     ),
                     height: 4,
                     style: BlockStyle::Fixed,
-                    render: Box::new(|_| div().into_any()),
+                    render: Arc::new(|_| div().into_any()),
                     priority: 0,
                 }],
                 cx,
@@ -2253,7 +2364,7 @@ pub mod tests {
 
         map.update(cx, |map, cx| {
             map.fold(
-                vec![(
+                vec![Crease::simple(
                     MultiBufferPoint::new(0, 6)..MultiBufferPoint::new(3, 2),
                     FoldPlaceholder::test(),
                 )],
@@ -2452,7 +2563,7 @@ pub mod tests {
                 snapshot.anchor_before(Point::new(2, 0))..snapshot.anchor_after(Point::new(3, 3));
 
             map.crease_map.insert(
-                [Crease::new(
+                [Crease::inline(
                     range,
                     FoldPlaceholder::test(),
                     |_row, _status, _toggle, _cx| div(),

crates/editor/src/display_map/block_map.rs 🔗

@@ -7,7 +7,7 @@ use collections::{Bound, HashMap, HashSet};
 use gpui::{AnyElement, EntityId, Pixels, WindowContext};
 use language::{Chunk, Patch, Point};
 use multi_buffer::{
-    Anchor, ExcerptId, ExcerptInfo, MultiBufferRow, MultiBufferSnapshot, ToPoint as _,
+    Anchor, ExcerptId, ExcerptInfo, MultiBufferRow, MultiBufferSnapshot, ToOffset, ToPoint as _,
 };
 use parking_lot::Mutex;
 use std::{
@@ -77,7 +77,7 @@ pub struct BlockRow(pub(super) u32);
 #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
 struct WrapRow(u32);
 
-pub type RenderBlock = Box<dyn Send + FnMut(&mut BlockContext) -> AnyElement>;
+pub type RenderBlock = Arc<dyn Send + Sync + Fn(&mut BlockContext) -> AnyElement>;
 
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub enum BlockPlacement<T> {
@@ -352,6 +352,13 @@ impl Block {
             Block::ExcerptBoundary { next_excerpt, .. } => next_excerpt.is_none(),
         }
     }
+
+    fn is_replacement(&self) -> bool {
+        match self {
+            Block::Custom(block) => matches!(block.placement, BlockPlacement::Replace(_)),
+            Block::ExcerptBoundary { .. } => false,
+        }
+    }
 }
 
 impl Debug for Block {
@@ -1119,6 +1126,64 @@ impl<'a> BlockMapWriter<'a> {
             .retain(|id, _| !block_ids.contains(id));
         self.0.sync(wrap_snapshot, edits);
     }
+
+    pub fn remove_intersecting_replace_blocks<T>(
+        &mut self,
+        ranges: impl IntoIterator<Item = Range<T>>,
+        inclusive: bool,
+    ) where
+        T: ToOffset,
+    {
+        let wrap_snapshot = self.0.wrap_snapshot.borrow();
+        let mut blocks_to_remove = HashSet::default();
+        for range in ranges {
+            let range = range.start.to_offset(wrap_snapshot.buffer_snapshot())
+                ..range.end.to_offset(wrap_snapshot.buffer_snapshot());
+            for block in self.blocks_intersecting_buffer_range(range, inclusive) {
+                if matches!(block.placement, BlockPlacement::Replace(_)) {
+                    blocks_to_remove.insert(block.id);
+                }
+            }
+        }
+        drop(wrap_snapshot);
+        self.remove(blocks_to_remove);
+    }
+
+    fn blocks_intersecting_buffer_range(
+        &self,
+        range: Range<usize>,
+        inclusive: bool,
+    ) -> &[Arc<CustomBlock>] {
+        let wrap_snapshot = self.0.wrap_snapshot.borrow();
+        let buffer = wrap_snapshot.buffer_snapshot();
+        let start_block_ix = match self.0.custom_blocks.binary_search_by(|probe| {
+            probe
+                .end()
+                .to_offset(buffer)
+                .cmp(&range.start)
+                .then(if inclusive {
+                    Ordering::Greater
+                } else {
+                    Ordering::Less
+                })
+        }) {
+            Ok(ix) | Err(ix) => ix,
+        };
+        let end_block_ix = match self.0.custom_blocks.binary_search_by(|probe| {
+            probe
+                .start()
+                .to_offset(buffer)
+                .cmp(&range.end)
+                .then(if inclusive {
+                    Ordering::Less
+                } else {
+                    Ordering::Greater
+                })
+        }) {
+            Ok(ix) | Err(ix) => ix,
+        };
+        &self.0.custom_blocks[start_block_ix..end_block_ix]
+    }
 }
 
 impl BlockSnapshot {
@@ -1298,6 +1363,21 @@ impl BlockSnapshot {
         cursor.item().map_or(false, |t| t.block.is_some())
     }
 
+    pub(super) fn is_line_replaced(&self, row: MultiBufferRow) -> bool {
+        let wrap_point = self
+            .wrap_snapshot
+            .make_wrap_point(Point::new(row.0, 0), Bias::Left);
+        let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>(&());
+        cursor.seek(&WrapRow(wrap_point.row()), Bias::Right, &());
+        cursor.item().map_or(false, |transform| {
+            if let Some(Block::Custom(block)) = transform.block.as_ref() {
+                matches!(block.placement, BlockPlacement::Replace(_))
+            } else {
+                false
+            }
+        })
+    }
+
     pub fn clip_point(&self, point: BlockPoint, bias: Bias) -> BlockPoint {
         let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
         cursor.seek(&BlockRow(point.row), Bias::Right, &());
@@ -1515,7 +1595,7 @@ impl<'a> Iterator for BlockChunks<'a> {
 }
 
 impl<'a> Iterator for BlockBufferRows<'a> {
-    type Item = Option<BlockRow>;
+    type Item = Option<u32>;
 
     fn next(&mut self) -> Option<Self::Item> {
         if self.started {
@@ -1538,16 +1618,25 @@ impl<'a> Iterator for BlockBufferRows<'a> {
                 }
             }
 
-            if self.transforms.item()?.block.is_none() {
+            let transform = self.transforms.item()?;
+            if transform
+                .block
+                .as_ref()
+                .map_or(true, |block| block.is_replacement())
+            {
                 self.input_buffer_rows.seek(self.transforms.start().1 .0);
             }
         }
 
         let transform = self.transforms.item()?;
-        if transform.block.is_some() {
-            Some(None)
+        if let Some(block) = transform.block.as_ref() {
+            if block.is_replacement() && self.transforms.start().0 == self.output_row {
+                Some(self.input_buffer_rows.next().unwrap())
+            } else {
+                Some(None)
+            }
         } else {
-            Some(self.input_buffer_rows.next().unwrap().map(BlockRow))
+            Some(self.input_buffer_rows.next().unwrap())
         }
     }
 }
@@ -1709,21 +1798,21 @@ mod tests {
                 style: BlockStyle::Fixed,
                 placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
                 height: 1,
-                render: Box::new(|_| div().into_any()),
+                render: Arc::new(|_| div().into_any()),
                 priority: 0,
             },
             BlockProperties {
                 style: BlockStyle::Fixed,
                 placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
                 height: 2,
-                render: Box::new(|_| div().into_any()),
+                render: Arc::new(|_| div().into_any()),
                 priority: 0,
             },
             BlockProperties {
                 style: BlockStyle::Fixed,
                 placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
                 height: 3,
-                render: Box::new(|_| div().into_any()),
+                render: Arc::new(|_| div().into_any()),
                 priority: 0,
             },
         ]);
@@ -1821,10 +1910,7 @@ mod tests {
         );
 
         assert_eq!(
-            snapshot
-                .buffer_rows(BlockRow(0))
-                .map(|row| row.map(|r| r.0))
-                .collect::<Vec<_>>(),
+            snapshot.buffer_rows(BlockRow(0)).collect::<Vec<_>>(),
             &[
                 Some(0),
                 None,
@@ -1960,21 +2046,21 @@ mod tests {
                 style: BlockStyle::Fixed,
                 placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
                 height: 1,
-                render: Box::new(|_| div().into_any()),
+                render: Arc::new(|_| div().into_any()),
                 priority: 0,
             },
             BlockProperties {
                 style: BlockStyle::Fixed,
                 placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
                 height: 2,
-                render: Box::new(|_| div().into_any()),
+                render: Arc::new(|_| div().into_any()),
                 priority: 0,
             },
             BlockProperties {
                 style: BlockStyle::Fixed,
                 placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
                 height: 3,
-                render: Box::new(|_| div().into_any()),
+                render: Arc::new(|_| div().into_any()),
                 priority: 0,
             },
         ]);
@@ -2062,14 +2148,14 @@ mod tests {
             BlockProperties {
                 style: BlockStyle::Fixed,
                 placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 12))),
-                render: Box::new(|_| div().into_any()),
+                render: Arc::new(|_| div().into_any()),
                 height: 1,
                 priority: 0,
             },
             BlockProperties {
                 style: BlockStyle::Fixed,
                 placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 1))),
-                render: Box::new(|_| div().into_any()),
+                render: Arc::new(|_| div().into_any()),
                 height: 1,
                 priority: 0,
             },
@@ -2109,7 +2195,7 @@ mod tests {
                     ..buffer_snapshot.anchor_before(Point::new(3, 1)),
             ),
             height: 4,
-            render: Box::new(|_| div().into_any()),
+            render: Arc::new(|_| div().into_any()),
             priority: 0,
         }]);
 
@@ -2162,14 +2248,14 @@ mod tests {
                 style: BlockStyle::Fixed,
                 placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 3))),
                 height: 1,
-                render: Box::new(|_| div().into_any()),
+                render: Arc::new(|_| div().into_any()),
                 priority: 0,
             },
             BlockProperties {
                 style: BlockStyle::Fixed,
                 placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(6, 2))),
                 height: 1,
-                render: Box::new(|_| div().into_any()),
+                render: Arc::new(|_| div().into_any()),
                 priority: 0,
             },
         ]);
@@ -2183,21 +2269,21 @@ mod tests {
                 style: BlockStyle::Fixed,
                 placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 3))),
                 height: 1,
-                render: Box::new(|_| div().into_any()),
+                render: Arc::new(|_| div().into_any()),
                 priority: 0,
             },
             BlockProperties {
                 style: BlockStyle::Fixed,
                 placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 1))),
                 height: 1,
-                render: Box::new(|_| div().into_any()),
+                render: Arc::new(|_| div().into_any()),
                 priority: 0,
             },
             BlockProperties {
                 style: BlockStyle::Fixed,
                 placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(6, 1))),
                 height: 1,
-                render: Box::new(|_| div().into_any()),
+                render: Arc::new(|_| div().into_any()),
                 priority: 0,
             },
         ]);
@@ -2302,7 +2388,7 @@ mod tests {
                                 style: BlockStyle::Fixed,
                                 placement,
                                 height,
-                                render: Box::new(|_| div().into_any()),
+                                render: Arc::new(|_| div().into_any()),
                                 priority: 0,
                             }
                         })
@@ -2321,7 +2407,7 @@ mod tests {
                         placement: props.placement.clone(),
                         height: props.height,
                         style: props.style,
-                        render: Box::new(|_| div().into_any()),
+                        render: Arc::new(|_| div().into_any()),
                         priority: 0,
                     }));
                 }
@@ -2409,6 +2495,7 @@ mod tests {
             let mut expected_buffer_rows = Vec::new();
             let mut expected_text = String::new();
             let mut expected_block_positions = Vec::new();
+            let mut expected_replaced_buffer_rows = HashSet::default();
             let input_text = wraps_snapshot.text();
 
             // Loop over the input lines, creating (N - 1) empty lines for
@@ -2422,6 +2509,9 @@ mod tests {
             let mut block_row = 0;
             while let Some((wrap_row, input_line)) = input_text_lines.next() {
                 let wrap_row = wrap_row as u32;
+                let multibuffer_row = wraps_snapshot
+                    .to_point(WrapPoint::new(wrap_row, 0), Bias::Left)
+                    .row;
 
                 // Create empty lines for the above block
                 while let Some((placement, block)) = sorted_blocks_iter.peek() {
@@ -2451,30 +2541,33 @@ mod tests {
                 {
                     if wrap_row >= replace_range.start.0 {
                         is_in_replace_block = true;
+
+                        if wrap_row == replace_range.start.0 {
+                            expected_buffer_rows.push(input_buffer_rows[multibuffer_row as usize]);
+                        }
+
                         if wrap_row == replace_range.end.0 {
                             expected_block_positions.push((block_row, block.id()));
-                            if block.height() > 0 {
-                                let text = "\n".repeat((block.height() - 1) as usize);
-                                if block_row > 0 {
-                                    expected_text.push('\n');
-                                }
-                                expected_text.push_str(&text);
-                                for _ in 0..block.height() {
-                                    expected_buffer_rows.push(None);
-                                }
-                                block_row += block.height();
+                            let text = "\n".repeat((block.height() - 1) as usize);
+                            if block_row > 0 {
+                                expected_text.push('\n');
                             }
+                            expected_text.push_str(&text);
+
+                            for _ in 1..block.height() {
+                                expected_buffer_rows.push(None);
+                            }
+                            block_row += block.height();
 
                             sorted_blocks_iter.next();
                         }
                     }
                 }
 
-                if !is_in_replace_block {
-                    let buffer_row = input_buffer_rows[wraps_snapshot
-                        .to_point(WrapPoint::new(wrap_row, 0), Bias::Left)
-                        .row as usize];
-
+                if is_in_replace_block {
+                    expected_replaced_buffer_rows.insert(MultiBufferRow(multibuffer_row));
+                } else {
+                    let buffer_row = input_buffer_rows[multibuffer_row as usize];
                     let soft_wrapped = wraps_snapshot
                         .to_tab_point(WrapPoint::new(wrap_row, 0))
                         .column()
@@ -2543,9 +2636,10 @@ mod tests {
                 assert_eq!(
                     blocks_snapshot
                         .buffer_rows(BlockRow(start_row as u32))
-                        .map(|row| row.map(|r| r.0))
                         .collect::<Vec<_>>(),
-                    &expected_buffer_rows[start_row..]
+                    &expected_buffer_rows[start_row..],
+                    "incorrect buffer_rows starting at row {:?}",
+                    start_row
                 );
             }
 
@@ -2666,6 +2760,16 @@ mod tests {
                     block_point.column += c.len_utf8() as u32;
                 }
             }
+
+            for buffer_row in 0..=buffer_snapshot.max_point().row {
+                let buffer_row = MultiBufferRow(buffer_row);
+                assert_eq!(
+                    blocks_snapshot.is_line_replaced(buffer_row),
+                    expected_replaced_buffer_rows.contains(&buffer_row),
+                    "incorrect is_line_replaced({:?})",
+                    buffer_row
+                );
+            }
         }
     }
 

crates/editor/src/display_map/crease_map.rs 🔗

@@ -2,12 +2,12 @@ use collections::HashMap;
 use gpui::{AnyElement, IntoElement};
 use multi_buffer::{Anchor, AnchorRangeExt, MultiBufferRow, MultiBufferSnapshot, ToPoint};
 use serde::{Deserialize, Serialize};
-use std::{cmp::Ordering, ops::Range, sync::Arc};
+use std::{cmp::Ordering, fmt::Debug, ops::Range, sync::Arc};
 use sum_tree::{Bias, SeekTarget, SumTree};
 use text::Point;
 use ui::{IconName, SharedString, WindowContext};
 
-use crate::FoldPlaceholder;
+use crate::{BlockStyle, FoldPlaceholder, RenderBlock};
 
 #[derive(Copy, Clone, Default, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
 pub struct CreaseId(usize);
@@ -45,15 +45,15 @@ impl CreaseSnapshot {
         &'a self,
         row: MultiBufferRow,
         snapshot: &'a MultiBufferSnapshot,
-    ) -> Option<&'a Crease> {
+    ) -> Option<&'a Crease<Anchor>> {
         let start = snapshot.anchor_before(Point::new(row.0, 0));
         let mut cursor = self.creases.cursor::<ItemSummary>(snapshot);
         cursor.seek(&start, Bias::Left, snapshot);
         while let Some(item) = cursor.item() {
-            match Ord::cmp(&item.crease.range.start.to_point(snapshot).row, &row.0) {
+            match Ord::cmp(&item.crease.range().start.to_point(snapshot).row, &row.0) {
                 Ordering::Less => cursor.next(snapshot),
                 Ordering::Equal => {
-                    if item.crease.range.start.is_valid(snapshot) {
+                    if item.crease.range().start.is_valid(snapshot) {
                         return Some(&item.crease);
                     } else {
                         cursor.next(snapshot);
@@ -69,7 +69,7 @@ impl CreaseSnapshot {
         &'a self,
         range: Range<MultiBufferRow>,
         snapshot: &'a MultiBufferSnapshot,
-    ) -> impl 'a + Iterator<Item = &'a Crease> {
+    ) -> impl 'a + Iterator<Item = &'a Crease<Anchor>> {
         let start = snapshot.anchor_before(Point::new(range.start.0, 0));
         let mut cursor = self.creases.cursor::<ItemSummary>(snapshot);
         cursor.seek(&start, Bias::Left, snapshot);
@@ -77,8 +77,9 @@ impl CreaseSnapshot {
         std::iter::from_fn(move || {
             while let Some(item) = cursor.item() {
                 cursor.next(snapshot);
-                let crease_start = item.crease.range.start.to_point(snapshot);
-                let crease_end = item.crease.range.end.to_point(snapshot);
+                let crease_range = item.crease.range();
+                let crease_start = crease_range.start.to_point(snapshot);
+                let crease_end = crease_range.end.to_point(snapshot);
                 if crease_end.row > range.end.0 {
                     continue;
                 }
@@ -99,8 +100,9 @@ impl CreaseSnapshot {
 
         cursor.next(snapshot);
         while let Some(item) = cursor.item() {
-            let start_point = item.crease.range.start.to_point(snapshot);
-            let end_point = item.crease.range.end.to_point(snapshot);
+            let crease_range = item.crease.range();
+            let start_point = crease_range.start.to_point(snapshot);
+            let end_point = crease_range.end.to_point(snapshot);
             results.push((item.id, start_point..end_point));
             cursor.next(snapshot);
         }
@@ -123,12 +125,22 @@ type RenderTrailerFn =
     Arc<dyn Send + Sync + Fn(MultiBufferRow, bool, &mut WindowContext) -> AnyElement>;
 
 #[derive(Clone)]
-pub struct Crease {
-    pub range: Range<Anchor>,
-    pub placeholder: FoldPlaceholder,
-    pub render_toggle: RenderToggleFn,
-    pub render_trailer: RenderTrailerFn,
-    pub metadata: Option<CreaseMetadata>,
+pub enum Crease<T> {
+    Inline {
+        range: Range<T>,
+        placeholder: FoldPlaceholder,
+        render_toggle: Option<RenderToggleFn>,
+        render_trailer: Option<RenderTrailerFn>,
+        metadata: Option<CreaseMetadata>,
+    },
+    Block {
+        range: Range<T>,
+        block_height: u32,
+        block_style: BlockStyle,
+        render_block: RenderBlock,
+        block_priority: usize,
+        render_toggle: Option<RenderToggleFn>,
+    },
 }
 
 /// Metadata about a [`Crease`], that is used for serialization.
@@ -138,9 +150,30 @@ pub struct CreaseMetadata {
     pub label: SharedString,
 }
 
-impl Crease {
-    pub fn new<RenderToggle, ToggleElement, RenderTrailer, TrailerElement>(
-        range: Range<Anchor>,
+impl<T> Crease<T> {
+    pub fn simple(range: Range<T>, placeholder: FoldPlaceholder) -> Self {
+        Crease::Inline {
+            range,
+            placeholder,
+            render_toggle: None,
+            render_trailer: None,
+            metadata: None,
+        }
+    }
+
+    pub fn block(range: Range<T>, height: u32, style: BlockStyle, render: RenderBlock) -> Self {
+        Self::Block {
+            range,
+            block_height: height,
+            block_style: style,
+            render_block: render,
+            block_priority: 0,
+            render_toggle: None,
+        }
+    }
+
+    pub fn inline<RenderToggle, ToggleElement, RenderTrailer, TrailerElement>(
+        range: Range<T>,
         placeholder: FoldPlaceholder,
         render_toggle: RenderToggle,
         render_trailer: RenderTrailer,
@@ -164,37 +197,76 @@ impl Crease {
             + 'static,
         TrailerElement: IntoElement,
     {
-        Crease {
+        Crease::Inline {
             range,
             placeholder,
-            render_toggle: Arc::new(move |row, folded, toggle, cx| {
+            render_toggle: Some(Arc::new(move |row, folded, toggle, cx| {
                 render_toggle(row, folded, toggle, cx).into_any_element()
-            }),
-            render_trailer: Arc::new(move |row, folded, cx| {
+            })),
+            render_trailer: Some(Arc::new(move |row, folded, cx| {
                 render_trailer(row, folded, cx).into_any_element()
-            }),
+            })),
             metadata: None,
         }
     }
 
-    pub fn with_metadata(mut self, metadata: CreaseMetadata) -> Self {
-        self.metadata = Some(metadata);
-        self
+    pub fn with_metadata(self, metadata: CreaseMetadata) -> Self {
+        match self {
+            Crease::Inline {
+                range,
+                placeholder,
+                render_toggle,
+                render_trailer,
+                ..
+            } => Crease::Inline {
+                range,
+                placeholder,
+                render_toggle,
+                render_trailer,
+                metadata: Some(metadata),
+            },
+            Crease::Block { .. } => self,
+        }
+    }
+
+    pub fn range(&self) -> &Range<T> {
+        match self {
+            Crease::Inline { range, .. } => range,
+            Crease::Block { range, .. } => range,
+        }
     }
 }
 
-impl std::fmt::Debug for Crease {
+impl<T> std::fmt::Debug for Crease<T>
+where
+    T: Debug,
+{
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("Crease")
-            .field("range", &self.range)
-            .finish()
+        match self {
+            Crease::Inline {
+                range, metadata, ..
+            } => f
+                .debug_struct("Crease::Inline")
+                .field("range", range)
+                .field("metadata", metadata)
+                .finish_non_exhaustive(),
+            Crease::Block {
+                range,
+                block_height,
+                ..
+            } => f
+                .debug_struct("Crease::Block")
+                .field("range", range)
+                .field("height", block_height)
+                .finish_non_exhaustive(),
+        }
     }
 }
 
 #[derive(Clone, Debug)]
 struct CreaseItem {
     id: CreaseId,
-    crease: Crease,
+    crease: Crease<Anchor>,
 }
 
 impl CreaseMap {
@@ -204,7 +276,7 @@ impl CreaseMap {
 
     pub fn insert(
         &mut self,
-        creases: impl IntoIterator<Item = Crease>,
+        creases: impl IntoIterator<Item = Crease<Anchor>>,
         snapshot: &MultiBufferSnapshot,
     ) -> Vec<CreaseId> {
         let mut new_ids = Vec::new();
@@ -212,11 +284,12 @@ impl CreaseMap {
             let mut new_creases = SumTree::new(snapshot);
             let mut cursor = self.snapshot.creases.cursor::<ItemSummary>(snapshot);
             for crease in creases {
-                new_creases.append(cursor.slice(&crease.range, Bias::Left, snapshot), snapshot);
+                let crease_range = crease.range().clone();
+                new_creases.append(cursor.slice(&crease_range, Bias::Left, snapshot), snapshot);
 
                 let id = self.next_id;
                 self.next_id.0 += 1;
-                self.id_to_range.insert(id, crease.range.clone());
+                self.id_to_range.insert(id, crease_range);
                 new_creases.push(CreaseItem { crease, id }, snapshot);
                 new_ids.push(id);
             }
@@ -293,7 +366,7 @@ impl sum_tree::Item for CreaseItem {
 
     fn summary(&self, _cx: &MultiBufferSnapshot) -> Self::Summary {
         ItemSummary {
-            range: self.crease.range.clone(),
+            range: self.crease.range().clone(),
         }
     }
 }
@@ -326,13 +399,13 @@ mod test {
 
         // Insert creases
         let creases = [
-            Crease::new(
+            Crease::inline(
                 snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(1, 5)),
                 FoldPlaceholder::test(),
                 |_row, _folded, _toggle, _cx| div(),
                 |_row, _folded, _cx| div(),
             ),
-            Crease::new(
+            Crease::inline(
                 snapshot.anchor_before(Point::new(3, 0))..snapshot.anchor_after(Point::new(3, 5)),
                 FoldPlaceholder::test(),
                 |_row, _folded, _toggle, _cx| div(),
@@ -372,19 +445,19 @@ mod test {
         let mut crease_map = CreaseMap::new(&snapshot);
 
         let creases = [
-            Crease::new(
+            Crease::inline(
                 snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(1, 5)),
                 FoldPlaceholder::test(),
                 |_row, _folded, _toggle, _cx| div(),
                 |_row, _folded, _cx| div(),
             ),
-            Crease::new(
+            Crease::inline(
                 snapshot.anchor_before(Point::new(3, 0))..snapshot.anchor_after(Point::new(3, 5)),
                 FoldPlaceholder::test(),
                 |_row, _folded, _toggle, _cx| div(),
                 |_row, _folded, _cx| div(),
             ),
-            Crease::new(
+            Crease::inline(
                 snapshot.anchor_before(Point::new(5, 0))..snapshot.anchor_after(Point::new(5, 5)),
                 FoldPlaceholder::test(),
                 |_row, _folded, _toggle, _cx| div(),
@@ -402,12 +475,12 @@ mod test {
         let range = MultiBufferRow(2)..MultiBufferRow(5);
         let creases: Vec<_> = crease_snapshot.creases_in_range(range, &snapshot).collect();
         assert_eq!(creases.len(), 1);
-        assert_eq!(creases[0].range.start.to_point(&snapshot).row, 3);
+        assert_eq!(creases[0].range().start.to_point(&snapshot).row, 3);
 
         let range = MultiBufferRow(0)..MultiBufferRow(2);
         let creases: Vec<_> = crease_snapshot.creases_in_range(range, &snapshot).collect();
         assert_eq!(creases.len(), 1);
-        assert_eq!(creases[0].range.start.to_point(&snapshot).row, 1);
+        assert_eq!(creases[0].range().start.to_point(&snapshot).row, 1);
 
         let range = MultiBufferRow(6)..MultiBufferRow(7);
         let creases: Vec<_> = crease_snapshot.creases_in_range(range, &snapshot).collect();

crates/editor/src/editor.rs 🔗

@@ -6779,7 +6779,7 @@ impl Editor {
 
         let mut edits = Vec::new();
         let mut unfold_ranges = Vec::new();
-        let mut refold_ranges = Vec::new();
+        let mut refold_creases = Vec::new();
 
         let selections = self.selections.all::<Point>(cx);
         let mut selections = selections.iter().peekable();
@@ -6854,7 +6854,7 @@ impl Editor {
                         let mut end = fold.range.end.to_point(&buffer);
                         start.row -= row_delta;
                         end.row -= row_delta;
-                        refold_ranges.push((start..end, fold.placeholder.clone()));
+                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
                     }
                 }
             }
@@ -6870,7 +6870,7 @@ impl Editor {
                     buffer.edit([(range, text)], None, cx);
                 }
             });
-            this.fold_ranges(refold_ranges, true, cx);
+            this.fold_creases(refold_creases, true, cx);
             this.change_selections(Some(Autoscroll::fit()), cx, |s| {
                 s.select(new_selections);
             })
@@ -6883,7 +6883,7 @@ impl Editor {
 
         let mut edits = Vec::new();
         let mut unfold_ranges = Vec::new();
-        let mut refold_ranges = Vec::new();
+        let mut refold_creases = Vec::new();
 
         let selections = self.selections.all::<Point>(cx);
         let mut selections = selections.iter().peekable();
@@ -6948,7 +6948,7 @@ impl Editor {
                         let mut end = fold.range.end.to_point(&buffer);
                         start.row += row_delta;
                         end.row += row_delta;
-                        refold_ranges.push((start..end, fold.placeholder.clone()));
+                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
                     }
                 }
             }
@@ -6964,7 +6964,7 @@ impl Editor {
                     buffer.edit([(range, text)], None, cx);
                 }
             });
-            this.fold_ranges(refold_ranges, true, cx);
+            this.fold_creases(refold_creases, true, cx);
             this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
         });
     }
@@ -10421,7 +10421,7 @@ impl Editor {
                             style: BlockStyle::Flex,
                             placement: BlockPlacement::Below(range.start),
                             height: 1,
-                            render: Box::new({
+                            render: Arc::new({
                                 let rename_editor = rename_editor.clone();
                                 move |cx: &mut BlockContext| {
                                     let mut text_style = cx.editor_style.text.clone();
@@ -10431,6 +10431,7 @@ impl Editor {
                                         text_style = text_style.highlight(highlight_style);
                                     }
                                     div()
+                                        .occlude()
                                         .pl(cx.anchor_x)
                                         .child(EditorElement::new(
                                             &rename_editor,
@@ -10894,7 +10895,7 @@ impl Editor {
     }
 
     pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext<Self>) {
-        let mut fold_ranges = Vec::new();
+        let mut to_fold = Vec::new();
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
         let selections = self.selections.all_adjusted(cx);
 
@@ -10906,12 +10907,10 @@ impl Editor {
                 let mut found = false;
                 let mut row = range.start.row;
                 while row <= range.end.row {
-                    if let Some((foldable_range, fold_text)) =
-                        { display_map.foldable_range(MultiBufferRow(row)) }
-                    {
+                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
                         found = true;
-                        row = foldable_range.end.row + 1;
-                        fold_ranges.push((foldable_range, fold_text));
+                        row = crease.range().end.row + 1;
+                        to_fold.push(crease);
                     } else {
                         row += 1
                     }
@@ -10922,11 +10921,9 @@ impl Editor {
             }
 
             for row in (0..=range.start.row).rev() {
-                if let Some((foldable_range, fold_text)) =
-                    display_map.foldable_range(MultiBufferRow(row))
-                {
-                    if foldable_range.end.row >= buffer_start_row {
-                        fold_ranges.push((foldable_range, fold_text));
+                if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
+                    if crease.range().end.row >= buffer_start_row {
+                        to_fold.push(crease);
                         if row <= range.start.row {
                             break;
                         }
@@ -10935,26 +10932,29 @@ impl Editor {
             }
         }
 
-        self.fold_ranges(fold_ranges, true, cx);
+        self.fold_creases(to_fold, true, cx);
     }
 
     fn fold_at_level(&mut self, fold_at: &FoldAtLevel, cx: &mut ViewContext<Self>) {
         let fold_at_level = fold_at.level;
         let snapshot = self.buffer.read(cx).snapshot(cx);
-        let mut fold_ranges = Vec::new();
+        let mut to_fold = Vec::new();
         let mut stack = vec![(0, snapshot.max_buffer_row().0, 1)];
 
         while let Some((mut start_row, end_row, current_level)) = stack.pop() {
             while start_row < end_row {
-                match self.snapshot(cx).foldable_range(MultiBufferRow(start_row)) {
-                    Some(foldable_range) => {
-                        let nested_start_row = foldable_range.0.start.row + 1;
-                        let nested_end_row = foldable_range.0.end.row;
+                match self
+                    .snapshot(cx)
+                    .crease_for_buffer_row(MultiBufferRow(start_row))
+                {
+                    Some(crease) => {
+                        let nested_start_row = crease.range().start.row + 1;
+                        let nested_end_row = crease.range().end.row;
 
                         if current_level < fold_at_level {
                             stack.push((nested_start_row, nested_end_row, current_level + 1));
                         } else if current_level == fold_at_level {
-                            fold_ranges.push(foldable_range);
+                            to_fold.push(crease);
                         }
 
                         start_row = nested_end_row + 1;
@@ -10964,7 +10964,7 @@ impl Editor {
             }
         }
 
-        self.fold_ranges(fold_ranges, true, cx);
+        self.fold_creases(to_fold, true, cx);
     }
 
     pub fn fold_all(&mut self, _: &actions::FoldAll, cx: &mut ViewContext<Self>) {
@@ -10972,16 +10972,18 @@ impl Editor {
         let snapshot = self.buffer.read(cx).snapshot(cx);
 
         for row in 0..snapshot.max_buffer_row().0 {
-            if let Some(foldable_range) = self.snapshot(cx).foldable_range(MultiBufferRow(row)) {
+            if let Some(foldable_range) =
+                self.snapshot(cx).crease_for_buffer_row(MultiBufferRow(row))
+            {
                 fold_ranges.push(foldable_range);
             }
         }
 
-        self.fold_ranges(fold_ranges, true, cx);
+        self.fold_creases(fold_ranges, true, cx);
     }
 
     pub fn fold_recursive(&mut self, _: &actions::FoldRecursive, cx: &mut ViewContext<Self>) {
-        let mut fold_ranges = Vec::new();
+        let mut to_fold = Vec::new();
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
         let selections = self.selections.all_adjusted(cx);
 
@@ -10992,11 +10994,9 @@ impl Editor {
             if range.start.row != range.end.row {
                 let mut found = false;
                 for row in range.start.row..=range.end.row {
-                    if let Some((foldable_range, fold_text)) =
-                        { display_map.foldable_range(MultiBufferRow(row)) }
-                    {
+                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
                         found = true;
-                        fold_ranges.push((foldable_range, fold_text));
+                        to_fold.push(crease);
                     }
                 }
                 if found {
@@ -11005,11 +11005,9 @@ impl Editor {
             }
 
             for row in (0..=range.start.row).rev() {
-                if let Some((foldable_range, fold_text)) =
-                    display_map.foldable_range(MultiBufferRow(row))
-                {
-                    if foldable_range.end.row >= buffer_start_row {
-                        fold_ranges.push((foldable_range, fold_text));
+                if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
+                    if crease.range().end.row >= buffer_start_row {
+                        to_fold.push(crease);
                     } else {
                         break;
                     }
@@ -11017,21 +11015,21 @@ impl Editor {
             }
         }
 
-        self.fold_ranges(fold_ranges, true, cx);
+        self.fold_creases(to_fold, true, cx);
     }
 
     pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
         let buffer_row = fold_at.buffer_row;
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 
-        if let Some((fold_range, placeholder)) = display_map.foldable_range(buffer_row) {
+        if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
             let autoscroll = self
                 .selections
                 .all::<Point>(cx)
                 .iter()
-                .any(|selection| fold_range.overlaps(&selection.range()));
+                .any(|selection| crease.range().overlaps(&selection.range()));
 
-            self.fold_ranges([(fold_range, placeholder)], autoscroll, cx);
+            self.fold_creases(vec![crease], autoscroll, cx);
         }
     }
 
@@ -11092,81 +11090,78 @@ impl Editor {
 
     pub fn unfold_all(&mut self, _: &actions::UnfoldAll, cx: &mut ViewContext<Self>) {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        self.unfold_ranges(
-            &[Point::zero()..display_map.max_point().to_point(&display_map)],
-            true,
-            true,
-            cx,
-        );
+        self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
     }
 
     pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
         let selections = self.selections.all::<Point>(cx);
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
         let line_mode = self.selections.line_mode;
-        let ranges = selections.into_iter().map(|s| {
-            if line_mode {
-                let start = Point::new(s.start.row, 0);
-                let end = Point::new(
-                    s.end.row,
-                    display_map
-                        .buffer_snapshot
-                        .line_len(MultiBufferRow(s.end.row)),
-                );
-                (start..end, display_map.fold_placeholder.clone())
-            } else {
-                (s.start..s.end, display_map.fold_placeholder.clone())
-            }
-        });
-        self.fold_ranges(ranges, true, cx);
+        let ranges = selections
+            .into_iter()
+            .map(|s| {
+                if line_mode {
+                    let start = Point::new(s.start.row, 0);
+                    let end = Point::new(
+                        s.end.row,
+                        display_map
+                            .buffer_snapshot
+                            .line_len(MultiBufferRow(s.end.row)),
+                    );
+                    Crease::simple(start..end, display_map.fold_placeholder.clone())
+                } else {
+                    Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
+                }
+            })
+            .collect::<Vec<_>>();
+        self.fold_creases(ranges, true, cx);
     }
 
-    pub fn fold_ranges<T: ToOffset + Clone>(
+    pub fn fold_creases<T: ToOffset + Clone>(
         &mut self,
-        ranges: impl IntoIterator<Item = (Range<T>, FoldPlaceholder)>,
+        creases: Vec<Crease<T>>,
         auto_scroll: bool,
         cx: &mut ViewContext<Self>,
     ) {
-        let mut fold_ranges = Vec::new();
+        if creases.is_empty() {
+            return;
+        }
+
         let mut buffers_affected = HashMap::default();
         let multi_buffer = self.buffer().read(cx);
-        for (fold_range, fold_text) in ranges {
+        for crease in &creases {
             if let Some((_, buffer, _)) =
-                multi_buffer.excerpt_containing(fold_range.start.clone(), cx)
+                multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
             {
                 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
             };
-            fold_ranges.push((fold_range, fold_text));
         }
 
-        let mut ranges = fold_ranges.into_iter().peekable();
-        if ranges.peek().is_some() {
-            self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
+        self.display_map.update(cx, |map, cx| map.fold(creases, cx));
 
-            if auto_scroll {
-                self.request_autoscroll(Autoscroll::fit(), cx);
-            }
+        if auto_scroll {
+            self.request_autoscroll(Autoscroll::fit(), cx);
+        }
 
-            for buffer in buffers_affected.into_values() {
-                self.sync_expanded_diff_hunks(buffer, cx);
-            }
+        for buffer in buffers_affected.into_values() {
+            self.sync_expanded_diff_hunks(buffer, cx);
+        }
 
-            cx.notify();
+        cx.notify();
 
-            if let Some(active_diagnostics) = self.active_diagnostics.take() {
-                // Clear diagnostics block when folding a range that contains it.
-                let snapshot = self.snapshot(cx);
-                if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
-                    drop(snapshot);
-                    self.active_diagnostics = Some(active_diagnostics);
-                    self.dismiss_diagnostics(cx);
-                } else {
-                    self.active_diagnostics = Some(active_diagnostics);
-                }
+        if let Some(active_diagnostics) = self.active_diagnostics.take() {
+            // Clear diagnostics block when folding a range that contains it.
+            let snapshot = self.snapshot(cx);
+            if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
+                drop(snapshot);
+                self.active_diagnostics = Some(active_diagnostics);
+                self.dismiss_diagnostics(cx);
+            } else {
+                self.active_diagnostics = Some(active_diagnostics);
             }
-
-            self.scrollbar_marker_state.dirty = true;
         }
+
+        self.scrollbar_marker_state.dirty = true;
     }
 
     /// Removes any folds whose ranges intersect any of the given ranges.
@@ -11215,6 +11210,7 @@ impl Editor {
         }
 
         self.display_map.update(cx, update);
+
         if auto_scroll {
             self.request_autoscroll(Autoscroll::fit(), cx);
         }
@@ -11317,7 +11313,7 @@ impl Editor {
 
     pub fn insert_creases(
         &mut self,
-        creases: impl IntoIterator<Item = Crease>,
+        creases: impl IntoIterator<Item = Crease<Anchor>>,
         cx: &mut ViewContext<Self>,
     ) -> Vec<CreaseId> {
         self.display_map
@@ -14056,7 +14052,7 @@ impl EditorSnapshot {
         }
     }
 
-    pub fn render_fold_toggle(
+    pub fn render_crease_toggle(
         &self,
         buffer_row: MultiBufferRow,
         row_contains_cursor: bool,
@@ -14064,34 +14060,38 @@ impl EditorSnapshot {
         cx: &mut WindowContext,
     ) -> Option<AnyElement> {
         let folded = self.is_line_folded(buffer_row);
+        let mut is_foldable = false;
 
         if let Some(crease) = self
             .crease_snapshot
             .query_row(buffer_row, &self.buffer_snapshot)
         {
-            let toggle_callback = Arc::new(move |folded, cx: &mut WindowContext| {
-                if folded {
-                    editor.update(cx, |editor, cx| {
-                        editor.fold_at(&crate::FoldAt { buffer_row }, cx)
-                    });
-                } else {
-                    editor.update(cx, |editor, cx| {
-                        editor.unfold_at(&crate::UnfoldAt { buffer_row }, cx)
-                    });
+            is_foldable = true;
+            match crease {
+                Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
+                    if let Some(render_toggle) = render_toggle {
+                        let toggle_callback = Arc::new(move |folded, cx: &mut WindowContext| {
+                            if folded {
+                                editor.update(cx, |editor, cx| {
+                                    editor.fold_at(&crate::FoldAt { buffer_row }, cx)
+                                });
+                            } else {
+                                editor.update(cx, |editor, cx| {
+                                    editor.unfold_at(&crate::UnfoldAt { buffer_row }, cx)
+                                });
+                            }
+                        });
+                        return Some((render_toggle)(buffer_row, folded, toggle_callback, cx));
+                    }
                 }
-            });
+            }
+        }
 
-            Some((crease.render_toggle)(
-                buffer_row,
-                folded,
-                toggle_callback,
-                cx,
-            ))
-        } else if folded
-            || (self.starts_indent(buffer_row) && (row_contains_cursor || self.gutter_hovered))
-        {
+        is_foldable |= self.starts_indent(buffer_row);
+
+        if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
             Some(
-                Disclosure::new(("indent-fold-indicator", buffer_row.0), !folded)
+                Disclosure::new(("gutter_crease", buffer_row.0), !folded)
                     .selected(folded)
                     .on_click(cx.listener_for(&editor, move |this, _e, cx| {
                         if folded {
@@ -14113,10 +14113,15 @@ impl EditorSnapshot {
         cx: &mut WindowContext,
     ) -> Option<AnyElement> {
         let folded = self.is_line_folded(buffer_row);
-        let crease = self
+        if let Crease::Inline { render_trailer, .. } = self
             .crease_snapshot
-            .query_row(buffer_row, &self.buffer_snapshot)?;
-        Some((crease.render_trailer)(buffer_row, folded, cx))
+            .query_row(buffer_row, &self.buffer_snapshot)?
+        {
+            let render_trailer = render_trailer.as_ref()?;
+            Some(render_trailer(buffer_row, folded, cx))
+        } else {
+            None
+        }
     }
 }
 
@@ -14621,7 +14626,7 @@ pub fn diagnostic_block_renderer(
     let (text_without_backticks, code_ranges) =
         highlight_diagnostic_message(&diagnostic, max_message_rows);
 
-    Box::new(move |cx: &mut BlockContext| {
+    Arc::new(move |cx: &mut BlockContext| {
         let group_id: SharedString = cx.block_id.to_string().into();
 
         let mut text_style = cx.text_style().clone();
@@ -14676,6 +14681,7 @@ pub fn diagnostic_block_renderer(
             .group(group_id.clone())
             .relative()
             .size_full()
+            .occlude()
             .pl(cx.gutter_dimensions.width)
             .w(cx.max_width - cx.gutter_dimensions.full_width())
             .child(

crates/editor/src/editor_tests.rs 🔗

@@ -596,10 +596,10 @@ fn test_clone(cx: &mut TestAppContext) {
 
     _ = editor.update(cx, |editor, cx| {
         editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
-        editor.fold_ranges(
-            [
-                (Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
-                (Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
+        editor.fold_creases(
+            vec![
+                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
+                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
             ],
             true,
             cx,
@@ -1283,11 +1283,11 @@ fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
     assert_eq!('α'.len_utf8(), 2);
 
     _ = view.update(cx, |view, cx| {
-        view.fold_ranges(
+        view.fold_creases(
             vec![
-                (Point::new(0, 6)..Point::new(0, 12), FoldPlaceholder::test()),
-                (Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
-                (Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
+                Crease::simple(Point::new(0, 6)..Point::new(0, 12), FoldPlaceholder::test()),
+                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
+                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
             ],
             true,
             cx,
@@ -3875,11 +3875,11 @@ fn test_move_line_up_down(cx: &mut TestAppContext) {
         build_editor(buffer, cx)
     });
     _ = view.update(cx, |view, cx| {
-        view.fold_ranges(
+        view.fold_creases(
             vec![
-                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
-                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
-                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
+                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
+                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
+                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
             ],
             true,
             cx,
@@ -3980,7 +3980,7 @@ fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
                 style: BlockStyle::Fixed,
                 placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
                 height: 1,
-                render: Box::new(|_| div().into_any()),
+                render: Arc::new(|_| div().into_any()),
                 priority: 0,
             }],
             Some(Autoscroll::fit()),
@@ -4022,7 +4022,7 @@ async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
                 placement,
                 height: 4,
                 style: BlockStyle::Sticky,
-                render: Box::new(|_| gpui::div().into_any_element()),
+                render: Arc::new(|_| gpui::div().into_any_element()),
                 priority: 0,
             }],
             None,
@@ -4717,11 +4717,11 @@ fn test_split_selection_into_lines(cx: &mut TestAppContext) {
         build_editor(buffer, cx)
     });
     _ = view.update(cx, |view, cx| {
-        view.fold_ranges(
+        view.fold_creases(
             vec![
-                (Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
-                (Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
-                (Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
+                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
+                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
+                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
             ],
             true,
             cx,
@@ -5398,13 +5398,13 @@ async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
     // Ensure that we keep expanding the selection if the larger selection starts or ends within
     // a fold.
     editor.update(cx, |view, cx| {
-        view.fold_ranges(
+        view.fold_creases(
             vec![
-                (
+                Crease::simple(
                     Point::new(0, 21)..Point::new(0, 24),
                     FoldPlaceholder::test(),
                 ),
-                (
+                Crease::simple(
                     Point::new(3, 20)..Point::new(3, 22),
                     FoldPlaceholder::test(),
                 ),
@@ -13139,7 +13139,7 @@ fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
                 callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
             }
 
-            let crease = Crease::new(
+            let crease = Crease::inline(
                 range,
                 FoldPlaceholder::test(),
                 {
@@ -13158,7 +13158,8 @@ fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
 
             editor.insert_creases(Some(crease), cx);
             let snapshot = editor.snapshot(cx);
-            let _div = snapshot.render_fold_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
+            let _div =
+                snapshot.render_crease_toggle(MultiBufferRow(1), false, cx.view().clone(), cx);
             snapshot
         })
         .unwrap();

crates/editor/src/element.rs 🔗

@@ -1227,9 +1227,9 @@ impl EditorElement {
     }
 
     #[allow(clippy::too_many_arguments)]
-    fn prepaint_gutter_fold_toggles(
+    fn prepaint_crease_toggles(
         &self,
-        toggles: &mut [Option<AnyElement>],
+        crease_toggles: &mut [Option<AnyElement>],
         line_height: Pixels,
         gutter_dimensions: &GutterDimensions,
         gutter_settings: crate::editor_settings::Gutter,
@@ -1237,25 +1237,25 @@ impl EditorElement {
         gutter_hitbox: &Hitbox,
         cx: &mut WindowContext,
     ) {
-        for (ix, fold_indicator) in toggles.iter_mut().enumerate() {
-            if let Some(fold_indicator) = fold_indicator {
+        for (ix, crease_toggle) in crease_toggles.iter_mut().enumerate() {
+            if let Some(crease_toggle) = crease_toggle {
                 debug_assert!(gutter_settings.folds);
                 let available_space = size(
                     AvailableSpace::MinContent,
                     AvailableSpace::Definite(line_height * 0.55),
                 );
-                let fold_indicator_size = fold_indicator.layout_as_root(available_space, cx);
+                let crease_toggle_size = crease_toggle.layout_as_root(available_space, cx);
 
                 let position = point(
                     gutter_dimensions.width - gutter_dimensions.right_padding,
                     ix as f32 * line_height - (scroll_pixel_position.y % line_height),
                 );
                 let centering_offset = point(
-                    (gutter_dimensions.fold_area_width() - fold_indicator_size.width) / 2.,
-                    (line_height - fold_indicator_size.height) / 2.,
+                    (gutter_dimensions.fold_area_width() - crease_toggle_size.width) / 2.,
+                    (line_height - crease_toggle_size.height) / 2.,
                 );
                 let origin = gutter_hitbox.origin + position + centering_offset;
-                fold_indicator.prepaint_as_root(origin, available_space, cx);
+                crease_toggle.prepaint_as_root(origin, available_space, cx);
             }
         }
     }
@@ -1915,7 +1915,7 @@ impl EditorElement {
             .collect()
     }
 
-    fn layout_gutter_fold_toggles(
+    fn layout_crease_toggles(
         &self,
         rows: Range<DisplayRow>,
         buffer_rows: impl IntoIterator<Item = Option<MultiBufferRow>>,
@@ -1934,7 +1934,7 @@ impl EditorElement {
                     if let Some(multibuffer_row) = row {
                         let display_row = DisplayRow(rows.start.0 + ix as u32);
                         let active = active_rows.contains_key(&display_row);
-                        snapshot.render_fold_toggle(
+                        snapshot.render_crease_toggle(
                             multibuffer_row,
                             active,
                             self.editor.clone(),
@@ -2122,9 +2122,7 @@ impl EditorElement {
                         max_width: text_hitbox.size.width.max(*scroll_width),
                         editor_style: &self.style,
                     }))
-                    .cursor(CursorStyle::Arrow)
-                    .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
-                    .into_any_element()
+                    .into_any()
             }
 
             Block::ExcerptBoundary {
@@ -3354,9 +3352,9 @@ impl EditorElement {
 
     fn paint_gutter_indicators(&self, layout: &mut EditorLayout, cx: &mut WindowContext) {
         cx.paint_layer(layout.gutter_hitbox.bounds, |cx| {
-            cx.with_element_namespace("gutter_fold_toggles", |cx| {
-                for fold_indicator in layout.gutter_fold_toggles.iter_mut().flatten() {
-                    fold_indicator.paint(cx);
+            cx.with_element_namespace("crease_toggles", |cx| {
+                for crease_toggle in layout.crease_toggles.iter_mut().flatten() {
+                    crease_toggle.paint(cx);
                 }
             });
 
@@ -5167,16 +5165,15 @@ impl Element for EditorElement {
                         cx,
                     );
 
-                    let mut gutter_fold_toggles =
-                        cx.with_element_namespace("gutter_fold_toggles", |cx| {
-                            self.layout_gutter_fold_toggles(
-                                start_row..end_row,
-                                buffer_rows.iter().copied(),
-                                &active_rows,
-                                &snapshot,
-                                cx,
-                            )
-                        });
+                    let mut crease_toggles = cx.with_element_namespace("crease_toggles", |cx| {
+                        self.layout_crease_toggles(
+                            start_row..end_row,
+                            buffer_rows.iter().copied(),
+                            &active_rows,
+                            &snapshot,
+                            cx,
+                        )
+                    });
                     let crease_trailers = cx.with_element_namespace("crease_trailers", |cx| {
                         self.layout_crease_trailers(buffer_rows.iter().copied(), &snapshot, cx)
                     });
@@ -5556,9 +5553,9 @@ impl Element for EditorElement {
                     let mouse_context_menu =
                         self.layout_mouse_context_menu(&snapshot, start_row..end_row, cx);
 
-                    cx.with_element_namespace("gutter_fold_toggles", |cx| {
-                        self.prepaint_gutter_fold_toggles(
-                            &mut gutter_fold_toggles,
+                    cx.with_element_namespace("crease_toggles", |cx| {
+                        self.prepaint_crease_toggles(
+                            &mut crease_toggles,
                             line_height,
                             &gutter_dimensions,
                             gutter_settings,
@@ -5638,7 +5635,7 @@ impl Element for EditorElement {
                         mouse_context_menu,
                         test_indicators,
                         code_actions_indicator,
-                        gutter_fold_toggles,
+                        crease_toggles,
                         crease_trailers,
                         tab_invisible,
                         space_invisible,
@@ -5671,7 +5668,6 @@ impl Element for EditorElement {
             line_height: Some(self.style.text.line_height),
             ..Default::default()
         };
-        let mouse_position = cx.mouse_position();
         let hovered_hunk = layout
             .display_hunks
             .iter()
@@ -5685,7 +5681,7 @@ impl Element for EditorElement {
                 } => {
                     if hunk_hitbox
                         .as_ref()
-                        .map(|hitbox| hitbox.contains(&mouse_position))
+                        .map(|hitbox| hitbox.is_hovered(cx))
                         .unwrap_or(false)
                     {
                         Some(HoveredHunk {
@@ -5778,7 +5774,7 @@ pub struct EditorLayout {
     selections: Vec<(PlayerColor, Vec<SelectionLayout>)>,
     code_actions_indicator: Option<AnyElement>,
     test_indicators: Vec<AnyElement>,
-    gutter_fold_toggles: Vec<Option<AnyElement>>,
+    crease_toggles: Vec<Option<AnyElement>>,
     crease_trailers: Vec<Option<CreaseTrailerLayout>>,
     mouse_context_menu: Option<AnyElement>,
     tab_invisible: ShapedLine,
@@ -6623,7 +6619,7 @@ mod tests {
                         style: BlockStyle::Fixed,
                         placement: BlockPlacement::Above(Anchor::min()),
                         height: 3,
-                        render: Box::new(|cx| div().h(3. * cx.line_height()).into_any()),
+                        render: Arc::new(|cx| div().h(3. * cx.line_height()).into_any()),
                         priority: 0,
                     }],
                     None,

crates/editor/src/hunk_diff.rs 🔗

@@ -425,7 +425,7 @@ impl Editor {
             height: 1,
             style: BlockStyle::Sticky,
             priority: 0,
-            render: Box::new({
+            render: Arc::new({
                 let editor = cx.view().clone();
                 let hunk = hunk.clone();
 
@@ -435,6 +435,7 @@ impl Editor {
 
                     h_flex()
                         .id(cx.block_id)
+                        .occlude()
                         .h(cx.line_height())
                         .w_full()
                         .border_t_1()
@@ -707,12 +708,13 @@ impl Editor {
             height,
             style: BlockStyle::Flex,
             priority: 0,
-            render: Box::new(move |cx| {
+            render: Arc::new(move |cx| {
                 let width = EditorElement::diff_hunk_strip_width(cx.line_height());
                 let gutter_dimensions = editor.read(cx.context).gutter_dimensions;
 
                 h_flex()
                     .id(cx.block_id)
+                    .occlude()
                     .bg(deleted_hunk_color)
                     .h(height as f32 * cx.line_height())
                     .w_full()

crates/multi_buffer/src/multi_buffer.rs 🔗

@@ -125,7 +125,7 @@ pub struct MultiBufferDiffHunk {
 
 pub type MultiBufferPoint = Point;
 
-#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq, serde::Deserialize)]
+#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq, Hash, serde::Deserialize)]
 #[serde(transparent)]
 pub struct MultiBufferRow(pub u32);
 

crates/repl/src/session.rs 🔗

@@ -121,7 +121,7 @@ impl EditorBlock {
         execution_view: View<ExecutionView>,
         on_close: CloseBlockFn,
     ) -> RenderBlock {
-        let render = move |cx: &mut BlockContext| {
+        Arc::new(move |cx: &mut BlockContext| {
             let execution_view = execution_view.clone();
             let text_style = crate::outputs::plain::text_style(cx);
 
@@ -163,6 +163,7 @@ impl EditorBlock {
 
             div()
                 .id(cx.block_id)
+                .occlude()
                 .flex()
                 .items_start()
                 .min_h(text_line_height)
@@ -186,9 +187,7 @@ impl EditorBlock {
                         .child(execution_view),
                 )
                 .into_any_element()
-        };
-
-        Box::new(render)
+        })
     }
 }