diff --git a/.gitignore b/.gitignore
index dbffa0f829..15a0a9f5f2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
**/target
+**/cargo-target
/zed.xcworkspace
.DS_Store
/plugins/bin
diff --git a/Cargo.lock b/Cargo.lock
index bf0ed9b163..eb7543491d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3164,6 +3164,7 @@ dependencies = [
"sqlez",
"sum_tree",
"taffy",
+ "thiserror",
"time 0.3.27",
"tiny-skia",
"usvg",
@@ -3172,6 +3173,36 @@ dependencies = [
"waker-fn",
]
+[[package]]
+name = "gpui2"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "derive_more",
+ "futures 0.3.28",
+ "gpui",
+ "gpui2_macros",
+ "log",
+ "parking_lot 0.11.2",
+ "refineable",
+ "rust-embed",
+ "serde",
+ "settings",
+ "simplelog",
+ "smallvec",
+ "theme",
+ "util",
+]
+
+[[package]]
+name = "gpui2_macros"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
[[package]]
name = "gpui_macros"
version = "0.1.0"
@@ -5232,33 +5263,6 @@ version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
-[[package]]
-name = "playground"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "derive_more",
- "gpui",
- "log",
- "parking_lot 0.11.2",
- "playground_macros",
- "refineable",
- "serde",
- "simplelog",
- "smallvec",
- "taffy",
- "util",
-]
-
-[[package]]
-name = "playground_macros"
-version = "0.1.0"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 1.0.109",
-]
-
[[package]]
name = "plist"
version = "1.5.0"
@@ -7368,6 +7372,21 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+[[package]]
+name = "storybook"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "gpui2",
+ "log",
+ "rust-embed",
+ "serde",
+ "settings",
+ "simplelog",
+ "theme",
+ "util",
+]
+
[[package]]
name = "stringprep"
version = "0.1.3"
@@ -7567,7 +7586,7 @@ dependencies = [
[[package]]
name = "taffy"
version = "0.3.11"
-source = "git+https://github.com/DioxusLabs/taffy?rev=dab541d6104d58e2e10ce90c4a1dad0b703160cd#dab541d6104d58e2e10ce90c4a1dad0b703160cd"
+source = "git+https://github.com/DioxusLabs/taffy?rev=4fb530bdd71609bb1d3f76c6a8bde1ba82805d5e#4fb530bdd71609bb1d3f76c6a8bde1ba82805d5e"
dependencies = [
"arrayvec 0.7.4",
"grid",
@@ -8407,6 +8426,15 @@ dependencies = [
"tree-sitter",
]
+[[package]]
+name = "tree-sitter-nu"
+version = "0.0.1"
+source = "git+https://github.com/nushell/tree-sitter-nu?rev=786689b0562b9799ce53e824cb45a1a2a04dc673#786689b0562b9799ce53e824cb45a1a2a04dc673"
+dependencies = [
+ "cc",
+ "tree-sitter",
+]
+
[[package]]
name = "tree-sitter-php"
version = "0.19.1"
@@ -8830,12 +8858,14 @@ dependencies = [
"collections",
"command_palette",
"editor",
+ "futures 0.3.28",
"gpui",
"indoc",
"itertools",
"language",
"language_selector",
"log",
+ "lsp",
"nvim-rs",
"parking_lot 0.11.2",
"project",
@@ -9867,6 +9897,7 @@ dependencies = [
"tree-sitter-lua",
"tree-sitter-markdown",
"tree-sitter-nix",
+ "tree-sitter-nu",
"tree-sitter-php",
"tree-sitter-python",
"tree-sitter-racket",
diff --git a/Cargo.toml b/Cargo.toml
index 5938ecb402..96070658b9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -32,9 +32,9 @@ members = [
"crates/git",
"crates/go_to_line",
"crates/gpui",
- "crates/gpui/playground",
- "crates/gpui/playground_macros",
"crates/gpui_macros",
+ "crates/gpui2",
+ "crates/gpui2_macros",
"crates/install_cli",
"crates/journal",
"crates/language",
@@ -63,6 +63,7 @@ members = [
"crates/sqlez",
"crates/sqlez_macros",
"crates/feature_flags",
+ "crates/storybook",
"crates/sum_tree",
"crates/terminal",
"crates/text",
@@ -141,6 +142,7 @@ tree-sitter-racket = { git = "https://github.com/zed-industries/tree-sitter-rack
tree-sitter-yaml = { git = "https://github.com/zed-industries/tree-sitter-yaml", rev = "f545a41f57502e1b5ddf2a6668896c1b0620f930"}
tree-sitter-lua = "0.0.14"
tree-sitter-nix = { git = "https://github.com/nix-community/tree-sitter-nix", rev = "66e3e9ce9180ae08fc57372061006ef83f0abde7" }
+tree-sitter-nu = { git = "https://github.com/nushell/tree-sitter-nu", rev = "786689b0562b9799ce53e824cb45a1a2a04dc673"}
[patch.crates-io]
tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "35a6052fbcafc5e5fc0f9415b8652be7dcaf7222" }
diff --git a/README.md b/README.md
index 8849f1aa73..2ee426a2a6 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,26 @@ Welcome to Zed, a lightning-fast, collaborative code editor that makes your drea
### Dependencies
-* Install [Postgres.app](https://postgresapp.com) and start it.
+* Install Xcode from https://apps.apple.com/us/app/xcode/id497799835?mt=12, and accept the license:
+ ```
+ sudo xcodebuild -license
+ ```
+
+* Install homebrew, node and rustup-init (rutup, rust, cargo, etc.)
+ ```
+ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
+ brew install node rustup-init
+ rustup-init # follow the installation steps
+ ```
+
+* Install postgres and configure the database
+ ```
+ brew install postgresql@15
+ brew services start postgresql@15
+ psql -c "CREATE ROLE postgres SUPERUSER LOGIN" postgres
+ psql -U postgres -c "CREATE DATABASE zed"
+ ```
+
* Install the `LiveKit` server and the `foreman` process supervisor:
```
@@ -41,6 +60,17 @@ Welcome to Zed, a lightning-fast, collaborative code editor that makes your drea
GITHUB_TOKEN=<$token> script/bootstrap
```
+* Now try running zed with collaboration disabled:
+ ```
+ cargo run
+ ```
+
+### Common errors
+
+* `xcrun: error: unable to find utility "metal", not a developer tool or in PATH`
+ * You need to install Xcode and then run: `xcode-select --switch /Applications/Xcode.app/Contents/Developer`
+ * (see https://github.com/gfx-rs/gfx/issues/2309)
+
### Testing against locally-running servers
Start the web and collab servers:
diff --git a/assets/icons/Icons/exit.svg b/assets/icons/Icons/exit.svg
new file mode 100644
index 0000000000..6d76849248
--- /dev/null
+++ b/assets/icons/Icons/exit.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/select-all.svg b/assets/icons/select-all.svg
new file mode 100644
index 0000000000..45a10bba42
--- /dev/null
+++ b/assets/icons/select-all.svg
@@ -0,0 +1,5 @@
+
+
diff --git a/assets/icons/stop_sharing.svg b/assets/icons/stop_sharing.svg
new file mode 100644
index 0000000000..e9aa7eac5a
--- /dev/null
+++ b/assets/icons/stop_sharing.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json
index fa62a74f3f..2211f9563d 100644
--- a/assets/keymaps/default.json
+++ b/assets/keymaps/default.json
@@ -231,7 +231,14 @@
}
},
{
- "context": "BufferSearchBar > Editor",
+ "context": "BufferSearchBar && in_replace",
+ "bindings": {
+ "enter": "search::ReplaceNext",
+ "cmd-enter": "search::ReplaceAll"
+ }
+ },
+ {
+ "context": "BufferSearchBar && !in_replace > Editor",
"bindings": {
"up": "search::PreviousHistoryQuery",
"down": "search::NextHistoryQuery"
@@ -533,7 +540,7 @@
// TODO: Move this to a dock open action
"cmd-shift-c": "collab_panel::ToggleFocus",
"cmd-alt-i": "zed::DebugElements",
- "ctrl-:": "editor::ToggleInlayHints",
+ "ctrl-:": "editor::ToggleInlayHints"
}
},
{
diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json
index da094ea7e4..1a7b81ee8f 100644
--- a/assets/keymaps/vim.json
+++ b/assets/keymaps/vim.json
@@ -32,6 +32,8 @@
"right": "vim::Right",
"$": "vim::EndOfLine",
"^": "vim::FirstNonWhitespace",
+ "_": "vim::StartOfLineDownward",
+ "g _": "vim::EndOfLineDownward",
"shift-g": "vim::EndOfDocument",
"w": "vim::NextWordStart",
"{": "vim::StartOfParagraph",
@@ -198,6 +200,18 @@
"z c": "editor::Fold",
"z o": "editor::UnfoldLines",
"z f": "editor::FoldSelectedRanges",
+ "shift-z shift-q": [
+ "pane::CloseActiveItem",
+ {
+ "saveBehavior": "dontSave"
+ }
+ ],
+ "shift-z shift-z": [
+ "pane::CloseActiveItem",
+ {
+ "saveBehavior": "promptOnConflict"
+ }
+ ],
// Count support
"1": [
"vim::Number",
@@ -314,8 +328,9 @@
}
},
{
- "context": "Editor && vim_mode == normal && (vim_operator == none || vim_operator == n) && !VimWaiting",
+ "context": "Editor && vim_mode == normal && vim_operator == none && !VimWaiting",
"bindings": {
+ ".": "vim::Repeat",
"c": [
"vim::PushOperator",
"Change"
@@ -326,15 +341,12 @@
"Delete"
],
"shift-d": "vim::DeleteToEndOfLine",
- "shift-j": "editor::JoinLines",
+ "shift-j": "vim::JoinLines",
"y": [
"vim::PushOperator",
"Yank"
],
- "i": [
- "vim::SwitchMode",
- "Insert"
- ],
+ "i": "vim::InsertBefore",
"shift-i": "vim::InsertFirstNonWhitespace",
"a": "vim::InsertAfter",
"shift-a": "vim::InsertEndOfLine",
@@ -379,7 +391,7 @@
}
},
{
- "context": "Editor && vim_operator == n",
+ "context": "Editor && VimCount",
"bindings": {
"0": [
"vim::Number",
@@ -448,13 +460,12 @@
],
"s": "vim::Substitute",
"shift-s": "vim::SubstituteLine",
+ "shift-r": "vim::SubstituteLine",
"c": "vim::Substitute",
"~": "vim::ChangeCase",
- "shift-i": [
- "vim::SwitchMode",
- "Insert"
- ],
+ "shift-i": "vim::InsertBefore",
"shift-a": "vim::InsertAfter",
+ "shift-j": "vim::JoinLines",
"r": [
"vim::PushOperator",
"Replace"
@@ -488,7 +499,7 @@
"around": true
}
}
- ],
+ ]
}
},
{
diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs
index fba10c61ba..8e0252ec60 100644
--- a/crates/collab_ui/src/collab_panel.rs
+++ b/crates/collab_ui/src/collab_panel.rs
@@ -2434,14 +2434,14 @@ fn render_tree_branch(
let cap_height = row_style.cap_height(font_cache);
let baseline_offset = row_style.baseline_offset(font_cache) + (size.y() - line_height) / 2.;
- Canvas::new(move |scene, bounds, _, _, _| {
- scene.paint_layer(None, |scene| {
+ Canvas::new(move |bounds, _, _, cx| {
+ cx.paint_layer(None, |cx| {
let start_x = bounds.min_x() + (bounds.width() / 2.) - (branch_style.width / 2.);
let end_x = bounds.max_x();
let start_y = bounds.min_y();
let end_y = bounds.min_y() + baseline_offset - (cap_height / 2.);
- scene.push_quad(gpui::Quad {
+ cx.scene().push_quad(gpui::Quad {
bounds: RectF::from_points(
vec2f(start_x, start_y),
vec2f(
@@ -2453,7 +2453,7 @@ fn render_tree_branch(
border: gpui::Border::default(),
corner_radii: (0.).into(),
});
- scene.push_quad(gpui::Quad {
+ cx.scene().push_quad(gpui::Quad {
bounds: RectF::from_points(
vec2f(start_x, end_y),
vec2f(end_x, end_y + branch_style.width),
diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs
index 95b9868937..f0e09e139e 100644
--- a/crates/collab_ui/src/collab_titlebar_item.rs
+++ b/crates/collab_ui/src/collab_titlebar_item.rs
@@ -13,8 +13,8 @@ use gpui::{
geometry::{rect::RectF, vector::vec2f, PathBuilder},
json::{self, ToJson},
platform::{CursorStyle, MouseButton},
- AppContext, Entity, ImageData, LayoutContext, ModelHandle, PaintContext, SceneBuilder,
- Subscription, View, ViewContext, ViewHandle, WeakViewHandle,
+ AppContext, Entity, ImageData, ModelHandle, Subscription, View, ViewContext, ViewHandle,
+ WeakViewHandle,
};
use picker::PickerEvent;
use project::{Project, RepositoryEntry};
@@ -771,7 +771,7 @@ impl CollabTitlebarItem {
})
.with_tooltip::(
0,
- "Toggle user menu".to_owned(),
+ "Toggle User Menu".to_owned(),
Some(Box::new(ToggleUserMenu)),
tooltip,
cx,
@@ -1165,19 +1165,18 @@ impl Element for AvatarRibbon {
&mut self,
constraint: gpui::SizeConstraint,
_: &mut CollabTitlebarItem,
- _: &mut LayoutContext,
+ _: &mut ViewContext,
) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
(constraint.max, ())
}
fn paint(
&mut self,
- scene: &mut SceneBuilder,
bounds: RectF,
_: RectF,
_: &mut Self::LayoutState,
_: &mut CollabTitlebarItem,
- _: &mut PaintContext,
+ cx: &mut ViewContext,
) -> Self::PaintState {
let mut path = PathBuilder::new();
path.reset(bounds.lower_left());
@@ -1188,7 +1187,7 @@ impl Element for AvatarRibbon {
path.line_to(bounds.upper_right() - vec2f(bounds.height(), 0.));
path.curve_to(bounds.lower_right(), bounds.upper_right());
path.line_to(bounds.lower_left());
- scene.push_path(path.build(self.color, None));
+ cx.scene().push_path(path.build(self.color, None));
}
fn rect_for_text_range(
diff --git a/crates/collab_ui/src/face_pile.rs b/crates/collab_ui/src/face_pile.rs
index a86b257686..5017666f7b 100644
--- a/crates/collab_ui/src/face_pile.rs
+++ b/crates/collab_ui/src/face_pile.rs
@@ -7,7 +7,7 @@ use gpui::{
},
json::ToJson,
serde_json::{self, json},
- AnyElement, Axis, Element, LayoutContext, PaintContext, SceneBuilder, View, ViewContext,
+ AnyElement, Axis, Element, View, ViewContext,
};
pub(crate) struct FacePile {
@@ -32,7 +32,7 @@ impl Element for FacePile {
&mut self,
constraint: gpui::SizeConstraint,
view: &mut V,
- cx: &mut LayoutContext,
+ cx: &mut ViewContext,
) -> (Vector2F, Self::LayoutState) {
debug_assert!(constraint.max_along(Axis::Horizontal) == f32::INFINITY);
@@ -53,12 +53,11 @@ impl Element for FacePile {
fn paint(
&mut self,
- scene: &mut SceneBuilder,
bounds: RectF,
visible_bounds: RectF,
_layout: &mut Self::LayoutState,
view: &mut V,
- cx: &mut PaintContext,
+ cx: &mut ViewContext,
) -> Self::PaintState {
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
@@ -69,9 +68,10 @@ impl Element for FacePile {
let size = face.size();
origin_x -= size.x();
let origin_y = origin_y + (bounds.height() - size.y()) / 2.0;
- scene.paint_layer(None, |scene| {
- face.paint(scene, vec2f(origin_x, origin_y), visible_bounds, view, cx);
- });
+
+ cx.scene().push_layer(None);
+ face.paint(vec2f(origin_x, origin_y), visible_bounds, view, cx);
+ cx.scene().pop_layer();
origin_x += self.overlap;
}
diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs
index 101d4dc545..4f9bb231ce 100644
--- a/crates/command_palette/src/command_palette.rs
+++ b/crates/command_palette/src/command_palette.rs
@@ -1,11 +1,11 @@
-use collections::CommandPaletteFilter;
+use collections::{CommandPaletteFilter, HashMap};
use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{
actions, anyhow::anyhow, elements::*, keymap_matcher::Keystroke, Action, AnyWindowHandle,
AppContext, Element, MouseState, ViewContext,
};
use picker::{Picker, PickerDelegate, PickerEvent};
-use std::cmp;
+use std::cmp::{self, Reverse};
use util::ResultExt;
use workspace::Workspace;
@@ -33,13 +33,18 @@ pub enum Event {
action: Box,
},
}
-
struct Command {
name: String,
action: Box,
keystrokes: Vec,
}
+/// Hit count for each command in the palette.
+/// We only account for commands triggered directly via command palette and not by e.g. keystrokes because
+/// if an user already knows a keystroke for a command, they are unlikely to use a command palette to look for it.
+#[derive(Default)]
+struct HitCounts(HashMap);
+
fn toggle_command_palette(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext) {
let focused_view_id = cx.focused_view_id().unwrap_or_else(|| cx.view_id());
workspace.toggle_modal(cx, |_, cx| {
@@ -83,7 +88,7 @@ impl PickerDelegate for CommandPaletteDelegate {
let view_id = self.focused_view_id;
let window = cx.window();
cx.spawn(move |picker, mut cx| async move {
- let actions = window
+ let mut actions = window
.available_actions(view_id, &cx)
.into_iter()
.flatten()
@@ -112,6 +117,16 @@ impl PickerDelegate for CommandPaletteDelegate {
}
})
.collect::>();
+ let actions = cx.read(move |cx| {
+ let hit_counts = cx.optional_global::();
+ actions.sort_by_key(|action| {
+ (
+ Reverse(hit_counts.and_then(|map| map.0.get(&action.name)).cloned()),
+ action.name.clone(),
+ )
+ });
+ actions
+ });
let candidates = actions
.iter()
.enumerate()
@@ -166,7 +181,12 @@ impl PickerDelegate for CommandPaletteDelegate {
let window = cx.window();
let focused_view_id = self.focused_view_id;
let action_ix = self.matches[self.selected_ix].candidate_id;
- let action = self.actions.remove(action_ix).action;
+ let command = self.actions.remove(action_ix);
+ cx.update_default_global(|hit_counts: &mut HitCounts, _| {
+ *hit_counts.0.entry(command.name).or_default() += 1;
+ });
+ let action = command.action;
+
cx.app_context()
.spawn(move |mut cx| async move {
window
@@ -319,6 +339,21 @@ mod tests {
workspace.modal::().unwrap()
});
+ palette
+ .update(cx, |palette, cx| {
+ // Fill up palette's command list by running an empty query;
+ // we only need it to subsequently assert that the palette is initially
+ // sorted by command's name.
+ palette.delegate_mut().update_matches("".to_string(), cx)
+ })
+ .await;
+
+ palette.update(cx, |palette, _| {
+ let is_sorted =
+ |actions: &[Command]| actions.windows(2).all(|pair| pair[0].name <= pair[1].name);
+ assert!(is_sorted(&palette.delegate().actions));
+ });
+
palette
.update(cx, |palette, cx| {
palette
diff --git a/crates/diagnostics/src/items.rs b/crates/diagnostics/src/items.rs
index 89b4469d42..c3733018b6 100644
--- a/crates/diagnostics/src/items.rs
+++ b/crates/diagnostics/src/items.rs
@@ -32,7 +32,8 @@ impl DiagnosticIndicator {
this.in_progress_checks.insert(*language_server_id);
cx.notify();
}
- project::Event::DiskBasedDiagnosticsFinished { language_server_id } => {
+ project::Event::DiskBasedDiagnosticsFinished { language_server_id }
+ | project::Event::LanguageServerRemoved(language_server_id) => {
this.summary = project.read(cx).diagnostic_summary(cx);
this.in_progress_checks.remove(language_server_id);
cx.notify();
diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs
index e8e15a927e..f306692b5e 100644
--- a/crates/editor/src/display_map.rs
+++ b/crates/editor/src/display_map.rs
@@ -555,67 +555,6 @@ impl DisplaySnapshot {
})
}
- /// Returns an iterator of the start positions of the occurrences of `target` in the `self` after `from`
- /// Stops if `condition` returns false for any of the character position pairs observed.
- pub fn find_while<'a>(
- &'a self,
- from: DisplayPoint,
- target: &str,
- condition: impl FnMut(char, DisplayPoint) -> bool + 'a,
- ) -> impl Iterator- + 'a {
- Self::find_internal(self.chars_at(from), target.chars().collect(), condition)
- }
-
- /// Returns an iterator of the end positions of the occurrences of `target` in the `self` before `from`
- /// Stops if `condition` returns false for any of the character position pairs observed.
- pub fn reverse_find_while<'a>(
- &'a self,
- from: DisplayPoint,
- target: &str,
- condition: impl FnMut(char, DisplayPoint) -> bool + 'a,
- ) -> impl Iterator
- + 'a {
- Self::find_internal(
- self.reverse_chars_at(from),
- target.chars().rev().collect(),
- condition,
- )
- }
-
- fn find_internal<'a>(
- iterator: impl Iterator
- + 'a,
- target: Vec,
- mut condition: impl FnMut(char, DisplayPoint) -> bool + 'a,
- ) -> impl Iterator
- + 'a {
- // List of partial matches with the index of the last seen character in target and the starting point of the match
- let mut partial_matches: Vec<(usize, DisplayPoint)> = Vec::new();
- iterator
- .take_while(move |(ch, point)| condition(*ch, *point))
- .filter_map(move |(ch, point)| {
- if Some(&ch) == target.get(0) {
- partial_matches.push((0, point));
- }
-
- let mut found = None;
- // Keep partial matches that have the correct next character
- partial_matches.retain_mut(|(match_position, match_start)| {
- if target.get(*match_position) == Some(&ch) {
- *match_position += 1;
- if *match_position == target.len() {
- found = Some(match_start.clone());
- // This match is completed. No need to keep tracking it
- false
- } else {
- true
- }
- } else {
- false
- }
- });
-
- found
- })
- }
-
pub fn column_to_chars(&self, display_row: u32, target: u32) -> u32 {
let mut count = 0;
let mut column = 0;
@@ -933,7 +872,7 @@ pub mod tests {
use smol::stream::StreamExt;
use std::{env, sync::Arc};
use theme::SyntaxTheme;
- use util::test::{marked_text_offsets, marked_text_ranges, sample_text};
+ use util::test::{marked_text_ranges, sample_text};
use Bias::*;
#[gpui::test(iterations = 100)]
@@ -1744,32 +1683,6 @@ pub mod tests {
)
}
- #[test]
- fn test_find_internal() {
- assert("This is a ˇtest of find internal", "test");
- assert("Some text ˇaˇaˇaa with repeated characters", "aa");
-
- fn assert(marked_text: &str, target: &str) {
- let (text, expected_offsets) = marked_text_offsets(marked_text);
-
- let chars = text
- .chars()
- .enumerate()
- .map(|(index, ch)| (ch, DisplayPoint::new(0, index as u32)));
- let target = target.chars();
-
- assert_eq!(
- expected_offsets
- .into_iter()
- .map(|offset| offset as u32)
- .collect::>(),
- DisplaySnapshot::find_internal(chars, target.collect(), |_, _| true)
- .map(|point| point.column())
- .collect::>()
- )
- }
- }
-
fn syntax_chunks<'a>(
rows: Range,
map: &ModelHandle,
diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs
index 12df29df1d..d651980f33 100644
--- a/crates/editor/src/editor.rs
+++ b/crates/editor/src/editor.rs
@@ -572,7 +572,7 @@ pub struct Editor {
project: Option>,
focused: bool,
blink_manager: ModelHandle,
- show_local_selections: bool,
+ pub show_local_selections: bool,
mode: EditorMode,
replica_id_mapping: Option>,
show_gutter: bool,
@@ -2273,10 +2273,6 @@ impl Editor {
if self.read_only {
return;
}
- if !self.input_enabled {
- cx.emit(Event::InputIgnored { text });
- return;
- }
let selections = self.selections.all_adjusted(cx);
let mut brace_inserted = false;
@@ -3211,17 +3207,30 @@ impl Editor {
.count();
let snapshot = self.buffer.read(cx).snapshot(cx);
+ let mut range_to_replace: Option> = None;
let mut ranges = Vec::new();
for selection in &selections {
if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
let start = selection.start.saturating_sub(lookbehind);
let end = selection.end + lookahead;
+ if selection.id == newest_selection.id {
+ range_to_replace = Some(
+ ((start + common_prefix_len) as isize - selection.start as isize)
+ ..(end as isize - selection.start as isize),
+ );
+ }
ranges.push(start + common_prefix_len..end);
} else {
common_prefix_len = 0;
ranges.clear();
ranges.extend(selections.iter().map(|s| {
if s.id == newest_selection.id {
+ range_to_replace = Some(
+ old_range.start.to_offset_utf16(&snapshot).0 as isize
+ - selection.start as isize
+ ..old_range.end.to_offset_utf16(&snapshot).0 as isize
+ - selection.start as isize,
+ );
old_range.clone()
} else {
s.start..s.end
@@ -3232,6 +3241,11 @@ impl Editor {
}
let text = &text[common_prefix_len..];
+ cx.emit(Event::InputHandled {
+ utf16_range_to_replace: range_to_replace,
+ text: text.into(),
+ });
+
self.transact(cx, |this, cx| {
if let Some(mut snippet) = snippet {
snippet.text = text.to_string();
@@ -3689,6 +3703,10 @@ impl Editor {
self.report_copilot_event(Some(completion.uuid.clone()), true, cx)
}
+ cx.emit(Event::InputHandled {
+ utf16_range_to_replace: None,
+ text: suggestion.text.to_string().into(),
+ });
self.insert_with_autoindent_mode(&suggestion.text.to_string(), None, cx);
cx.notify();
true
@@ -8437,6 +8455,41 @@ impl Editor {
pub fn inlay_hint_cache(&self) -> &InlayHintCache {
&self.inlay_hint_cache
}
+
+ pub fn replay_insert_event(
+ &mut self,
+ text: &str,
+ relative_utf16_range: Option>,
+ cx: &mut ViewContext,
+ ) {
+ if !self.input_enabled {
+ cx.emit(Event::InputIgnored { text: text.into() });
+ return;
+ }
+ if let Some(relative_utf16_range) = relative_utf16_range {
+ let selections = self.selections.all::(cx);
+ self.change_selections(None, cx, |s| {
+ let new_ranges = selections.into_iter().map(|range| {
+ let start = OffsetUtf16(
+ range
+ .head()
+ .0
+ .saturating_add_signed(relative_utf16_range.start),
+ );
+ let end = OffsetUtf16(
+ range
+ .head()
+ .0
+ .saturating_add_signed(relative_utf16_range.end),
+ );
+ start..end
+ });
+ s.select_ranges(new_ranges);
+ });
+ }
+
+ self.handle_input(text, cx);
+ }
}
fn document_to_inlay_range(
@@ -8525,6 +8578,10 @@ pub enum Event {
InputIgnored {
text: Arc,
},
+ InputHandled {
+ utf16_range_to_replace: Option>,
+ text: Arc,
+ },
ExcerptsAdded {
buffer: ModelHandle,
predecessor: ExcerptId,
@@ -8742,29 +8799,51 @@ impl View for Editor {
text: &str,
cx: &mut ViewContext,
) {
- self.transact(cx, |this, cx| {
- if this.input_enabled {
- let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
- let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
- Some(this.selection_replacement_ranges(range_utf16, cx))
- } else {
- this.marked_text_ranges(cx)
- };
+ if !self.input_enabled {
+ cx.emit(Event::InputIgnored { text: text.into() });
+ return;
+ }
- if let Some(new_selected_ranges) = new_selected_ranges {
- this.change_selections(None, cx, |selections| {
- selections.select_ranges(new_selected_ranges)
- });
- }
+ self.transact(cx, |this, cx| {
+ let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
+ let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
+ Some(this.selection_replacement_ranges(range_utf16, cx))
+ } else {
+ this.marked_text_ranges(cx)
+ };
+
+ let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
+ let newest_selection_id = this.selections.newest_anchor().id;
+ this.selections
+ .all::(cx)
+ .iter()
+ .zip(ranges_to_replace.iter())
+ .find_map(|(selection, range)| {
+ if selection.id == newest_selection_id {
+ Some(
+ (range.start.0 as isize - selection.head().0 as isize)
+ ..(range.end.0 as isize - selection.head().0 as isize),
+ )
+ } else {
+ None
+ }
+ })
+ });
+
+ cx.emit(Event::InputHandled {
+ utf16_range_to_replace: range_to_replace,
+ text: text.into(),
+ });
+
+ if let Some(new_selected_ranges) = new_selected_ranges {
+ this.change_selections(None, cx, |selections| {
+ selections.select_ranges(new_selected_ranges)
+ });
}
this.handle_input(text, cx);
});
- if !self.input_enabled {
- return;
- }
-
if let Some(transaction) = self.ime_transaction {
self.buffer.update(cx, |buffer, cx| {
buffer.group_until_transaction(transaction, cx);
@@ -8782,6 +8861,7 @@ impl View for Editor {
cx: &mut ViewContext,
) {
if !self.input_enabled {
+ cx.emit(Event::InputIgnored { text: text.into() });
return;
}
@@ -8806,6 +8886,29 @@ impl View for Editor {
None
};
+ let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
+ let newest_selection_id = this.selections.newest_anchor().id;
+ this.selections
+ .all::(cx)
+ .iter()
+ .zip(ranges_to_replace.iter())
+ .find_map(|(selection, range)| {
+ if selection.id == newest_selection_id {
+ Some(
+ (range.start.0 as isize - selection.head().0 as isize)
+ ..(range.end.0 as isize - selection.head().0 as isize),
+ )
+ } else {
+ None
+ }
+ })
+ });
+
+ cx.emit(Event::InputHandled {
+ utf16_range_to_replace: range_to_replace,
+ text: text.into(),
+ });
+
if let Some(ranges) = ranges_to_replace {
this.change_selections(None, cx, |s| s.select_ranges(ranges));
}
diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs
index 74bd67e03a..f11639a770 100644
--- a/crates/editor/src/editor_tests.rs
+++ b/crates/editor/src/editor_tests.rs
@@ -7807,7 +7807,7 @@ fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewCo
/// Handle completion request passing a marked string specifying where the completion
/// should be triggered from using '|' character, what range should be replaced, and what completions
/// should be returned using '<' and '>' to delimit the range
-fn handle_completion_request<'a>(
+pub fn handle_completion_request<'a>(
cx: &mut EditorLspTestContext<'a>,
marked_string: &str,
completions: Vec<&'static str>,
diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs
index 90fe6ccc52..b7e34fda53 100644
--- a/crates/editor/src/element.rs
+++ b/crates/editor/src/element.rs
@@ -32,8 +32,8 @@ use gpui::{
json::{self, ToJson},
platform::{CursorStyle, Modifiers, MouseButton, MouseButtonEvent, MouseMovedEvent},
text_layout::{self, Line, RunStyle, TextLayoutCache},
- AnyElement, Axis, Border, CursorRegion, Element, EventContext, FontCache, LayoutContext,
- MouseRegion, PaintContext, Quad, SceneBuilder, SizeConstraint, ViewContext, WindowContext,
+ AnyElement, Axis, CursorRegion, Element, EventContext, FontCache, MouseRegion, Quad,
+ SizeConstraint, ViewContext, WindowContext,
};
use itertools::Itertools;
use json::json;
@@ -131,7 +131,6 @@ impl EditorElement {
}
fn attach_mouse_handlers(
- scene: &mut SceneBuilder,
position_map: &Arc,
has_popovers: bool,
visible_bounds: RectF,
@@ -141,124 +140,124 @@ impl EditorElement {
cx: &mut ViewContext,
) {
enum EditorElementMouseHandlers {}
- scene.push_mouse_region(
- MouseRegion::new::(
- cx.view_id(),
- cx.view_id(),
- visible_bounds,
- )
- .on_down(MouseButton::Left, {
- let position_map = position_map.clone();
- move |event, editor, cx| {
- if !Self::mouse_down(
- editor,
- event.platform_event,
- position_map.as_ref(),
- text_bounds,
- gutter_bounds,
- cx,
- ) {
- cx.propagate_event();
+ let view_id = cx.view_id();
+ cx.scene().push_mouse_region(
+ MouseRegion::new::(view_id, view_id, visible_bounds)
+ .on_down(MouseButton::Left, {
+ let position_map = position_map.clone();
+ move |event, editor, cx| {
+ if !Self::mouse_down(
+ editor,
+ event.platform_event,
+ position_map.as_ref(),
+ text_bounds,
+ gutter_bounds,
+ cx,
+ ) {
+ cx.propagate_event();
+ }
}
- }
- })
- .on_down(MouseButton::Right, {
- let position_map = position_map.clone();
- move |event, editor, cx| {
- if !Self::mouse_right_down(
- editor,
- event.position,
- position_map.as_ref(),
- text_bounds,
- cx,
- ) {
- cx.propagate_event();
+ })
+ .on_down(MouseButton::Right, {
+ let position_map = position_map.clone();
+ move |event, editor, cx| {
+ if !Self::mouse_right_down(
+ editor,
+ event.position,
+ position_map.as_ref(),
+ text_bounds,
+ cx,
+ ) {
+ cx.propagate_event();
+ }
}
- }
- })
- .on_up(MouseButton::Left, {
- let position_map = position_map.clone();
- move |event, editor, cx| {
- if !Self::mouse_up(
- editor,
- event.position,
- event.cmd,
- event.shift,
- event.alt,
- position_map.as_ref(),
- text_bounds,
- cx,
- ) {
- cx.propagate_event()
- }
- }
- })
- .on_drag(MouseButton::Left, {
- let position_map = position_map.clone();
- move |event, editor, cx| {
- if event.end {
- return;
+ })
+ .on_up(MouseButton::Left, {
+ let position_map = position_map.clone();
+ move |event, editor, cx| {
+ if !Self::mouse_up(
+ editor,
+ event.position,
+ event.cmd,
+ event.shift,
+ event.alt,
+ position_map.as_ref(),
+ text_bounds,
+ cx,
+ ) {
+ cx.propagate_event()
+ }
}
+ })
+ .on_drag(MouseButton::Left, {
+ let position_map = position_map.clone();
+ move |event, editor, cx| {
+ if event.end {
+ return;
+ }
- if !Self::mouse_dragged(
- editor,
- event.platform_event,
- position_map.as_ref(),
- text_bounds,
- cx,
- ) {
- cx.propagate_event()
+ if !Self::mouse_dragged(
+ editor,
+ event.platform_event,
+ position_map.as_ref(),
+ text_bounds,
+ cx,
+ ) {
+ cx.propagate_event()
+ }
}
- }
- })
- .on_move({
- let position_map = position_map.clone();
- move |event, editor, cx| {
- if !Self::mouse_moved(
- editor,
- event.platform_event,
- &position_map,
- text_bounds,
- cx,
- ) {
- cx.propagate_event()
+ })
+ .on_move({
+ let position_map = position_map.clone();
+ move |event, editor, cx| {
+ if !Self::mouse_moved(
+ editor,
+ event.platform_event,
+ &position_map,
+ text_bounds,
+ cx,
+ ) {
+ cx.propagate_event()
+ }
}
- }
- })
- .on_move_out(move |_, editor: &mut Editor, cx| {
- if has_popovers {
- hide_hover(editor, cx);
- }
- })
- .on_scroll({
- let position_map = position_map.clone();
- move |event, editor, cx| {
- if !Self::scroll(
- editor,
- event.position,
- *event.delta.raw(),
- event.delta.precise(),
- &position_map,
- bounds,
- cx,
- ) {
- cx.propagate_event()
+ })
+ .on_move_out(move |_, editor: &mut Editor, cx| {
+ if has_popovers {
+ hide_hover(editor, cx);
}
- }
- }),
+ })
+ .on_scroll({
+ let position_map = position_map.clone();
+ move |event, editor, cx| {
+ if !Self::scroll(
+ editor,
+ event.position,
+ *event.delta.raw(),
+ event.delta.precise(),
+ &position_map,
+ bounds,
+ cx,
+ ) {
+ cx.propagate_event()
+ }
+ }
+ }),
);
enum GutterHandlers {}
- scene.push_mouse_region(
- MouseRegion::new::(cx.view_id(), cx.view_id() + 1, gutter_bounds)
- .on_hover(|hover, editor: &mut Editor, cx| {
+ let view_id = cx.view_id();
+ let region_id = cx.view_id() + 1;
+ cx.scene().push_mouse_region(
+ MouseRegion::new::(view_id, region_id, gutter_bounds).on_hover(
+ |hover, editor: &mut Editor, cx| {
editor.gutter_hover(
&GutterHover {
hovered: hover.started,
},
cx,
);
- }),
+ },
+ ),
)
}
@@ -528,24 +527,24 @@ impl EditorElement {
fn paint_background(
&self,
- scene: &mut SceneBuilder,
gutter_bounds: RectF,
text_bounds: RectF,
layout: &LayoutState,
+ cx: &mut ViewContext,
) {
let bounds = gutter_bounds.union_rect(text_bounds);
let scroll_top =
layout.position_map.snapshot.scroll_position().y() * layout.position_map.line_height;
- scene.push_quad(Quad {
+ cx.scene().push_quad(Quad {
bounds: gutter_bounds,
background: Some(self.style.gutter_background),
- border: Border::new(0., Color::transparent_black()),
+ border: Border::new(0., Color::transparent_black()).into(),
corner_radii: Default::default(),
});
- scene.push_quad(Quad {
+ cx.scene().push_quad(Quad {
bounds: text_bounds,
background: Some(self.style.background),
- border: Border::new(0., Color::transparent_black()),
+ border: Border::new(0., Color::transparent_black()).into(),
corner_radii: Default::default(),
});
@@ -570,10 +569,10 @@ impl EditorElement {
bounds.width(),
layout.position_map.line_height * (end_row - start_row + 1) as f32,
);
- scene.push_quad(Quad {
+ cx.scene().push_quad(Quad {
bounds: RectF::new(origin, size),
background: Some(self.style.active_line_background),
- border: Border::default(),
+ border: Border::default().into(),
corner_radii: Default::default(),
});
}
@@ -590,10 +589,10 @@ impl EditorElement {
bounds.width(),
layout.position_map.line_height * highlighted_rows.len() as f32,
);
- scene.push_quad(Quad {
+ cx.scene().push_quad(Quad {
bounds: RectF::new(origin, size),
background: Some(self.style.highlighted_line_background),
- border: Border::default(),
+ border: Border::default().into(),
corner_radii: Default::default(),
});
}
@@ -617,13 +616,13 @@ impl EditorElement {
} else {
self.style.wrap_guide
};
- scene.push_quad(Quad {
+ cx.scene().push_quad(Quad {
bounds: RectF::new(
vec2f(x, text_bounds.origin_y()),
vec2f(1., text_bounds.height()),
),
background: Some(color),
- border: Border::new(0., Color::transparent_black()),
+ border: Border::new(0., Color::transparent_black()).into(),
corner_radii: Default::default(),
});
}
@@ -632,12 +631,11 @@ impl EditorElement {
fn paint_gutter(
&mut self,
- scene: &mut SceneBuilder,
bounds: RectF,
visible_bounds: RectF,
layout: &mut LayoutState,
editor: &mut Editor,
- cx: &mut PaintContext,
+ cx: &mut ViewContext,
) {
let line_height = layout.position_map.line_height;
@@ -650,7 +648,7 @@ impl EditorElement {
);
if show_gutter {
- Self::paint_diff_hunks(scene, bounds, layout, cx);
+ Self::paint_diff_hunks(bounds, layout, cx);
}
for (ix, line) in layout.line_number_layouts.iter().enumerate() {
@@ -661,7 +659,7 @@ impl EditorElement {
ix as f32 * line_height - (scroll_top % line_height),
);
- line.paint(scene, line_origin, visible_bounds, line_height, cx);
+ line.paint(line_origin, visible_bounds, line_height, cx);
}
}
@@ -678,7 +676,7 @@ impl EditorElement {
let indicator_origin = bounds.origin() + position + centering_offset;
- indicator.paint(scene, indicator_origin, visible_bounds, editor, cx);
+ indicator.paint(indicator_origin, visible_bounds, editor, cx);
}
}
@@ -687,22 +685,11 @@ impl EditorElement {
let mut y = *row as f32 * line_height - scroll_top;
x += ((layout.gutter_padding + layout.gutter_margin) - indicator.size().x()) / 2.;
y += (line_height - indicator.size().y()) / 2.;
- indicator.paint(
- scene,
- bounds.origin() + vec2f(x, y),
- visible_bounds,
- editor,
- cx,
- );
+ indicator.paint(bounds.origin() + vec2f(x, y), visible_bounds, editor, cx);
}
}
- fn paint_diff_hunks(
- scene: &mut SceneBuilder,
- bounds: RectF,
- layout: &mut LayoutState,
- cx: &mut ViewContext,
- ) {
+ fn paint_diff_hunks(bounds: RectF, layout: &mut LayoutState, cx: &mut ViewContext) {
let diff_style = &theme::current(cx).editor.diff.clone();
let line_height = layout.position_map.line_height;
@@ -721,10 +708,10 @@ impl EditorElement {
let highlight_size = vec2f(width * 2., end_y - start_y);
let highlight_bounds = RectF::new(highlight_origin, highlight_size);
- scene.push_quad(Quad {
+ cx.scene().push_quad(Quad {
bounds: highlight_bounds,
background: Some(diff_style.modified),
- border: Border::new(0., Color::transparent_black()),
+ border: Border::new(0., Color::transparent_black()).into(),
corner_radii: (1. * line_height).into(),
});
@@ -754,10 +741,10 @@ impl EditorElement {
let highlight_size = vec2f(width * 2., end_y - start_y);
let highlight_bounds = RectF::new(highlight_origin, highlight_size);
- scene.push_quad(Quad {
+ cx.scene().push_quad(Quad {
bounds: highlight_bounds,
background: Some(diff_style.deleted),
- border: Border::new(0., Color::transparent_black()),
+ border: Border::new(0., Color::transparent_black()).into(),
corner_radii: (1. * line_height).into(),
});
@@ -776,10 +763,10 @@ impl EditorElement {
let highlight_size = vec2f(width * 2., end_y - start_y);
let highlight_bounds = RectF::new(highlight_origin, highlight_size);
- scene.push_quad(Quad {
+ cx.scene().push_quad(Quad {
bounds: highlight_bounds,
background: Some(color),
- border: Border::new(0., Color::transparent_black()),
+ border: Border::new(0., Color::transparent_black()).into(),
corner_radii: (diff_style.corner_radius * line_height).into(),
});
}
@@ -787,12 +774,11 @@ impl EditorElement {
fn paint_text(
&mut self,
- scene: &mut SceneBuilder,
bounds: RectF,
visible_bounds: RectF,
layout: &mut LayoutState,
editor: &mut Editor,
- cx: &mut PaintContext,
+ cx: &mut ViewContext,
) {
let style = &self.style;
let scroll_position = layout.position_map.snapshot.scroll_position();
@@ -804,9 +790,9 @@ impl EditorElement {
let line_end_overshoot = 0.15 * layout.position_map.line_height;
let whitespace_setting = editor.buffer.read(cx).settings_at(0, cx).show_whitespaces;
- scene.push_layer(Some(bounds));
+ cx.scene().push_layer(Some(bounds));
- scene.push_cursor_region(CursorRegion {
+ cx.scene().push_cursor_region(CursorRegion {
bounds,
style: if !editor.link_go_to_definition_state.definitions.is_empty() {
CursorStyle::PointingHand
@@ -819,7 +805,6 @@ impl EditorElement {
self.style.folds.ellipses.corner_radius_factor * layout.position_map.line_height;
for (id, range, color) in layout.fold_ranges.iter() {
self.paint_highlighted_range(
- scene,
range.clone(),
*color,
fold_corner_radius,
@@ -829,6 +814,7 @@ impl EditorElement {
scroll_top,
scroll_left,
bounds,
+ cx,
);
for bound in range_to_bounds(
@@ -840,7 +826,7 @@ impl EditorElement {
line_end_overshoot,
&layout.position_map,
) {
- scene.push_cursor_region(CursorRegion {
+ cx.scene().push_cursor_region(CursorRegion {
bounds: bound,
style: CursorStyle::PointingHand,
});
@@ -851,8 +837,9 @@ impl EditorElement {
.to_point(&layout.position_map.snapshot.display_snapshot)
.row;
- scene.push_mouse_region(
- MouseRegion::new::(cx.view_id(), *id as usize, bound)
+ let view_id = cx.view_id();
+ cx.scene().push_mouse_region(
+ MouseRegion::new::(view_id, *id as usize, bound)
.on_click(MouseButton::Left, move |_, editor: &mut Editor, cx| {
editor.unfold_at(&UnfoldAt { buffer_row }, cx)
})
@@ -864,7 +851,6 @@ impl EditorElement {
for (range, color) in &layout.highlighted_ranges {
self.paint_highlighted_range(
- scene,
range.clone(),
*color,
0.,
@@ -874,6 +860,7 @@ impl EditorElement {
scroll_top,
scroll_left,
bounds,
+ cx,
);
}
@@ -891,7 +878,6 @@ impl EditorElement {
for selection in selections {
self.paint_highlighted_range(
- scene,
selection.range.clone(),
selection_style.selection,
corner_radius,
@@ -901,6 +887,7 @@ impl EditorElement {
scroll_top,
scroll_left,
bounds,
+ cx,
);
if selection.is_local && !selection.range.is_empty() {
@@ -980,7 +967,6 @@ impl EditorElement {
layout,
row,
scroll_top,
- scene,
content_origin,
scroll_left,
visible_text_bounds,
@@ -992,14 +978,14 @@ impl EditorElement {
}
}
- scene.paint_layer(Some(bounds), |scene| {
- for cursor in cursors {
- cursor.paint(scene, content_origin, cx);
- }
- });
+ cx.scene().push_layer(Some(bounds));
+ for cursor in cursors {
+ cursor.paint(content_origin, cx);
+ }
+ cx.scene().pop_layer();
if let Some((position, context_menu)) = layout.context_menu.as_mut() {
- scene.push_stacking_context(None, None);
+ cx.scene().push_stacking_context(None, None);
let cursor_row_layout =
&layout.position_map.line_layouts[(position.row() - start_row) as usize].line;
let x = cursor_row_layout.x_for_index(position.column() as usize) - scroll_left;
@@ -1019,18 +1005,17 @@ impl EditorElement {
}
context_menu.paint(
- scene,
list_origin,
RectF::from_points(Vector2F::zero(), vec2f(f32::MAX, f32::MAX)), // Let content bleed outside of editor
editor,
cx,
);
- scene.pop_stacking_context();
+ cx.scene().pop_stacking_context();
}
if let Some((position, hover_popovers)) = layout.hover_popovers.as_mut() {
- scene.push_stacking_context(None, None);
+ cx.scene().push_stacking_context(None, None);
// This is safe because we check on layout whether the required row is available
let hovered_row_layout =
@@ -1061,7 +1046,6 @@ impl EditorElement {
}
hover_popover.paint(
- scene,
popover_origin,
RectF::from_points(Vector2F::zero(), vec2f(f32::MAX, f32::MAX)), // Let content bleed outside of editor
editor,
@@ -1083,7 +1067,6 @@ impl EditorElement {
}
hover_popover.paint(
- scene,
popover_origin,
RectF::from_points(Vector2F::zero(), vec2f(f32::MAX, f32::MAX)), // Let content bleed outside of editor
editor,
@@ -1094,10 +1077,10 @@ impl EditorElement {
}
}
- scene.pop_stacking_context();
+ cx.scene().pop_stacking_context();
}
- scene.pop_layer();
+ cx.scene().pop_layer();
}
fn scrollbar_left(&self, bounds: &RectF) -> f32 {
@@ -1106,11 +1089,10 @@ impl EditorElement {
fn paint_scrollbar(
&mut self,
- scene: &mut SceneBuilder,
bounds: RectF,
layout: &mut LayoutState,
- cx: &mut ViewContext,
editor: &Editor,
+ cx: &mut ViewContext,
) {
enum ScrollbarMouseHandlers {}
if layout.mode != EditorMode::Full {
@@ -1147,9 +1129,9 @@ impl EditorElement {
let thumb_bounds = RectF::from_points(vec2f(left, thumb_top), vec2f(right, thumb_bottom));
if layout.show_scrollbars {
- scene.push_quad(Quad {
+ cx.scene().push_quad(Quad {
bounds: track_bounds,
- border: style.track.border,
+ border: style.track.border.into(),
background: style.track.background_color,
..Default::default()
});
@@ -1177,10 +1159,10 @@ impl EditorElement {
}
let bounds = RectF::from_points(vec2f(left, start_y), vec2f(right, end_y));
- scene.push_quad(Quad {
+ cx.scene().push_quad(Quad {
bounds,
background: Some(color),
- border,
+ border: border.into(),
corner_radii: style.thumb.corner_radii.into(),
})
};
@@ -1237,29 +1219,30 @@ impl EditorElement {
left: true,
};
- scene.push_quad(Quad {
+ cx.scene().push_quad(Quad {
bounds,
background: Some(color),
- border,
+ border: border.into(),
corner_radii: style.thumb.corner_radii.into(),
})
}
}
- scene.push_quad(Quad {
+ cx.scene().push_quad(Quad {
bounds: thumb_bounds,
- border: style.thumb.border,
+ border: style.thumb.border.into(),
background: style.thumb.background_color,
corner_radii: style.thumb.corner_radii.into(),
});
}
- scene.push_cursor_region(CursorRegion {
+ cx.scene().push_cursor_region(CursorRegion {
bounds: track_bounds,
style: CursorStyle::Arrow,
});
- scene.push_mouse_region(
- MouseRegion::new::(cx.view_id(), cx.view_id(), track_bounds)
+ let region_id = cx.view_id();
+ cx.scene().push_mouse_region(
+ MouseRegion::new::(region_id, region_id, track_bounds)
.on_move(move |event, editor: &mut Editor, cx| {
if event.pressed_button.is_none() {
editor.scroll_manager.show_scrollbar(cx);
@@ -1305,7 +1288,6 @@ impl EditorElement {
#[allow(clippy::too_many_arguments)]
fn paint_highlighted_range(
&self,
- scene: &mut SceneBuilder,
range: Range,
color: Color,
corner_radius: f32,
@@ -1315,6 +1297,7 @@ impl EditorElement {
scroll_top: f32,
scroll_left: f32,
bounds: RectF,
+ cx: &mut ViewContext,
) {
let start_row = layout.visible_display_row_range.start;
let end_row = layout.visible_display_row_range.end;
@@ -1358,18 +1341,17 @@ impl EditorElement {
.collect(),
};
- highlighted_range.paint(bounds, scene);
+ highlighted_range.paint(bounds, cx);
}
}
fn paint_blocks(
&mut self,
- scene: &mut SceneBuilder,
bounds: RectF,
visible_bounds: RectF,
layout: &mut LayoutState,
editor: &mut Editor,
- cx: &mut PaintContext,
+ cx: &mut ViewContext,
) {
let scroll_position = layout.position_map.snapshot.scroll_position();
let scroll_left = scroll_position.x() * layout.position_map.em_width;
@@ -1384,9 +1366,7 @@ impl EditorElement {
if !matches!(block.style, BlockStyle::Sticky) {
origin += vec2f(-scroll_left, 0.);
}
- block
- .element
- .paint(scene, origin, visible_bounds, editor, cx);
+ block.element.paint(origin, visible_bounds, editor, cx);
}
}
@@ -1690,7 +1670,7 @@ impl EditorElement {
style: &EditorStyle,
line_layouts: &[LineWithInvisibles],
editor: &mut Editor,
- cx: &mut LayoutContext,
+ cx: &mut ViewContext,
) -> (f32, Vec) {
let mut block_id = 0;
let scroll_x = snapshot.scroll_anchor.offset.x();
@@ -2022,7 +2002,6 @@ impl LineWithInvisibles {
layout: &LayoutState,
row: u32,
scroll_top: f32,
- scene: &mut SceneBuilder,
content_origin: Vector2F,
scroll_left: f32,
visible_text_bounds: RectF,
@@ -2035,7 +2014,6 @@ impl LineWithInvisibles {
let line_y = row as f32 * line_height - scroll_top;
self.line.paint(
- scene,
content_origin + vec2f(-scroll_left, line_y),
visible_text_bounds,
line_height,
@@ -2049,7 +2027,6 @@ impl LineWithInvisibles {
scroll_left,
line_y,
row,
- scene,
visible_bounds,
line_height,
whitespace_setting,
@@ -2065,7 +2042,6 @@ impl LineWithInvisibles {
scroll_left: f32,
line_y: f32,
row: u32,
- scene: &mut SceneBuilder,
visible_bounds: RectF,
line_height: f32,
whitespace_setting: ShowWhitespaceSetting,
@@ -2097,7 +2073,7 @@ impl LineWithInvisibles {
continue;
}
}
- invisible_symbol.paint(scene, origin, visible_bounds, line_height, cx);
+ invisible_symbol.paint(origin, visible_bounds, line_height, cx);
}
}
}
@@ -2116,7 +2092,7 @@ impl Element for EditorElement {
&mut self,
constraint: SizeConstraint,
editor: &mut Editor,
- cx: &mut LayoutContext,
+ cx: &mut ViewContext,
) -> (Vector2F, Self::LayoutState) {
let mut size = constraint.max;
if size.x().is_infinite() {
@@ -2590,15 +2566,14 @@ impl Element for EditorElement {
fn paint(
&mut self,
- scene: &mut SceneBuilder,
bounds: RectF,
visible_bounds: RectF,
layout: &mut Self::LayoutState,
editor: &mut Editor,
- cx: &mut PaintContext,
+ cx: &mut ViewContext,
) -> Self::PaintState {
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
- scene.push_layer(Some(visible_bounds));
+ cx.scene().push_layer(Some(visible_bounds));
let gutter_bounds = RectF::new(bounds.origin(), layout.gutter_size);
let text_bounds = RectF::new(
@@ -2607,7 +2582,6 @@ impl Element for EditorElement {
);
Self::attach_mouse_handlers(
- scene,
&layout.position_map,
layout.hover_popovers.is_some(),
visible_bounds,
@@ -2617,20 +2591,19 @@ impl Element for EditorElement {
cx,
);
- self.paint_background(scene, gutter_bounds, text_bounds, layout);
+ self.paint_background(gutter_bounds, text_bounds, layout, cx);
if layout.gutter_size.x() > 0. {
- self.paint_gutter(scene, gutter_bounds, visible_bounds, layout, editor, cx);
+ self.paint_gutter(gutter_bounds, visible_bounds, layout, editor, cx);
}
- self.paint_text(scene, text_bounds, visible_bounds, layout, editor, cx);
+ self.paint_text(text_bounds, visible_bounds, layout, editor, cx);
- scene.push_layer(Some(bounds));
+ cx.scene().push_layer(Some(bounds));
if !layout.blocks.is_empty() {
- self.paint_blocks(scene, bounds, visible_bounds, layout, editor, cx);
+ self.paint_blocks(bounds, visible_bounds, layout, editor, cx);
}
- self.paint_scrollbar(scene, bounds, layout, cx, &editor);
- scene.pop_layer();
-
- scene.pop_layer();
+ self.paint_scrollbar(bounds, layout, &editor, cx);
+ cx.scene().pop_layer();
+ cx.scene().pop_layer();
}
fn rect_for_text_range(
@@ -2873,7 +2846,7 @@ impl Cursor {
)
}
- pub fn paint(&self, scene: &mut SceneBuilder, origin: Vector2F, cx: &mut WindowContext) {
+ pub fn paint(&self, origin: Vector2F, cx: &mut WindowContext) {
let bounds = match self.shape {
CursorShape::Bar => RectF::new(self.origin + origin, vec2f(2.0, self.line_height)),
CursorShape::Block | CursorShape::Hollow => RectF::new(
@@ -2888,14 +2861,14 @@ impl Cursor {
//Draw background or border quad
if matches!(self.shape, CursorShape::Hollow) {
- scene.push_quad(Quad {
+ cx.scene().push_quad(Quad {
bounds,
background: None,
- border: Border::all(1., self.color),
+ border: Border::all(1., self.color).into(),
corner_radii: Default::default(),
});
} else {
- scene.push_quad(Quad {
+ cx.scene().push_quad(Quad {
bounds,
background: Some(self.color),
border: Default::default(),
@@ -2904,7 +2877,7 @@ impl Cursor {
}
if let Some(block_text) = &self.block_text {
- block_text.paint(scene, self.origin + origin, bounds, self.line_height, cx);
+ block_text.paint(self.origin + origin, bounds, self.line_height, cx);
}
}
@@ -2929,17 +2902,17 @@ pub struct HighlightedRangeLine {
}
impl HighlightedRange {
- pub fn paint(&self, bounds: RectF, scene: &mut SceneBuilder) {
+ pub fn paint(&self, bounds: RectF, cx: &mut WindowContext) {
if self.lines.len() >= 2 && self.lines[0].start_x > self.lines[1].end_x {
- self.paint_lines(self.start_y, &self.lines[0..1], bounds, scene);
+ self.paint_lines(self.start_y, &self.lines[0..1], bounds, cx);
self.paint_lines(
self.start_y + self.line_height,
&self.lines[1..],
bounds,
- scene,
+ cx,
);
} else {
- self.paint_lines(self.start_y, &self.lines, bounds, scene);
+ self.paint_lines(self.start_y, &self.lines, bounds, cx);
}
}
@@ -2948,7 +2921,7 @@ impl HighlightedRange {
start_y: f32,
lines: &[HighlightedRangeLine],
bounds: RectF,
- scene: &mut SceneBuilder,
+ cx: &mut WindowContext,
) {
if lines.is_empty() {
return;
@@ -3046,7 +3019,7 @@ impl HighlightedRange {
}
path.line_to(first_top_right - top_curve_width);
- scene.push_path(path.build(self.color, Some(bounds)));
+ cx.scene().push_path(path.build(self.color, Some(bounds)));
}
}
@@ -3204,18 +3177,10 @@ mod tests {
Point::new(5, 6)..Point::new(6, 0),
]);
});
- let mut new_parents = Default::default();
- let mut notify_views_if_parents_change = Default::default();
- let mut layout_cx = LayoutContext::new(
- cx,
- &mut new_parents,
- &mut notify_views_if_parents_change,
- false,
- );
element.layout(
SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)),
editor,
- &mut layout_cx,
+ cx,
)
});
assert_eq!(state.selections.len(), 1);
@@ -3296,18 +3261,10 @@ mod tests {
DisplayPoint::new(10, 0)..DisplayPoint::new(13, 0),
]);
});
- let mut new_parents = Default::default();
- let mut notify_views_if_parents_change = Default::default();
- let mut layout_cx = LayoutContext::new(
- cx,
- &mut new_parents,
- &mut notify_views_if_parents_change,
- false,
- );
element.layout(
SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)),
editor,
- &mut layout_cx,
+ cx,
)
});
@@ -3363,18 +3320,10 @@ mod tests {
let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
let (size, mut state) = editor.update(cx, |editor, cx| {
- let mut new_parents = Default::default();
- let mut notify_views_if_parents_change = Default::default();
- let mut layout_cx = LayoutContext::new(
- cx,
- &mut new_parents,
- &mut notify_views_if_parents_change,
- false,
- );
element.layout(
SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)),
editor,
- &mut layout_cx,
+ cx,
)
});
@@ -3389,17 +3338,9 @@ mod tests {
);
// Don't panic.
- let mut scene = SceneBuilder::new(1.0);
let bounds = RectF::new(Default::default(), size);
editor.update(cx, |editor, cx| {
- element.paint(
- &mut scene,
- bounds,
- bounds,
- &mut state,
- editor,
- &mut PaintContext::new(cx),
- );
+ element.paint(bounds, bounds, &mut state, editor, cx);
});
}
@@ -3567,18 +3508,10 @@ mod tests {
editor.set_soft_wrap_mode(language_settings::SoftWrap::EditorWidth, cx);
editor.set_wrap_width(Some(editor_width), cx);
- let mut new_parents = Default::default();
- let mut notify_views_if_parents_change = Default::default();
- let mut layout_cx = LayoutContext::new(
- cx,
- &mut new_parents,
- &mut notify_views_if_parents_change,
- false,
- );
element.layout(
SizeConstraint::new(vec2f(editor_width, 500.), vec2f(editor_width, 500.)),
editor,
- &mut layout_cx,
+ cx,
)
});
diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs
index 2f278ce262..3d5b1d2113 100644
--- a/crates/editor/src/hover_popover.rs
+++ b/crates/editor/src/hover_popover.rs
@@ -691,15 +691,15 @@ impl InfoPopover {
.with_highlights(rendered_content.highlights.clone())
.with_custom_runs(
rendered_content.region_ranges.clone(),
- move |ix, bounds, scene, _| {
+ move |ix, bounds, cx| {
region_id += 1;
let region = regions[ix].clone();
if let Some(url) = region.link_url {
- scene.push_cursor_region(CursorRegion {
+ cx.scene().push_cursor_region(CursorRegion {
bounds,
style: CursorStyle::PointingHand,
});
- scene.push_mouse_region(
+ cx.scene().push_mouse_region(
MouseRegion::new::(view_id, region_id, bounds)
.on_click::(
MouseButton::Left,
@@ -708,7 +708,7 @@ impl InfoPopover {
);
}
if region.code {
- scene.push_quad(gpui::Quad {
+ cx.scene().push_quad(gpui::Quad {
bounds,
background: Some(code_span_background_color),
border: Default::default(),
diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs
index d999872592..b31c9dcd1b 100644
--- a/crates/editor/src/items.rs
+++ b/crates/editor/src/items.rs
@@ -16,7 +16,7 @@ use language::{
proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, OffsetRangeExt, Point,
SelectionGoal,
};
-use project::{FormatTrigger, Item as _, Project, ProjectPath};
+use project::{search::SearchQuery, FormatTrigger, Item as _, Project, ProjectPath};
use rpc::proto::{self, update_view};
use smallvec::SmallVec;
use std::{
@@ -26,6 +26,7 @@ use std::{
iter,
ops::Range,
path::{Path, PathBuf},
+ sync::Arc,
};
use text::Selection;
use util::{
@@ -978,7 +979,26 @@ impl SearchableItem for Editor {
}
self.change_selections(None, cx, |s| s.select_ranges(ranges));
}
+ fn replace(
+ &mut self,
+ identifier: &Self::Match,
+ query: &SearchQuery,
+ cx: &mut ViewContext,
+ ) {
+ let text = self.buffer.read(cx);
+ let text = text.snapshot(cx);
+ let text = text.text_for_range(identifier.clone()).collect::>();
+ let text: Cow<_> = if text.len() == 1 {
+ text.first().cloned().unwrap().into()
+ } else {
+ let joined_chunks = text.join("");
+ joined_chunks.into()
+ };
+ if let Some(replacement) = query.replacement(&text) {
+ self.edit([(identifier.clone(), Arc::from(&*replacement))], cx);
+ }
+ }
fn match_index_for_direction(
&mut self,
matches: &Vec>,
@@ -1030,7 +1050,7 @@ impl SearchableItem for Editor {
fn find_matches(
&mut self,
- query: project::search::SearchQuery,
+ query: Arc,
cx: &mut ViewContext,
) -> Task>> {
let buffer = self.buffer().read(cx).snapshot(cx);
diff --git a/crates/feedback/src/feedback_editor.rs b/crates/feedback/src/feedback_editor.rs
index a717223f6d..0b8a29e114 100644
--- a/crates/feedback/src/feedback_editor.rs
+++ b/crates/feedback/src/feedback_editor.rs
@@ -13,7 +13,7 @@ use gpui::{
use isahc::Request;
use language::Buffer;
use postage::prelude::Stream;
-use project::Project;
+use project::{search::SearchQuery, Project};
use regex::Regex;
use serde::Serialize;
use smallvec::SmallVec;
@@ -418,10 +418,13 @@ impl SearchableItem for FeedbackEditor {
self.editor
.update(cx, |e, cx| e.select_matches(matches, cx))
}
-
+ fn replace(&mut self, matches: &Self::Match, query: &SearchQuery, cx: &mut ViewContext) {
+ self.editor
+ .update(cx, |e, cx| e.replace(matches, query, cx));
+ }
fn find_matches(
&mut self,
- query: project::search::SearchQuery,
+ query: Arc,
cx: &mut ViewContext,
) -> Task> {
self.editor
diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs
index 523d6e8a5c..c2d8cc52b2 100644
--- a/crates/file_finder/src/file_finder.rs
+++ b/crates/file_finder/src/file_finder.rs
@@ -1528,8 +1528,13 @@ mod tests {
let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone());
active_pane
.update(cx, |pane, cx| {
- pane.close_active_item(&workspace::CloseActiveItem, cx)
- .unwrap()
+ pane.close_active_item(
+ &workspace::CloseActiveItem {
+ save_behavior: None,
+ },
+ cx,
+ )
+ .unwrap()
})
.await
.unwrap();
diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml
index 24397f6193..705feb351d 100644
--- a/crates/gpui/Cargo.toml
+++ b/crates/gpui/Cargo.toml
@@ -48,7 +48,8 @@ serde_derive.workspace = true
serde_json.workspace = true
smallvec.workspace = true
smol.workspace = true
-taffy = { git = "https://github.com/DioxusLabs/taffy", rev = "dab541d6104d58e2e10ce90c4a1dad0b703160cd", features = ["flexbox"] }
+taffy = { git = "https://github.com/DioxusLabs/taffy", rev = "4fb530bdd71609bb1d3f76c6a8bde1ba82805d5e" }
+thiserror.workspace = true
time.workspace = true
tiny-skia = "0.5"
usvg = { version = "0.14", features = [] }
diff --git a/crates/gpui/examples/corner_radii.rs b/crates/gpui/examples/corner_radii.rs
index 1f33917529..75ea3aeec6 100644
--- a/crates/gpui/examples/corner_radii.rs
+++ b/crates/gpui/examples/corner_radii.rs
@@ -42,29 +42,28 @@ impl gpui::Element for CornersElement {
&mut self,
constraint: gpui::SizeConstraint,
_: &mut V,
- _: &mut gpui::LayoutContext,
+ _: &mut gpui::ViewContext,
) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
(constraint.max, ())
}
fn paint(
&mut self,
- scene: &mut gpui::SceneBuilder,
bounds: pathfinder_geometry::rect::RectF,
_: pathfinder_geometry::rect::RectF,
_: &mut Self::LayoutState,
_: &mut V,
- _: &mut gpui::PaintContext,
+ cx: &mut gpui::ViewContext,
) -> Self::PaintState {
- scene.push_quad(Quad {
+ cx.scene().push_quad(Quad {
bounds,
background: Some(Color::white()),
..Default::default()
});
- scene.push_layer(None);
+ cx.scene().push_layer(None);
- scene.push_quad(Quad {
+ cx.scene().push_quad(Quad {
bounds: RectF::new(vec2f(100., 100.), vec2f(100., 100.)),
background: Some(Color::red()),
border: Default::default(),
@@ -74,7 +73,7 @@ impl gpui::Element for CornersElement {
},
});
- scene.push_quad(Quad {
+ cx.scene().push_quad(Quad {
bounds: RectF::new(vec2f(200., 100.), vec2f(100., 100.)),
background: Some(Color::green()),
border: Default::default(),
@@ -84,7 +83,7 @@ impl gpui::Element for CornersElement {
},
});
- scene.push_quad(Quad {
+ cx.scene().push_quad(Quad {
bounds: RectF::new(vec2f(100., 200.), vec2f(100., 100.)),
background: Some(Color::blue()),
border: Default::default(),
@@ -94,7 +93,7 @@ impl gpui::Element for CornersElement {
},
});
- scene.push_quad(Quad {
+ cx.scene().push_quad(Quad {
bounds: RectF::new(vec2f(200., 200.), vec2f(100., 100.)),
background: Some(Color::yellow()),
border: Default::default(),
@@ -104,7 +103,7 @@ impl gpui::Element for CornersElement {
},
});
- scene.push_shadow(Shadow {
+ cx.scene().push_shadow(Shadow {
bounds: RectF::new(vec2f(400., 100.), vec2f(100., 100.)),
corner_radii: gpui::scene::CornerRadii {
bottom_right: 20.,
@@ -114,8 +113,8 @@ impl gpui::Element for CornersElement {
color: Color::black(),
});
- scene.push_layer(None);
- scene.push_quad(Quad {
+ cx.scene().push_layer(None);
+ cx.scene().push_quad(Quad {
bounds: RectF::new(vec2f(400., 100.), vec2f(100., 100.)),
background: Some(Color::red()),
border: Default::default(),
@@ -125,8 +124,8 @@ impl gpui::Element for CornersElement {
},
});
- scene.pop_layer();
- scene.pop_layer();
+ cx.scene().pop_layer();
+ cx.scene().pop_layer();
}
fn rect_for_text_range(
diff --git a/crates/gpui/examples/text.rs b/crates/gpui/examples/text.rs
index bda70a49dc..bc62d75ec2 100644
--- a/crates/gpui/examples/text.rs
+++ b/crates/gpui/examples/text.rs
@@ -62,12 +62,12 @@ impl gpui::View for TextView {
},
)
.with_highlights(vec![(17..26, underline), (34..40, underline)])
- .with_custom_runs(vec![(17..26), (34..40)], move |ix, bounds, scene, _| {
- scene.push_cursor_region(CursorRegion {
+ .with_custom_runs(vec![(17..26), (34..40)], move |ix, bounds, cx| {
+ cx.scene().push_cursor_region(CursorRegion {
bounds,
style: CursorStyle::PointingHand,
});
- scene.push_mouse_region(
+ cx.scene().push_mouse_region(
MouseRegion::new::(view_id, ix, bounds).on_click::(
MouseButton::Left,
move |_, _, _| {
diff --git a/crates/gpui/playground/Cargo.toml b/crates/gpui/playground/Cargo.toml
deleted file mode 100644
index 3e5a5e5606..0000000000
--- a/crates/gpui/playground/Cargo.toml
+++ /dev/null
@@ -1,26 +0,0 @@
-[package]
-name = "playground"
-version = "0.1.0"
-edition = "2021"
-publish = false
-
-[[bin]]
-name = "playground"
-path = "src/playground.rs"
-
-[dependencies]
-anyhow.workspace = true
-derive_more.workspace = true
-gpui = { path = ".." }
-log.workspace = true
-playground_macros = { path = "../playground_macros" }
-parking_lot.workspace = true
-refineable.workspace = true
-serde.workspace = true
-simplelog = "0.9"
-smallvec.workspace = true
-taffy = { git = "https://github.com/DioxusLabs/taffy", rev = "dab541d6104d58e2e10ce90c4a1dad0b703160cd", features = ["flexbox"] }
-util = { path = "../../util" }
-
-[dev-dependencies]
-gpui = { path = "..", features = ["test-support"] }
diff --git a/crates/gpui/playground/src/div.rs b/crates/gpui/playground/src/div.rs
deleted file mode 100644
index 8efe359025..0000000000
--- a/crates/gpui/playground/src/div.rs
+++ /dev/null
@@ -1,108 +0,0 @@
-use crate::{
- element::{AnyElement, Element, Layout, ParentElement},
- interactive::{InteractionHandlers, Interactive},
- layout_context::LayoutContext,
- paint_context::PaintContext,
- style::{Style, StyleHelpers, StyleRefinement, Styleable},
-};
-use anyhow::Result;
-use gpui::LayoutId;
-use smallvec::SmallVec;
-
-pub struct Div {
- style: StyleRefinement,
- handlers: InteractionHandlers,
- children: SmallVec<[AnyElement; 2]>,
-}
-
-pub fn div() -> Div {
- Div {
- style: Default::default(),
- handlers: Default::default(),
- children: Default::default(),
- }
-}
-
-impl Element for Div {
- type Layout = ();
-
- fn layout(&mut self, view: &mut V, cx: &mut LayoutContext) -> Result>
- where
- Self: Sized,
- {
- let children = self
- .children
- .iter_mut()
- .map(|child| child.layout(view, cx))
- .collect::>>()?;
-
- cx.add_layout_node(self.style(), (), children)
- }
-
- fn paint(&mut self, view: &mut V, layout: &mut Layout, cx: &mut PaintContext)
- where
- Self: Sized,
- {
- let style = self.style();
-
- style.paint_background::(layout, cx);
- for child in &mut self.children {
- child.paint(view, cx);
- }
- }
-}
-
-impl Styleable for Div {
- type Style = Style;
-
- fn declared_style(&mut self) -> &mut StyleRefinement {
- &mut self.style
- }
-}
-
-impl StyleHelpers for Div {}
-
-impl Interactive for Div {
- fn interaction_handlers(&mut self) -> &mut InteractionHandlers {
- &mut self.handlers
- }
-}
-
-impl ParentElement for Div {
- fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
- &mut self.children
- }
-}
-
-#[test]
-fn test() {
- // let elt = div().w_auto();
-}
-
-// trait Element {
-// type Style;
-
-// fn layout()
-// }
-
-// trait Stylable: Element {
-// type Style;
-
-// fn with_style(self, style: Self::Style) -> Self;
-// }
-
-// pub struct HoverStyle
{
-// default: S,
-// hovered: S,
-// }
-
-// struct Hover> {
-// child: C,
-// style: HoverStyle,
-// }
-
-// impl> Hover {
-// fn new(child: C, style: HoverStyle) -> Self {
-// Self { child, style }
-// }
-// }
diff --git a/crates/gpui/playground/src/element.rs b/crates/gpui/playground/src/element.rs
deleted file mode 100644
index 082f3b02b0..0000000000
--- a/crates/gpui/playground/src/element.rs
+++ /dev/null
@@ -1,158 +0,0 @@
-use anyhow::Result;
-use derive_more::{Deref, DerefMut};
-use gpui::{geometry::rect::RectF, EngineLayout};
-use smallvec::SmallVec;
-use std::marker::PhantomData;
-use util::ResultExt;
-
-pub use crate::layout_context::LayoutContext;
-pub use crate::paint_context::PaintContext;
-
-type LayoutId = gpui::LayoutId;
-
-pub trait Element: 'static {
- type Layout;
-
- fn layout(
- &mut self,
- view: &mut V,
- cx: &mut LayoutContext,
- ) -> Result>
- where
- Self: Sized;
-
- fn paint(
- &mut self,
- view: &mut V,
- layout: &mut Layout,
- cx: &mut PaintContext,
- ) where
- Self: Sized;
-
- fn into_any(self) -> AnyElement
- where
- Self: 'static + Sized,
- {
- AnyElement(Box::new(ElementState {
- element: self,
- layout: None,
- }))
- }
-}
-
-/// Used to make ElementState into a trait object, so we can wrap it in AnyElement.
-trait ElementStateObject {
- fn layout(&mut self, view: &mut V, cx: &mut LayoutContext) -> Result;
- fn paint(&mut self, view: &mut V, cx: &mut PaintContext);
-}
-
-/// A wrapper around an element that stores its layout state.
-struct ElementState> {
- element: E,
- layout: Option>,
-}
-
-/// We blanket-implement the object-safe ElementStateObject interface to make ElementStates into trait objects
-impl> ElementStateObject for ElementState {
- fn layout(&mut self, view: &mut V, cx: &mut LayoutContext) -> Result {
- let layout = self.element.layout(view, cx)?;
- let layout_id = layout.id;
- self.layout = Some(layout);
- Ok(layout_id)
- }
-
- fn paint(&mut self, view: &mut V, cx: &mut PaintContext) {
- let layout = self.layout.as_mut().expect("paint called before layout");
- if layout.engine_layout.is_none() {
- layout.engine_layout = cx.computed_layout(layout.id).log_err()
- }
- self.element.paint(view, layout, cx)
- }
-}
-
-/// A dynamic element.
-pub struct AnyElement(Box>);
-
-impl AnyElement {
- pub fn layout(&mut self, view: &mut V, cx: &mut LayoutContext) -> Result {
- self.0.layout(view, cx)
- }
-
- pub fn paint(&mut self, view: &mut V, cx: &mut PaintContext) {
- self.0.paint(view, cx)
- }
-}
-
-#[derive(Deref, DerefMut)]
-pub struct Layout {
- id: LayoutId,
- engine_layout: Option,
- #[deref]
- #[deref_mut]
- element_data: D,
- view_type: PhantomData,
-}
-
-impl Layout {
- pub fn new(id: LayoutId, element_data: D) -> Self {
- Self {
- id,
- engine_layout: None,
- element_data: element_data,
- view_type: PhantomData,
- }
- }
-
- pub fn bounds(&mut self, cx: &mut PaintContext) -> RectF {
- self.engine_layout(cx).bounds
- }
-
- pub fn order(&mut self, cx: &mut PaintContext) -> u32 {
- self.engine_layout(cx).order
- }
-
- fn engine_layout(&mut self, cx: &mut PaintContext<'_, '_, '_, '_, V>) -> &mut EngineLayout {
- self.engine_layout
- .get_or_insert_with(|| cx.computed_layout(self.id).log_err().unwrap_or_default())
- }
-}
-
-impl Layout>> {
- pub fn paint(&mut self, view: &mut V, cx: &mut PaintContext) {
- let mut element = self.element_data.take().unwrap();
- element.paint(view, cx);
- self.element_data = Some(element);
- }
-}
-
-pub trait ParentElement {
- fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]>;
-
- fn child(mut self, child: impl IntoElement) -> Self
- where
- Self: Sized,
- {
- self.children_mut().push(child.into_element().into_any());
- self
- }
-
- fn children(mut self, children: I) -> Self
- where
- I: IntoIterator- ,
- E: IntoElement,
- Self: Sized,
- {
- self.children_mut().extend(
- children
- .into_iter()
- .map(|child| child.into_element().into_any()),
- );
- self
- }
-}
-
-pub trait IntoElement {
- type Element: Element;
-
- fn into_element(self) -> Self::Element;
-}
diff --git a/crates/gpui/playground/src/hoverable.rs b/crates/gpui/playground/src/hoverable.rs
deleted file mode 100644
index 5545155a60..0000000000
--- a/crates/gpui/playground/src/hoverable.rs
+++ /dev/null
@@ -1,76 +0,0 @@
-use crate::{
- element::{Element, Layout},
- layout_context::LayoutContext,
- paint_context::PaintContext,
- style::{StyleRefinement, Styleable},
-};
-use anyhow::Result;
-use gpui::platform::MouseMovedEvent;
-use refineable::Refineable;
-use std::{cell::Cell, marker::PhantomData};
-
-pub struct Hoverable + Styleable> {
- hovered: Cell,
- child_style: StyleRefinement,
- hovered_style: StyleRefinement,
- child: E,
- view_type: PhantomData,
-}
-
-pub fn hoverable + Styleable>(mut child: E) -> Hoverable {
- Hoverable {
- hovered: Cell::new(false),
- child_style: child.declared_style().clone(),
- hovered_style: Default::default(),
- child,
- view_type: PhantomData,
- }
-}
-
-impl + Styleable> Styleable for Hoverable {
- type Style = E::Style;
-
- fn declared_style(&mut self) -> &mut crate::style::StyleRefinement {
- self.child.declared_style()
- }
-}
-
-impl + Styleable> Element for Hoverable {
- type Layout = E::Layout;
-
- fn layout(&mut self, view: &mut V, cx: &mut LayoutContext) -> Result>
- where
- Self: Sized,
- {
- self.child.layout(view, cx)
- }
-
- fn paint(
- &mut self,
- view: &mut V,
- layout: &mut Layout,
- cx: &mut PaintContext,
- ) where
- Self: Sized,
- {
- if self.hovered.get() {
- // If hovered, refine the child's style with this element's style.
- self.child.declared_style().refine(&self.hovered_style);
- } else {
- // Otherwise, set the child's style back to its original style.
- *self.child.declared_style() = self.child_style.clone();
- }
-
- let bounds = layout.bounds(cx);
- let order = layout.order(cx);
- self.hovered.set(bounds.contains_point(cx.mouse_position()));
- let was_hovered = self.hovered.clone();
- cx.on_event(order, move |view, event: &MouseMovedEvent, cx| {
- let is_hovered = bounds.contains_point(event.position);
- if is_hovered != was_hovered.get() {
- was_hovered.set(is_hovered);
- cx.repaint();
- }
- });
- }
-}
diff --git a/crates/gpui/playground/src/interactive.rs b/crates/gpui/playground/src/interactive.rs
deleted file mode 100644
index 8debcb1692..0000000000
--- a/crates/gpui/playground/src/interactive.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-use gpui::{platform::MouseMovedEvent, EventContext};
-use smallvec::SmallVec;
-use std::rc::Rc;
-
-pub trait Interactive {
- fn interaction_handlers(&mut self) -> &mut InteractionHandlers;
-
- fn on_mouse_move(mut self, handler: H) -> Self
- where
- H: 'static + Fn(&mut V, &MouseMovedEvent, bool, &mut EventContext),
- Self: Sized,
- {
- self.interaction_handlers()
- .mouse_moved
- .push(Rc::new(move |view, event, hit_test, cx| {
- handler(view, event, hit_test, cx);
- cx.bubble
- }));
- self
- }
-}
-
-pub struct InteractionHandlers {
- mouse_moved:
- SmallVec<[Rc) -> bool>; 2]>,
-}
-
-impl Default for InteractionHandlers {
- fn default() -> Self {
- Self {
- mouse_moved: Default::default(),
- }
- }
-}
diff --git a/crates/gpui/playground/src/layout_context.rs b/crates/gpui/playground/src/layout_context.rs
deleted file mode 100644
index 7c9f13e7f0..0000000000
--- a/crates/gpui/playground/src/layout_context.rs
+++ /dev/null
@@ -1,54 +0,0 @@
-use anyhow::{anyhow, Result};
-use derive_more::{Deref, DerefMut};
-pub use gpui::LayoutContext as LegacyLayoutContext;
-use gpui::{RenderContext, ViewContext};
-pub use taffy::tree::NodeId;
-
-use crate::{element::Layout, style::Style};
-
-#[derive(Deref, DerefMut)]
-pub struct LayoutContext<'a, 'b, 'c, 'd, V> {
- #[deref]
- #[deref_mut]
- pub(crate) legacy_cx: &'d mut LegacyLayoutContext<'a, 'b, 'c, V>,
-}
-
-impl<'a, 'b, V> RenderContext<'a, 'b, V> for LayoutContext<'a, 'b, '_, '_, V> {
- fn text_style(&self) -> gpui::fonts::TextStyle {
- self.legacy_cx.text_style()
- }
-
- fn push_text_style(&mut self, style: gpui::fonts::TextStyle) {
- self.legacy_cx.push_text_style(style)
- }
-
- fn pop_text_style(&mut self) {
- self.legacy_cx.pop_text_style()
- }
-
- fn as_view_context(&mut self) -> &mut ViewContext<'a, 'b, V> {
- &mut self.view_context
- }
-}
-
-impl<'a, 'b, 'c, 'd, V: 'static> LayoutContext<'a, 'b, 'c, 'd, V> {
- pub fn new(legacy_cx: &'d mut LegacyLayoutContext<'a, 'b, 'c, V>) -> Self {
- Self { legacy_cx }
- }
-
- pub fn add_layout_node(
- &mut self,
- style: Style,
- element_data: D,
- children: impl IntoIterator
- ,
- ) -> Result> {
- let rem_size = self.rem_pixels();
- let id = self
- .legacy_cx
- .layout_engine()
- .ok_or_else(|| anyhow!("no layout engine"))?
- .add_node(style.to_taffy(rem_size), children)?;
-
- Ok(Layout::new(id, element_data))
- }
-}
diff --git a/crates/gpui/playground/src/paint_context.rs b/crates/gpui/playground/src/paint_context.rs
deleted file mode 100644
index d853aff7f8..0000000000
--- a/crates/gpui/playground/src/paint_context.rs
+++ /dev/null
@@ -1,71 +0,0 @@
-use anyhow::{anyhow, Result};
-use derive_more::{Deref, DerefMut};
-use gpui::{scene::EventHandler, EngineLayout, EventContext, LayoutId, RenderContext, ViewContext};
-pub use gpui::{LayoutContext, PaintContext as LegacyPaintContext};
-use std::{any::TypeId, rc::Rc};
-pub use taffy::tree::NodeId;
-
-#[derive(Deref, DerefMut)]
-pub struct PaintContext<'a, 'b, 'c, 'd, V> {
- #[deref]
- #[deref_mut]
- pub(crate) legacy_cx: &'d mut LegacyPaintContext<'a, 'b, 'c, V>,
- pub(crate) scene: &'d mut gpui::SceneBuilder,
-}
-
-impl<'a, 'b, V> RenderContext<'a, 'b, V> for PaintContext<'a, 'b, '_, '_, V> {
- fn text_style(&self) -> gpui::fonts::TextStyle {
- self.legacy_cx.text_style()
- }
-
- fn push_text_style(&mut self, style: gpui::fonts::TextStyle) {
- self.legacy_cx.push_text_style(style)
- }
-
- fn pop_text_style(&mut self) {
- self.legacy_cx.pop_text_style()
- }
-
- fn as_view_context(&mut self) -> &mut ViewContext<'a, 'b, V> {
- &mut self.view_context
- }
-}
-
-impl<'a, 'b, 'c, 'd, V: 'static> PaintContext<'a, 'b, 'c, 'd, V> {
- pub fn new(
- legacy_cx: &'d mut LegacyPaintContext<'a, 'b, 'c, V>,
- scene: &'d mut gpui::SceneBuilder,
- ) -> Self {
- Self { legacy_cx, scene }
- }
-
- pub fn on_event(
- &mut self,
- order: u32,
- handler: impl Fn(&mut V, &E, &mut ViewContext) + 'static,
- ) {
- let view = self.weak_handle();
-
- self.scene.event_handlers.push(EventHandler {
- order,
- handler: Rc::new(move |event, window_cx| {
- if let Some(view) = view.upgrade(window_cx) {
- view.update(window_cx, |view, view_cx| {
- let mut event_cx = EventContext::new(view_cx);
- handler(view, event.downcast_ref().unwrap(), &mut event_cx);
- event_cx.bubble
- })
- } else {
- true
- }
- }),
- event_type: TypeId::of::(),
- })
- }
-
- pub(crate) fn computed_layout(&mut self, layout_id: LayoutId) -> Result {
- self.layout_engine()
- .ok_or_else(|| anyhow!("no layout engine present"))?
- .computed_layout(layout_id)
- }
-}
diff --git a/crates/gpui/playground/src/playground.rs b/crates/gpui/playground/src/playground.rs
deleted file mode 100644
index 2462ac99f5..0000000000
--- a/crates/gpui/playground/src/playground.rs
+++ /dev/null
@@ -1,83 +0,0 @@
-#![allow(dead_code, unused_variables)]
-use crate::{color::black, style::StyleHelpers};
-use element::Element;
-use gpui::{
- geometry::{rect::RectF, vector::vec2f},
- platform::WindowOptions,
-};
-use log::LevelFilter;
-use simplelog::SimpleLogger;
-use themes::{rose_pine, ThemeColors};
-use view::view;
-
-mod adapter;
-mod color;
-mod components;
-mod div;
-mod element;
-mod hoverable;
-mod interactive;
-mod layout_context;
-mod paint_context;
-mod style;
-mod text;
-mod themes;
-mod view;
-
-fn main() {
- SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
-
- gpui::App::new(()).unwrap().run(|cx| {
- cx.add_window(
- WindowOptions {
- bounds: gpui::platform::WindowBounds::Fixed(RectF::new(
- vec2f(0., 0.),
- vec2f(400., 300.),
- )),
- center: true,
- ..Default::default()
- },
- |_| view(|_| playground(&rose_pine::moon())),
- );
- cx.platform().activate(true);
- });
-}
-
-fn playground(theme: &ThemeColors) -> impl Element {
- use div::div;
-
- div()
- .text_color(black())
- .h_full()
- .w_1_2()
- .fill(theme.success(0.5))
- // .hover()
- // .fill(theme.error(0.5))
- // .child(button().label("Hello").click(|_, _, _| println!("click!")))
-}
-
-// todo!()
-// // column()
-// // .size(auto())
-// // .fill(theme.base(0.5))
-// // .text_color(theme.text(0.5))
-// // .child(title_bar(theme))
-// // .child(stage(theme))
-// // .child(status_bar(theme))
-// }
-
-// fn title_bar(theme: &ThemeColors) -> impl Element {
-// row()
-// .fill(theme.base(0.2))
-// .justify(0.)
-// .width(auto())
-// .child(text("Zed Playground"))
-// }
-
-// fn stage(theme: &ThemeColors) -> impl Element {
-// row().fill(theme.surface(0.9))
-// }
-
-// fn status_bar(theme: &ThemeColors) -> impl Element {
-// row().fill(theme.surface(0.1))
-// }
diff --git a/crates/gpui/playground/src/style.rs b/crates/gpui/playground/src/style.rs
deleted file mode 100644
index 9216702f7f..0000000000
--- a/crates/gpui/playground/src/style.rs
+++ /dev/null
@@ -1,286 +0,0 @@
-use crate::{
- color::Hsla,
- element::{Element, Layout},
- paint_context::PaintContext,
-};
-use gpui::{
- fonts::TextStyleRefinement,
- geometry::{
- AbsoluteLength, DefiniteLength, Edges, EdgesRefinement, Length, Point, PointRefinement,
- Size, SizeRefinement,
- },
-};
-use playground_macros::styleable_helpers;
-use refineable::Refineable;
-pub use taffy::style::{
- AlignContent, AlignItems, AlignSelf, Display, FlexDirection, FlexWrap, JustifyContent,
- Overflow, Position,
-};
-
-#[derive(Clone, Refineable)]
-pub struct Style {
- /// What layout strategy should be used?
- pub display: Display,
-
- // Overflow properties
- /// How children overflowing their container should affect layout
- #[refineable]
- pub overflow: Point,
- /// How much space (in points) should be reserved for the scrollbars of `Overflow::Scroll` and `Overflow::Auto` nodes.
- pub scrollbar_width: f32,
-
- // Position properties
- /// What should the `position` value of this struct use as a base offset?
- pub position: Position,
- /// How should the position of this element be tweaked relative to the layout defined?
- #[refineable]
- pub inset: Edges,
-
- // Size properies
- /// Sets the initial size of the item
- #[refineable]
- pub size: Size,
- /// Controls the minimum size of the item
- #[refineable]
- pub min_size: Size,
- /// Controls the maximum size of the item
- #[refineable]
- pub max_size: Size,
- /// Sets the preferred aspect ratio for the item. The ratio is calculated as width divided by height.
- pub aspect_ratio: Option,
-
- // Spacing Properties
- /// How large should the margin be on each side?
- #[refineable]
- pub margin: Edges,
- /// How large should the padding be on each side?
- #[refineable]
- pub padding: Edges,
- /// How large should the border be on each side?
- #[refineable]
- pub border: Edges,
-
- // Alignment properties
- /// How this node's children aligned in the cross/block axis?
- pub align_items: Option,
- /// How this node should be aligned in the cross/block axis. Falls back to the parents [`AlignItems`] if not set
- pub align_self: Option,
- /// How should content contained within this item be aligned in the cross/block axis
- pub align_content: Option,
- /// How should contained within this item be aligned in the main/inline axis
- pub justify_content: Option,
- /// How large should the gaps between items in a flex container be?
- #[refineable]
- pub gap: Size,
-
- // Flexbox properies
- /// Which direction does the main axis flow in?
- pub flex_direction: FlexDirection,
- /// Should elements wrap, or stay in a single line?
- pub flex_wrap: FlexWrap,
- /// Sets the initial main axis size of the item
- pub flex_basis: Length,
- /// The relative rate at which this item grows when it is expanding to fill space, 0.0 is the default value, and this value must be positive.
- pub flex_grow: f32,
- /// The relative rate at which this item shrinks when it is contracting to fit into space, 1.0 is the default value, and this value must be positive.
- pub flex_shrink: f32,
-
- /// The fill color of this element
- pub fill: Option,
- /// The radius of the corners of this element
- #[refineable]
- pub corner_radii: CornerRadii,
- /// The color of text within this element. Cascades to children unless overridden.
- pub text_color: Option,
-}
-
-impl Style {
- pub fn to_taffy(&self, rem_size: f32) -> taffy::style::Style {
- taffy::style::Style {
- display: self.display,
- overflow: self.overflow.clone().into(),
- scrollbar_width: self.scrollbar_width,
- position: self.position,
- inset: self.inset.to_taffy(rem_size),
- size: self.size.to_taffy(rem_size),
- min_size: self.min_size.to_taffy(rem_size),
- max_size: self.max_size.to_taffy(rem_size),
- aspect_ratio: self.aspect_ratio,
- margin: self.margin.to_taffy(rem_size),
- padding: self.padding.to_taffy(rem_size),
- border: self.border.to_taffy(rem_size),
- align_items: self.align_items,
- align_self: self.align_self,
- align_content: self.align_content,
- justify_content: self.justify_content,
- gap: self.gap.to_taffy(rem_size),
- flex_direction: self.flex_direction,
- flex_wrap: self.flex_wrap,
- flex_basis: self.flex_basis.to_taffy(rem_size).into(),
- flex_grow: self.flex_grow,
- flex_shrink: self.flex_shrink,
- ..Default::default() // Ignore grid properties for now
- }
- }
-
- /// Paints the background of an element styled with this style.
- /// Return the bounds in which to paint the content.
- pub fn paint_background>(
- &self,
- layout: &mut Layout,
- cx: &mut PaintContext