Extract a scheduler crate from GPUI to enable unified integration testing of client and server code (#37326)

Nathan Sobo and Antonio Scandurra created

Extracts and cleans up GPUI's scheduler code into a new `scheduler`
crate, making it pluggable by external runtimes. This will enable
deterministic integration testing with cloud components by providing a
unified test scheduler across Zed and backend code. In Zed, it will
replace the existing GPUI scheduler for consistent async task management
across platforms.

## Changes

- **Core Implementation**: `TestScheduler` with seed-based
randomization, session tracking (`SessionId`), and foreground/background
task separation for reproducible testing.
- **Executors**: `ForegroundExecutor` (!Send, thread-local) and
`BackgroundExecutor` (Send, with blocking/timeout support) as
GPUI-compatible wrappers.
- **Clock and Timer**: Controllable `TestClock` and future-based `Timer`
for time-sensitive tests.
- **Testing APIs**: `once()`, `with_seed()`, and `many()` methods for
configurable test runs.
- **Dependencies**: Added `async-task`, `chrono`, `futures`, etc., with
updates to `Cargo.toml` and lock file.

## Benefits

- **Integration Testing**: Facilitates reliable async tests involving
cloud sessions, reducing flakiness via deterministic execution.
- **Pluggability**: Trait-based design (`Scheduler`) allows easy
integration into non-GPUI runtimes while maintaining GPUI compatibility.
- **Cleanup**: Refactors GPUI scheduler logic for clarity, correctness
(no `unwrap()`, proper error handling), and extensibility.

Follows Rust guidelines; run `./script/clippy` for verification.

- [x] Define and test a core scheduler that we think can power our cloud
code and GPUI
- [ ] Replace GPUI's scheduler


Release Notes:

- N/A

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>

Change summary

.rules                                                           |  13 
Cargo.lock                                                       |  73 
Cargo.toml                                                       |   6 
crates/acp_thread/src/acp_thread.rs                              |   6 
crates/action_log/src/action_log.rs                              |   6 
crates/agent_ui/src/buffer_codegen.rs                            |   6 
crates/assistant_context/src/assistant_context_tests.rs          |  14 
crates/assistant_tools/src/edit_agent.rs                         |   8 
crates/assistant_tools/src/edit_agent/create_file_parser.rs      |   2 
crates/assistant_tools/src/edit_agent/edit_parser.rs             |   2 
crates/assistant_tools/src/edit_agent/evals.rs                   |   4 
crates/assistant_tools/src/edit_agent/streaming_fuzzy_matcher.rs |   2 
crates/buffer_diff/src/buffer_diff.rs                            |  17 
crates/channel/src/channel_chat.rs                               |   6 
crates/client/src/client.rs                                      |   7 
crates/collab/src/auth.rs                                        |  37 
crates/collab/src/db.rs                                          |   2 
crates/collab/src/db/tests.rs                                    |   4 
crates/collab/src/tests/integration_tests.rs                     |   2 
crates/collab/src/tests/random_channel_buffer_tests.rs           |   2 
crates/collab/src/tests/random_project_collaboration_tests.rs    |  83 
crates/collab/src/tests/randomized_test_helpers.rs               |  18 
crates/diagnostics/src/diagnostics_tests.rs                      |  45 
crates/editor/src/display_map.rs                                 |  59 
crates/editor/src/display_map/block_map.rs                       |  56 
crates/editor/src/display_map/fold_map.rs                        |  48 
crates/editor/src/display_map/inlay_map.rs                       |  36 
crates/editor/src/display_map/tab_map.rs                         |  14 
crates/editor/src/display_map/wrap_map.rs                        |  24 
crates/editor/src/editor.rs                                      |   4 
crates/editor/src/git/blame.rs                                   |   8 
crates/gpui/examples/data_table.rs                               |  54 
crates/gpui/src/app/test_context.rs                              |   2 
crates/gpui/src/bounds_tree.rs                                   |  10 
crates/gpui/src/platform/test/dispatcher.rs                      |  10 
crates/gpui/src/platform/windows/platform.rs                     |   6 
crates/language/src/buffer.rs                                    |   4 
crates/language/src/buffer_tests.rs                              |  32 
crates/multi_buffer/src/multi_buffer.rs                          |  39 
crates/multi_buffer/src/multi_buffer_tests.rs                    |  48 
crates/project/src/lsp_store.rs                                  |   4 
crates/project/src/project_tests.rs                              |   6 
crates/rope/benches/rope_benchmark.rs                            |   6 
crates/rope/src/chunk.rs                                         |   8 
crates/rope/src/rope.rs                                          |  22 
crates/rpc/src/auth.rs                                           |  40 
crates/scheduler/Cargo.toml                                      |  25 
crates/scheduler/LICENSE-APACHE                                  |   1 
crates/scheduler/src/clock.rs                                    |  34 
crates/scheduler/src/executor.rs                                 | 137 
crates/scheduler/src/scheduler.rs                                |  63 
crates/scheduler/src/test_scheduler.rs                           | 352 ++
crates/scheduler/src/tests.rs                                    | 348 +
crates/streaming_diff/src/streaming_diff.rs                      |  12 
crates/sum_tree/src/sum_tree.rs                                  |  46 
crates/terminal/src/terminal.rs                                  |  17 
crates/text/src/locator.rs                                       |   8 
crates/text/src/network.rs                                       |   6 
crates/text/src/patch.rs                                         |  12 
crates/text/src/tests.rs                                         |  21 
crates/text/src/text.rs                                          |   8 
crates/util/src/util.rs                                          |  13 
crates/worktree/src/worktree_tests.rs                            |  30 
crates/zeta/src/input_excerpt.rs                                 |   4 
64 files changed, 1,569 insertions(+), 473 deletions(-)

Detailed changes

.rules 🔗

@@ -12,6 +12,19 @@
   - Example: avoid `let _ = client.request(...).await?;` - use `client.request(...).await?;` instead
 * When implementing async operations that may fail, ensure errors propagate to the UI layer so users get meaningful feedback.
 * Never create files with `mod.rs` paths - prefer `src/some_module.rs` instead of `src/some_module/mod.rs`.
+* When creating new crates, prefer specifying the library root path in `Cargo.toml` using `[lib] path = "...rs"` instead of the default `lib.rs`, to maintain consistent and descriptive naming (e.g., `gpui.rs` or `main.rs`).
+* Avoid creative additions unless explicitly requested
+* Use full words for variable names (no abbreviations like "q" for "queue")
+* Use variable shadowing to scope clones in async contexts for clarity, minimizing the lifetime of borrowed references.
+  Example:
+  ```rust
+  executor.spawn({
+      let task_ran = task_ran.clone();
+      async move {
+          *task_ran.borrow_mut() = true;
+      }
+  });
+  ```
 
 # GPUI
 

Cargo.lock 🔗

@@ -26,7 +26,7 @@ dependencies = [
  "portable-pty",
  "project",
  "prompt_store",
- "rand 0.8.5",
+ "rand 0.9.1",
  "serde",
  "serde_json",
  "settings",
@@ -79,7 +79,7 @@ dependencies = [
  "log",
  "pretty_assertions",
  "project",
- "rand 0.8.5",
+ "rand 0.9.1",
  "serde_json",
  "settings",
  "text",
@@ -172,7 +172,7 @@ dependencies = [
  "pretty_assertions",
  "project",
  "prompt_store",
- "rand 0.8.5",
+ "rand 0.9.1",
  "ref-cast",
  "rope",
  "schemars",
@@ -408,7 +408,7 @@ dependencies = [
  "project",
  "prompt_store",
  "proto",
- "rand 0.8.5",
+ "rand 0.9.1",
  "release_channel",
  "rope",
  "rules_library",
@@ -834,7 +834,7 @@ dependencies = [
  "project",
  "prompt_store",
  "proto",
- "rand 0.8.5",
+ "rand 0.9.1",
  "regex",
  "rpc",
  "serde",
@@ -933,7 +933,7 @@ dependencies = [
  "parking_lot",
  "pretty_assertions",
  "project",
- "rand 0.8.5",
+ "rand 0.9.1",
  "regex",
  "serde",
  "serde_json",
@@ -985,7 +985,7 @@ dependencies = [
  "pretty_assertions",
  "project",
  "prompt_store",
- "rand 0.8.5",
+ "rand 0.9.1",
  "regex",
  "reqwest_client",
  "rust-embed",
@@ -2478,7 +2478,7 @@ dependencies = [
  "language",
  "log",
  "pretty_assertions",
- "rand 0.8.5",
+ "rand 0.9.1",
  "rope",
  "serde_json",
  "sum_tree",
@@ -2899,7 +2899,7 @@ dependencies = [
  "language",
  "log",
  "postage",
- "rand 0.8.5",
+ "rand 0.9.1",
  "release_channel",
  "rpc",
  "settings",
@@ -3086,7 +3086,7 @@ dependencies = [
  "parking_lot",
  "paths",
  "postage",
- "rand 0.8.5",
+ "rand 0.9.1",
  "regex",
  "release_channel",
  "rpc",
@@ -3335,7 +3335,7 @@ dependencies = [
  "prometheus",
  "prompt_store",
  "prost 0.9.0",
- "rand 0.8.5",
+ "rand 0.9.1",
  "recent_projects",
  "release_channel",
  "remote",
@@ -4697,7 +4697,7 @@ dependencies = [
  "markdown",
  "pretty_assertions",
  "project",
- "rand 0.8.5",
+ "rand 0.9.1",
  "serde",
  "serde_json",
  "settings",
@@ -5068,7 +5068,7 @@ dependencies = [
  "parking_lot",
  "pretty_assertions",
  "project",
- "rand 0.8.5",
+ "rand 0.9.1",
  "regex",
  "release_channel",
  "rpc",
@@ -5563,7 +5563,7 @@ dependencies = [
  "parking_lot",
  "paths",
  "project",
- "rand 0.8.5",
+ "rand 0.9.1",
  "release_channel",
  "remote",
  "reqwest_client",
@@ -6412,7 +6412,7 @@ dependencies = [
  "log",
  "parking_lot",
  "pretty_assertions",
- "rand 0.8.5",
+ "rand 0.9.1",
  "regex",
  "rope",
  "schemars",
@@ -7465,7 +7465,7 @@ dependencies = [
  "pathfinder_geometry",
  "postage",
  "profiling",
- "rand 0.8.5",
+ "rand 0.9.1",
  "raw-window-handle",
  "refineable",
  "reqwest_client",
@@ -9078,7 +9078,7 @@ dependencies = [
  "parking_lot",
  "postage",
  "pretty_assertions",
- "rand 0.8.5",
+ "rand 0.9.1",
  "regex",
  "rpc",
  "schemars",
@@ -10392,7 +10392,7 @@ dependencies = [
  "parking_lot",
  "pretty_assertions",
  "project",
- "rand 0.8.5",
+ "rand 0.9.1",
  "rope",
  "serde",
  "settings",
@@ -12618,7 +12618,7 @@ dependencies = [
  "postage",
  "prettier",
  "pretty_assertions",
- "rand 0.8.5",
+ "rand 0.9.1",
  "regex",
  "release_channel",
  "remote",
@@ -13892,7 +13892,7 @@ dependencies = [
  "ctor",
  "gpui",
  "log",
- "rand 0.8.5",
+ "rand 0.9.1",
  "rayon",
  "smallvec",
  "sum_tree",
@@ -13921,7 +13921,7 @@ dependencies = [
  "gpui",
  "parking_lot",
  "proto",
- "rand 0.8.5",
+ "rand 0.9.1",
  "rsa",
  "serde",
  "serde_json",
@@ -14356,6 +14356,19 @@ dependencies = [
  "windows-sys 0.59.0",
 ]
 
+[[package]]
+name = "scheduler"
+version = "0.1.0"
+dependencies = [
+ "async-task",
+ "chrono",
+ "futures 0.3.31",
+ "parking",
+ "parking_lot",
+ "rand 0.9.1",
+ "workspace-hack",
+]
+
 [[package]]
 name = "schema_generator"
 version = "0.1.0"
@@ -15655,7 +15668,7 @@ name = "streaming_diff"
 version = "0.1.0"
 dependencies = [
  "ordered-float 2.10.1",
- "rand 0.8.5",
+ "rand 0.9.1",
  "rope",
  "util",
  "workspace-hack",
@@ -15769,7 +15782,7 @@ dependencies = [
  "arrayvec",
  "ctor",
  "log",
- "rand 0.8.5",
+ "rand 0.9.1",
  "rayon",
  "workspace-hack",
  "zlog",
@@ -16360,7 +16373,7 @@ dependencies = [
  "futures 0.3.31",
  "gpui",
  "libc",
- "rand 0.8.5",
+ "rand 0.9.1",
  "regex",
  "release_channel",
  "schemars",
@@ -16408,7 +16421,7 @@ dependencies = [
  "language",
  "log",
  "project",
- "rand 0.8.5",
+ "rand 0.9.1",
  "regex",
  "schemars",
  "search",
@@ -16440,7 +16453,7 @@ dependencies = [
  "log",
  "parking_lot",
  "postage",
- "rand 0.8.5",
+ "rand 0.9.1",
  "regex",
  "rope",
  "smallvec",
@@ -17797,7 +17810,7 @@ dependencies = [
  "libc",
  "log",
  "nix 0.29.0",
- "rand 0.8.5",
+ "rand 0.9.1",
  "regex",
  "rust-embed",
  "schemars",
@@ -18588,7 +18601,7 @@ dependencies = [
  "futures 0.3.31",
  "gpui",
  "parking_lot",
- "rand 0.8.5",
+ "rand 0.9.1",
  "workspace-hack",
  "zlog",
 ]
@@ -20047,7 +20060,7 @@ dependencies = [
  "paths",
  "postage",
  "pretty_assertions",
- "rand 0.8.5",
+ "rand 0.9.1",
  "rpc",
  "schemars",
  "serde",
@@ -20812,7 +20825,7 @@ dependencies = [
  "menu",
  "postage",
  "project",
- "rand 0.8.5",
+ "rand 0.9.1",
  "regex",
  "release_channel",
  "reqwest_client",

Cargo.toml 🔗

@@ -131,6 +131,7 @@ members = [
     "crates/refineable",
     "crates/refineable/derive_refineable",
     "crates/release_channel",
+    "crates/scheduler",
     "crates/remote",
     "crates/remote_server",
     "crates/repl",
@@ -360,6 +361,7 @@ proto = { path = "crates/proto" }
 recent_projects = { path = "crates/recent_projects" }
 refineable = { path = "crates/refineable" }
 release_channel = { path = "crates/release_channel" }
+scheduler = { path = "crates/scheduler" }
 remote = { path = "crates/remote" }
 remote_server = { path = "crates/remote_server" }
 repl = { path = "crates/repl" }
@@ -444,6 +446,7 @@ async-fs = "2.1"
 async-pipe = { git = "https://github.com/zed-industries/async-pipe-rs", rev = "82d00a04211cf4e1236029aa03e6b6ce2a74c553" }
 async-recursion = "1.0.0"
 async-tar = "0.5.0"
+async-task = "4.7"
 async-trait = "0.1"
 async-tungstenite = "0.29.1"
 async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
@@ -538,6 +541,7 @@ objc = "0.2"
 open = "5.0.0"
 ordered-float = "2.1.1"
 palette = { version = "0.7.5", default-features = false, features = ["std"] }
+parking = "2.0"
 parking_lot = "0.12.1"
 partial-json-fixer = "0.5.3"
 parse_int = "0.9"
@@ -560,7 +564,7 @@ prost-build = "0.9"
 prost-types = "0.9"
 pulldown-cmark = { version = "0.12.0", default-features = false }
 quote = "1.0.9"
-rand = "0.8.5"
+rand = "0.9"
 rayon = "1.8"
 ref-cast = "1.0.24"
 regex = "1.5"

crates/acp_thread/src/acp_thread.rs 🔗

@@ -2114,7 +2114,7 @@ mod tests {
     use gpui::{App, AsyncApp, TestAppContext, WeakEntity};
     use indoc::indoc;
     use project::{FakeFs, Fs};
-    use rand::Rng as _;
+    use rand::{distr, prelude::*};
     use serde_json::json;
     use settings::SettingsStore;
     use smol::stream::StreamExt as _;
@@ -3057,8 +3057,8 @@ mod tests {
             cx: &mut App,
         ) -> Task<gpui::Result<Entity<AcpThread>>> {
             let session_id = acp::SessionId(
-                rand::thread_rng()
-                    .sample_iter(&rand::distributions::Alphanumeric)
+                rand::rng()
+                    .sample_iter(&distr::Alphanumeric)
                     .take(7)
                     .map(char::from)
                     .collect::<String>()

crates/action_log/src/action_log.rs 🔗

@@ -2218,7 +2218,7 @@ mod tests {
         action_log.update(cx, |log, cx| log.buffer_read(buffer.clone(), cx));
 
         for _ in 0..operations {
-            match rng.gen_range(0..100) {
+            match rng.random_range(0..100) {
                 0..25 => {
                     action_log.update(cx, |log, cx| {
                         let range = buffer.read(cx).random_byte_range(0, &mut rng);
@@ -2237,7 +2237,7 @@ mod tests {
                         .unwrap();
                 }
                 _ => {
-                    let is_agent_edit = rng.gen_bool(0.5);
+                    let is_agent_edit = rng.random_bool(0.5);
                     if is_agent_edit {
                         log::info!("agent edit");
                     } else {
@@ -2252,7 +2252,7 @@ mod tests {
                 }
             }
 
-            if rng.gen_bool(0.2) {
+            if rng.random_bool(0.2) {
                 quiesce(&action_log, &buffer, cx);
             }
         }

crates/agent_ui/src/buffer_codegen.rs 🔗

@@ -1139,7 +1139,7 @@ mod tests {
         );
         while !new_text.is_empty() {
             let max_len = cmp::min(new_text.len(), 10);
-            let len = rng.gen_range(1..=max_len);
+            let len = rng.random_range(1..=max_len);
             let (chunk, suffix) = new_text.split_at(len);
             chunks_tx.unbounded_send(chunk.to_string()).unwrap();
             new_text = suffix;
@@ -1208,7 +1208,7 @@ mod tests {
         );
         while !new_text.is_empty() {
             let max_len = cmp::min(new_text.len(), 10);
-            let len = rng.gen_range(1..=max_len);
+            let len = rng.random_range(1..=max_len);
             let (chunk, suffix) = new_text.split_at(len);
             chunks_tx.unbounded_send(chunk.to_string()).unwrap();
             new_text = suffix;
@@ -1277,7 +1277,7 @@ mod tests {
         );
         while !new_text.is_empty() {
             let max_len = cmp::min(new_text.len(), 10);
-            let len = rng.gen_range(1..=max_len);
+            let len = rng.random_range(1..=max_len);
             let (chunk, suffix) = new_text.split_at(len);
             chunks_tx.unbounded_send(chunk.to_string()).unwrap();
             new_text = suffix;

crates/assistant_context/src/assistant_context_tests.rs 🔗

@@ -764,7 +764,7 @@ async fn test_random_context_collaboration(cx: &mut TestAppContext, mut rng: Std
     let network = Arc::new(Mutex::new(Network::new(rng.clone())));
     let mut contexts = Vec::new();
 
-    let num_peers = rng.gen_range(min_peers..=max_peers);
+    let num_peers = rng.random_range(min_peers..=max_peers);
     let context_id = ContextId::new();
     let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
     for i in 0..num_peers {
@@ -806,10 +806,10 @@ async fn test_random_context_collaboration(cx: &mut TestAppContext, mut rng: Std
         || !network.lock().is_idle()
         || network.lock().contains_disconnected_peers()
     {
-        let context_index = rng.gen_range(0..contexts.len());
+        let context_index = rng.random_range(0..contexts.len());
         let context = &contexts[context_index];
 
-        match rng.gen_range(0..100) {
+        match rng.random_range(0..100) {
             0..=29 if mutation_count > 0 => {
                 log::info!("Context {}: edit buffer", context_index);
                 context.update(cx, |context, cx| {
@@ -874,10 +874,10 @@ async fn test_random_context_collaboration(cx: &mut TestAppContext, mut rng: Std
                         merge_same_roles: true,
                     })];
 
-                    let num_sections = rng.gen_range(0..=3);
+                    let num_sections = rng.random_range(0..=3);
                     let mut section_start = 0;
                     for _ in 0..num_sections {
-                        let mut section_end = rng.gen_range(section_start..=output_text.len());
+                        let mut section_end = rng.random_range(section_start..=output_text.len());
                         while !output_text.is_char_boundary(section_end) {
                             section_end += 1;
                         }
@@ -924,7 +924,7 @@ async fn test_random_context_collaboration(cx: &mut TestAppContext, mut rng: Std
             75..=84 if mutation_count > 0 => {
                 context.update(cx, |context, cx| {
                     if let Some(message) = context.messages(cx).choose(&mut rng) {
-                        let new_status = match rng.gen_range(0..3) {
+                        let new_status = match rng.random_range(0..3) {
                             0 => MessageStatus::Done,
                             1 => MessageStatus::Pending,
                             _ => MessageStatus::Error(SharedString::from("Random error")),
@@ -971,7 +971,7 @@ async fn test_random_context_collaboration(cx: &mut TestAppContext, mut rng: Std
 
                     network.lock().broadcast(replica_id, ops_to_send);
                     context.update(cx, |context, cx| context.apply_ops(ops_to_receive, cx));
-                } else if rng.gen_bool(0.1) && replica_id != 0 {
+                } else if rng.random_bool(0.1) && replica_id != 0 {
                     log::info!("Context {}: disconnecting", context_index);
                     network.lock().disconnect_peer(replica_id);
                 } else if network.lock().has_unreceived(replica_id) {

crates/assistant_tools/src/edit_agent.rs 🔗

@@ -1315,17 +1315,17 @@ mod tests {
 
     #[gpui::test(iterations = 100)]
     async fn test_random_indents(mut rng: StdRng) {
-        let len = rng.gen_range(1..=100);
+        let len = rng.random_range(1..=100);
         let new_text = util::RandomCharIter::new(&mut rng)
             .with_simple_text()
             .take(len)
             .collect::<String>();
         let new_text = new_text
             .split('\n')
-            .map(|line| format!("{}{}", " ".repeat(rng.gen_range(0..=8)), line))
+            .map(|line| format!("{}{}", " ".repeat(rng.random_range(0..=8)), line))
             .collect::<Vec<_>>()
             .join("\n");
-        let delta = IndentDelta::Spaces(rng.gen_range(-4..=4));
+        let delta = IndentDelta::Spaces(rng.random_range(-4i8..=4i8) as isize);
 
         let chunks = to_random_chunks(&mut rng, &new_text);
         let new_text_chunks = stream::iter(chunks.iter().enumerate().map(|(index, chunk)| {
@@ -1357,7 +1357,7 @@ mod tests {
     }
 
     fn to_random_chunks(rng: &mut StdRng, input: &str) -> Vec<String> {
-        let chunk_count = rng.gen_range(1..=cmp::min(input.len(), 50));
+        let chunk_count = rng.random_range(1..=cmp::min(input.len(), 50));
         let mut chunk_indices = (0..input.len()).choose_multiple(rng, chunk_count);
         chunk_indices.sort();
         chunk_indices.push(input.len());

crates/assistant_tools/src/edit_agent/create_file_parser.rs 🔗

@@ -204,7 +204,7 @@ mod tests {
     }
 
     fn parse_random_chunks(input: &str, parser: &mut CreateFileParser, rng: &mut StdRng) -> String {
-        let chunk_count = rng.gen_range(1..=cmp::min(input.len(), 50));
+        let chunk_count = rng.random_range(1..=cmp::min(input.len(), 50));
         let mut chunk_indices = (0..input.len()).choose_multiple(rng, chunk_count);
         chunk_indices.sort();
         chunk_indices.push(input.len());

crates/assistant_tools/src/edit_agent/edit_parser.rs 🔗

@@ -996,7 +996,7 @@ mod tests {
     }
 
     fn parse_random_chunks(input: &str, parser: &mut EditParser, rng: &mut StdRng) -> Vec<Edit> {
-        let chunk_count = rng.gen_range(1..=cmp::min(input.len(), 50));
+        let chunk_count = rng.random_range(1..=cmp::min(input.len(), 50));
         let mut chunk_indices = (0..input.len()).choose_multiple(rng, chunk_count);
         chunk_indices.sort();
         chunk_indices.push(input.len());

crates/assistant_tools/src/edit_agent/evals.rs 🔗

@@ -1399,7 +1399,7 @@ fn eval(
 }
 
 fn run_eval(eval: EvalInput, tx: mpsc::Sender<Result<EvalOutput>>) {
-    let dispatcher = gpui::TestDispatcher::new(StdRng::from_entropy());
+    let dispatcher = gpui::TestDispatcher::new(StdRng::from_os_rng());
     let mut cx = TestAppContext::build(dispatcher, None);
     let output = cx.executor().block_test(async {
         let test = EditAgentTest::new(&mut cx).await;
@@ -1707,7 +1707,7 @@ async fn retry_on_rate_limit<R>(mut request: impl AsyncFnMut() -> Result<R>) ->
         };
 
         if let Some(retry_after) = retry_delay {
-            let jitter = retry_after.mul_f64(rand::thread_rng().gen_range(0.0..1.0));
+            let jitter = retry_after.mul_f64(rand::rng().random_range(0.0..1.0));
             eprintln!("Attempt #{attempt}: Retry after {retry_after:?} + jitter of {jitter:?}");
             Timer::after(retry_after + jitter).await;
         } else {

crates/assistant_tools/src/edit_agent/streaming_fuzzy_matcher.rs 🔗

@@ -771,7 +771,7 @@ mod tests {
     }
 
     fn to_random_chunks(rng: &mut StdRng, input: &str) -> Vec<String> {
-        let chunk_count = rng.gen_range(1..=cmp::min(input.len(), 50));
+        let chunk_count = rng.random_range(1..=cmp::min(input.len(), 50));
         let mut chunk_indices = (0..input.len()).choose_multiple(rng, chunk_count);
         chunk_indices.sort();
         chunk_indices.push(input.len());

crates/buffer_diff/src/buffer_diff.rs 🔗

@@ -2044,10 +2044,10 @@ mod tests {
     #[gpui::test(iterations = 100)]
     async fn test_staging_and_unstaging_hunks(cx: &mut TestAppContext, mut rng: StdRng) {
         fn gen_line(rng: &mut StdRng) -> String {
-            if rng.gen_bool(0.2) {
+            if rng.random_bool(0.2) {
                 "\n".to_owned()
             } else {
-                let c = rng.gen_range('A'..='Z');
+                let c = rng.random_range('A'..='Z');
                 format!("{c}{c}{c}\n")
             }
         }
@@ -2066,7 +2066,7 @@ mod tests {
                 old_lines.into_iter()
             };
             let mut result = String::new();
-            let unchanged_count = rng.gen_range(0..=old_lines.len());
+            let unchanged_count = rng.random_range(0..=old_lines.len());
             result +=
                 &old_lines
                     .by_ref()
@@ -2076,14 +2076,14 @@ mod tests {
                         s
                     });
             while old_lines.len() > 0 {
-                let deleted_count = rng.gen_range(0..=old_lines.len());
+                let deleted_count = rng.random_range(0..=old_lines.len());
                 let _advance = old_lines
                     .by_ref()
                     .take(deleted_count)
                     .map(|line| line.len() + 1)
                     .sum::<usize>();
                 let minimum_added = if deleted_count == 0 { 1 } else { 0 };
-                let added_count = rng.gen_range(minimum_added..=5);
+                let added_count = rng.random_range(minimum_added..=5);
                 let addition = (0..added_count).map(|_| gen_line(rng)).collect::<String>();
                 result += &addition;
 
@@ -2092,7 +2092,8 @@ mod tests {
                     if blank_lines == old_lines.len() {
                         break;
                     };
-                    let unchanged_count = rng.gen_range((blank_lines + 1).max(1)..=old_lines.len());
+                    let unchanged_count =
+                        rng.random_range((blank_lines + 1).max(1)..=old_lines.len());
                     result += &old_lines.by_ref().take(unchanged_count).fold(
                         String::new(),
                         |mut s, line| {
@@ -2149,7 +2150,7 @@ mod tests {
             )
         });
         let working_copy = working_copy.read_with(cx, |working_copy, _| working_copy.snapshot());
-        let mut index_text = if rng.r#gen() {
+        let mut index_text = if rng.random() {
             Rope::from(head_text.as_str())
         } else {
             working_copy.as_rope().clone()
@@ -2165,7 +2166,7 @@ mod tests {
         }
 
         for _ in 0..operations {
-            let i = rng.gen_range(0..hunks.len());
+            let i = rng.random_range(0..hunks.len());
             let hunk = &mut hunks[i];
             let hunk_to_change = hunk.clone();
             let stage = match hunk.secondary_status {

crates/channel/src/channel_chat.rs 🔗

@@ -129,7 +129,7 @@ impl ChannelChat {
                 loaded_all_messages: false,
                 next_pending_message_id: 0,
                 last_acknowledged_id: None,
-                rng: StdRng::from_entropy(),
+                rng: StdRng::from_os_rng(),
                 first_loaded_message_id: None,
                 _subscription: subscription.set_entity(&cx.entity(), &cx.to_async()),
             }
@@ -183,7 +183,7 @@ impl ChannelChat {
 
         let channel_id = self.channel_id;
         let pending_id = ChannelMessageId::Pending(post_inc(&mut self.next_pending_message_id));
-        let nonce = self.rng.r#gen();
+        let nonce = self.rng.random();
         self.insert_messages(
             SumTree::from_item(
                 ChannelMessage {
@@ -257,7 +257,7 @@ impl ChannelChat {
             cx,
         );
 
-        let nonce: u128 = self.rng.r#gen();
+        let nonce: u128 = self.rng.random();
 
         let request = self.rpc.request(proto::UpdateChannelMessage {
             channel_id: self.channel_id.0,

crates/client/src/client.rs 🔗

@@ -691,7 +691,7 @@ impl Client {
                     #[cfg(any(test, feature = "test-support"))]
                     let mut rng = StdRng::seed_from_u64(0);
                     #[cfg(not(any(test, feature = "test-support")))]
-                    let mut rng = StdRng::from_entropy();
+                    let mut rng = StdRng::from_os_rng();
 
                     let mut delay = INITIAL_RECONNECTION_DELAY;
                     loop {
@@ -721,8 +721,9 @@ impl Client {
                                 },
                                 cx,
                             );
-                            let jitter =
-                                Duration::from_millis(rng.gen_range(0..delay.as_millis() as u64));
+                            let jitter = Duration::from_millis(
+                                rng.random_range(0..delay.as_millis() as u64),
+                            );
                             cx.background_executor().timer(delay + jitter).await;
                             delay = cmp::min(delay * 2, MAX_RECONNECTION_DELAY);
                         } else {

crates/collab/src/auth.rs 🔗

@@ -227,7 +227,7 @@ pub async fn verify_access_token(
 
 #[cfg(test)]
 mod test {
-    use rand::thread_rng;
+    use rand::prelude::*;
     use scrypt::password_hash::{PasswordHasher, SaltString};
     use sea_orm::EntityTrait;
 
@@ -358,9 +358,42 @@ mod test {
                 None,
                 None,
                 params,
-                &SaltString::generate(thread_rng()),
+                &SaltString::generate(PasswordHashRngCompat::new()),
             )
             .map_err(anyhow::Error::new)?
             .to_string())
     }
+
+    // TODO: remove once we password_hash v0.6 is released.
+    struct PasswordHashRngCompat(rand::rngs::ThreadRng);
+
+    impl PasswordHashRngCompat {
+        fn new() -> Self {
+            Self(rand::rng())
+        }
+    }
+
+    impl scrypt::password_hash::rand_core::RngCore for PasswordHashRngCompat {
+        fn next_u32(&mut self) -> u32 {
+            self.0.next_u32()
+        }
+
+        fn next_u64(&mut self) -> u64 {
+            self.0.next_u64()
+        }
+
+        fn fill_bytes(&mut self, dest: &mut [u8]) {
+            self.0.fill_bytes(dest);
+        }
+
+        fn try_fill_bytes(
+            &mut self,
+            dest: &mut [u8],
+        ) -> Result<(), scrypt::password_hash::rand_core::Error> {
+            self.fill_bytes(dest);
+            Ok(())
+        }
+    }
+
+    impl scrypt::password_hash::rand_core::CryptoRng for PasswordHashRngCompat {}
 }

crates/collab/src/db.rs 🔗

@@ -256,7 +256,7 @@ impl Database {
             let test_options = self.test_options.as_ref().unwrap();
             test_options.executor.simulate_random_delay().await;
             let fail_probability = *test_options.query_failure_probability.lock();
-            if test_options.executor.rng().gen_bool(fail_probability) {
+            if test_options.executor.rng().random_bool(fail_probability) {
                 return Err(anyhow!("simulated query failure"))?;
             }
 

crates/collab/src/db/tests.rs 🔗

@@ -75,10 +75,10 @@ impl TestDb {
         static LOCK: Mutex<()> = Mutex::new(());
 
         let _guard = LOCK.lock();
-        let mut rng = StdRng::from_entropy();
+        let mut rng = StdRng::from_os_rng();
         let url = format!(
             "postgres://postgres@localhost/zed-test-{}",
-            rng.r#gen::<u128>()
+            rng.random::<u128>()
         );
         let runtime = tokio::runtime::Builder::new_current_thread()
             .enable_io()

crates/collab/src/tests/integration_tests.rs 🔗

@@ -5746,7 +5746,7 @@ async fn test_open_buffer_while_getting_definition_pointing_to_it(
 
     let definitions;
     let buffer_b2;
-    if rng.r#gen() {
+    if rng.random() {
         cx_a.run_until_parked();
         cx_b.run_until_parked();
         definitions = project_b.update(cx_b, |p, cx| p.definitions(&buffer_b1, 23, cx));

crates/collab/src/tests/random_channel_buffer_tests.rs 🔗

@@ -84,7 +84,7 @@ impl RandomizedTest for RandomChannelBufferTest {
         }
 
         loop {
-            match rng.gen_range(0..100_u32) {
+            match rng.random_range(0..100_u32) {
                 0..=29 => {
                     let channel_name = client.channel_store().read_with(cx, |store, cx| {
                         store.ordered_channels().find_map(|(_, channel)| {

crates/collab/src/tests/random_project_collaboration_tests.rs 🔗

@@ -17,7 +17,7 @@ use project::{
     DEFAULT_COMPLETION_CONTEXT, Project, ProjectPath, search::SearchQuery, search::SearchResult,
 };
 use rand::{
-    distributions::{Alphanumeric, DistString},
+    distr::{self, SampleString},
     prelude::*,
 };
 use serde::{Deserialize, Serialize};
@@ -168,19 +168,19 @@ impl RandomizedTest for ProjectCollaborationTest {
     ) -> ClientOperation {
         let call = cx.read(ActiveCall::global);
         loop {
-            match rng.gen_range(0..100_u32) {
+            match rng.random_range(0..100_u32) {
                 // Mutate the call
                 0..=29 => {
                     // Respond to an incoming call
                     if call.read_with(cx, |call, _| call.incoming().borrow().is_some()) {
-                        break if rng.gen_bool(0.7) {
+                        break if rng.random_bool(0.7) {
                             ClientOperation::AcceptIncomingCall
                         } else {
                             ClientOperation::RejectIncomingCall
                         };
                     }
 
-                    match rng.gen_range(0..100_u32) {
+                    match rng.random_range(0..100_u32) {
                         // Invite a contact to the current call
                         0..=70 => {
                             let available_contacts =
@@ -212,7 +212,7 @@ impl RandomizedTest for ProjectCollaborationTest {
                 }
 
                 // Mutate projects
-                30..=59 => match rng.gen_range(0..100_u32) {
+                30..=59 => match rng.random_range(0..100_u32) {
                     // Open a new project
                     0..=70 => {
                         // Open a remote project
@@ -270,7 +270,7 @@ impl RandomizedTest for ProjectCollaborationTest {
                     }
 
                     // Mutate project worktrees
-                    81.. => match rng.gen_range(0..100_u32) {
+                    81.. => match rng.random_range(0..100_u32) {
                         // Add a worktree to a local project
                         0..=50 => {
                             let Some(project) = client.local_projects().choose(rng).cloned() else {
@@ -279,7 +279,7 @@ impl RandomizedTest for ProjectCollaborationTest {
                             let project_root_name = root_name_for_project(&project, cx);
                             let mut paths = client.fs().paths(false);
                             paths.remove(0);
-                            let new_root_path = if paths.is_empty() || rng.r#gen() {
+                            let new_root_path = if paths.is_empty() || rng.random() {
                                 Path::new(path!("/")).join(plan.next_root_dir_name())
                             } else {
                                 paths.choose(rng).unwrap().clone()
@@ -309,7 +309,7 @@ impl RandomizedTest for ProjectCollaborationTest {
                                     .choose(rng)
                             });
                             let Some(worktree) = worktree else { continue };
-                            let is_dir = rng.r#gen::<bool>();
+                            let is_dir = rng.random::<bool>();
                             let mut full_path =
                                 worktree.read_with(cx, |w, _| PathBuf::from(w.root_name()));
                             full_path.push(gen_file_name(rng));
@@ -334,7 +334,7 @@ impl RandomizedTest for ProjectCollaborationTest {
                     let project_root_name = root_name_for_project(&project, cx);
                     let is_local = project.read_with(cx, |project, _| project.is_local());
 
-                    match rng.gen_range(0..100_u32) {
+                    match rng.random_range(0..100_u32) {
                         // Manipulate an existing buffer
                         0..=70 => {
                             let Some(buffer) = client
@@ -349,7 +349,7 @@ impl RandomizedTest for ProjectCollaborationTest {
                             let full_path = buffer
                                 .read_with(cx, |buffer, cx| buffer.file().unwrap().full_path(cx));
 
-                            match rng.gen_range(0..100_u32) {
+                            match rng.random_range(0..100_u32) {
                                 // Close the buffer
                                 0..=15 => {
                                     break ClientOperation::CloseBuffer {
@@ -360,7 +360,7 @@ impl RandomizedTest for ProjectCollaborationTest {
                                 }
                                 // Save the buffer
                                 16..=29 if buffer.read_with(cx, |b, _| b.is_dirty()) => {
-                                    let detach = rng.gen_bool(0.3);
+                                    let detach = rng.random_bool(0.3);
                                     break ClientOperation::SaveBuffer {
                                         project_root_name,
                                         is_local,
@@ -383,17 +383,17 @@ impl RandomizedTest for ProjectCollaborationTest {
                                 _ => {
                                     let offset = buffer.read_with(cx, |buffer, _| {
                                         buffer.clip_offset(
-                                            rng.gen_range(0..=buffer.len()),
+                                            rng.random_range(0..=buffer.len()),
                                             language::Bias::Left,
                                         )
                                     });
-                                    let detach = rng.r#gen();
+                                    let detach = rng.random();
                                     break ClientOperation::RequestLspDataInBuffer {
                                         project_root_name,
                                         full_path,
                                         offset,
                                         is_local,
-                                        kind: match rng.gen_range(0..5_u32) {
+                                        kind: match rng.random_range(0..5_u32) {
                                             0 => LspRequestKind::Rename,
                                             1 => LspRequestKind::Highlights,
                                             2 => LspRequestKind::Definition,
@@ -407,8 +407,8 @@ impl RandomizedTest for ProjectCollaborationTest {
                         }
 
                         71..=80 => {
-                            let query = rng.gen_range('a'..='z').to_string();
-                            let detach = rng.gen_bool(0.3);
+                            let query = rng.random_range('a'..='z').to_string();
+                            let detach = rng.random_bool(0.3);
                             break ClientOperation::SearchProject {
                                 project_root_name,
                                 is_local,
@@ -460,7 +460,7 @@ impl RandomizedTest for ProjectCollaborationTest {
 
                 // Create or update a file or directory
                 96.. => {
-                    let is_dir = rng.r#gen::<bool>();
+                    let is_dir = rng.random::<bool>();
                     let content;
                     let mut path;
                     let dir_paths = client.fs().directories(false);
@@ -470,11 +470,11 @@ impl RandomizedTest for ProjectCollaborationTest {
                         path = dir_paths.choose(rng).unwrap().clone();
                         path.push(gen_file_name(rng));
                     } else {
-                        content = Alphanumeric.sample_string(rng, 16);
+                        content = distr::Alphanumeric.sample_string(rng, 16);
 
                         // Create a new file or overwrite an existing file
                         let file_paths = client.fs().files();
-                        if file_paths.is_empty() || rng.gen_bool(0.5) {
+                        if file_paths.is_empty() || rng.random_bool(0.5) {
                             path = dir_paths.choose(rng).unwrap().clone();
                             path.push(gen_file_name(rng));
                             path.set_extension("rs");
@@ -1090,7 +1090,7 @@ impl RandomizedTest for ProjectCollaborationTest {
                             move |_, cx| {
                                 let background = cx.background_executor();
                                 let mut rng = background.rng();
-                                let count = rng.gen_range::<usize, _>(1..3);
+                                let count = rng.random_range::<usize, _>(1..3);
                                 let files = fs.as_fake().files();
                                 let files = (0..count)
                                     .map(|_| files.choose(&mut rng).unwrap().clone())
@@ -1117,12 +1117,12 @@ impl RandomizedTest for ProjectCollaborationTest {
                                     let background = cx.background_executor();
                                     let mut rng = background.rng();
 
-                                    let highlight_count = rng.gen_range(1..=5);
+                                    let highlight_count = rng.random_range(1..=5);
                                     for _ in 0..highlight_count {
-                                        let start_row = rng.gen_range(0..100);
-                                        let start_column = rng.gen_range(0..100);
-                                        let end_row = rng.gen_range(0..100);
-                                        let end_column = rng.gen_range(0..100);
+                                        let start_row = rng.random_range(0..100);
+                                        let start_column = rng.random_range(0..100);
+                                        let end_row = rng.random_range(0..100);
+                                        let end_column = rng.random_range(0..100);
                                         let start = PointUtf16::new(start_row, start_column);
                                         let end = PointUtf16::new(end_row, end_column);
                                         let range =
@@ -1219,8 +1219,8 @@ impl RandomizedTest for ProjectCollaborationTest {
                                         guest_project.remote_id(),
                                     );
                                     assert_eq!(
-                                        guest_snapshot.entries(false, 0).collect::<Vec<_>>(),
-                                        host_snapshot.entries(false, 0).collect::<Vec<_>>(),
+                                        guest_snapshot.entries(false, 0).map(null_out_entry_size).collect::<Vec<_>>(),
+                                        host_snapshot.entries(false, 0).map(null_out_entry_size).collect::<Vec<_>>(),
                                         "{} has different snapshot than the host for worktree {:?} ({:?}) and project {:?}",
                                         client.username,
                                         host_snapshot.abs_path(),
@@ -1248,6 +1248,18 @@ impl RandomizedTest for ProjectCollaborationTest {
                             );
                         }
                     });
+
+                // A hack to work around a hack in
+                // https://github.com/zed-industries/zed/pull/16696 that wasn't
+                // detected until we upgraded the rng crate. This whole crate is
+                // going away with DeltaDB soon, so we hold our nose and
+                // continue.
+                fn null_out_entry_size(entry: &project::Entry) -> project::Entry {
+                    project::Entry {
+                        size: 0,
+                        ..entry.clone()
+                    }
+                }
             }
 
             let buffers = client.buffers().clone();
@@ -1422,7 +1434,7 @@ fn generate_git_operation(rng: &mut StdRng, client: &TestClient) -> GitOperation
             .filter(|path| path.starts_with(repo_path))
             .collect::<Vec<_>>();
 
-        let count = rng.gen_range(0..=paths.len());
+        let count = rng.random_range(0..=paths.len());
         paths.shuffle(rng);
         paths.truncate(count);
 
@@ -1434,13 +1446,13 @@ fn generate_git_operation(rng: &mut StdRng, client: &TestClient) -> GitOperation
 
     let repo_path = client.fs().directories(false).choose(rng).unwrap().clone();
 
-    match rng.gen_range(0..100_u32) {
+    match rng.random_range(0..100_u32) {
         0..=25 => {
             let file_paths = generate_file_paths(&repo_path, rng, client);
 
             let contents = file_paths
                 .into_iter()
-                .map(|path| (path, Alphanumeric.sample_string(rng, 16)))
+                .map(|path| (path, distr::Alphanumeric.sample_string(rng, 16)))
                 .collect();
 
             GitOperation::WriteGitIndex {
@@ -1449,7 +1461,8 @@ fn generate_git_operation(rng: &mut StdRng, client: &TestClient) -> GitOperation
             }
         }
         26..=63 => {
-            let new_branch = (rng.gen_range(0..10) > 3).then(|| Alphanumeric.sample_string(rng, 8));
+            let new_branch =
+                (rng.random_range(0..10) > 3).then(|| distr::Alphanumeric.sample_string(rng, 8));
 
             GitOperation::WriteGitBranch {
                 repo_path,
@@ -1596,7 +1609,7 @@ fn choose_random_project(client: &TestClient, rng: &mut StdRng) -> Option<Entity
 fn gen_file_name(rng: &mut StdRng) -> String {
     let mut name = String::new();
     for _ in 0..10 {
-        let letter = rng.gen_range('a'..='z');
+        let letter = rng.random_range('a'..='z');
         name.push(letter);
     }
     name
@@ -1604,7 +1617,7 @@ fn gen_file_name(rng: &mut StdRng) -> String {
 
 fn gen_status(rng: &mut StdRng) -> FileStatus {
     fn gen_tracked_status(rng: &mut StdRng) -> TrackedStatus {
-        match rng.gen_range(0..3) {
+        match rng.random_range(0..3) {
             0 => TrackedStatus {
                 index_status: StatusCode::Unmodified,
                 worktree_status: StatusCode::Unmodified,
@@ -1626,7 +1639,7 @@ fn gen_status(rng: &mut StdRng) -> FileStatus {
     }
 
     fn gen_unmerged_status_code(rng: &mut StdRng) -> UnmergedStatusCode {
-        match rng.gen_range(0..3) {
+        match rng.random_range(0..3) {
             0 => UnmergedStatusCode::Updated,
             1 => UnmergedStatusCode::Added,
             2 => UnmergedStatusCode::Deleted,
@@ -1634,7 +1647,7 @@ fn gen_status(rng: &mut StdRng) -> FileStatus {
         }
     }
 
-    match rng.gen_range(0..2) {
+    match rng.random_range(0..2) {
         0 => FileStatus::Unmerged(UnmergedStatus {
             first_head: gen_unmerged_status_code(rng),
             second_head: gen_unmerged_status_code(rng),

crates/collab/src/tests/randomized_test_helpers.rs 🔗

@@ -208,9 +208,9 @@ pub fn save_randomized_test_plan() {
 
 impl<T: RandomizedTest> TestPlan<T> {
     pub async fn new(server: &mut TestServer, mut rng: StdRng) -> Arc<Mutex<Self>> {
-        let allow_server_restarts = rng.gen_bool(0.7);
-        let allow_client_reconnection = rng.gen_bool(0.7);
-        let allow_client_disconnection = rng.gen_bool(0.1);
+        let allow_server_restarts = rng.random_bool(0.7);
+        let allow_client_reconnection = rng.random_bool(0.7);
+        let allow_client_disconnection = rng.random_bool(0.1);
 
         let mut users = Vec::new();
         for ix in 0..max_peers() {
@@ -407,7 +407,7 @@ impl<T: RandomizedTest> TestPlan<T> {
         }
 
         Some(loop {
-            break match self.rng.gen_range(0..100) {
+            break match self.rng.random_range(0..100) {
                 0..=29 if clients.len() < self.users.len() => {
                     let user = self
                         .users
@@ -421,13 +421,13 @@ impl<T: RandomizedTest> TestPlan<T> {
                     }
                 }
                 30..=34 if clients.len() > 1 && self.allow_client_disconnection => {
-                    let (client, cx) = &clients[self.rng.gen_range(0..clients.len())];
+                    let (client, cx) = &clients[self.rng.random_range(0..clients.len())];
                     let user_id = client.current_user_id(cx);
                     self.operation_ix += 1;
                     ServerOperation::RemoveConnection { user_id }
                 }
                 35..=39 if clients.len() > 1 && self.allow_client_reconnection => {
-                    let (client, cx) = &clients[self.rng.gen_range(0..clients.len())];
+                    let (client, cx) = &clients[self.rng.random_range(0..clients.len())];
                     let user_id = client.current_user_id(cx);
                     self.operation_ix += 1;
                     ServerOperation::BounceConnection { user_id }
@@ -439,12 +439,12 @@ impl<T: RandomizedTest> TestPlan<T> {
                 _ if !clients.is_empty() => {
                     let count = self
                         .rng
-                        .gen_range(1..10)
+                        .random_range(1..10)
                         .min(self.max_operations - self.operation_ix);
                     let batch_id = util::post_inc(&mut self.next_batch_id);
                     let mut user_ids = (0..count)
                         .map(|_| {
-                            let ix = self.rng.gen_range(0..clients.len());
+                            let ix = self.rng.random_range(0..clients.len());
                             let (client, cx) = &clients[ix];
                             client.current_user_id(cx)
                         })
@@ -453,7 +453,7 @@ impl<T: RandomizedTest> TestPlan<T> {
                     ServerOperation::MutateClients {
                         user_ids,
                         batch_id,
-                        quiesce: self.rng.gen_bool(0.7),
+                        quiesce: self.rng.random_bool(0.7),
                     }
                 }
                 _ => continue,

crates/diagnostics/src/diagnostics_tests.rs 🔗

@@ -682,7 +682,7 @@ async fn test_random_diagnostics_blocks(cx: &mut TestAppContext, mut rng: StdRng
         Default::default();
 
     for _ in 0..operations {
-        match rng.gen_range(0..100) {
+        match rng.random_range(0..100) {
             // language server completes its diagnostic check
             0..=20 if !updated_language_servers.is_empty() => {
                 let server_id = *updated_language_servers.iter().choose(&mut rng).unwrap();
@@ -691,7 +691,7 @@ async fn test_random_diagnostics_blocks(cx: &mut TestAppContext, mut rng: StdRng
                     lsp_store.disk_based_diagnostics_finished(server_id, cx)
                 });
 
-                if rng.gen_bool(0.5) {
+                if rng.random_bool(0.5) {
                     cx.run_until_parked();
                 }
             }
@@ -701,7 +701,7 @@ async fn test_random_diagnostics_blocks(cx: &mut TestAppContext, mut rng: StdRng
                 let (path, server_id, diagnostics) =
                     match current_diagnostics.iter_mut().choose(&mut rng) {
                         // update existing set of diagnostics
-                        Some(((path, server_id), diagnostics)) if rng.gen_bool(0.5) => {
+                        Some(((path, server_id), diagnostics)) if rng.random_bool(0.5) => {
                             (path.clone(), *server_id, diagnostics)
                         }
 
@@ -709,13 +709,13 @@ async fn test_random_diagnostics_blocks(cx: &mut TestAppContext, mut rng: StdRng
                         _ => {
                             let path: PathBuf =
                                 format!(path!("/test/{}.rs"), post_inc(&mut next_filename)).into();
-                            let len = rng.gen_range(128..256);
+                            let len = rng.random_range(128..256);
                             let content =
                                 RandomCharIter::new(&mut rng).take(len).collect::<String>();
                             fs.insert_file(&path, content.into_bytes()).await;
 
                             let server_id = match language_server_ids.iter().choose(&mut rng) {
-                                Some(server_id) if rng.gen_bool(0.5) => *server_id,
+                                Some(server_id) if rng.random_bool(0.5) => *server_id,
                                 _ => {
                                     let id = LanguageServerId(language_server_ids.len());
                                     language_server_ids.push(id);
@@ -846,7 +846,7 @@ async fn test_random_diagnostics_with_inlays(cx: &mut TestAppContext, mut rng: S
     let mut next_inlay_id = 0;
 
     for _ in 0..operations {
-        match rng.gen_range(0..100) {
+        match rng.random_range(0..100) {
             // language server completes its diagnostic check
             0..=20 if !updated_language_servers.is_empty() => {
                 let server_id = *updated_language_servers.iter().choose(&mut rng).unwrap();
@@ -855,7 +855,7 @@ async fn test_random_diagnostics_with_inlays(cx: &mut TestAppContext, mut rng: S
                     lsp_store.disk_based_diagnostics_finished(server_id, cx)
                 });
 
-                if rng.gen_bool(0.5) {
+                if rng.random_bool(0.5) {
                     cx.run_until_parked();
                 }
             }
@@ -864,7 +864,7 @@ async fn test_random_diagnostics_with_inlays(cx: &mut TestAppContext, mut rng: S
                 diagnostics.editor.update(cx, |editor, cx| {
                     let snapshot = editor.snapshot(window, cx);
                     if !snapshot.buffer_snapshot.is_empty() {
-                        let position = rng.gen_range(0..snapshot.buffer_snapshot.len());
+                        let position = rng.random_range(0..snapshot.buffer_snapshot.len());
                         let position = snapshot.buffer_snapshot.clip_offset(position, Bias::Left);
                         log::info!(
                             "adding inlay at {position}/{}: {:?}",
@@ -890,7 +890,7 @@ async fn test_random_diagnostics_with_inlays(cx: &mut TestAppContext, mut rng: S
                 let (path, server_id, diagnostics) =
                     match current_diagnostics.iter_mut().choose(&mut rng) {
                         // update existing set of diagnostics
-                        Some(((path, server_id), diagnostics)) if rng.gen_bool(0.5) => {
+                        Some(((path, server_id), diagnostics)) if rng.random_bool(0.5) => {
                             (path.clone(), *server_id, diagnostics)
                         }
 
@@ -898,13 +898,13 @@ async fn test_random_diagnostics_with_inlays(cx: &mut TestAppContext, mut rng: S
                         _ => {
                             let path: PathBuf =
                                 format!(path!("/test/{}.rs"), post_inc(&mut next_filename)).into();
-                            let len = rng.gen_range(128..256);
+                            let len = rng.random_range(128..256);
                             let content =
                                 RandomCharIter::new(&mut rng).take(len).collect::<String>();
                             fs.insert_file(&path, content.into_bytes()).await;
 
                             let server_id = match language_server_ids.iter().choose(&mut rng) {
-                                Some(server_id) if rng.gen_bool(0.5) => *server_id,
+                                Some(server_id) if rng.random_bool(0.5) => *server_id,
                                 _ => {
                                     let id = LanguageServerId(language_server_ids.len());
                                     language_server_ids.push(id);
@@ -1589,10 +1589,10 @@ fn randomly_update_diagnostics_for_path(
     next_id: &mut usize,
     rng: &mut impl Rng,
 ) {
-    let mutation_count = rng.gen_range(1..=3);
+    let mutation_count = rng.random_range(1..=3);
     for _ in 0..mutation_count {
-        if rng.gen_bool(0.3) && !diagnostics.is_empty() {
-            let idx = rng.gen_range(0..diagnostics.len());
+        if rng.random_bool(0.3) && !diagnostics.is_empty() {
+            let idx = rng.random_range(0..diagnostics.len());
             log::info!("  removing diagnostic at index {idx}");
             diagnostics.remove(idx);
         } else {
@@ -1601,7 +1601,7 @@ fn randomly_update_diagnostics_for_path(
 
             let new_diagnostic = random_lsp_diagnostic(rng, fs, path, unique_id);
 
-            let ix = rng.gen_range(0..=diagnostics.len());
+            let ix = rng.random_range(0..=diagnostics.len());
             log::info!(
                 "  inserting {} at index {ix}. {},{}..{},{}",
                 new_diagnostic.message,
@@ -1638,8 +1638,8 @@ fn random_lsp_diagnostic(
     let file_content = fs.read_file_sync(path).unwrap();
     let file_text = Rope::from(String::from_utf8_lossy(&file_content).as_ref());
 
-    let start = rng.gen_range(0..file_text.len().saturating_add(ERROR_MARGIN));
-    let end = rng.gen_range(start..file_text.len().saturating_add(ERROR_MARGIN));
+    let start = rng.random_range(0..file_text.len().saturating_add(ERROR_MARGIN));
+    let end = rng.random_range(start..file_text.len().saturating_add(ERROR_MARGIN));
 
     let start_point = file_text.offset_to_point_utf16(start);
     let end_point = file_text.offset_to_point_utf16(end);
@@ -1649,7 +1649,7 @@ fn random_lsp_diagnostic(
         lsp::Position::new(end_point.row, end_point.column),
     );
 
-    let severity = if rng.gen_bool(0.5) {
+    let severity = if rng.random_bool(0.5) {
         Some(lsp::DiagnosticSeverity::ERROR)
     } else {
         Some(lsp::DiagnosticSeverity::WARNING)
@@ -1657,13 +1657,14 @@ fn random_lsp_diagnostic(
 
     let message = format!("diagnostic {unique_id}");
 
-    let related_information = if rng.gen_bool(0.3) {
-        let info_count = rng.gen_range(1..=3);
+    let related_information = if rng.random_bool(0.3) {
+        let info_count = rng.random_range(1..=3);
         let mut related_info = Vec::with_capacity(info_count);
 
         for i in 0..info_count {
-            let info_start = rng.gen_range(0..file_text.len().saturating_add(ERROR_MARGIN));
-            let info_end = rng.gen_range(info_start..file_text.len().saturating_add(ERROR_MARGIN));
+            let info_start = rng.random_range(0..file_text.len().saturating_add(ERROR_MARGIN));
+            let info_end =
+                rng.random_range(info_start..file_text.len().saturating_add(ERROR_MARGIN));
 
             let info_start_point = file_text.offset_to_point_utf16(info_start);
             let info_end_point = file_text.offset_to_point_utf16(info_end);

crates/editor/src/display_map.rs 🔗

@@ -1552,15 +1552,15 @@ pub mod tests {
             .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
             .unwrap_or(10);
 
-        let mut tab_size = rng.gen_range(1..=4);
-        let buffer_start_excerpt_header_height = rng.gen_range(1..=5);
-        let excerpt_header_height = rng.gen_range(1..=5);
+        let mut tab_size = rng.random_range(1..=4);
+        let buffer_start_excerpt_header_height = rng.random_range(1..=5);
+        let excerpt_header_height = rng.random_range(1..=5);
         let font_size = px(14.0);
         let max_wrap_width = 300.0;
-        let mut wrap_width = if rng.gen_bool(0.1) {
+        let mut wrap_width = if rng.random_bool(0.1) {
             None
         } else {
-            Some(px(rng.gen_range(0.0..=max_wrap_width)))
+            Some(px(rng.random_range(0.0..=max_wrap_width)))
         };
 
         log::info!("tab size: {}", tab_size);
@@ -1571,8 +1571,8 @@ pub mod tests {
         });
 
         let buffer = cx.update(|cx| {
-            if rng.r#gen() {
-                let len = rng.gen_range(0..10);
+            if rng.random() {
+                let len = rng.random_range(0..10);
                 let text = util::RandomCharIter::new(&mut rng)
                     .take(len)
                     .collect::<String>();
@@ -1609,12 +1609,12 @@ pub mod tests {
         log::info!("display text: {:?}", snapshot.text());
 
         for _i in 0..operations {
-            match rng.gen_range(0..100) {
+            match rng.random_range(0..100) {
                 0..=19 => {
-                    wrap_width = if rng.gen_bool(0.2) {
+                    wrap_width = if rng.random_bool(0.2) {
                         None
                     } else {
-                        Some(px(rng.gen_range(0.0..=max_wrap_width)))
+                        Some(px(rng.random_range(0.0..=max_wrap_width)))
                     };
                     log::info!("setting wrap width to {:?}", wrap_width);
                     map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
@@ -1634,28 +1634,27 @@ pub mod tests {
                 }
                 30..=44 => {
                     map.update(cx, |map, cx| {
-                        if rng.r#gen() || blocks.is_empty() {
+                        if rng.random() || blocks.is_empty() {
                             let buffer = map.snapshot(cx).buffer_snapshot;
-                            let block_properties = (0..rng.gen_range(1..=1))
+                            let block_properties = (0..rng.random_range(1..=1))
                                 .map(|_| {
-                                    let position =
-                                        buffer.anchor_after(buffer.clip_offset(
-                                            rng.gen_range(0..=buffer.len()),
-                                            Bias::Left,
-                                        ));
+                                    let position = buffer.anchor_after(buffer.clip_offset(
+                                        rng.random_range(0..=buffer.len()),
+                                        Bias::Left,
+                                    ));
 
-                                    let placement = if rng.r#gen() {
+                                    let placement = if rng.random() {
                                         BlockPlacement::Above(position)
                                     } else {
                                         BlockPlacement::Below(position)
                                     };
-                                    let height = rng.gen_range(1..5);
+                                    let height = rng.random_range(1..5);
                                     log::info!(
                                         "inserting block {:?} with height {}",
                                         placement.as_ref().map(|p| p.to_point(&buffer)),
                                         height
                                     );
-                                    let priority = rng.gen_range(1..100);
+                                    let priority = rng.random_range(1..100);
                                     BlockProperties {
                                         placement,
                                         style: BlockStyle::Fixed,
@@ -1668,9 +1667,9 @@ pub mod tests {
                             blocks.extend(map.insert_blocks(block_properties, cx));
                         } else {
                             blocks.shuffle(&mut rng);
-                            let remove_count = rng.gen_range(1..=4.min(blocks.len()));
+                            let remove_count = rng.random_range(1..=4.min(blocks.len()));
                             let block_ids_to_remove = (0..remove_count)
-                                .map(|_| blocks.remove(rng.gen_range(0..blocks.len())))
+                                .map(|_| blocks.remove(rng.random_range(0..blocks.len())))
                                 .collect();
                             log::info!("removing block ids {:?}", block_ids_to_remove);
                             map.remove_blocks(block_ids_to_remove, cx);
@@ -1679,16 +1678,16 @@ pub mod tests {
                 }
                 45..=79 => {
                     let mut ranges = Vec::new();
-                    for _ in 0..rng.gen_range(1..=3) {
+                    for _ in 0..rng.random_range(1..=3) {
                         buffer.read_with(cx, |buffer, cx| {
                             let buffer = buffer.read(cx);
-                            let end = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Right);
-                            let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
+                            let end = buffer.clip_offset(rng.random_range(0..=buffer.len()), Right);
+                            let start = buffer.clip_offset(rng.random_range(0..=end), Left);
                             ranges.push(start..end);
                         });
                     }
 
-                    if rng.r#gen() && fold_count > 0 {
+                    if rng.random() && fold_count > 0 {
                         log::info!("unfolding ranges: {:?}", ranges);
                         map.update(cx, |map, cx| {
                             map.unfold_intersecting(ranges, true, cx);
@@ -1727,8 +1726,8 @@ pub mod tests {
             // Line boundaries
             let buffer = &snapshot.buffer_snapshot;
             for _ in 0..5 {
-                let row = rng.gen_range(0..=buffer.max_point().row);
-                let column = rng.gen_range(0..=buffer.line_len(MultiBufferRow(row)));
+                let row = rng.random_range(0..=buffer.max_point().row);
+                let column = rng.random_range(0..=buffer.line_len(MultiBufferRow(row)));
                 let point = buffer.clip_point(Point::new(row, column), Left);
 
                 let (prev_buffer_bound, prev_display_bound) = snapshot.prev_line_boundary(point);
@@ -1776,8 +1775,8 @@ pub mod tests {
             let min_point = snapshot.clip_point(DisplayPoint::new(DisplayRow(0), 0), Left);
             let max_point = snapshot.clip_point(snapshot.max_point(), Right);
             for _ in 0..5 {
-                let row = rng.gen_range(0..=snapshot.max_point().row().0);
-                let column = rng.gen_range(0..=snapshot.line_len(DisplayRow(row)));
+                let row = rng.random_range(0..=snapshot.max_point().row().0);
+                let column = rng.random_range(0..=snapshot.line_len(DisplayRow(row)));
                 let point = snapshot.clip_point(DisplayPoint::new(DisplayRow(row), column), Left);
 
                 log::info!("Moving from point {:?}", point);

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

@@ -128,10 +128,10 @@ impl<T> BlockPlacement<T> {
         }
     }
 
-    fn sort_order(&self) -> u8 {
+    fn tie_break(&self) -> u8 {
         match self {
-            BlockPlacement::Above(_) => 0,
-            BlockPlacement::Replace(_) => 1,
+            BlockPlacement::Replace(_) => 0,
+            BlockPlacement::Above(_) => 1,
             BlockPlacement::Near(_) => 2,
             BlockPlacement::Below(_) => 3,
         }
@@ -143,7 +143,7 @@ impl BlockPlacement<Anchor> {
         self.start()
             .cmp(other.start(), buffer)
             .then_with(|| other.end().cmp(self.end(), buffer))
-            .then_with(|| self.sort_order().cmp(&other.sort_order()))
+            .then_with(|| self.tie_break().cmp(&other.tie_break()))
     }
 
     fn to_wrap_row(&self, wrap_snapshot: &WrapSnapshot) -> Option<BlockPlacement<WrapRow>> {
@@ -847,6 +847,7 @@ impl BlockMap {
                 .start()
                 .cmp(placement_b.start())
                 .then_with(|| placement_b.end().cmp(placement_a.end()))
+                .then_with(|| placement_a.tie_break().cmp(&placement_b.tie_break()))
                 .then_with(|| {
                     if block_a.is_header() {
                         Ordering::Less
@@ -856,7 +857,6 @@ impl BlockMap {
                         Ordering::Equal
                     }
                 })
-                .then_with(|| placement_a.sort_order().cmp(&placement_b.sort_order()))
                 .then_with(|| match (block_a, block_b) {
                     (
                         Block::ExcerptBoundary {
@@ -2922,21 +2922,21 @@ mod tests {
             .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
             .unwrap_or(10);
 
-        let wrap_width = if rng.gen_bool(0.2) {
+        let wrap_width = if rng.random_bool(0.2) {
             None
         } else {
-            Some(px(rng.gen_range(0.0..=100.0)))
+            Some(px(rng.random_range(0.0..=100.0)))
         };
         let tab_size = 1.try_into().unwrap();
         let font_size = px(14.0);
-        let buffer_start_header_height = rng.gen_range(1..=5);
-        let excerpt_header_height = rng.gen_range(1..=5);
+        let buffer_start_header_height = rng.random_range(1..=5);
+        let excerpt_header_height = rng.random_range(1..=5);
 
         log::info!("Wrap width: {:?}", wrap_width);
         log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
-        let is_singleton = rng.r#gen();
+        let is_singleton = rng.random();
         let buffer = if is_singleton {
-            let len = rng.gen_range(0..10);
+            let len = rng.random_range(0..10);
             let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
             log::info!("initial singleton buffer text: {:?}", text);
             cx.update(|cx| MultiBuffer::build_simple(&text, cx))
@@ -2966,30 +2966,30 @@ mod tests {
 
         for _ in 0..operations {
             let mut buffer_edits = Vec::new();
-            match rng.gen_range(0..=100) {
+            match rng.random_range(0..=100) {
                 0..=19 => {
-                    let wrap_width = if rng.gen_bool(0.2) {
+                    let wrap_width = if rng.random_bool(0.2) {
                         None
                     } else {
-                        Some(px(rng.gen_range(0.0..=100.0)))
+                        Some(px(rng.random_range(0.0..=100.0)))
                     };
                     log::info!("Setting wrap width to {:?}", wrap_width);
                     wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
                 }
                 20..=39 => {
-                    let block_count = rng.gen_range(1..=5);
+                    let block_count = rng.random_range(1..=5);
                     let block_properties = (0..block_count)
                         .map(|_| {
                             let buffer = cx.update(|cx| buffer.read(cx).read(cx).clone());
                             let offset =
-                                buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Left);
+                                buffer.clip_offset(rng.random_range(0..=buffer.len()), Bias::Left);
                             let mut min_height = 0;
-                            let placement = match rng.gen_range(0..3) {
+                            let placement = match rng.random_range(0..3) {
                                 0 => {
                                     min_height = 1;
                                     let start = buffer.anchor_after(offset);
                                     let end = buffer.anchor_after(buffer.clip_offset(
-                                        rng.gen_range(offset..=buffer.len()),
+                                        rng.random_range(offset..=buffer.len()),
                                         Bias::Left,
                                     ));
                                     BlockPlacement::Replace(start..=end)
@@ -2998,7 +2998,7 @@ mod tests {
                                 _ => BlockPlacement::Below(buffer.anchor_after(offset)),
                             };
 
-                            let height = rng.gen_range(min_height..5);
+                            let height = rng.random_range(min_height..5);
                             BlockProperties {
                                 style: BlockStyle::Fixed,
                                 placement,
@@ -3040,7 +3040,7 @@ mod tests {
                     }
                 }
                 40..=59 if !block_map.custom_blocks.is_empty() => {
-                    let block_count = rng.gen_range(1..=4.min(block_map.custom_blocks.len()));
+                    let block_count = rng.random_range(1..=4.min(block_map.custom_blocks.len()));
                     let block_ids_to_remove = block_map
                         .custom_blocks
                         .choose_multiple(&mut rng, block_count)
@@ -3095,8 +3095,8 @@ mod tests {
                     let mut folded_count = folded_buffers.len();
                     let mut unfolded_count = unfolded_buffers.len();
 
-                    let fold = !unfolded_buffers.is_empty() && rng.gen_bool(0.5);
-                    let unfold = !folded_buffers.is_empty() && rng.gen_bool(0.5);
+                    let fold = !unfolded_buffers.is_empty() && rng.random_bool(0.5);
+                    let unfold = !folded_buffers.is_empty() && rng.random_bool(0.5);
                     if !fold && !unfold {
                         log::info!(
                             "Noop fold/unfold operation. Unfolded buffers: {unfolded_count}, folded buffers: {folded_count}"
@@ -3107,7 +3107,7 @@ mod tests {
                     buffer.update(cx, |buffer, cx| {
                         if fold {
                             let buffer_to_fold =
-                                unfolded_buffers[rng.gen_range(0..unfolded_buffers.len())];
+                                unfolded_buffers[rng.random_range(0..unfolded_buffers.len())];
                             log::info!("Folding {buffer_to_fold:?}");
                             let related_excerpts = buffer_snapshot
                                 .excerpts()
@@ -3133,7 +3133,7 @@ mod tests {
                         }
                         if unfold {
                             let buffer_to_unfold =
-                                folded_buffers[rng.gen_range(0..folded_buffers.len())];
+                                folded_buffers[rng.random_range(0..folded_buffers.len())];
                             log::info!("Unfolding {buffer_to_unfold:?}");
                             unfolded_count += 1;
                             folded_count -= 1;
@@ -3146,7 +3146,7 @@ mod tests {
                 }
                 _ => {
                     buffer.update(cx, |buffer, cx| {
-                        let mutation_count = rng.gen_range(1..=5);
+                        let mutation_count = rng.random_range(1..=5);
                         let subscription = buffer.subscribe();
                         buffer.randomly_mutate(&mut rng, mutation_count, cx);
                         buffer_snapshot = buffer.snapshot(cx);
@@ -3331,7 +3331,7 @@ mod tests {
             );
 
             for start_row in 0..expected_row_count {
-                let end_row = rng.gen_range(start_row + 1..=expected_row_count);
+                let end_row = rng.random_range(start_row + 1..=expected_row_count);
                 let mut expected_text = expected_lines[start_row..end_row].join("\n");
                 if end_row < expected_row_count {
                     expected_text.push('\n');
@@ -3426,8 +3426,8 @@ mod tests {
             );
 
             for _ in 0..10 {
-                let end_row = rng.gen_range(1..=expected_lines.len());
-                let start_row = rng.gen_range(0..end_row);
+                let end_row = rng.random_range(1..=expected_lines.len());
+                let start_row = rng.random_range(0..end_row);
 
                 let mut expected_longest_rows_in_range = vec![];
                 let mut longest_line_len_in_range = 0;

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

@@ -1771,9 +1771,9 @@ mod tests {
             .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
             .unwrap_or(10);
 
-        let len = rng.gen_range(0..10);
+        let len = rng.random_range(0..10);
         let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
-        let buffer = if rng.r#gen() {
+        let buffer = if rng.random() {
             MultiBuffer::build_simple(&text, cx)
         } else {
             MultiBuffer::build_random(&mut rng, cx)
@@ -1790,7 +1790,7 @@ mod tests {
             log::info!("text: {:?}", buffer_snapshot.text());
             let mut buffer_edits = Vec::new();
             let mut inlay_edits = Vec::new();
-            match rng.gen_range(0..=100) {
+            match rng.random_range(0..=100) {
                 0..=39 => {
                     snapshot_edits.extend(map.randomly_mutate(&mut rng));
                 }
@@ -1800,7 +1800,7 @@ mod tests {
                 }
                 _ => buffer.update(cx, |buffer, cx| {
                     let subscription = buffer.subscribe();
-                    let edit_count = rng.gen_range(1..=5);
+                    let edit_count = rng.random_range(1..=5);
                     buffer.randomly_mutate(&mut rng, edit_count, cx);
                     buffer_snapshot = buffer.snapshot(cx);
                     let edits = subscription.consume().into_inner();
@@ -1917,10 +1917,14 @@ mod tests {
             }
 
             for _ in 0..5 {
-                let mut start = snapshot
-                    .clip_offset(FoldOffset(rng.gen_range(0..=snapshot.len().0)), Bias::Left);
-                let mut end = snapshot
-                    .clip_offset(FoldOffset(rng.gen_range(0..=snapshot.len().0)), Bias::Right);
+                let mut start = snapshot.clip_offset(
+                    FoldOffset(rng.random_range(0..=snapshot.len().0)),
+                    Bias::Left,
+                );
+                let mut end = snapshot.clip_offset(
+                    FoldOffset(rng.random_range(0..=snapshot.len().0)),
+                    Bias::Right,
+                );
                 if start > end {
                     mem::swap(&mut start, &mut end);
                 }
@@ -1975,8 +1979,8 @@ mod tests {
 
             for _ in 0..5 {
                 let end =
-                    buffer_snapshot.clip_offset(rng.gen_range(0..=buffer_snapshot.len()), Right);
-                let start = buffer_snapshot.clip_offset(rng.gen_range(0..=end), Left);
+                    buffer_snapshot.clip_offset(rng.random_range(0..=buffer_snapshot.len()), Right);
+                let start = buffer_snapshot.clip_offset(rng.random_range(0..=end), Left);
                 let expected_folds = map
                     .snapshot
                     .folds
@@ -2001,10 +2005,10 @@ mod tests {
 
             let text = snapshot.text();
             for _ in 0..5 {
-                let start_row = rng.gen_range(0..=snapshot.max_point().row());
-                let start_column = rng.gen_range(0..=snapshot.line_len(start_row));
-                let end_row = rng.gen_range(0..=snapshot.max_point().row());
-                let end_column = rng.gen_range(0..=snapshot.line_len(end_row));
+                let start_row = rng.random_range(0..=snapshot.max_point().row());
+                let start_column = rng.random_range(0..=snapshot.line_len(start_row));
+                let end_row = rng.random_range(0..=snapshot.max_point().row());
+                let end_column = rng.random_range(0..=snapshot.line_len(end_row));
                 let mut start =
                     snapshot.clip_point(FoldPoint::new(start_row, start_column), Bias::Left);
                 let mut end = snapshot.clip_point(FoldPoint::new(end_row, end_column), Bias::Right);
@@ -2109,17 +2113,17 @@ mod tests {
             rng: &mut impl Rng,
         ) -> Vec<(FoldSnapshot, Vec<FoldEdit>)> {
             let mut snapshot_edits = Vec::new();
-            match rng.gen_range(0..=100) {
+            match rng.random_range(0..=100) {
                 0..=39 if !self.snapshot.folds.is_empty() => {
                     let inlay_snapshot = self.snapshot.inlay_snapshot.clone();
                     let buffer = &inlay_snapshot.buffer;
                     let mut to_unfold = Vec::new();
-                    for _ in 0..rng.gen_range(1..=3) {
-                        let end = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Right);
-                        let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
+                    for _ in 0..rng.random_range(1..=3) {
+                        let end = buffer.clip_offset(rng.random_range(0..=buffer.len()), Right);
+                        let start = buffer.clip_offset(rng.random_range(0..=end), Left);
                         to_unfold.push(start..end);
                     }
-                    let inclusive = rng.r#gen();
+                    let inclusive = rng.random();
                     log::info!("unfolding {:?} (inclusive: {})", to_unfold, inclusive);
                     let (mut writer, snapshot, edits) = self.write(inlay_snapshot, vec![]);
                     snapshot_edits.push((snapshot, edits));
@@ -2130,9 +2134,9 @@ mod tests {
                     let inlay_snapshot = self.snapshot.inlay_snapshot.clone();
                     let buffer = &inlay_snapshot.buffer;
                     let mut to_fold = Vec::new();
-                    for _ in 0..rng.gen_range(1..=2) {
-                        let end = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Right);
-                        let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
+                    for _ in 0..rng.random_range(1..=2) {
+                        let end = buffer.clip_offset(rng.random_range(0..=buffer.len()), Right);
+                        let start = buffer.clip_offset(rng.random_range(0..=end), Left);
                         to_fold.push((start..end, FoldPlaceholder::test()));
                     }
                     log::info!("folding {:?}", to_fold);

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

@@ -719,14 +719,18 @@ impl InlayMap {
         let mut to_remove = Vec::new();
         let mut to_insert = Vec::new();
         let snapshot = &mut self.snapshot;
-        for i in 0..rng.gen_range(1..=5) {
-            if self.inlays.is_empty() || rng.r#gen() {
+        for i in 0..rng.random_range(1..=5) {
+            if self.inlays.is_empty() || rng.random() {
                 let position = snapshot.buffer.random_byte_range(0, rng).start;
-                let bias = if rng.r#gen() { Bias::Left } else { Bias::Right };
-                let len = if rng.gen_bool(0.01) {
+                let bias = if rng.random() {
+                    Bias::Left
+                } else {
+                    Bias::Right
+                };
+                let len = if rng.random_bool(0.01) {
                     0
                 } else {
-                    rng.gen_range(1..=5)
+                    rng.random_range(1..=5)
                 };
                 let text = util::RandomCharIter::new(&mut *rng)
                     .filter(|ch| *ch != '\r')
@@ -1665,8 +1669,8 @@ mod tests {
             .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
             .unwrap_or(10);
 
-        let len = rng.gen_range(0..30);
-        let buffer = if rng.r#gen() {
+        let len = rng.random_range(0..30);
+        let buffer = if rng.random() {
             let text = util::RandomCharIter::new(&mut rng)
                 .take(len)
                 .collect::<String>();
@@ -1683,7 +1687,7 @@ mod tests {
 
             let mut prev_inlay_text = inlay_snapshot.text();
             let mut buffer_edits = Vec::new();
-            match rng.gen_range(0..=100) {
+            match rng.random_range(0..=100) {
                 0..=50 => {
                     let (snapshot, edits) = inlay_map.randomly_mutate(&mut next_inlay_id, &mut rng);
                     log::info!("mutated text: {:?}", snapshot.text());
@@ -1691,7 +1695,7 @@ mod tests {
                 }
                 _ => buffer.update(cx, |buffer, cx| {
                     let subscription = buffer.subscribe();
-                    let edit_count = rng.gen_range(1..=5);
+                    let edit_count = rng.random_range(1..=5);
                     buffer.randomly_mutate(&mut rng, edit_count, cx);
                     buffer_snapshot = buffer.snapshot(cx);
                     let edits = subscription.consume().into_inner();
@@ -1740,7 +1744,7 @@ mod tests {
             }
 
             let mut text_highlights = TextHighlights::default();
-            let text_highlight_count = rng.gen_range(0_usize..10);
+            let text_highlight_count = rng.random_range(0_usize..10);
             let mut text_highlight_ranges = (0..text_highlight_count)
                 .map(|_| buffer_snapshot.random_byte_range(0, &mut rng))
                 .collect::<Vec<_>>();
@@ -1762,10 +1766,10 @@ mod tests {
 
             let mut inlay_highlights = InlayHighlights::default();
             if !inlays.is_empty() {
-                let inlay_highlight_count = rng.gen_range(0..inlays.len());
+                let inlay_highlight_count = rng.random_range(0..inlays.len());
                 let mut inlay_indices = BTreeSet::default();
                 while inlay_indices.len() < inlay_highlight_count {
-                    inlay_indices.insert(rng.gen_range(0..inlays.len()));
+                    inlay_indices.insert(rng.random_range(0..inlays.len()));
                 }
                 let new_highlights = TreeMap::from_ordered_entries(
                     inlay_indices
@@ -1782,8 +1786,8 @@ mod tests {
                                 }),
                                 n => {
                                     let inlay_text = inlay.text.to_string();
-                                    let mut highlight_end = rng.gen_range(1..n);
-                                    let mut highlight_start = rng.gen_range(0..highlight_end);
+                                    let mut highlight_end = rng.random_range(1..n);
+                                    let mut highlight_start = rng.random_range(0..highlight_end);
                                     while !inlay_text.is_char_boundary(highlight_end) {
                                         highlight_end += 1;
                                     }
@@ -1805,9 +1809,9 @@ mod tests {
             }
 
             for _ in 0..5 {
-                let mut end = rng.gen_range(0..=inlay_snapshot.len().0);
+                let mut end = rng.random_range(0..=inlay_snapshot.len().0);
                 end = expected_text.clip_offset(end, Bias::Right);
-                let mut start = rng.gen_range(0..=end);
+                let mut start = rng.random_range(0..=end);
                 start = expected_text.clip_offset(start, Bias::Right);
 
                 let range = InlayOffset(start)..InlayOffset(end);

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

@@ -736,9 +736,9 @@ mod tests {
 
     #[gpui::test(iterations = 100)]
     fn test_random_tabs(cx: &mut gpui::App, mut rng: StdRng) {
-        let tab_size = NonZeroU32::new(rng.gen_range(1..=4)).unwrap();
-        let len = rng.gen_range(0..30);
-        let buffer = if rng.r#gen() {
+        let tab_size = NonZeroU32::new(rng.random_range(1..=4)).unwrap();
+        let len = rng.random_range(0..30);
+        let buffer = if rng.random() {
             let text = util::RandomCharIter::new(&mut rng)
                 .take(len)
                 .collect::<String>();
@@ -769,11 +769,11 @@ mod tests {
         );
 
         for _ in 0..5 {
-            let end_row = rng.gen_range(0..=text.max_point().row);
-            let end_column = rng.gen_range(0..=text.line_len(end_row));
+            let end_row = rng.random_range(0..=text.max_point().row);
+            let end_column = rng.random_range(0..=text.line_len(end_row));
             let mut end = TabPoint(text.clip_point(Point::new(end_row, end_column), Bias::Right));
-            let start_row = rng.gen_range(0..=text.max_point().row);
-            let start_column = rng.gen_range(0..=text.line_len(start_row));
+            let start_row = rng.random_range(0..=text.max_point().row);
+            let start_column = rng.random_range(0..=text.line_len(start_row));
             let mut start =
                 TabPoint(text.clip_point(Point::new(start_row, start_column), Bias::Left));
             if start > end {

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

@@ -1215,12 +1215,12 @@ mod tests {
             .unwrap_or(10);
 
         let text_system = cx.read(|cx| cx.text_system().clone());
-        let mut wrap_width = if rng.gen_bool(0.1) {
+        let mut wrap_width = if rng.random_bool(0.1) {
             None
         } else {
-            Some(px(rng.gen_range(0.0..=1000.0)))
+            Some(px(rng.random_range(0.0..=1000.0)))
         };
-        let tab_size = NonZeroU32::new(rng.gen_range(1..=4)).unwrap();
+        let tab_size = NonZeroU32::new(rng.random_range(1..=4)).unwrap();
 
         let font = test_font();
         let _font_id = text_system.resolve_font(&font);
@@ -1230,10 +1230,10 @@ mod tests {
         log::info!("Wrap width: {:?}", wrap_width);
 
         let buffer = cx.update(|cx| {
-            if rng.r#gen() {
+            if rng.random() {
                 MultiBuffer::build_random(&mut rng, cx)
             } else {
-                let len = rng.gen_range(0..10);
+                let len = rng.random_range(0..10);
                 let text = util::RandomCharIter::new(&mut rng)
                     .take(len)
                     .collect::<String>();
@@ -1281,12 +1281,12 @@ mod tests {
             log::info!("{} ==============================================", _i);
 
             let mut buffer_edits = Vec::new();
-            match rng.gen_range(0..=100) {
+            match rng.random_range(0..=100) {
                 0..=19 => {
-                    wrap_width = if rng.gen_bool(0.2) {
+                    wrap_width = if rng.random_bool(0.2) {
                         None
                     } else {
-                        Some(px(rng.gen_range(0.0..=1000.0)))
+                        Some(px(rng.random_range(0.0..=1000.0)))
                     };
                     log::info!("Setting wrap width to {:?}", wrap_width);
                     wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
@@ -1317,7 +1317,7 @@ mod tests {
                 _ => {
                     buffer.update(cx, |buffer, cx| {
                         let subscription = buffer.subscribe();
-                        let edit_count = rng.gen_range(1..=5);
+                        let edit_count = rng.random_range(1..=5);
                         buffer.randomly_mutate(&mut rng, edit_count, cx);
                         buffer_snapshot = buffer.snapshot(cx);
                         buffer_edits.extend(subscription.consume());
@@ -1341,7 +1341,7 @@ mod tests {
             snapshot.verify_chunks(&mut rng);
             edits.push((snapshot, wrap_edits));
 
-            if wrap_map.read_with(cx, |map, _| map.is_rewrapping()) && rng.gen_bool(0.4) {
+            if wrap_map.read_with(cx, |map, _| map.is_rewrapping()) && rng.random_bool(0.4) {
                 log::info!("Waiting for wrapping to finish");
                 while wrap_map.read_with(cx, |map, _| map.is_rewrapping()) {
                     notifications.next().await.unwrap();
@@ -1479,8 +1479,8 @@ mod tests {
     impl WrapSnapshot {
         fn verify_chunks(&mut self, rng: &mut impl Rng) {
             for _ in 0..5 {
-                let mut end_row = rng.gen_range(0..=self.max_point().row());
-                let start_row = rng.gen_range(0..=end_row);
+                let mut end_row = rng.random_range(0..=self.max_point().row());
+                let start_row = rng.random_range(0..=end_row);
                 end_row += 1;
 
                 let mut expected_text = self.text_chunks(start_row).collect::<String>();

crates/editor/src/editor.rs 🔗

@@ -164,7 +164,7 @@ use project::{
         DiagnosticSeverity, GitGutterSetting, GoToDiagnosticSeverityFilter, ProjectSettings,
     },
 };
-use rand::{seq::SliceRandom, thread_rng};
+use rand::seq::SliceRandom;
 use rpc::{ErrorCode, ErrorExt, proto::PeerId};
 use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
 use selections_collection::{
@@ -10971,7 +10971,7 @@ impl Editor {
     }
 
     pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
-        self.manipulate_immutable_lines(window, cx, |lines| lines.shuffle(&mut thread_rng()))
+        self.manipulate_immutable_lines(window, cx, |lines| lines.shuffle(&mut rand::rng()))
     }
 
     fn manipulate_lines<M>(

crates/editor/src/git/blame.rs 🔗

@@ -1107,7 +1107,7 @@ mod tests {
         init_test(cx);
 
         let fs = FakeFs::new(cx.executor());
-        let buffer_initial_text_len = rng.gen_range(5..15);
+        let buffer_initial_text_len = rng.random_range(5..15);
         let mut buffer_initial_text = Rope::from(
             RandomCharIter::new(&mut rng)
                 .take(buffer_initial_text_len)
@@ -1159,7 +1159,7 @@ mod tests {
         git_blame.update(cx, |blame, cx| blame.check_invariants(cx));
 
         for _ in 0..operations {
-            match rng.gen_range(0..100) {
+            match rng.random_range(0..100) {
                 0..=19 => {
                     log::info!("quiescing");
                     cx.executor().run_until_parked();
@@ -1202,8 +1202,8 @@ mod tests {
         let mut blame_entries = Vec::new();
         for ix in 0..5 {
             if last_row < max_row {
-                let row_start = rng.gen_range(last_row..max_row);
-                let row_end = rng.gen_range(row_start + 1..cmp::min(row_start + 3, max_row) + 1);
+                let row_start = rng.random_range(last_row..max_row);
+                let row_end = rng.random_range(row_start + 1..cmp::min(row_start + 3, max_row) + 1);
                 blame_entries.push(blame_entry(&ix.to_string(), row_start..row_end));
                 last_row = row_end;
             } else {

crates/gpui/examples/data_table.rs 🔗

@@ -38,58 +38,58 @@ pub struct Quote {
 impl Quote {
     pub fn random() -> Self {
         use rand::Rng;
-        let mut rng = rand::thread_rng();
+        let mut rng = rand::rng();
         // simulate a base price in a realistic range
-        let prev_close = rng.gen_range(100.0..200.0);
-        let change = rng.gen_range(-5.0..5.0);
+        let prev_close = rng.random_range(100.0..200.0);
+        let change = rng.random_range(-5.0..5.0);
         let last_done = prev_close + change;
-        let open = prev_close + rng.gen_range(-3.0..3.0);
-        let high = (prev_close + rng.gen_range::<f64, _>(0.0..10.0)).max(open);
-        let low = (prev_close - rng.gen_range::<f64, _>(0.0..10.0)).min(open);
-        let timestamp = Duration::from_secs(rng.gen_range(0..86400));
-        let volume = rng.gen_range(1_000_000..100_000_000);
+        let open = prev_close + rng.random_range(-3.0..3.0);
+        let high = (prev_close + rng.random_range::<f64, _>(0.0..10.0)).max(open);
+        let low = (prev_close - rng.random_range::<f64, _>(0.0..10.0)).min(open);
+        let timestamp = Duration::from_secs(rng.random_range(0..86400));
+        let volume = rng.random_range(1_000_000..100_000_000);
         let turnover = last_done * volume as f64;
         let symbol = {
             let mut ticker = String::new();
-            if rng.gen_bool(0.5) {
+            if rng.random_bool(0.5) {
                 ticker.push_str(&format!(
                     "{:03}.{}",
-                    rng.gen_range(100..1000),
-                    rng.gen_range(0..10)
+                    rng.random_range(100..1000),
+                    rng.random_range(0..10)
                 ));
             } else {
                 ticker.push_str(&format!(
                     "{}{}",
-                    rng.gen_range('A'..='Z'),
-                    rng.gen_range('A'..='Z')
+                    rng.random_range('A'..='Z'),
+                    rng.random_range('A'..='Z')
                 ));
             }
-            ticker.push_str(&format!(".{}", rng.gen_range('A'..='Z')));
+            ticker.push_str(&format!(".{}", rng.random_range('A'..='Z')));
             ticker
         };
         let name = format!(
             "{} {} - #{}",
             symbol,
-            rng.gen_range(1..100),
-            rng.gen_range(10000..100000)
+            rng.random_range(1..100),
+            rng.random_range(10000..100000)
         );
-        let ttm = rng.gen_range(0.0..10.0);
-        let market_cap = rng.gen_range(1_000_000.0..10_000_000.0);
-        let float_cap = market_cap + rng.gen_range(1_000.0..10_000.0);
-        let shares = rng.gen_range(100.0..1000.0);
+        let ttm = rng.random_range(0.0..10.0);
+        let market_cap = rng.random_range(1_000_000.0..10_000_000.0);
+        let float_cap = market_cap + rng.random_range(1_000.0..10_000.0);
+        let shares = rng.random_range(100.0..1000.0);
         let pb = market_cap / shares;
         let pe = market_cap / shares;
         let eps = market_cap / shares;
-        let dividend = rng.gen_range(0.0..10.0);
-        let dividend_yield = rng.gen_range(0.0..10.0);
-        let dividend_per_share = rng.gen_range(0.0..10.0);
+        let dividend = rng.random_range(0.0..10.0);
+        let dividend_yield = rng.random_range(0.0..10.0);
+        let dividend_per_share = rng.random_range(0.0..10.0);
         let dividend_date = SharedString::new(format!(
             "{}-{}-{}",
-            rng.gen_range(2000..2023),
-            rng.gen_range(1..12),
-            rng.gen_range(1..28)
+            rng.random_range(2000..2023),
+            rng.random_range(1..12),
+            rng.random_range(1..28)
         ));
-        let dividend_payment = rng.gen_range(0.0..10.0);
+        let dividend_payment = rng.random_range(0.0..10.0);
 
         Self {
             name: name.into(),

crates/gpui/src/app/test_context.rs 🔗

@@ -144,7 +144,7 @@ impl TestAppContext {
 
     /// Create a single TestAppContext, for non-multi-client tests
     pub fn single() -> Self {
-        let dispatcher = TestDispatcher::new(StdRng::from_entropy());
+        let dispatcher = TestDispatcher::new(StdRng::seed_from_u64(0));
         Self::build(dispatcher, None)
     }
 

crates/gpui/src/bounds_tree.rs 🔗

@@ -309,12 +309,12 @@ mod tests {
             let mut expected_quads: Vec<(Bounds<f32>, u32)> = Vec::new();
 
             // Insert a random number of random AABBs into the tree.
-            let num_bounds = rng.gen_range(1..=max_bounds);
+            let num_bounds = rng.random_range(1..=max_bounds);
             for _ in 0..num_bounds {
-                let min_x: f32 = rng.gen_range(-100.0..100.0);
-                let min_y: f32 = rng.gen_range(-100.0..100.0);
-                let width: f32 = rng.gen_range(0.0..50.0);
-                let height: f32 = rng.gen_range(0.0..50.0);
+                let min_x: f32 = rng.random_range(-100.0..100.0);
+                let min_y: f32 = rng.random_range(-100.0..100.0);
+                let width: f32 = rng.random_range(0.0..50.0);
+                let height: f32 = rng.random_range(0.0..50.0);
                 let bounds = Bounds {
                     origin: Point { x: min_x, y: min_y },
                     size: Size { width, height },

crates/gpui/src/platform/test/dispatcher.rs 🔗

@@ -118,7 +118,7 @@ impl TestDispatcher {
         }
 
         YieldNow {
-            count: self.state.lock().random.gen_range(0..10),
+            count: self.state.lock().random.random_range(0..10),
         }
     }
 
@@ -151,11 +151,11 @@ impl TestDispatcher {
             if deprioritized_background_len == 0 {
                 return false;
             }
-            let ix = state.random.gen_range(0..deprioritized_background_len);
+            let ix = state.random.random_range(0..deprioritized_background_len);
             main_thread = false;
             runnable = state.deprioritized_background.swap_remove(ix);
         } else {
-            main_thread = state.random.gen_ratio(
+            main_thread = state.random.random_ratio(
                 foreground_len as u32,
                 (foreground_len + background_len) as u32,
             );
@@ -170,7 +170,7 @@ impl TestDispatcher {
                     .pop_front()
                     .unwrap();
             } else {
-                let ix = state.random.gen_range(0..background_len);
+                let ix = state.random.random_range(0..background_len);
                 runnable = state.background.swap_remove(ix);
             };
         };
@@ -241,7 +241,7 @@ impl TestDispatcher {
     pub fn gen_block_on_ticks(&self) -> usize {
         let mut lock = self.state.lock();
         let block_on_ticks = lock.block_on_ticks.clone();
-        lock.random.gen_range(block_on_ticks)
+        lock.random.random_range(block_on_ticks)
     }
 }
 

crates/gpui/src/platform/windows/platform.rs 🔗

@@ -94,7 +94,11 @@ impl WindowsPlatform {
         }
         let directx_devices = DirectXDevices::new().context("Creating DirectX devices")?;
         let (main_sender, main_receiver) = flume::unbounded::<Runnable>();
-        let validation_number = rand::random::<usize>();
+        let validation_number = if usize::BITS == 64 {
+            rand::random::<u64>() as usize
+        } else {
+            rand::random::<u32>() as usize
+        };
         let raw_window_handles = Arc::new(RwLock::new(SmallVec::new()));
         let text_system = Arc::new(
             DirectWriteTextSystem::new(&directx_devices)

crates/language/src/buffer.rs 🔗

@@ -2842,12 +2842,12 @@ impl Buffer {
 
             let new_start = last_end.map_or(0, |last_end| last_end + 1);
             let mut range = self.random_byte_range(new_start, rng);
-            if rng.gen_bool(0.2) {
+            if rng.random_bool(0.2) {
                 mem::swap(&mut range.start, &mut range.end);
             }
             last_end = Some(range.end);
 
-            let new_text_len = rng.gen_range(0..10);
+            let new_text_len = rng.random_range(0..10);
             let mut new_text: String = RandomCharIter::new(&mut *rng).take(new_text_len).collect();
             new_text = new_text.to_uppercase();
 

crates/language/src/buffer_tests.rs 🔗

@@ -3013,7 +3013,7 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
         .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
         .unwrap_or(10);
 
-    let base_text_len = rng.gen_range(0..10);
+    let base_text_len = rng.random_range(0..10);
     let base_text = RandomCharIter::new(&mut rng)
         .take(base_text_len)
         .collect::<String>();
@@ -3022,7 +3022,7 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
     let network = Arc::new(Mutex::new(Network::new(rng.clone())));
     let base_buffer = cx.new(|cx| Buffer::local(base_text.as_str(), cx));
 
-    for i in 0..rng.gen_range(min_peers..=max_peers) {
+    for i in 0..rng.random_range(min_peers..=max_peers) {
         let buffer = cx.new(|cx| {
             let state = base_buffer.read(cx).to_proto(cx);
             let ops = cx
@@ -3035,7 +3035,7 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
                     .map(|op| proto::deserialize_operation(op).unwrap()),
                 cx,
             );
-            buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
+            buffer.set_group_interval(Duration::from_millis(rng.random_range(0..=200)));
             let network = network.clone();
             cx.subscribe(&cx.entity(), move |buffer, _, event, _| {
                 if let BufferEvent::Operation {
@@ -3066,11 +3066,11 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
     let mut next_diagnostic_id = 0;
     let mut active_selections = BTreeMap::default();
     loop {
-        let replica_index = rng.gen_range(0..replica_ids.len());
+        let replica_index = rng.random_range(0..replica_ids.len());
         let replica_id = replica_ids[replica_index];
         let buffer = &mut buffers[replica_index];
         let mut new_buffer = None;
-        match rng.gen_range(0..100) {
+        match rng.random_range(0..100) {
             0..=29 if mutation_count != 0 => {
                 buffer.update(cx, |buffer, cx| {
                     buffer.start_transaction_at(now);
@@ -3082,13 +3082,13 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
             }
             30..=39 if mutation_count != 0 => {
                 buffer.update(cx, |buffer, cx| {
-                    if rng.gen_bool(0.2) {
+                    if rng.random_bool(0.2) {
                         log::info!("peer {} clearing active selections", replica_id);
                         active_selections.remove(&replica_id);
                         buffer.remove_active_selections(cx);
                     } else {
                         let mut selections = Vec::new();
-                        for id in 0..rng.gen_range(1..=5) {
+                        for id in 0..rng.random_range(1..=5) {
                             let range = buffer.random_byte_range(0, &mut rng);
                             selections.push(Selection {
                                 id,
@@ -3111,7 +3111,7 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
                 mutation_count -= 1;
             }
             40..=49 if mutation_count != 0 && replica_id == 0 => {
-                let entry_count = rng.gen_range(1..=5);
+                let entry_count = rng.random_range(1..=5);
                 buffer.update(cx, |buffer, cx| {
                     let diagnostics = DiagnosticSet::new(
                         (0..entry_count).map(|_| {
@@ -3166,7 +3166,7 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
                         new_buffer.replica_id(),
                         new_buffer.text()
                     );
-                    new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
+                    new_buffer.set_group_interval(Duration::from_millis(rng.random_range(0..=200)));
                     let network = network.clone();
                     cx.subscribe(&cx.entity(), move |buffer, _, event, _| {
                         if let BufferEvent::Operation {
@@ -3238,7 +3238,7 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
             _ => {}
         }
 
-        now += Duration::from_millis(rng.gen_range(0..=200));
+        now += Duration::from_millis(rng.random_range(0..=200));
         buffers.extend(new_buffer);
 
         for buffer in &buffers {
@@ -3320,23 +3320,23 @@ fn test_trailing_whitespace_ranges(mut rng: StdRng) {
     // Generate a random multi-line string containing
     // some lines with trailing whitespace.
     let mut text = String::new();
-    for _ in 0..rng.gen_range(0..16) {
-        for _ in 0..rng.gen_range(0..36) {
-            text.push(match rng.gen_range(0..10) {
+    for _ in 0..rng.random_range(0..16) {
+        for _ in 0..rng.random_range(0..36) {
+            text.push(match rng.random_range(0..10) {
                 0..=1 => ' ',
                 3 => '\t',
-                _ => rng.gen_range('a'..='z'),
+                _ => rng.random_range('a'..='z'),
             });
         }
         text.push('\n');
     }
 
-    match rng.gen_range(0..10) {
+    match rng.random_range(0..10) {
         // sometimes remove the last newline
         0..=1 => drop(text.pop()), //
 
         // sometimes add extra newlines
-        2..=3 => text.push_str(&"\n".repeat(rng.gen_range(1..5))),
+        2..=3 => text.push_str(&"\n".repeat(rng.random_range(1..5))),
         _ => {}
     }
 

crates/multi_buffer/src/multi_buffer.rs 🔗

@@ -3580,7 +3580,7 @@ impl MultiBuffer {
     pub fn build_random(rng: &mut impl rand::Rng, cx: &mut gpui::App) -> Entity<Self> {
         cx.new(|cx| {
             let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
-            let mutation_count = rng.gen_range(1..=5);
+            let mutation_count = rng.random_range(1..=5);
             multibuffer.randomly_edit_excerpts(rng, mutation_count, cx);
             multibuffer
         })
@@ -3603,16 +3603,17 @@ impl MultiBuffer {
             }
 
             let new_start = last_end.map_or(0, |last_end| last_end + 1);
-            let end = snapshot.clip_offset(rng.gen_range(new_start..=snapshot.len()), Bias::Right);
-            let start = snapshot.clip_offset(rng.gen_range(new_start..=end), Bias::Right);
+            let end =
+                snapshot.clip_offset(rng.random_range(new_start..=snapshot.len()), Bias::Right);
+            let start = snapshot.clip_offset(rng.random_range(new_start..=end), Bias::Right);
             last_end = Some(end);
 
             let mut range = start..end;
-            if rng.gen_bool(0.2) {
+            if rng.random_bool(0.2) {
                 mem::swap(&mut range.start, &mut range.end);
             }
 
-            let new_text_len = rng.gen_range(0..10);
+            let new_text_len = rng.random_range(0..10);
             let new_text: String = RandomCharIter::new(&mut *rng).take(new_text_len).collect();
 
             edits.push((range, new_text.into()));
@@ -3639,18 +3640,18 @@ impl MultiBuffer {
 
         let mut buffers = Vec::new();
         for _ in 0..mutation_count {
-            if rng.gen_bool(0.05) {
+            if rng.random_bool(0.05) {
                 log::info!("Clearing multi-buffer");
                 self.clear(cx);
                 continue;
-            } else if rng.gen_bool(0.1) && !self.excerpt_ids().is_empty() {
+            } else if rng.random_bool(0.1) && !self.excerpt_ids().is_empty() {
                 let ids = self.excerpt_ids();
                 let mut excerpts = HashSet::default();
-                for _ in 0..rng.gen_range(0..ids.len()) {
+                for _ in 0..rng.random_range(0..ids.len()) {
                     excerpts.extend(ids.choose(rng).copied());
                 }
 
-                let line_count = rng.gen_range(0..5);
+                let line_count = rng.random_range(0..5);
 
                 log::info!("Expanding excerpts {excerpts:?} by {line_count} lines");
 
@@ -3664,8 +3665,8 @@ impl MultiBuffer {
             }
 
             let excerpt_ids = self.excerpt_ids();
-            if excerpt_ids.is_empty() || (rng.r#gen() && excerpt_ids.len() < max_excerpts) {
-                let buffer_handle = if rng.r#gen() || self.buffers.borrow().is_empty() {
+            if excerpt_ids.is_empty() || (rng.random() && excerpt_ids.len() < max_excerpts) {
+                let buffer_handle = if rng.random() || self.buffers.borrow().is_empty() {
                     let text = RandomCharIter::new(&mut *rng).take(10).collect::<String>();
                     buffers.push(cx.new(|cx| Buffer::local(text, cx)));
                     let buffer = buffers.last().unwrap().read(cx);
@@ -3687,11 +3688,11 @@ impl MultiBuffer {
 
                 let buffer = buffer_handle.read(cx);
                 let buffer_text = buffer.text();
-                let ranges = (0..rng.gen_range(0..5))
+                let ranges = (0..rng.random_range(0..5))
                     .map(|_| {
                         let end_ix =
-                            buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Right);
-                        let start_ix = buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
+                            buffer.clip_offset(rng.random_range(0..=buffer.len()), Bias::Right);
+                        let start_ix = buffer.clip_offset(rng.random_range(0..=end_ix), Bias::Left);
                         ExcerptRange::new(start_ix..end_ix)
                     })
                     .collect::<Vec<_>>();
@@ -3708,7 +3709,7 @@ impl MultiBuffer {
                 let excerpt_id = self.push_excerpts(buffer_handle.clone(), ranges, cx);
                 log::info!("Inserted with ids: {:?}", excerpt_id);
             } else {
-                let remove_count = rng.gen_range(1..=excerpt_ids.len());
+                let remove_count = rng.random_range(1..=excerpt_ids.len());
                 let mut excerpts_to_remove = excerpt_ids
                     .choose_multiple(rng, remove_count)
                     .cloned()
@@ -3730,7 +3731,7 @@ impl MultiBuffer {
     ) {
         use rand::prelude::*;
 
-        if rng.gen_bool(0.7) || self.singleton {
+        if rng.random_bool(0.7) || self.singleton {
             let buffer = self
                 .buffers
                 .borrow()
@@ -3740,7 +3741,7 @@ impl MultiBuffer {
 
             if let Some(buffer) = buffer {
                 buffer.update(cx, |buffer, cx| {
-                    if rng.r#gen() {
+                    if rng.random() {
                         buffer.randomly_edit(rng, mutation_count, cx);
                     } else {
                         buffer.randomly_undo_redo(rng, cx);
@@ -6388,8 +6389,8 @@ impl MultiBufferSnapshot {
 #[cfg(any(test, feature = "test-support"))]
 impl MultiBufferSnapshot {
     pub fn random_byte_range(&self, start_offset: usize, rng: &mut impl rand::Rng) -> Range<usize> {
-        let end = self.clip_offset(rng.gen_range(start_offset..=self.len()), Bias::Right);
-        let start = self.clip_offset(rng.gen_range(start_offset..=end), Bias::Right);
+        let end = self.clip_offset(rng.random_range(start_offset..=self.len()), Bias::Right);
+        let start = self.clip_offset(rng.random_range(start_offset..=end), Bias::Right);
         start..end
     }
 

crates/multi_buffer/src/multi_buffer_tests.rs 🔗

@@ -2491,12 +2491,12 @@ async fn test_random_set_ranges(cx: &mut TestAppContext, mut rng: StdRng) {
 
     for _ in 0..operations {
         let snapshot = buf.update(cx, |buf, _| buf.snapshot());
-        let num_ranges = rng.gen_range(0..=10);
+        let num_ranges = rng.random_range(0..=10);
         let max_row = snapshot.max_point().row;
         let mut ranges = (0..num_ranges)
             .map(|_| {
-                let start = rng.gen_range(0..max_row);
-                let end = rng.gen_range(start + 1..max_row + 1);
+                let start = rng.random_range(0..max_row);
+                let end = rng.random_range(start + 1..max_row + 1);
                 Point::row_range(start..end)
             })
             .collect::<Vec<_>>();
@@ -2562,11 +2562,11 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
     let mut needs_diff_calculation = false;
 
     for _ in 0..operations {
-        match rng.gen_range(0..100) {
+        match rng.random_range(0..100) {
             0..=14 if !buffers.is_empty() => {
                 let buffer = buffers.choose(&mut rng).unwrap();
                 buffer.update(cx, |buf, cx| {
-                    let edit_count = rng.gen_range(1..5);
+                    let edit_count = rng.random_range(1..5);
                     buf.randomly_edit(&mut rng, edit_count, cx);
                     log::info!("buffer text:\n{}", buf.text());
                     needs_diff_calculation = true;
@@ -2577,11 +2577,11 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
                 multibuffer.update(cx, |multibuffer, cx| {
                     let ids = multibuffer.excerpt_ids();
                     let mut excerpts = HashSet::default();
-                    for _ in 0..rng.gen_range(0..ids.len()) {
+                    for _ in 0..rng.random_range(0..ids.len()) {
                         excerpts.extend(ids.choose(&mut rng).copied());
                     }
 
-                    let line_count = rng.gen_range(0..5);
+                    let line_count = rng.random_range(0..5);
 
                     let excerpt_ixs = excerpts
                         .iter()
@@ -2600,7 +2600,7 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
             }
             20..=29 if !reference.excerpts.is_empty() => {
                 let mut ids_to_remove = vec![];
-                for _ in 0..rng.gen_range(1..=3) {
+                for _ in 0..rng.random_range(1..=3) {
                     let Some(excerpt) = reference.excerpts.choose(&mut rng) else {
                         break;
                     };
@@ -2620,8 +2620,12 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
                 let multibuffer =
                     multibuffer.read_with(cx, |multibuffer, cx| multibuffer.snapshot(cx));
                 let offset =
-                    multibuffer.clip_offset(rng.gen_range(0..=multibuffer.len()), Bias::Left);
-                let bias = if rng.r#gen() { Bias::Left } else { Bias::Right };
+                    multibuffer.clip_offset(rng.random_range(0..=multibuffer.len()), Bias::Left);
+                let bias = if rng.random() {
+                    Bias::Left
+                } else {
+                    Bias::Right
+                };
                 log::info!("Creating anchor at {} with bias {:?}", offset, bias);
                 anchors.push(multibuffer.anchor_at(offset, bias));
                 anchors.sort_by(|a, b| a.cmp(b, &multibuffer));
@@ -2654,7 +2658,7 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
             45..=55 if !reference.excerpts.is_empty() => {
                 multibuffer.update(cx, |multibuffer, cx| {
                     let snapshot = multibuffer.snapshot(cx);
-                    let excerpt_ix = rng.gen_range(0..reference.excerpts.len());
+                    let excerpt_ix = rng.random_range(0..reference.excerpts.len());
                     let excerpt = &reference.excerpts[excerpt_ix];
                     let start = excerpt.range.start;
                     let end = excerpt.range.end;
@@ -2691,7 +2695,7 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
                 });
             }
             _ => {
-                let buffer_handle = if buffers.is_empty() || rng.gen_bool(0.4) {
+                let buffer_handle = if buffers.is_empty() || rng.random_bool(0.4) {
                     let mut base_text = util::RandomCharIter::new(&mut rng)
                         .take(256)
                         .collect::<String>();
@@ -2708,7 +2712,7 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
                     buffers.choose(&mut rng).unwrap()
                 };
 
-                let prev_excerpt_ix = rng.gen_range(0..=reference.excerpts.len());
+                let prev_excerpt_ix = rng.random_range(0..=reference.excerpts.len());
                 let prev_excerpt_id = reference
                     .excerpts
                     .get(prev_excerpt_ix)
@@ -2716,8 +2720,8 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
                 let excerpt_ix = (prev_excerpt_ix + 1).min(reference.excerpts.len());
 
                 let (range, anchor_range) = buffer_handle.read_with(cx, |buffer, _| {
-                    let end_row = rng.gen_range(0..=buffer.max_point().row);
-                    let start_row = rng.gen_range(0..=end_row);
+                    let end_row = rng.random_range(0..=buffer.max_point().row);
+                    let start_row = rng.random_range(0..=end_row);
                     let end_ix = buffer.point_to_offset(Point::new(end_row, 0));
                     let start_ix = buffer.point_to_offset(Point::new(start_row, 0));
                     let anchor_range = buffer.anchor_before(start_ix)..buffer.anchor_after(end_ix);
@@ -2766,7 +2770,7 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
             }
         }
 
-        if rng.gen_bool(0.3) {
+        if rng.random_bool(0.3) {
             multibuffer.update(cx, |multibuffer, cx| {
                 old_versions.push((multibuffer.snapshot(cx), multibuffer.subscribe()));
             })
@@ -2815,7 +2819,7 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
         pretty_assertions::assert_eq!(actual_row_infos, expected_row_infos);
 
         for _ in 0..5 {
-            let start_row = rng.gen_range(0..=expected_row_infos.len());
+            let start_row = rng.random_range(0..=expected_row_infos.len());
             assert_eq!(
                 snapshot
                     .row_infos(MultiBufferRow(start_row as u32))
@@ -2872,8 +2876,8 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
 
         let text_rope = Rope::from(expected_text.as_str());
         for _ in 0..10 {
-            let end_ix = text_rope.clip_offset(rng.gen_range(0..=text_rope.len()), Bias::Right);
-            let start_ix = text_rope.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
+            let end_ix = text_rope.clip_offset(rng.random_range(0..=text_rope.len()), Bias::Right);
+            let start_ix = text_rope.clip_offset(rng.random_range(0..=end_ix), Bias::Left);
 
             let text_for_range = snapshot
                 .text_for_range(start_ix..end_ix)
@@ -2908,7 +2912,7 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
         }
 
         for _ in 0..10 {
-            let end_ix = text_rope.clip_offset(rng.gen_range(0..=text_rope.len()), Bias::Right);
+            let end_ix = text_rope.clip_offset(rng.random_range(0..=text_rope.len()), Bias::Right);
             assert_eq!(
                 snapshot.reversed_chars_at(end_ix).collect::<String>(),
                 expected_text[..end_ix].chars().rev().collect::<String>(),
@@ -2916,8 +2920,8 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
         }
 
         for _ in 0..10 {
-            let end_ix = rng.gen_range(0..=text_rope.len());
-            let start_ix = rng.gen_range(0..=end_ix);
+            let end_ix = rng.random_range(0..=text_rope.len());
+            let start_ix = rng.random_range(0..=end_ix);
             assert_eq!(
                 snapshot
                     .bytes_in_range(start_ix..end_ix)

crates/project/src/lsp_store.rs 🔗

@@ -3761,7 +3761,7 @@ impl LspStore {
             worktree_store,
             languages: languages.clone(),
             language_server_statuses: Default::default(),
-            nonce: StdRng::from_entropy().r#gen(),
+            nonce: StdRng::from_os_rng().random(),
             diagnostic_summaries: HashMap::default(),
             lsp_server_capabilities: HashMap::default(),
             lsp_document_colors: HashMap::default(),
@@ -3823,7 +3823,7 @@ impl LspStore {
             worktree_store,
             languages: languages.clone(),
             language_server_statuses: Default::default(),
-            nonce: StdRng::from_entropy().r#gen(),
+            nonce: StdRng::from_os_rng().random(),
             diagnostic_summaries: HashMap::default(),
             lsp_server_capabilities: HashMap::default(),
             lsp_document_colors: HashMap::default(),

crates/project/src/project_tests.rs 🔗

@@ -7661,7 +7661,7 @@ async fn test_staging_random_hunks(
         .unwrap_or(20);
 
     // Try to induce races between diff recalculation and index writes.
-    if rng.gen_bool(0.5) {
+    if rng.random_bool(0.5) {
         executor.deprioritize(*CALCULATE_DIFF_TASK);
     }
 
@@ -7717,7 +7717,7 @@ async fn test_staging_random_hunks(
     assert_eq!(hunks.len(), 6);
 
     for _i in 0..operations {
-        let hunk_ix = rng.gen_range(0..hunks.len());
+        let hunk_ix = rng.random_range(0..hunks.len());
         let hunk = &mut hunks[hunk_ix];
         let row = hunk.range.start.row;
 
@@ -7735,7 +7735,7 @@ async fn test_staging_random_hunks(
             hunk.secondary_status = SecondaryHunkAdditionPending;
         }
 
-        for _ in 0..rng.gen_range(0..10) {
+        for _ in 0..rng.random_range(0..10) {
             log::info!("yielding");
             cx.executor().simulate_random_delay().await;
         }

crates/rope/benches/rope_benchmark.rs 🔗

@@ -28,11 +28,11 @@ fn generate_random_rope_ranges(mut rng: StdRng, rope: &Rope) -> Vec<Range<usize>
     let mut start = 0;
     for _ in 0..num_ranges {
         let range_start = rope.clip_offset(
-            rng.gen_range(start..=(start + range_max_len)),
+            rng.random_range(start..=(start + range_max_len)),
             sum_tree::Bias::Left,
         );
         let range_end = rope.clip_offset(
-            rng.gen_range(range_start..(range_start + range_max_len)),
+            rng.random_range(range_start..(range_start + range_max_len)),
             sum_tree::Bias::Right,
         );
 
@@ -52,7 +52,7 @@ fn generate_random_rope_points(mut rng: StdRng, rope: &Rope) -> Vec<Point> {
 
     let mut points = Vec::new();
     for _ in 0..num_points {
-        points.push(rope.offset_to_point(rng.gen_range(0..rope.len())));
+        points.push(rope.offset_to_point(rng.random_range(0..rope.len())));
     }
     points
 }

crates/rope/src/chunk.rs 🔗

@@ -612,7 +612,7 @@ mod tests {
 
     #[gpui::test(iterations = 100)]
     fn test_random_chunks(mut rng: StdRng) {
-        let chunk_len = rng.gen_range(0..=MAX_BASE);
+        let chunk_len = rng.random_range(0..=MAX_BASE);
         let text = RandomCharIter::new(&mut rng)
             .take(chunk_len)
             .collect::<String>();
@@ -627,8 +627,8 @@ mod tests {
         verify_chunk(chunk.as_slice(), text);
 
         for _ in 0..10 {
-            let mut start = rng.gen_range(0..=chunk.text.len());
-            let mut end = rng.gen_range(start..=chunk.text.len());
+            let mut start = rng.random_range(0..=chunk.text.len());
+            let mut end = rng.random_range(start..=chunk.text.len());
             while !chunk.text.is_char_boundary(start) {
                 start -= 1;
             }
@@ -645,7 +645,7 @@ mod tests {
 
     #[gpui::test(iterations = 1000)]
     fn test_nth_set_bit_random(mut rng: StdRng) {
-        let set_count = rng.gen_range(0..=128);
+        let set_count = rng.random_range(0..=128);
         let mut set_bits = (0..128).choose_multiple(&mut rng, set_count);
         set_bits.sort();
         let mut n = 0;

crates/rope/src/rope.rs 🔗

@@ -1610,9 +1610,9 @@ mod tests {
         let mut expected = String::new();
         let mut actual = Rope::new();
         for _ in 0..operations {
-            let end_ix = clip_offset(&expected, rng.gen_range(0..=expected.len()), Right);
-            let start_ix = clip_offset(&expected, rng.gen_range(0..=end_ix), Left);
-            let len = rng.gen_range(0..=64);
+            let end_ix = clip_offset(&expected, rng.random_range(0..=expected.len()), Right);
+            let start_ix = clip_offset(&expected, rng.random_range(0..=end_ix), Left);
+            let len = rng.random_range(0..=64);
             let new_text: String = RandomCharIter::new(&mut rng).take(len).collect();
 
             let mut new_actual = Rope::new();
@@ -1629,8 +1629,8 @@ mod tests {
             log::info!("text: {:?}", expected);
 
             for _ in 0..5 {
-                let end_ix = clip_offset(&expected, rng.gen_range(0..=expected.len()), Right);
-                let start_ix = clip_offset(&expected, rng.gen_range(0..=end_ix), Left);
+                let end_ix = clip_offset(&expected, rng.random_range(0..=expected.len()), Right);
+                let start_ix = clip_offset(&expected, rng.random_range(0..=end_ix), Left);
 
                 let actual_text = actual.chunks_in_range(start_ix..end_ix).collect::<String>();
                 assert_eq!(actual_text, &expected[start_ix..end_ix]);
@@ -1695,14 +1695,14 @@ mod tests {
                 );
 
                 // Check that next_line/prev_line work correctly from random positions
-                let mut offset = rng.gen_range(start_ix..=end_ix);
+                let mut offset = rng.random_range(start_ix..=end_ix);
                 while !expected.is_char_boundary(offset) {
                     offset -= 1;
                 }
                 chunks.seek(offset);
 
                 for _ in 0..5 {
-                    if rng.r#gen() {
+                    if rng.random() {
                         let expected_next_line_start = expected[offset..end_ix]
                             .find('\n')
                             .map(|newline_ix| offset + newline_ix + 1);
@@ -1791,8 +1791,8 @@ mod tests {
                     }
 
                     assert!((start_ix..=end_ix).contains(&chunks.offset()));
-                    if rng.r#gen() {
-                        offset = rng.gen_range(start_ix..=end_ix);
+                    if rng.random() {
+                        offset = rng.random_range(start_ix..=end_ix);
                         while !expected.is_char_boundary(offset) {
                             offset -= 1;
                         }
@@ -1876,8 +1876,8 @@ mod tests {
             }
 
             for _ in 0..5 {
-                let end_ix = clip_offset(&expected, rng.gen_range(0..=expected.len()), Right);
-                let start_ix = clip_offset(&expected, rng.gen_range(0..=end_ix), Left);
+                let end_ix = clip_offset(&expected, rng.random_range(0..=expected.len()), Right);
+                let start_ix = clip_offset(&expected, rng.random_range(0..=end_ix), Left);
                 assert_eq!(
                     actual.cursor(start_ix).summary::<TextSummary>(end_ix),
                     TextSummary::from(&expected[start_ix..end_ix])

crates/rpc/src/auth.rs 🔗

@@ -1,6 +1,6 @@
 use anyhow::{Context as _, Result};
 use base64::prelude::*;
-use rand::{Rng as _, thread_rng};
+use rand::prelude::*;
 use rsa::pkcs1::{DecodeRsaPublicKey, EncodeRsaPublicKey};
 use rsa::traits::PaddingScheme;
 use rsa::{Oaep, Pkcs1v15Encrypt, RsaPrivateKey, RsaPublicKey};
@@ -31,7 +31,7 @@ pub struct PrivateKey(RsaPrivateKey);
 
 /// Generate a public and private key for asymmetric encryption.
 pub fn keypair() -> Result<(PublicKey, PrivateKey)> {
-    let mut rng = thread_rng();
+    let mut rng = RsaRngCompat::new();
     let bits = 2048;
     let private_key = RsaPrivateKey::new(&mut rng, bits)?;
     let public_key = RsaPublicKey::from(&private_key);
@@ -40,10 +40,10 @@ pub fn keypair() -> Result<(PublicKey, PrivateKey)> {
 
 /// Generate a random 64-character base64 string.
 pub fn random_token() -> String {
-    let mut rng = thread_rng();
+    let mut rng = rand::rng();
     let mut token_bytes = [0; 48];
     for byte in token_bytes.iter_mut() {
-        *byte = rng.r#gen();
+        *byte = rng.random();
     }
     BASE64_URL_SAFE.encode(token_bytes)
 }
@@ -52,7 +52,7 @@ impl PublicKey {
     /// Convert a string to a base64-encoded string that can only be decoded with the corresponding
     /// private key.
     pub fn encrypt_string(&self, string: &str, format: EncryptionFormat) -> Result<String> {
-        let mut rng = thread_rng();
+        let mut rng = RsaRngCompat::new();
         let bytes = string.as_bytes();
         let encrypted_bytes = match format {
             EncryptionFormat::V0 => self.0.encrypt(&mut rng, Pkcs1v15Encrypt, bytes),
@@ -107,6 +107,36 @@ impl TryFrom<String> for PublicKey {
     }
 }
 
+// TODO: remove once we rsa v0.10 is released.
+struct RsaRngCompat(rand::rngs::ThreadRng);
+
+impl RsaRngCompat {
+    fn new() -> Self {
+        Self(rand::rng())
+    }
+}
+
+impl rsa::signature::rand_core::RngCore for RsaRngCompat {
+    fn next_u32(&mut self) -> u32 {
+        self.0.next_u32()
+    }
+
+    fn next_u64(&mut self) -> u64 {
+        self.0.next_u64()
+    }
+
+    fn fill_bytes(&mut self, dest: &mut [u8]) {
+        self.0.fill_bytes(dest);
+    }
+
+    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rsa::signature::rand_core::Error> {
+        self.fill_bytes(dest);
+        Ok(())
+    }
+}
+
+impl rsa::signature::rand_core::CryptoRng for RsaRngCompat {}
+
 #[cfg(test)]
 mod tests {
     use super::*;

crates/scheduler/Cargo.toml 🔗

@@ -0,0 +1,25 @@
+[package]
+name = "scheduler"
+version = "0.1.0"
+edition.workspace = true
+publish.workspace = true
+license = "Apache-2.0"
+
+[lints]
+workspace = true
+
+[lib]
+path = "src/scheduler.rs"
+doctest = false
+
+[features]
+test-support = []
+
+[dependencies]
+async-task.workspace = true
+chrono.workspace = true
+futures.workspace = true
+parking.workspace = true
+parking_lot.workspace = true
+rand.workspace = true
+workspace-hack.workspace = true

crates/scheduler/src/clock.rs 🔗

@@ -0,0 +1,34 @@
+use chrono::{DateTime, Duration, Utc};
+use parking_lot::Mutex;
+
+pub trait Clock {
+    fn now(&self) -> DateTime<Utc>;
+}
+
+pub struct TestClock {
+    now: Mutex<DateTime<Utc>>,
+}
+
+impl TestClock {
+    pub fn new() -> Self {
+        const START_TIME: &str = "2025-07-01T23:59:58-00:00";
+        let now = DateTime::parse_from_rfc3339(START_TIME).unwrap().to_utc();
+        Self {
+            now: Mutex::new(now),
+        }
+    }
+
+    pub fn set_now(&self, now: DateTime<Utc>) {
+        *self.now.lock() = now;
+    }
+
+    pub fn advance(&self, duration: Duration) {
+        *self.now.lock() += duration;
+    }
+}
+
+impl Clock for TestClock {
+    fn now(&self) -> DateTime<Utc> {
+        *self.now.lock()
+    }
+}

crates/scheduler/src/executor.rs 🔗

@@ -0,0 +1,137 @@
+use crate::{Scheduler, SessionId, Timer};
+use std::{
+    future::Future,
+    marker::PhantomData,
+    pin::Pin,
+    rc::Rc,
+    sync::Arc,
+    task::{Context, Poll},
+    time::Duration,
+};
+
+#[derive(Clone)]
+pub struct ForegroundExecutor {
+    session_id: SessionId,
+    scheduler: Arc<dyn Scheduler>,
+    not_send: PhantomData<Rc<()>>,
+}
+
+impl ForegroundExecutor {
+    pub fn spawn<F>(&self, future: F) -> Task<F::Output>
+    where
+        F: Future + 'static,
+        F::Output: 'static,
+    {
+        let session_id = self.session_id;
+        let scheduler = Arc::clone(&self.scheduler);
+        let (runnable, task) = async_task::spawn_local(future, move |runnable| {
+            scheduler.schedule_foreground(session_id, runnable);
+        });
+        runnable.schedule();
+        Task(TaskState::Spawned(task))
+    }
+
+    pub fn timer(&self, duration: Duration) -> Timer {
+        self.scheduler.timer(duration)
+    }
+}
+
+impl ForegroundExecutor {
+    pub fn new(session_id: SessionId, scheduler: Arc<dyn Scheduler>) -> Self {
+        assert!(
+            scheduler.is_main_thread(),
+            "ForegroundExecutor must be created on the same thread as the Scheduler"
+        );
+        Self {
+            session_id,
+            scheduler,
+            not_send: PhantomData,
+        }
+    }
+}
+
+impl BackgroundExecutor {
+    pub fn new(scheduler: Arc<dyn Scheduler>) -> Self {
+        Self { scheduler }
+    }
+}
+
+pub struct BackgroundExecutor {
+    scheduler: Arc<dyn Scheduler>,
+}
+
+impl BackgroundExecutor {
+    pub fn spawn<F>(&self, future: F) -> Task<F::Output>
+    where
+        F: Future + Send + 'static,
+        F::Output: Send + 'static,
+    {
+        let scheduler = Arc::clone(&self.scheduler);
+        let (runnable, task) = async_task::spawn(future, move |runnable| {
+            scheduler.schedule_background(runnable);
+        });
+        runnable.schedule();
+        Task(TaskState::Spawned(task))
+    }
+
+    pub fn block_on<Fut: Future>(&self, future: Fut) -> Fut::Output {
+        self.scheduler.block_on(future)
+    }
+
+    pub fn block_with_timeout<Fut: Unpin + Future>(
+        &self,
+        future: &mut Fut,
+        timeout: Duration,
+    ) -> Option<Fut::Output> {
+        self.scheduler.block_with_timeout(future, timeout)
+    }
+
+    pub fn timer(&self, duration: Duration) -> Timer {
+        self.scheduler.timer(duration)
+    }
+}
+
+/// Task is a primitive that allows work to happen in the background.
+///
+/// It implements [`Future`] so you can `.await` on it.
+///
+/// If you drop a task it will be cancelled immediately. Calling [`Task::detach`] allows
+/// the task to continue running, but with no way to return a value.
+#[must_use]
+#[derive(Debug)]
+pub struct Task<T>(TaskState<T>);
+
+#[derive(Debug)]
+enum TaskState<T> {
+    /// A task that is ready to return a value
+    Ready(Option<T>),
+
+    /// A task that is currently running.
+    Spawned(async_task::Task<T>),
+}
+
+impl<T> Task<T> {
+    /// Creates a new task that will resolve with the value
+    pub fn ready(val: T) -> Self {
+        Task(TaskState::Ready(Some(val)))
+    }
+
+    /// Detaching a task runs it to completion in the background
+    pub fn detach(self) {
+        match self {
+            Task(TaskState::Ready(_)) => {}
+            Task(TaskState::Spawned(task)) => task.detach(),
+        }
+    }
+}
+
+impl<T> Future for Task<T> {
+    type Output = T;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
+        match unsafe { self.get_unchecked_mut() } {
+            Task(TaskState::Ready(val)) => Poll::Ready(val.take().unwrap()),
+            Task(TaskState::Spawned(task)) => Pin::new(task).poll(cx),
+        }
+    }
+}

crates/scheduler/src/scheduler.rs 🔗

@@ -0,0 +1,63 @@
+mod clock;
+mod executor;
+mod test_scheduler;
+#[cfg(test)]
+mod tests;
+
+pub use clock::*;
+pub use executor::*;
+pub use test_scheduler::*;
+
+use async_task::Runnable;
+use futures::{FutureExt as _, channel::oneshot, future::LocalBoxFuture};
+use std::{
+    future::Future,
+    pin::Pin,
+    task::{Context, Poll},
+    time::Duration,
+};
+
+pub trait Scheduler: Send + Sync {
+    fn block(&self, future: LocalBoxFuture<()>, timeout: Option<Duration>);
+    fn schedule_foreground(&self, session_id: SessionId, runnable: Runnable);
+    fn schedule_background(&self, runnable: Runnable);
+    fn timer(&self, timeout: Duration) -> Timer;
+    fn is_main_thread(&self) -> bool;
+}
+
+impl dyn Scheduler {
+    pub fn block_on<Fut: Future>(&self, future: Fut) -> Fut::Output {
+        let mut output = None;
+        self.block(async { output = Some(future.await) }.boxed_local(), None);
+        output.unwrap()
+    }
+
+    pub fn block_with_timeout<Fut: Unpin + Future>(
+        &self,
+        future: &mut Fut,
+        timeout: Duration,
+    ) -> Option<Fut::Output> {
+        let mut output = None;
+        self.block(
+            async { output = Some(future.await) }.boxed_local(),
+            Some(timeout),
+        );
+        output
+    }
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
+pub struct SessionId(u16);
+
+pub struct Timer(oneshot::Receiver<()>);
+
+impl Future for Timer {
+    type Output = ();
+
+    fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> {
+        match self.0.poll_unpin(cx) {
+            Poll::Ready(_) => Poll::Ready(()),
+            Poll::Pending => Poll::Pending,
+        }
+    }
+}

crates/scheduler/src/test_scheduler.rs 🔗

@@ -0,0 +1,352 @@
+use crate::{
+    BackgroundExecutor, Clock as _, ForegroundExecutor, Scheduler, SessionId, TestClock, Timer,
+};
+use async_task::Runnable;
+use chrono::{DateTime, Duration as ChronoDuration, Utc};
+use futures::{FutureExt as _, channel::oneshot, future::LocalBoxFuture};
+use parking_lot::Mutex;
+use rand::prelude::*;
+use std::{
+    collections::VecDeque,
+    future::Future,
+    panic::{self, AssertUnwindSafe},
+    pin::Pin,
+    sync::{
+        Arc,
+        atomic::{AtomicBool, Ordering::SeqCst},
+    },
+    task::{Context, Poll, Wake, Waker},
+    thread,
+    time::{Duration, Instant},
+};
+
+pub struct TestScheduler {
+    clock: Arc<TestClock>,
+    rng: Arc<Mutex<StdRng>>,
+    state: Mutex<SchedulerState>,
+    pub thread_id: thread::ThreadId,
+    pub config: SchedulerConfig,
+}
+
+impl TestScheduler {
+    /// Run a test once with default configuration (seed 0)
+    pub fn once<R>(f: impl AsyncFnOnce(Arc<TestScheduler>) -> R) -> R {
+        Self::with_seed(0, f)
+    }
+
+    /// Run a test multiple times with sequential seeds (0, 1, 2, ...)
+    pub fn many<R>(iterations: usize, mut f: impl AsyncFnMut(Arc<TestScheduler>) -> R) -> Vec<R> {
+        (0..iterations as u64)
+            .map(|seed| {
+                let mut unwind_safe_f = AssertUnwindSafe(&mut f);
+                match panic::catch_unwind(move || Self::with_seed(seed, &mut *unwind_safe_f)) {
+                    Ok(result) => result,
+                    Err(error) => {
+                        eprintln!("Failing Seed: {seed}");
+                        panic::resume_unwind(error);
+                    }
+                }
+            })
+            .collect()
+    }
+
+    /// Run a test once with a specific seed
+    pub fn with_seed<R>(seed: u64, f: impl AsyncFnOnce(Arc<TestScheduler>) -> R) -> R {
+        let scheduler = Arc::new(TestScheduler::new(SchedulerConfig::with_seed(seed)));
+        let future = f(scheduler.clone());
+        let result = scheduler.block_on(future);
+        scheduler.run();
+        result
+    }
+
+    pub fn new(config: SchedulerConfig) -> Self {
+        Self {
+            rng: Arc::new(Mutex::new(StdRng::seed_from_u64(config.seed))),
+            state: Mutex::new(SchedulerState {
+                runnables: VecDeque::new(),
+                timers: Vec::new(),
+                randomize_order: config.randomize_order,
+                allow_parking: config.allow_parking,
+                next_session_id: SessionId(0),
+            }),
+            thread_id: thread::current().id(),
+            clock: Arc::new(TestClock::new()),
+            config,
+        }
+    }
+
+    pub fn clock(&self) -> Arc<TestClock> {
+        self.clock.clone()
+    }
+
+    pub fn rng(&self) -> Arc<Mutex<StdRng>> {
+        self.rng.clone()
+    }
+
+    /// Create a foreground executor for this scheduler
+    pub fn foreground(self: &Arc<Self>) -> ForegroundExecutor {
+        let session_id = {
+            let mut state = self.state.lock();
+            state.next_session_id.0 += 1;
+            state.next_session_id
+        };
+        ForegroundExecutor::new(session_id, self.clone())
+    }
+
+    /// Create a background executor for this scheduler
+    pub fn background(self: &Arc<Self>) -> BackgroundExecutor {
+        BackgroundExecutor::new(self.clone())
+    }
+
+    pub fn block_on<Fut: Future>(&self, future: Fut) -> Fut::Output {
+        (self as &dyn Scheduler).block_on(future)
+    }
+
+    pub fn yield_random(&self) -> Yield {
+        Yield(self.rng.lock().random_range(0..20))
+    }
+
+    pub fn run(&self) {
+        while self.step() || self.advance_clock() {
+            // Continue until no work remains
+        }
+    }
+
+    fn step(&self) -> bool {
+        let elapsed_timers = {
+            let mut state = self.state.lock();
+            let end_ix = state
+                .timers
+                .partition_point(|timer| timer.expiration <= self.clock.now());
+            state.timers.drain(..end_ix).collect::<Vec<_>>()
+        };
+
+        if !elapsed_timers.is_empty() {
+            return true;
+        }
+
+        let runnable = self.state.lock().runnables.pop_front();
+        if let Some(runnable) = runnable {
+            runnable.run();
+            return true;
+        }
+
+        false
+    }
+
+    fn advance_clock(&self) -> bool {
+        if let Some(timer) = self.state.lock().timers.first() {
+            self.clock.set_now(timer.expiration);
+            true
+        } else {
+            false
+        }
+    }
+}
+
+impl Scheduler for TestScheduler {
+    fn is_main_thread(&self) -> bool {
+        thread::current().id() == self.thread_id
+    }
+
+    fn schedule_foreground(&self, session_id: SessionId, runnable: Runnable) {
+        let mut state = self.state.lock();
+        let ix = if state.randomize_order {
+            let start_ix = state
+                .runnables
+                .iter()
+                .rposition(|task| task.session_id == Some(session_id))
+                .map_or(0, |ix| ix + 1);
+            self.rng
+                .lock()
+                .random_range(start_ix..=state.runnables.len())
+        } else {
+            state.runnables.len()
+        };
+        state.runnables.insert(
+            ix,
+            ScheduledRunnable {
+                session_id: Some(session_id),
+                runnable,
+            },
+        );
+    }
+
+    fn schedule_background(&self, runnable: Runnable) {
+        let mut state = self.state.lock();
+        let ix = if state.randomize_order {
+            self.rng.lock().random_range(0..=state.runnables.len())
+        } else {
+            state.runnables.len()
+        };
+        state.runnables.insert(
+            ix,
+            ScheduledRunnable {
+                session_id: None,
+                runnable,
+            },
+        );
+    }
+
+    fn timer(&self, duration: Duration) -> Timer {
+        let (tx, rx) = oneshot::channel();
+        let expiration = self.clock.now() + ChronoDuration::from_std(duration).unwrap();
+        let state = &mut *self.state.lock();
+        state.timers.push(ScheduledTimer {
+            expiration,
+            _notify: tx,
+        });
+        state.timers.sort_by_key(|timer| timer.expiration);
+        Timer(rx)
+    }
+
+    /// Block until the given future completes, with an optional timeout. If the
+    /// future is unable to make progress at any moment before the timeout and
+    /// no other tasks or timers remain, we panic unless parking is allowed. If
+    /// parking is allowed, we block up to the timeout or indefinitely if none
+    /// is provided. This is to allow testing a mix of deterministic and
+    /// non-deterministic async behavior, such as when interacting with I/O in
+    /// an otherwise deterministic test.
+    fn block(&self, mut future: LocalBoxFuture<()>, timeout: Option<Duration>) {
+        let (parker, unparker) = parking::pair();
+        let deadline = timeout.map(|timeout| Instant::now() + timeout);
+        let awoken = Arc::new(AtomicBool::new(false));
+        let waker = Waker::from(Arc::new(WakerFn::new({
+            let awoken = awoken.clone();
+            move || {
+                awoken.store(true, SeqCst);
+                unparker.unpark();
+            }
+        })));
+        let max_ticks = if timeout.is_some() {
+            self.rng
+                .lock()
+                .random_range(0..=self.config.max_timeout_ticks)
+        } else {
+            usize::MAX
+        };
+        let mut cx = Context::from_waker(&waker);
+
+        for _ in 0..max_ticks {
+            let Poll::Pending = future.poll_unpin(&mut cx) else {
+                break;
+            };
+
+            let mut stepped = None;
+            while self.rng.lock().random() && stepped.unwrap_or(true) {
+                *stepped.get_or_insert(false) |= self.step();
+            }
+
+            let stepped = stepped.unwrap_or(true);
+            let awoken = awoken.swap(false, SeqCst);
+            if !stepped && !awoken && !self.advance_clock() {
+                if self.state.lock().allow_parking {
+                    if !park(&parker, deadline) {
+                        break;
+                    }
+                } else if deadline.is_some() {
+                    break;
+                } else {
+                    panic!("Parking forbidden");
+                }
+            }
+        }
+    }
+}
+
+#[derive(Clone, Debug)]
+pub struct SchedulerConfig {
+    pub seed: u64,
+    pub randomize_order: bool,
+    pub allow_parking: bool,
+    pub max_timeout_ticks: usize,
+}
+
+impl SchedulerConfig {
+    pub fn with_seed(seed: u64) -> Self {
+        Self {
+            seed,
+            ..Default::default()
+        }
+    }
+}
+
+impl Default for SchedulerConfig {
+    fn default() -> Self {
+        Self {
+            seed: 0,
+            randomize_order: true,
+            allow_parking: false,
+            max_timeout_ticks: 1000,
+        }
+    }
+}
+
+struct ScheduledRunnable {
+    session_id: Option<SessionId>,
+    runnable: Runnable,
+}
+
+impl ScheduledRunnable {
+    fn run(self) {
+        self.runnable.run();
+    }
+}
+
+struct ScheduledTimer {
+    expiration: DateTime<Utc>,
+    _notify: oneshot::Sender<()>,
+}
+
+struct SchedulerState {
+    runnables: VecDeque<ScheduledRunnable>,
+    timers: Vec<ScheduledTimer>,
+    randomize_order: bool,
+    allow_parking: bool,
+    next_session_id: SessionId,
+}
+
+struct WakerFn<F> {
+    f: F,
+}
+
+impl<F: Fn()> WakerFn<F> {
+    fn new(f: F) -> Self {
+        Self { f }
+    }
+}
+
+impl<F: Fn()> Wake for WakerFn<F> {
+    fn wake(self: Arc<Self>) {
+        (self.f)();
+    }
+
+    fn wake_by_ref(self: &Arc<Self>) {
+        (self.f)();
+    }
+}
+
+pub struct Yield(usize);
+
+impl Future for Yield {
+    type Output = ();
+
+    fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
+        if self.0 == 0 {
+            Poll::Ready(())
+        } else {
+            self.0 -= 1;
+            cx.waker().wake_by_ref();
+            Poll::Pending
+        }
+    }
+}
+
+fn park(parker: &parking::Parker, deadline: Option<Instant>) -> bool {
+    if let Some(deadline) = deadline {
+        parker.park_deadline(deadline)
+    } else {
+        parker.park();
+        true
+    }
+}

crates/scheduler/src/tests.rs 🔗

@@ -0,0 +1,348 @@
+use super::*;
+use futures::{
+    FutureExt,
+    channel::{mpsc, oneshot},
+    executor::block_on,
+    future,
+    sink::SinkExt,
+    stream::{FuturesUnordered, StreamExt},
+};
+use std::{
+    cell::RefCell,
+    collections::{BTreeSet, HashSet},
+    pin::Pin,
+    rc::Rc,
+    sync::Arc,
+    task::{Context, Poll},
+};
+
+#[test]
+fn test_foreground_executor_spawn() {
+    let result = TestScheduler::once(async |scheduler| {
+        let task = scheduler.foreground().spawn(async move { 42 });
+        task.await
+    });
+    assert_eq!(result, 42);
+}
+
+#[test]
+fn test_background_executor_spawn() {
+    TestScheduler::once(async |scheduler| {
+        let task = scheduler.background().spawn(async move { 42 });
+        let result = task.await;
+        assert_eq!(result, 42);
+    });
+}
+
+#[test]
+fn test_foreground_ordering() {
+    let mut traces = HashSet::new();
+
+    TestScheduler::many(100, async |scheduler| {
+        #[derive(Hash, PartialEq, Eq)]
+        struct TraceEntry {
+            session: usize,
+            task: usize,
+        }
+
+        let trace = Rc::new(RefCell::new(Vec::new()));
+
+        let foreground_1 = scheduler.foreground();
+        for task in 0..10 {
+            foreground_1
+                .spawn({
+                    let trace = trace.clone();
+                    async move {
+                        trace.borrow_mut().push(TraceEntry { session: 0, task });
+                    }
+                })
+                .detach();
+        }
+
+        let foreground_2 = scheduler.foreground();
+        for task in 0..10 {
+            foreground_2
+                .spawn({
+                    let trace = trace.clone();
+                    async move {
+                        trace.borrow_mut().push(TraceEntry { session: 1, task });
+                    }
+                })
+                .detach();
+        }
+
+        scheduler.run();
+
+        assert_eq!(
+            trace
+                .borrow()
+                .iter()
+                .filter(|entry| entry.session == 0)
+                .map(|entry| entry.task)
+                .collect::<Vec<_>>(),
+            (0..10).collect::<Vec<_>>()
+        );
+        assert_eq!(
+            trace
+                .borrow()
+                .iter()
+                .filter(|entry| entry.session == 1)
+                .map(|entry| entry.task)
+                .collect::<Vec<_>>(),
+            (0..10).collect::<Vec<_>>()
+        );
+
+        traces.insert(trace.take());
+    });
+
+    assert!(traces.len() > 1, "Expected at least two traces");
+}
+
+#[test]
+fn test_timer_ordering() {
+    TestScheduler::many(1, async |scheduler| {
+        let background = scheduler.background();
+        let futures = FuturesUnordered::new();
+        futures.push(
+            async {
+                background.timer(Duration::from_millis(100)).await;
+                2
+            }
+            .boxed(),
+        );
+        futures.push(
+            async {
+                background.timer(Duration::from_millis(50)).await;
+                1
+            }
+            .boxed(),
+        );
+        futures.push(
+            async {
+                background.timer(Duration::from_millis(150)).await;
+                3
+            }
+            .boxed(),
+        );
+        assert_eq!(futures.collect::<Vec<_>>().await, vec![1, 2, 3]);
+    });
+}
+
+#[test]
+fn test_send_from_bg_to_fg() {
+    TestScheduler::once(async |scheduler| {
+        let foreground = scheduler.foreground();
+        let background = scheduler.background();
+
+        let (sender, receiver) = oneshot::channel::<i32>();
+
+        background
+            .spawn(async move {
+                sender.send(42).unwrap();
+            })
+            .detach();
+
+        let task = foreground.spawn(async move { receiver.await.unwrap() });
+        let result = task.await;
+        assert_eq!(result, 42);
+    });
+}
+
+#[test]
+fn test_randomize_order() {
+    // Test deterministic mode: different seeds should produce same execution order
+    let mut deterministic_results = HashSet::new();
+    for seed in 0..10 {
+        let config = SchedulerConfig {
+            seed,
+            randomize_order: false,
+            ..Default::default()
+        };
+        let order = block_on(capture_execution_order(config));
+        assert_eq!(order.len(), 6);
+        deterministic_results.insert(order);
+    }
+
+    // All deterministic runs should produce the same result
+    assert_eq!(
+        deterministic_results.len(),
+        1,
+        "Deterministic mode should always produce same execution order"
+    );
+
+    // Test randomized mode: different seeds can produce different execution orders
+    let mut randomized_results = HashSet::new();
+    for seed in 0..20 {
+        let config = SchedulerConfig::with_seed(seed);
+        let order = block_on(capture_execution_order(config));
+        assert_eq!(order.len(), 6);
+        randomized_results.insert(order);
+    }
+
+    // Randomized mode should produce multiple different execution orders
+    assert!(
+        randomized_results.len() > 1,
+        "Randomized mode should produce multiple different orders"
+    );
+}
+
+async fn capture_execution_order(config: SchedulerConfig) -> Vec<String> {
+    let scheduler = Arc::new(TestScheduler::new(config));
+    let foreground = scheduler.foreground();
+    let background = scheduler.background();
+
+    let (sender, receiver) = mpsc::unbounded::<String>();
+
+    // Spawn foreground tasks
+    for i in 0..3 {
+        let mut sender = sender.clone();
+        foreground
+            .spawn(async move {
+                sender.send(format!("fg-{}", i)).await.ok();
+            })
+            .detach();
+    }
+
+    // Spawn background tasks
+    for i in 0..3 {
+        let mut sender = sender.clone();
+        background
+            .spawn(async move {
+                sender.send(format!("bg-{}", i)).await.ok();
+            })
+            .detach();
+    }
+
+    drop(sender); // Close sender to signal no more messages
+    scheduler.run();
+
+    receiver.collect().await
+}
+
+#[test]
+fn test_block() {
+    let scheduler = Arc::new(TestScheduler::new(SchedulerConfig::default()));
+    let executor = BackgroundExecutor::new(scheduler);
+    let (tx, rx) = oneshot::channel();
+
+    // Spawn background task to send value
+    let _ = executor
+        .spawn(async move {
+            tx.send(42).unwrap();
+        })
+        .detach();
+
+    // Block on receiving the value
+    let result = executor.block_on(async { rx.await.unwrap() });
+    assert_eq!(result, 42);
+}
+
+#[test]
+#[should_panic(expected = "Parking forbidden")]
+fn test_parking_panics() {
+    let scheduler = Arc::new(TestScheduler::new(SchedulerConfig::default()));
+    let executor = BackgroundExecutor::new(scheduler);
+    executor.block_on(future::pending::<()>());
+}
+
+#[test]
+fn test_block_with_parking() {
+    let config = SchedulerConfig {
+        allow_parking: true,
+        ..Default::default()
+    };
+    let scheduler = Arc::new(TestScheduler::new(config));
+    let executor = BackgroundExecutor::new(scheduler);
+    let (tx, rx) = oneshot::channel();
+
+    // Spawn background task to send value
+    let _ = executor
+        .spawn(async move {
+            tx.send(42).unwrap();
+        })
+        .detach();
+
+    // Block on receiving the value (will park if needed)
+    let result = executor.block_on(async { rx.await.unwrap() });
+    assert_eq!(result, 42);
+}
+
+#[test]
+fn test_helper_methods() {
+    // Test the once method
+    let result = TestScheduler::once(async |scheduler: Arc<TestScheduler>| {
+        let background = scheduler.background();
+        background.spawn(async { 42 }).await
+    });
+    assert_eq!(result, 42);
+
+    // Test the many method
+    let results = TestScheduler::many(3, async |scheduler: Arc<TestScheduler>| {
+        let background = scheduler.background();
+        background.spawn(async { 10 }).await
+    });
+    assert_eq!(results, vec![10, 10, 10]);
+
+    // Test the with_seed method
+    let result = TestScheduler::with_seed(123, async |scheduler: Arc<TestScheduler>| {
+        let background = scheduler.background();
+
+        // Spawn a background task and wait for its result
+        let task = background.spawn(async { 99 });
+        task.await
+    });
+    assert_eq!(result, 99);
+}
+
+#[test]
+fn test_block_with_timeout() {
+    // Test case: future completes within timeout
+    TestScheduler::once(async |scheduler| {
+        let background = scheduler.background();
+        let mut future = future::ready(42);
+        let output = background.block_with_timeout(&mut future, Duration::from_millis(100));
+        assert_eq!(output, Some(42));
+    });
+
+    // Test case: future times out
+    TestScheduler::once(async |scheduler| {
+        let background = scheduler.background();
+        let mut future = future::pending::<()>();
+        let output = background.block_with_timeout(&mut future, Duration::from_millis(50));
+        assert_eq!(output, None);
+    });
+
+    // Test case: future makes progress via timer but still times out
+    let mut results = BTreeSet::new();
+    TestScheduler::many(100, async |scheduler| {
+        let background = scheduler.background();
+        let mut task = background.spawn(async move {
+            Yield { polls: 10 }.await;
+            42
+        });
+        let output = background.block_with_timeout(&mut task, Duration::from_millis(50));
+        results.insert(output);
+    });
+    assert_eq!(
+        results.into_iter().collect::<Vec<_>>(),
+        vec![None, Some(42)]
+    );
+}
+
+struct Yield {
+    polls: usize,
+}
+
+impl Future for Yield {
+    type Output = ();
+
+    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        self.polls -= 1;
+        if self.polls == 0 {
+            Poll::Ready(())
+        } else {
+            cx.waker().wake_by_ref();
+            Poll::Pending
+        }
+    }
+}

crates/streaming_diff/src/streaming_diff.rs 🔗

@@ -945,7 +945,7 @@ mod tests {
         let mut new_len = 0;
 
         while new_len < new.len() {
-            let mut chunk_len = rng.gen_range(1..=new.len() - new_len);
+            let mut chunk_len = rng.random_range(1..=new.len() - new_len);
             while !new.is_char_boundary(new_len + chunk_len) {
                 chunk_len += 1;
             }
@@ -1034,14 +1034,14 @@ mod tests {
 
     fn randomly_edit(text: &str, rng: &mut impl Rng) -> String {
         let mut result = String::from(text);
-        let edit_count = rng.gen_range(1..=5);
+        let edit_count = rng.random_range(1..=5);
 
         fn random_char_range(text: &str, rng: &mut impl Rng) -> (usize, usize) {
-            let mut start = rng.gen_range(0..=text.len());
+            let mut start = rng.random_range(0..=text.len());
             while !text.is_char_boundary(start) {
                 start -= 1;
             }
-            let mut end = rng.gen_range(start..=text.len());
+            let mut end = rng.random_range(start..=text.len());
             while !text.is_char_boundary(end) {
                 end += 1;
             }
@@ -1049,11 +1049,11 @@ mod tests {
         }
 
         for _ in 0..edit_count {
-            match rng.gen_range(0..3) {
+            match rng.random_range(0..3) {
                 0 => {
                     // Insert
                     let (pos, _) = random_char_range(&result, rng);
-                    let insert_len = rng.gen_range(1..=5);
+                    let insert_len = rng.random_range(1..=5);
                     let insert_text: String = random_text(rng, insert_len);
                     result.insert_str(pos, &insert_text);
                 }

crates/sum_tree/src/sum_tree.rs 🔗

@@ -909,7 +909,7 @@ where
 #[cfg(test)]
 mod tests {
     use super::*;
-    use rand::{distributions, prelude::*};
+    use rand::{distr::StandardUniform, prelude::*};
     use std::cmp;
 
     #[ctor::ctor]
@@ -951,24 +951,24 @@ mod tests {
 
             let rng = &mut rng;
             let mut tree = SumTree::<u8>::default();
-            let count = rng.gen_range(0..10);
-            if rng.r#gen() {
-                tree.extend(rng.sample_iter(distributions::Standard).take(count), &());
+            let count = rng.random_range(0..10);
+            if rng.random() {
+                tree.extend(rng.sample_iter(StandardUniform).take(count), &());
             } else {
                 let items = rng
-                    .sample_iter(distributions::Standard)
+                    .sample_iter(StandardUniform)
                     .take(count)
                     .collect::<Vec<_>>();
                 tree.par_extend(items, &());
             }
 
             for _ in 0..num_operations {
-                let splice_end = rng.gen_range(0..tree.extent::<Count>(&()).0 + 1);
-                let splice_start = rng.gen_range(0..splice_end + 1);
-                let count = rng.gen_range(0..10);
+                let splice_end = rng.random_range(0..tree.extent::<Count>(&()).0 + 1);
+                let splice_start = rng.random_range(0..splice_end + 1);
+                let count = rng.random_range(0..10);
                 let tree_end = tree.extent::<Count>(&());
                 let new_items = rng
-                    .sample_iter(distributions::Standard)
+                    .sample_iter(StandardUniform)
                     .take(count)
                     .collect::<Vec<u8>>();
 
@@ -978,7 +978,7 @@ mod tests {
                 tree = {
                     let mut cursor = tree.cursor::<Count>(&());
                     let mut new_tree = cursor.slice(&Count(splice_start), Bias::Right);
-                    if rng.r#gen() {
+                    if rng.random() {
                         new_tree.extend(new_items, &());
                     } else {
                         new_tree.par_extend(new_items, &());
@@ -1005,7 +1005,7 @@ mod tests {
                     .filter(|(_, item)| (item & 1) == 0)
                     .collect::<Vec<_>>();
 
-                let mut item_ix = if rng.r#gen() {
+                let mut item_ix = if rng.random() {
                     filter_cursor.next();
                     0
                 } else {
@@ -1022,12 +1022,12 @@ mod tests {
                     filter_cursor.next();
                     item_ix += 1;
 
-                    while item_ix > 0 && rng.gen_bool(0.2) {
+                    while item_ix > 0 && rng.random_bool(0.2) {
                         log::info!("prev");
                         filter_cursor.prev();
                         item_ix -= 1;
 
-                        if item_ix == 0 && rng.gen_bool(0.2) {
+                        if item_ix == 0 && rng.random_bool(0.2) {
                             filter_cursor.prev();
                             assert_eq!(filter_cursor.item(), None);
                             assert_eq!(filter_cursor.start().0, 0);
@@ -1039,9 +1039,9 @@ mod tests {
 
                 let mut before_start = false;
                 let mut cursor = tree.cursor::<Count>(&());
-                let start_pos = rng.gen_range(0..=reference_items.len());
+                let start_pos = rng.random_range(0..=reference_items.len());
                 cursor.seek(&Count(start_pos), Bias::Right);
-                let mut pos = rng.gen_range(start_pos..=reference_items.len());
+                let mut pos = rng.random_range(start_pos..=reference_items.len());
                 cursor.seek_forward(&Count(pos), Bias::Right);
 
                 for i in 0..10 {
@@ -1084,10 +1084,18 @@ mod tests {
             }
 
             for _ in 0..10 {
-                let end = rng.gen_range(0..tree.extent::<Count>(&()).0 + 1);
-                let start = rng.gen_range(0..end + 1);
-                let start_bias = if rng.r#gen() { Bias::Left } else { Bias::Right };
-                let end_bias = if rng.r#gen() { Bias::Left } else { Bias::Right };
+                let end = rng.random_range(0..tree.extent::<Count>(&()).0 + 1);
+                let start = rng.random_range(0..end + 1);
+                let start_bias = if rng.random() {
+                    Bias::Left
+                } else {
+                    Bias::Right
+                };
+                let end_bias = if rng.random() {
+                    Bias::Left
+                } else {
+                    Bias::Right
+                };
 
                 let mut cursor = tree.cursor::<Count>(&());
                 cursor.seek(&Count(start), start_bias);

crates/terminal/src/terminal.rs 🔗

@@ -2198,7 +2198,7 @@ mod tests {
     };
     use collections::HashMap;
     use gpui::{Pixels, Point, TestAppContext, bounds, point, size};
-    use rand::{Rng, distributions::Alphanumeric, rngs::ThreadRng, thread_rng};
+    use rand::{Rng, distr, rngs::ThreadRng};
 
     #[ignore = "Test is flaky on macOS, and doesn't run on Windows"]
     #[gpui::test]
@@ -2249,13 +2249,14 @@ mod tests {
 
     #[test]
     fn test_mouse_to_cell_test() {
-        let mut rng = thread_rng();
+        let mut rng = rand::rng();
         const ITERATIONS: usize = 10;
         const PRECISION: usize = 1000;
 
         for _ in 0..ITERATIONS {
-            let viewport_cells = rng.gen_range(15..20);
-            let cell_size = rng.gen_range(5 * PRECISION..20 * PRECISION) as f32 / PRECISION as f32;
+            let viewport_cells = rng.random_range(15..20);
+            let cell_size =
+                rng.random_range(5 * PRECISION..20 * PRECISION) as f32 / PRECISION as f32;
 
             let size = crate::TerminalBounds {
                 cell_width: Pixels::from(cell_size),
@@ -2277,8 +2278,8 @@ mod tests {
                 for col in 0..(viewport_cells - 1) {
                     let col = col as usize;
 
-                    let row_offset = rng.gen_range(0..PRECISION) as f32 / PRECISION as f32;
-                    let col_offset = rng.gen_range(0..PRECISION) as f32 / PRECISION as f32;
+                    let row_offset = rng.random_range(0..PRECISION) as f32 / PRECISION as f32;
+                    let col_offset = rng.random_range(0..PRECISION) as f32 / PRECISION as f32;
 
                     let mouse_pos = point(
                         Pixels::from(col as f32 * cell_size + col_offset),
@@ -2298,7 +2299,7 @@ mod tests {
 
     #[test]
     fn test_mouse_to_cell_clamp() {
-        let mut rng = thread_rng();
+        let mut rng = rand::rng();
 
         let size = crate::TerminalBounds {
             cell_width: Pixels::from(10.),
@@ -2336,7 +2337,7 @@ mod tests {
         for _ in 0..((size.height() / size.line_height()) as usize) {
             let mut row_vec = Vec::new();
             for _ in 0..((size.width() / size.cell_width()) as usize) {
-                let cell_char = rng.sample(Alphanumeric) as char;
+                let cell_char = rng.sample(distr::Alphanumeric) as char;
                 row_vec.push(cell_char)
             }
             cells.push(row_vec)

crates/text/src/locator.rs 🔗

@@ -106,13 +106,13 @@ mod tests {
         let mut rhs = Default::default();
         while lhs == rhs {
             lhs = Locator(
-                (0..rng.gen_range(1..=5))
-                    .map(|_| rng.gen_range(0..=100))
+                (0..rng.random_range(1..=5))
+                    .map(|_| rng.random_range(0..=100))
                     .collect(),
             );
             rhs = Locator(
-                (0..rng.gen_range(1..=5))
-                    .map(|_| rng.gen_range(0..=100))
+                (0..rng.random_range(1..=5))
+                    .map(|_| rng.random_range(0..=100))
                     .collect(),
             );
         }

crates/text/src/network.rs 🔗

@@ -65,8 +65,8 @@ impl<T: Clone, R: rand::Rng> Network<T, R> {
                 for message in &messages {
                     // Insert one or more duplicates of this message, potentially *before* the previous
                     // message sent by this peer to simulate out-of-order delivery.
-                    for _ in 0..self.rng.gen_range(1..4) {
-                        let insertion_index = self.rng.gen_range(0..inbox.len() + 1);
+                    for _ in 0..self.rng.random_range(1..4) {
+                        let insertion_index = self.rng.random_range(0..inbox.len() + 1);
                         inbox.insert(
                             insertion_index,
                             Envelope {
@@ -85,7 +85,7 @@ impl<T: Clone, R: rand::Rng> Network<T, R> {
 
     pub fn receive(&mut self, receiver: ReplicaId) -> Vec<T> {
         let inbox = self.inboxes.get_mut(&receiver).unwrap();
-        let count = self.rng.gen_range(0..inbox.len() + 1);
+        let count = self.rng.random_range(0..inbox.len() + 1);
         inbox
             .drain(0..count)
             .map(|envelope| envelope.message)

crates/text/src/patch.rs 🔗

@@ -497,8 +497,8 @@ mod tests {
             .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
             .unwrap_or(20);
 
-        let initial_chars = (0..rng.gen_range(0..=100))
-            .map(|_| rng.gen_range(b'a'..=b'z') as char)
+        let initial_chars = (0..rng.random_range(0..=100))
+            .map(|_| rng.random_range(b'a'..=b'z') as char)
             .collect::<Vec<_>>();
         log::info!("initial chars: {:?}", initial_chars);
 
@@ -517,11 +517,11 @@ mod tests {
                     break;
                 }
 
-                let end = rng.gen_range(last_edit_end..=expected_chars.len());
-                let start = rng.gen_range(last_edit_end..=end);
+                let end = rng.random_range(last_edit_end..=expected_chars.len());
+                let start = rng.random_range(last_edit_end..=end);
                 let old_len = end - start;
 
-                let mut new_len = rng.gen_range(0..=3);
+                let mut new_len = rng.random_range(0..=3);
                 if start == end && new_len == 0 {
                     new_len += 1;
                 }
@@ -529,7 +529,7 @@ mod tests {
                 last_edit_end = start + new_len + 1;
 
                 let new_chars = (0..new_len)
-                    .map(|_| rng.gen_range(b'A'..=b'Z') as char)
+                    .map(|_| rng.random_range(b'A'..=b'Z') as char)
                     .collect::<Vec<_>>();
                 log::info!(
                     "  editing {:?}: {:?}",

crates/text/src/tests.rs 🔗

@@ -36,14 +36,14 @@ fn test_random_edits(mut rng: StdRng) {
         .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
         .unwrap_or(10);
 
-    let reference_string_len = rng.gen_range(0..3);
+    let reference_string_len = rng.random_range(0..3);
     let mut reference_string = RandomCharIter::new(&mut rng)
         .take(reference_string_len)
         .collect::<String>();
     let mut buffer = Buffer::new(0, BufferId::new(1).unwrap(), reference_string.clone());
     LineEnding::normalize(&mut reference_string);
 
-    buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
+    buffer.set_group_interval(Duration::from_millis(rng.random_range(0..=200)));
     let mut buffer_versions = Vec::new();
     log::info!(
         "buffer text {:?}, version: {:?}",
@@ -64,7 +64,7 @@ fn test_random_edits(mut rng: StdRng) {
             buffer.version()
         );
 
-        if rng.gen_bool(0.25) {
+        if rng.random_bool(0.25) {
             buffer.randomly_undo_redo(&mut rng);
             reference_string = buffer.text();
             log::info!(
@@ -82,7 +82,7 @@ fn test_random_edits(mut rng: StdRng) {
 
         buffer.check_invariants();
 
-        if rng.gen_bool(0.3) {
+        if rng.random_bool(0.3) {
             buffer_versions.push((buffer.clone(), buffer.subscribe()));
         }
     }
@@ -112,8 +112,9 @@ fn test_random_edits(mut rng: StdRng) {
         );
 
         for _ in 0..5 {
-            let end_ix = old_buffer.clip_offset(rng.gen_range(0..=old_buffer.len()), Bias::Right);
-            let start_ix = old_buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
+            let end_ix =
+                old_buffer.clip_offset(rng.random_range(0..=old_buffer.len()), Bias::Right);
+            let start_ix = old_buffer.clip_offset(rng.random_range(0..=end_ix), Bias::Left);
             let range = old_buffer.anchor_before(start_ix)..old_buffer.anchor_after(end_ix);
             let mut old_text = old_buffer.text_for_range(range.clone()).collect::<String>();
             let edits = buffer
@@ -731,7 +732,7 @@ fn test_random_concurrent_edits(mut rng: StdRng) {
         .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
         .unwrap_or(10);
 
-    let base_text_len = rng.gen_range(0..10);
+    let base_text_len = rng.random_range(0..10);
     let base_text = RandomCharIter::new(&mut rng)
         .take(base_text_len)
         .collect::<String>();
@@ -741,7 +742,7 @@ fn test_random_concurrent_edits(mut rng: StdRng) {
 
     for i in 0..peers {
         let mut buffer = Buffer::new(i as ReplicaId, BufferId::new(1).unwrap(), base_text.clone());
-        buffer.history.group_interval = Duration::from_millis(rng.gen_range(0..=200));
+        buffer.history.group_interval = Duration::from_millis(rng.random_range(0..=200));
         buffers.push(buffer);
         replica_ids.push(i as u16);
         network.add_peer(i as u16);
@@ -751,10 +752,10 @@ fn test_random_concurrent_edits(mut rng: StdRng) {
 
     let mut mutation_count = operations;
     loop {
-        let replica_index = rng.gen_range(0..peers);
+        let replica_index = rng.random_range(0..peers);
         let replica_id = replica_ids[replica_index];
         let buffer = &mut buffers[replica_index];
-        match rng.gen_range(0..=100) {
+        match rng.random_range(0..=100) {
             0..=50 if mutation_count != 0 => {
                 let op = buffer.randomly_edit(&mut rng, 5).1;
                 network.broadcast(buffer.replica_id, vec![op]);

crates/text/src/text.rs 🔗

@@ -1818,8 +1818,8 @@ impl Buffer {
     }
 
     pub fn random_byte_range(&self, start_offset: usize, rng: &mut impl rand::Rng) -> Range<usize> {
-        let end = self.clip_offset(rng.gen_range(start_offset..=self.len()), Bias::Right);
-        let start = self.clip_offset(rng.gen_range(start_offset..=end), Bias::Right);
+        let end = self.clip_offset(rng.random_range(start_offset..=self.len()), Bias::Right);
+        let start = self.clip_offset(rng.random_range(start_offset..=end), Bias::Right);
         start..end
     }
 
@@ -1841,7 +1841,7 @@ impl Buffer {
             let range = self.random_byte_range(new_start, rng);
             last_end = Some(range.end);
 
-            let new_text_len = rng.gen_range(0..10);
+            let new_text_len = rng.random_range(0..10);
             let new_text: String = RandomCharIter::new(&mut *rng).take(new_text_len).collect();
 
             edits.push((range, new_text.into()));
@@ -1877,7 +1877,7 @@ impl Buffer {
         use rand::prelude::*;
 
         let mut ops = Vec::new();
-        for _ in 0..rng.gen_range(1..=5) {
+        for _ in 0..rng.random_range(1..=5) {
             if let Some(entry) = self.history.undo_stack.choose(rng) {
                 let transaction = entry.transaction.clone();
                 log::info!(

crates/util/src/util.rs 🔗

@@ -815,7 +815,8 @@ pub fn defer<F: FnOnce()>(f: F) -> Deferred<F> {
 
 #[cfg(any(test, feature = "test-support"))]
 mod rng {
-    use rand::{Rng, seq::SliceRandom};
+    use rand::prelude::*;
+
     pub struct RandomCharIter<T: Rng> {
         rng: T,
         simple_text: bool,
@@ -840,18 +841,18 @@ mod rng {
 
         fn next(&mut self) -> Option<Self::Item> {
             if self.simple_text {
-                return if self.rng.gen_range(0..100) < 5 {
+                return if self.rng.random_range(0..100) < 5 {
                     Some('\n')
                 } else {
-                    Some(self.rng.gen_range(b'a'..b'z' + 1).into())
+                    Some(self.rng.random_range(b'a'..b'z' + 1).into())
                 };
             }
 
-            match self.rng.gen_range(0..100) {
+            match self.rng.random_range(0..100) {
                 // whitespace
                 0..=19 => [' ', '\n', '\r', '\t'].choose(&mut self.rng).copied(),
                 // two-byte greek letters
-                20..=32 => char::from_u32(self.rng.gen_range(('α' as u32)..('ω' as u32 + 1))),
+                20..=32 => char::from_u32(self.rng.random_range(('α' as u32)..('ω' as u32 + 1))),
                 // // three-byte characters
                 33..=45 => ['✋', '✅', '❌', '❎', '⭐']
                     .choose(&mut self.rng)
@@ -859,7 +860,7 @@ mod rng {
                 // // four-byte characters
                 46..=58 => ['🍐', '🏀', '🍗', '🎉'].choose(&mut self.rng).copied(),
                 // ascii letters
-                _ => Some(self.rng.gen_range(b'a'..b'z' + 1).into()),
+                _ => Some(self.rng.random_range(b'a'..b'z' + 1).into()),
             }
         }
     }

crates/worktree/src/worktree_tests.rs 🔗

@@ -1464,7 +1464,7 @@ async fn test_random_worktree_operations_during_initial_scan(
             tree.as_local().unwrap().snapshot().check_invariants(true)
         });
 
-        if rng.gen_bool(0.6) {
+        if rng.random_bool(0.6) {
             snapshots.push(worktree.read_with(cx, |tree, _| tree.as_local().unwrap().snapshot()));
         }
     }
@@ -1551,7 +1551,7 @@ async fn test_random_worktree_changes(cx: &mut TestAppContext, mut rng: StdRng)
     let mut snapshots = Vec::new();
     let mut mutations_len = operations;
     while mutations_len > 1 {
-        if rng.gen_bool(0.2) {
+        if rng.random_bool(0.2) {
             worktree
                 .update(cx, |worktree, cx| {
                     randomly_mutate_worktree(worktree, &mut rng, cx)
@@ -1563,8 +1563,8 @@ async fn test_random_worktree_changes(cx: &mut TestAppContext, mut rng: StdRng)
         }
 
         let buffered_event_count = fs.as_fake().buffered_event_count();
-        if buffered_event_count > 0 && rng.gen_bool(0.3) {
-            let len = rng.gen_range(0..=buffered_event_count);
+        if buffered_event_count > 0 && rng.random_bool(0.3) {
+            let len = rng.random_range(0..=buffered_event_count);
             log::info!("flushing {} events", len);
             fs.as_fake().flush_events(len);
         } else {
@@ -1573,7 +1573,7 @@ async fn test_random_worktree_changes(cx: &mut TestAppContext, mut rng: StdRng)
         }
 
         cx.executor().run_until_parked();
-        if rng.gen_bool(0.2) {
+        if rng.random_bool(0.2) {
             log::info!("storing snapshot {}", snapshots.len());
             let snapshot = worktree.read_with(cx, |tree, _| tree.as_local().unwrap().snapshot());
             snapshots.push(snapshot);
@@ -1701,7 +1701,7 @@ fn randomly_mutate_worktree(
     let snapshot = worktree.snapshot();
     let entry = snapshot.entries(false, 0).choose(rng).unwrap();
 
-    match rng.gen_range(0_u32..100) {
+    match rng.random_range(0_u32..100) {
         0..=33 if entry.path.as_ref() != Path::new("") => {
             log::info!("deleting entry {:?} ({})", entry.path, entry.id.0);
             worktree.delete_entry(entry.id, false, cx).unwrap()
@@ -1733,7 +1733,7 @@ fn randomly_mutate_worktree(
         _ => {
             if entry.is_dir() {
                 let child_path = entry.path.join(random_filename(rng));
-                let is_dir = rng.gen_bool(0.3);
+                let is_dir = rng.random_bool(0.3);
                 log::info!(
                     "creating {} at {:?}",
                     if is_dir { "dir" } else { "file" },
@@ -1776,11 +1776,11 @@ async fn randomly_mutate_fs(
         }
     }
 
-    if (files.is_empty() && dirs.len() == 1) || rng.gen_bool(insertion_probability) {
+    if (files.is_empty() && dirs.len() == 1) || rng.random_bool(insertion_probability) {
         let path = dirs.choose(rng).unwrap();
         let new_path = path.join(random_filename(rng));
 
-        if rng.r#gen() {
+        if rng.random() {
             log::info!(
                 "creating dir {:?}",
                 new_path.strip_prefix(root_path).unwrap()
@@ -1793,7 +1793,7 @@ async fn randomly_mutate_fs(
             );
             fs.create_file(&new_path, Default::default()).await.unwrap();
         }
-    } else if rng.gen_bool(0.05) {
+    } else if rng.random_bool(0.05) {
         let ignore_dir_path = dirs.choose(rng).unwrap();
         let ignore_path = ignore_dir_path.join(*GITIGNORE);
 
@@ -1808,11 +1808,11 @@ async fn randomly_mutate_fs(
             .cloned()
             .collect::<Vec<_>>();
         let files_to_ignore = {
-            let len = rng.gen_range(0..=subfiles.len());
+            let len = rng.random_range(0..=subfiles.len());
             subfiles.choose_multiple(rng, len)
         };
         let dirs_to_ignore = {
-            let len = rng.gen_range(0..subdirs.len());
+            let len = rng.random_range(0..subdirs.len());
             subdirs.choose_multiple(rng, len)
         };
 
@@ -1848,7 +1848,7 @@ async fn randomly_mutate_fs(
             file_path.into_iter().chain(dir_path).choose(rng).unwrap()
         };
 
-        let is_rename = rng.r#gen();
+        let is_rename = rng.random();
         if is_rename {
             let new_path_parent = dirs
                 .iter()
@@ -1857,7 +1857,7 @@ async fn randomly_mutate_fs(
                 .unwrap();
 
             let overwrite_existing_dir =
-                !old_path.starts_with(new_path_parent) && rng.gen_bool(0.3);
+                !old_path.starts_with(new_path_parent) && rng.random_bool(0.3);
             let new_path = if overwrite_existing_dir {
                 fs.remove_dir(
                     new_path_parent,
@@ -1919,7 +1919,7 @@ async fn randomly_mutate_fs(
 
 fn random_filename(rng: &mut impl Rng) -> String {
     (0..6)
-        .map(|_| rng.sample(rand::distributions::Alphanumeric))
+        .map(|_| rng.sample(rand::distr::Alphanumeric))
         .map(char::from)
         .collect()
 }

crates/zeta/src/input_excerpt.rs 🔗

@@ -149,7 +149,7 @@ mod tests {
                 let mut rng = rand::thread_rng();
                 let mut numbers = Vec::new();
                 for _ in 0..5 {
-                    numbers.push(rng.gen_range(1..101));
+                    numbers.push(rng.random_range(1..101));
                 }
                 numbers
             }
@@ -208,7 +208,7 @@ mod tests {
             <|editable_region_end|>
                 let mut numbers = Vec::new();
                 for _ in 0..5 {
-                    numbers.push(rng.gen_range(1..101));
+                    numbers.push(rng.random_range(1..101));
             ```"#}
         );
     }