diff --git a/Cargo.lock b/Cargo.lock
index 7dcecd009390c92a0602ae3fc70c421be27c6f1d..0fd0c8a212eb84638b1a4659f1f80b1a732bfb1a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4878,6 +4878,8 @@ name = "terminal"
version = "0.1.0"
dependencies = [
"alacritty_terminal",
+ "client",
+ "dirs 4.0.0",
"editor",
"futures",
"gpui",
@@ -6159,7 +6161,7 @@ dependencies = [
[[package]]
name = "zed"
-version = "0.44.1"
+version = "0.45.0"
dependencies = [
"activity_indicator",
"anyhow",
diff --git a/assets/icons/arrow-left.svg b/assets/icons/arrow-left.svg
new file mode 100644
index 0000000000000000000000000000000000000000..904fdaa1a73221efda6946614db7c707dc74c2ed
--- /dev/null
+++ b/assets/icons/arrow-left.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/arrow-right.svg b/assets/icons/arrow-right.svg
new file mode 100644
index 0000000000000000000000000000000000000000..b7e1bec6d8a0830b8ebfcf677cd1a5b3480ce5b0
--- /dev/null
+++ b/assets/icons/arrow-right.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json
index bea53ece45b2a672879453bb522fd64814e635f3..6cd3660bf5120d4b16f1f6988588a537b7b92a31 100644
--- a/assets/keymaps/default.json
+++ b/assets/keymaps/default.json
@@ -417,7 +417,8 @@
"up": "terminal::Up",
"down": "terminal::Down",
"tab": "terminal::Tab",
- "cmd-v": "terminal::Paste"
+ "cmd-v": "terminal::Paste",
+ "cmd-c": "terminal::Copy"
}
}
]
\ No newline at end of file
diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs
index 538b0fa4b0101be73c64d366faf09238e5d8da16..0e9ec4076ad43754a53e11f833935c9b807f025c 100644
--- a/crates/client/src/client.rs
+++ b/crates/client/src/client.rs
@@ -549,7 +549,7 @@ impl Client {
client.respond_with_error(
receipt,
proto::Error {
- message: error.to_string(),
+ message: format!("{:?}", error),
},
)?;
Err(error)
diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs
index a4c8386b13fb58a88fd2a4a0f1d5664ed9226852..7767b361c1bbbd327ae765ab62a8308ca3cb6b61 100644
--- a/crates/collab/src/integration_tests.rs
+++ b/crates/collab/src/integration_tests.rs
@@ -35,7 +35,7 @@ use project::{
use rand::prelude::*;
use rpc::PeerId;
use serde_json::json;
-use settings::Settings;
+use settings::{FormatOnSave, Settings};
use sqlx::types::time::OffsetDateTime;
use std::{
cell::RefCell,
@@ -1912,7 +1912,6 @@ async fn test_reloading_buffer_manually(cx_a: &mut TestAppContext, cx_b: &mut Te
#[gpui::test(iterations = 10)]
async fn test_formatting_buffer(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
- cx_a.foreground().forbid_parking();
let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
@@ -1932,11 +1931,15 @@ async fn test_formatting_buffer(cx_a: &mut TestAppContext, cx_b: &mut TestAppCon
let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default());
client_a.language_registry.add(Arc::new(language));
+ // Here we insert a fake tree with a directory that exists on disk. This is needed
+ // because later we'll invoke a command, which requires passing a working directory
+ // that points to a valid location on disk.
+ let directory = env::current_dir().unwrap();
client_a
.fs
- .insert_tree("/a", json!({ "a.rs": "let one = two" }))
+ .insert_tree(&directory, json!({ "a.rs": "let one = \"two\"" }))
.await;
- let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
+ let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
let buffer_b = cx_b
@@ -1967,7 +1970,28 @@ async fn test_formatting_buffer(cx_a: &mut TestAppContext, cx_b: &mut TestAppCon
.unwrap();
assert_eq!(
buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
- "let honey = two"
+ "let honey = \"two\""
+ );
+
+ // Ensure buffer can be formatted using an external command. Notice how the
+ // host's configuration is honored as opposed to using the guest's settings.
+ cx_a.update(|cx| {
+ cx.update_global(|settings: &mut Settings, _| {
+ settings.language_settings.format_on_save = Some(FormatOnSave::External {
+ command: "awk".to_string(),
+ arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()],
+ });
+ });
+ });
+ project_b
+ .update(cx_b, |project, cx| {
+ project.format(HashSet::from_iter([buffer_b.clone()]), true, cx)
+ })
+ .await
+ .unwrap();
+ assert_eq!(
+ buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
+ format!("let honey = \"{}/a.rs\"\n", directory.to_str().unwrap())
);
}
diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs
index 9dd40413fd254e7ef852643acfdb5cbe464ee415..808926ff50f3e71b157f6b8380980fc1bd109044 100644
--- a/crates/editor/src/editor.rs
+++ b/crates/editor/src/editor.rs
@@ -4065,13 +4065,16 @@ impl Editor {
}
}
- nav_history.push(Some(NavigationData {
- cursor_anchor: position,
- cursor_position: point,
- scroll_position: self.scroll_position,
- scroll_top_anchor: self.scroll_top_anchor.clone(),
- scroll_top_row,
- }));
+ nav_history.push(
+ Some(NavigationData {
+ cursor_anchor: position,
+ cursor_position: point,
+ scroll_position: self.scroll_position,
+ scroll_top_anchor: self.scroll_top_anchor.clone(),
+ scroll_top_row,
+ }),
+ cx,
+ );
}
}
@@ -4669,7 +4672,7 @@ impl Editor {
definitions: Vec,
cx: &mut ViewContext,
) {
- let nav_history = workspace.active_pane().read(cx).nav_history().clone();
+ let pane = workspace.active_pane().clone();
for definition in definitions {
let range = definition
.target
@@ -4681,13 +4684,13 @@ impl Editor {
// When selecting a definition in a different buffer, disable the nav history
// to avoid creating a history entry at the previous cursor location.
if editor_handle != target_editor_handle {
- nav_history.borrow_mut().disable();
+ pane.update(cx, |pane, _| pane.disable_history());
}
target_editor.change_selections(Some(Autoscroll::Center), cx, |s| {
s.select_ranges([range]);
});
- nav_history.borrow_mut().enable();
+ pane.update(cx, |pane, _| pane.enable_history());
});
}
}
@@ -5641,8 +5644,8 @@ impl Editor {
editor_handle.update(cx, |editor, cx| {
editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
});
- let nav_history = workspace.active_pane().read(cx).nav_history().clone();
- nav_history.borrow_mut().disable();
+ let pane = workspace.active_pane().clone();
+ pane.update(cx, |pane, _| pane.disable_history());
// We defer the pane interaction because we ourselves are a workspace item
// and activating a new item causes the pane to call a method on us reentrantly,
@@ -5657,7 +5660,7 @@ impl Editor {
});
}
- nav_history.borrow_mut().enable();
+ pane.update(cx, |pane, _| pane.enable_history());
});
}
@@ -6241,7 +6244,7 @@ mod tests {
assert_set_eq,
test::{marked_text_by, marked_text_ranges, marked_text_ranges_by, sample_text},
};
- use workspace::{FollowableItem, ItemHandle};
+ use workspace::{FollowableItem, ItemHandle, NavigationEntry, Pane};
#[gpui::test]
fn test_edit_events(cx: &mut MutableAppContext) {
@@ -6589,12 +6592,20 @@ mod tests {
fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
cx.set_global(Settings::test(cx));
use workspace::Item;
- let nav_history = Rc::new(RefCell::new(workspace::NavHistory::default()));
+ let pane = cx.add_view(Default::default(), |cx| Pane::new(cx));
let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
cx.add_window(Default::default(), |cx| {
let mut editor = build_editor(buffer.clone(), cx);
- editor.nav_history = Some(ItemNavHistory::new(nav_history.clone(), &cx.handle()));
+ let handle = cx.handle();
+ editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
+
+ fn pop_history(
+ editor: &mut Editor,
+ cx: &mut MutableAppContext,
+ ) -> Option {
+ editor.nav_history.as_mut().unwrap().pop_backward(cx)
+ }
// Move the cursor a small distance.
// Nothing is added to the navigation history.
@@ -6604,21 +6615,21 @@ mod tests {
editor.change_selections(None, cx, |s| {
s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
});
- assert!(nav_history.borrow_mut().pop_backward().is_none());
+ assert!(pop_history(&mut editor, cx).is_none());
// Move the cursor a large distance.
// The history can jump back to the previous position.
editor.change_selections(None, cx, |s| {
s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
});
- let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
+ let nav_entry = pop_history(&mut editor, cx).unwrap();
editor.navigate(nav_entry.data.unwrap(), cx);
assert_eq!(nav_entry.item.id(), cx.view_id());
assert_eq!(
editor.selections.display_ranges(cx),
&[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
);
- assert!(nav_history.borrow_mut().pop_backward().is_none());
+ assert!(pop_history(&mut editor, cx).is_none());
// Move the cursor a small distance via the mouse.
// Nothing is added to the navigation history.
@@ -6628,7 +6639,7 @@ mod tests {
editor.selections.display_ranges(cx),
&[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
);
- assert!(nav_history.borrow_mut().pop_backward().is_none());
+ assert!(pop_history(&mut editor, cx).is_none());
// Move the cursor a large distance via the mouse.
// The history can jump back to the previous position.
@@ -6638,14 +6649,14 @@ mod tests {
editor.selections.display_ranges(cx),
&[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
);
- let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
+ let nav_entry = pop_history(&mut editor, cx).unwrap();
editor.navigate(nav_entry.data.unwrap(), cx);
assert_eq!(nav_entry.item.id(), cx.view_id());
assert_eq!(
editor.selections.display_ranges(cx),
&[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
);
- assert!(nav_history.borrow_mut().pop_backward().is_none());
+ assert!(pop_history(&mut editor, cx).is_none());
// Set scroll position to check later
editor.set_scroll_position(Vector2F::new(5.5, 5.5), cx);
@@ -6658,7 +6669,7 @@ mod tests {
assert_ne!(editor.scroll_position, original_scroll_position);
assert_ne!(editor.scroll_top_anchor, original_scroll_top_anchor);
- let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
+ let nav_entry = pop_history(&mut editor, cx).unwrap();
editor.navigate(nav_entry.data.unwrap(), cx);
assert_eq!(editor.scroll_position, original_scroll_position);
assert_eq!(editor.scroll_top_anchor, original_scroll_top_anchor);
@@ -8221,7 +8232,7 @@ mod tests {
fox ju|mps over
the lazy dog"});
cx.update_editor(|e, cx| e.copy(&Copy, cx));
- cx.assert_clipboard_content(Some("fox jumps over\n"));
+ cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
// Paste with three selections, noticing how the copied full-line selection is inserted
// before the empty selections but replaces the selection that is non-empty.
diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs
index 1169df3fd1d72f725a72beaa81154c58e9bd4884..56f664566ecd03bba53cbd7960e3ac5be8d33287 100644
--- a/crates/editor/src/element.rs
+++ b/crates/editor/src/element.rs
@@ -23,8 +23,9 @@ use gpui::{
json::{self, ToJson},
platform::CursorStyle,
text_layout::{self, Line, RunStyle, TextLayoutCache},
- AppContext, Axis, Border, CursorRegion, Element, ElementBox, Event, EventContext,
- LayoutContext, MutableAppContext, PaintContext, Quad, Scene, SizeConstraint, ViewContext,
+ AppContext, Axis, Border, CursorRegion, Element, ElementBox, Event, EventContext, KeyDownEvent,
+ LayoutContext, ModifiersChangedEvent, MouseButton, MouseEvent, MouseMovedEvent,
+ MutableAppContext, PaintContext, Quad, Scene, ScrollWheelEvent, SizeConstraint, ViewContext,
WeakViewHandle,
};
use json::json;
@@ -1463,14 +1464,15 @@ impl Element for EditorElement {
}
match event {
- Event::LeftMouseDown {
+ Event::MouseDown(MouseEvent {
+ button: MouseButton::Left,
position,
cmd,
alt,
shift,
click_count,
..
- } => self.mouse_down(
+ }) => self.mouse_down(
*position,
*cmd,
*alt,
@@ -1480,18 +1482,26 @@ impl Element for EditorElement {
paint,
cx,
),
- Event::LeftMouseUp { position, .. } => self.mouse_up(*position, cx),
- Event::LeftMouseDragged { position, .. } => {
- self.mouse_dragged(*position, layout, paint, cx)
- }
- Event::ScrollWheel {
+ Event::MouseUp(MouseEvent {
+ button: MouseButton::Left,
+ position,
+ ..
+ }) => self.mouse_up(*position, cx),
+ Event::MouseMoved(MouseMovedEvent {
+ pressed_button: Some(MouseButton::Left),
+ position,
+ ..
+ }) => self.mouse_dragged(*position, layout, paint, cx),
+ Event::ScrollWheel(ScrollWheelEvent {
position,
delta,
precise,
- } => self.scroll(*position, *delta, *precise, layout, paint, cx),
- Event::KeyDown { input, .. } => self.key_down(input.as_deref(), cx),
- Event::ModifiersChanged { cmd, .. } => self.modifiers_changed(*cmd, cx),
- Event::MouseMoved { position, cmd, .. } => {
+ }) => self.scroll(*position, *delta, *precise, layout, paint, cx),
+ Event::KeyDown(KeyDownEvent { input, .. }) => self.key_down(input.as_deref(), cx),
+ Event::ModifiersChanged(ModifiersChangedEvent { cmd, .. }) => {
+ self.modifiers_changed(*cmd, cx)
+ }
+ Event::MouseMoved(MouseMovedEvent { position, cmd, .. }) => {
self.mouse_moved(*position, *cmd, layout, paint, cx)
}
@@ -1685,22 +1695,22 @@ impl Cursor {
}
#[derive(Debug)]
-struct HighlightedRange {
- start_y: f32,
- line_height: f32,
- lines: Vec,
- color: Color,
- corner_radius: f32,
+pub struct HighlightedRange {
+ pub start_y: f32,
+ pub line_height: f32,
+ pub lines: Vec,
+ pub color: Color,
+ pub corner_radius: f32,
}
#[derive(Debug)]
-struct HighlightedRangeLine {
- start_x: f32,
- end_x: f32,
+pub struct HighlightedRangeLine {
+ pub start_x: f32,
+ pub end_x: f32,
}
impl HighlightedRange {
- fn paint(&self, bounds: RectF, scene: &mut Scene) {
+ pub fn paint(&self, bounds: RectF, scene: &mut Scene) {
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(
diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs
index f7aa80beaa6aa9219d42e091d258306781aef62c..0e3aca1447043aaba3354fb925babcbaa324b35f 100644
--- a/crates/editor/src/items.rs
+++ b/crates/editor/src/items.rs
@@ -352,13 +352,8 @@ impl Item for Editor {
project: ModelHandle,
cx: &mut ViewContext,
) -> Task> {
- let settings = cx.global::();
let buffer = self.buffer().clone();
- let mut buffers = buffer.read(cx).all_buffers();
- buffers.retain(|buffer| {
- let language_name = buffer.read(cx).language().map(|l| l.name());
- settings.format_on_save(language_name.as_deref())
- });
+ let buffers = buffer.read(cx).all_buffers();
let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
let format = project.update(cx, |project, cx| project.format(buffers, true, cx));
cx.spawn(|this, mut cx| async move {
diff --git a/crates/editor/src/test.rs b/crates/editor/src/test.rs
index d1316a85a0bf38afe5c39cb596b6647b89119252..0affe06f64b555890ea3b6289ec497ace6aeb18d 100644
--- a/crates/editor/src/test.rs
+++ b/crates/editor/src/test.rs
@@ -404,14 +404,6 @@ impl<'a> EditorTestContext<'a> {
editor_text_with_selections
}
-
- pub fn assert_clipboard_content(&mut self, expected_content: Option<&str>) {
- self.cx.update(|cx| {
- let actual_content = cx.read_from_clipboard().map(|item| item.text().to_owned());
- let expected_content = expected_content.map(|content| content.to_owned());
- assert_eq!(actual_content, expected_content);
- })
- }
}
impl<'a> Deref for EditorTestContext<'a> {
diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs
index fd447e2469fc24c538bc7a05ab77fe64460a0a36..901c131d032bd6683951c866b7000bb2f4b4fb4a 100644
--- a/crates/gpui/src/app.rs
+++ b/crates/gpui/src/app.rs
@@ -4,7 +4,7 @@ use crate::{
elements::ElementBox,
executor::{self, Task},
keymap::{self, Binding, Keystroke},
- platform::{self, Platform, PromptLevel, WindowOptions},
+ platform::{self, KeyDownEvent, Platform, PromptLevel, WindowOptions},
presenter::Presenter,
util::post_inc,
AssetCache, AssetSource, ClipboardItem, FontCache, MouseRegionId, PathPromptOptions,
@@ -151,6 +151,7 @@ pub struct AsyncAppContext(Rc>);
pub struct TestAppContext {
cx: Rc>,
foreground_platform: Rc,
+ condition_duration: Option,
}
impl App {
@@ -337,6 +338,7 @@ impl TestAppContext {
let cx = TestAppContext {
cx: Rc::new(RefCell::new(cx)),
foreground_platform,
+ condition_duration: None,
};
cx.cx.borrow_mut().weak_self = Some(Rc::downgrade(&cx.cx));
cx
@@ -377,11 +379,11 @@ impl TestAppContext {
if !cx.dispatch_keystroke(window_id, dispatch_path, &keystroke) {
presenter.borrow_mut().dispatch_event(
- Event::KeyDown {
+ Event::KeyDown(KeyDownEvent {
keystroke,
input,
is_held,
- },
+ }),
cx,
);
}
@@ -612,6 +614,27 @@ impl TestAppContext {
test_window
})
}
+
+ pub fn set_condition_duration(&mut self, duration: Duration) {
+ self.condition_duration = Some(duration);
+ }
+ pub fn condition_duration(&self) -> Duration {
+ self.condition_duration.unwrap_or_else(|| {
+ if std::env::var("CI").is_ok() {
+ Duration::from_secs(2)
+ } else {
+ Duration::from_millis(500)
+ }
+ })
+ }
+
+ pub fn assert_clipboard_content(&mut self, expected_content: Option<&str>) {
+ self.update(|cx| {
+ let actual_content = cx.read_from_clipboard().map(|item| item.text().to_owned());
+ let expected_content = expected_content.map(|content| content.to_owned());
+ assert_eq!(actual_content, expected_content);
+ })
+ }
}
impl AsyncAppContext {
@@ -1820,7 +1843,7 @@ impl MutableAppContext {
window.on_event(Box::new(move |event| {
app.update(|cx| {
if let Some(presenter) = presenter.upgrade() {
- if let Event::KeyDown { keystroke, .. } = &event {
+ if let Event::KeyDown(KeyDownEvent { keystroke, .. }) = &event {
if cx.dispatch_keystroke(
window_id,
presenter.borrow().dispatch_path(cx.as_ref()),
@@ -4424,6 +4447,7 @@ impl ViewHandle {
use postage::prelude::{Sink as _, Stream as _};
let (tx, mut rx) = postage::mpsc::channel(1024);
+ let timeout_duration = cx.condition_duration();
let mut cx = cx.cx.borrow_mut();
let subscriptions = self.update(&mut *cx, |_, cx| {
@@ -4445,14 +4469,9 @@ impl ViewHandle {
let cx = cx.weak_self.as_ref().unwrap().upgrade().unwrap();
let handle = self.downgrade();
- let duration = if std::env::var("CI").is_ok() {
- Duration::from_secs(2)
- } else {
- Duration::from_millis(500)
- };
async move {
- crate::util::timeout(duration, async move {
+ crate::util::timeout(timeout_duration, async move {
loop {
{
let cx = cx.borrow();
@@ -5381,7 +5400,7 @@ impl RefCounts {
#[cfg(test)]
mod tests {
use super::*;
- use crate::{actions, elements::*, impl_actions};
+ use crate::{actions, elements::*, impl_actions, MouseButton, MouseEvent};
use serde::Deserialize;
use smol::future::poll_once;
use std::{
@@ -5734,14 +5753,15 @@ mod tests {
let presenter = cx.presenters_and_platform_windows[&window_id].0.clone();
// Ensure window's root element is in a valid lifecycle state.
presenter.borrow_mut().dispatch_event(
- Event::LeftMouseDown {
+ Event::MouseDown(MouseEvent {
position: Default::default(),
+ button: MouseButton::Left,
ctrl: false,
alt: false,
shift: false,
cmd: false,
click_count: 1,
- },
+ }),
cx,
);
assert_eq!(mouse_down_count.load(SeqCst), 1);
diff --git a/crates/gpui/src/elements/event_handler.rs b/crates/gpui/src/elements/event_handler.rs
index 7144b21dd0f19ed42dd9403e40173fdec4cf7506..1fec838788e28217d94671877cae26439e03b467 100644
--- a/crates/gpui/src/elements/event_handler.rs
+++ b/crates/gpui/src/elements/event_handler.rs
@@ -1,6 +1,7 @@
use crate::{
geometry::vector::Vector2F, CursorRegion, DebugContext, Element, ElementBox, Event,
- EventContext, LayoutContext, MouseRegion, NavigationDirection, PaintContext, SizeConstraint,
+ EventContext, LayoutContext, MouseButton, MouseEvent, MouseRegion, NavigationDirection,
+ PaintContext, SizeConstraint,
};
use pathfinder_geometry::rect::RectF;
use serde_json::json;
@@ -90,7 +91,7 @@ impl Element for EventHandler {
click: Some(Rc::new(|_, _, _| {})),
right_mouse_down: Some(Rc::new(|_, _| {})),
right_click: Some(Rc::new(|_, _, _| {})),
- drag: Some(Rc::new(|_, _| {})),
+ drag: Some(Rc::new(|_, _, _| {})),
mouse_down_out: Some(Rc::new(|_, _| {})),
right_mouse_down_out: Some(Rc::new(|_, _| {})),
});
@@ -116,7 +117,11 @@ impl Element for EventHandler {
true
} else {
match event {
- Event::LeftMouseDown { position, .. } => {
+ Event::MouseDown(MouseEvent {
+ button: MouseButton::Left,
+ position,
+ ..
+ }) => {
if let Some(callback) = self.mouse_down.as_mut() {
if visible_bounds.contains_point(*position) {
return callback(cx);
@@ -124,7 +129,11 @@ impl Element for EventHandler {
}
false
}
- Event::RightMouseDown { position, .. } => {
+ Event::MouseDown(MouseEvent {
+ button: MouseButton::Right,
+ position,
+ ..
+ }) => {
if let Some(callback) = self.right_mouse_down.as_mut() {
if visible_bounds.contains_point(*position) {
return callback(cx);
@@ -132,11 +141,11 @@ impl Element for EventHandler {
}
false
}
- Event::NavigateMouseDown {
+ Event::MouseDown(MouseEvent {
+ button: MouseButton::Navigate(direction),
position,
- direction,
..
- } => {
+ }) => {
if let Some(callback) = self.navigate_mouse_down.as_mut() {
if visible_bounds.contains_point(*position) {
return callback(*direction, cx);
diff --git a/crates/gpui/src/elements/flex.rs b/crates/gpui/src/elements/flex.rs
index 82a6299d10d07537cca26e179902e2b5b9af4f84..cb43c1db68e87112b7f991171c00e72119c757bd 100644
--- a/crates/gpui/src/elements/flex.rs
+++ b/crates/gpui/src/elements/flex.rs
@@ -3,7 +3,8 @@ use std::{any::Any, f32::INFINITY};
use crate::{
json::{self, ToJson, Value},
Axis, DebugContext, Element, ElementBox, ElementStateHandle, Event, EventContext,
- LayoutContext, PaintContext, RenderContext, SizeConstraint, Vector2FExt, View,
+ LayoutContext, MouseMovedEvent, PaintContext, RenderContext, ScrollWheelEvent, SizeConstraint,
+ Vector2FExt, View,
};
use pathfinder_geometry::{
rect::RectF,
@@ -287,11 +288,11 @@ impl Element for Flex {
handled = child.dispatch_event(event, cx) || handled;
}
if !handled {
- if let &Event::ScrollWheel {
+ if let &Event::ScrollWheel(ScrollWheelEvent {
position,
delta,
precise,
- } = event
+ }) = event
{
if *remaining_space < 0. && bounds.contains_point(position) {
if let Some(scroll_state) = self.scroll_state.as_ref() {
@@ -321,7 +322,7 @@ impl Element for Flex {
}
if !handled {
- if let &Event::MouseMoved { position, .. } = event {
+ if let &Event::MouseMoved(MouseMovedEvent { position, .. }) = event {
// If this is a scrollable flex, and the mouse is over it, eat the scroll event to prevent
// propogating it to the element below.
if self.scroll_state.is_some() && bounds.contains_point(position) {
diff --git a/crates/gpui/src/elements/list.rs b/crates/gpui/src/elements/list.rs
index 6479f2ee283dac64c6b815f47b16131f05ec8796..e368b45288ce5490bb16ad121943b3d3ea5c4ab2 100644
--- a/crates/gpui/src/elements/list.rs
+++ b/crates/gpui/src/elements/list.rs
@@ -5,7 +5,7 @@ use crate::{
},
json::json,
DebugContext, Element, ElementBox, ElementRc, Event, EventContext, LayoutContext, PaintContext,
- RenderContext, SizeConstraint, View, ViewContext,
+ RenderContext, ScrollWheelEvent, SizeConstraint, View, ViewContext,
};
use std::{cell::RefCell, collections::VecDeque, ops::Range, rc::Rc};
use sum_tree::{Bias, SumTree};
@@ -311,11 +311,11 @@ impl Element for List {
state.items = new_items;
match event {
- Event::ScrollWheel {
+ Event::ScrollWheel(ScrollWheelEvent {
position,
delta,
precise,
- } => {
+ }) => {
if bounds.contains_point(*position) {
if state.scroll(scroll_top, bounds.height(), *delta, *precise, cx) {
handled = true;
diff --git a/crates/gpui/src/elements/mouse_event_handler.rs b/crates/gpui/src/elements/mouse_event_handler.rs
index 8f70daf9e6d695a5c91bc9e1ecbbe508e5b878ae..832aafaa9e8b890cd613027c982cd2ef841c266e 100644
--- a/crates/gpui/src/elements/mouse_event_handler.rs
+++ b/crates/gpui/src/elements/mouse_event_handler.rs
@@ -24,7 +24,7 @@ pub struct MouseEventHandler {
right_click: Option>,
mouse_down_out: Option>,
right_mouse_down_out: Option>,
- drag: Option>,
+ drag: Option>,
hover: Option>,
padding: Padding,
}
@@ -106,7 +106,10 @@ impl MouseEventHandler {
self
}
- pub fn on_drag(mut self, handler: impl Fn(Vector2F, &mut EventContext) + 'static) -> Self {
+ pub fn on_drag(
+ mut self,
+ handler: impl Fn(Vector2F, Vector2F, &mut EventContext) + 'static,
+ ) -> Self {
self.drag = Some(Rc::new(handler));
self
}
diff --git a/crates/gpui/src/elements/uniform_list.rs b/crates/gpui/src/elements/uniform_list.rs
index de217a017c7699bddad0d991fadca9cf72ba02a9..9b2d966a7d6cc44b39fee390747fe7d28f4265b8 100644
--- a/crates/gpui/src/elements/uniform_list.rs
+++ b/crates/gpui/src/elements/uniform_list.rs
@@ -5,7 +5,7 @@ use crate::{
vector::{vec2f, Vector2F},
},
json::{self, json},
- ElementBox, RenderContext, View,
+ ElementBox, RenderContext, ScrollWheelEvent, View,
};
use json::ToJson;
use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
@@ -310,11 +310,11 @@ impl Element for UniformList {
}
match event {
- Event::ScrollWheel {
+ Event::ScrollWheel(ScrollWheelEvent {
position,
delta,
precise,
- } => {
+ }) => {
if bounds.contains_point(*position) {
if self.scroll(*position, *delta, *precise, layout.scroll_max, cx) {
handled = true;
diff --git a/crates/gpui/src/gpui.rs b/crates/gpui/src/gpui.rs
index 5a1fc2fe143a32a15c13e1bea92389ef9b0a7898..723e25c55d62a55951a013255992167bbe6676bf 100644
--- a/crates/gpui/src/gpui.rs
+++ b/crates/gpui/src/gpui.rs
@@ -28,8 +28,7 @@ pub mod json;
pub mod keymap;
pub mod platform;
pub use gpui_macros::test;
-pub use platform::FontSystem;
-pub use platform::{Event, NavigationDirection, PathPromptOptions, Platform, PromptLevel};
+pub use platform::*;
pub use presenter::{
Axis, DebugContext, EventContext, LayoutContext, PaintContext, SizeConstraint, Vector2FExt,
};
diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs
index b4875df3f5708010172fc0447865fca7116faef5..cf508a5634a4341ee65573f6613fb93087d2bb6d 100644
--- a/crates/gpui/src/platform.rs
+++ b/crates/gpui/src/platform.rs
@@ -20,7 +20,7 @@ use crate::{
};
use anyhow::{anyhow, Result};
use async_task::Runnable;
-pub use event::{Event, NavigationDirection};
+pub use event::*;
use postage::oneshot;
use serde::Deserialize;
use std::{
diff --git a/crates/gpui/src/platform/event.rs b/crates/gpui/src/platform/event.rs
index f43d5bea49796b7dad579af8a0e4c137b9ddc6fe..90b5d21fc2f7e1f8ec9092c598ca4a4c58cde658 100644
--- a/crates/gpui/src/platform/event.rs
+++ b/crates/gpui/src/platform/event.rs
@@ -1,85 +1,77 @@
use crate::{geometry::vector::Vector2F, keymap::Keystroke};
+#[derive(Clone, Debug)]
+pub struct KeyDownEvent {
+ pub keystroke: Keystroke,
+ pub input: Option,
+ pub is_held: bool,
+}
+
+#[derive(Clone, Debug)]
+pub struct KeyUpEvent {
+ pub keystroke: Keystroke,
+ pub input: Option,
+}
+
+#[derive(Clone, Debug)]
+pub struct ModifiersChangedEvent {
+ pub ctrl: bool,
+ pub alt: bool,
+ pub shift: bool,
+ pub cmd: bool,
+}
+
+#[derive(Clone, Debug)]
+pub struct ScrollWheelEvent {
+ pub position: Vector2F,
+ pub delta: Vector2F,
+ pub precise: bool,
+}
+
#[derive(Copy, Clone, Debug)]
pub enum NavigationDirection {
Back,
Forward,
}
+#[derive(Copy, Clone, Debug)]
+pub enum MouseButton {
+ Left,
+ Right,
+ Middle,
+ Navigate(NavigationDirection),
+}
+
+#[derive(Clone, Debug)]
+pub struct MouseEvent {
+ pub button: MouseButton,
+ pub position: Vector2F,
+ pub ctrl: bool,
+ pub alt: bool,
+ pub shift: bool,
+ pub cmd: bool,
+ pub click_count: usize,
+}
+
+#[derive(Clone, Copy, Debug)]
+pub struct MouseMovedEvent {
+ pub position: Vector2F,
+ pub pressed_button: Option,
+ pub ctrl: bool,
+ pub cmd: bool,
+ pub alt: bool,
+ pub shift: bool,
+}
+
#[derive(Clone, Debug)]
pub enum Event {
- KeyDown {
- keystroke: Keystroke,
- input: Option,
- is_held: bool,
- },
- KeyUp {
- keystroke: Keystroke,
- input: Option,
- },
- ModifiersChanged {
- ctrl: bool,
- alt: bool,
- shift: bool,
- cmd: bool,
- },
- ScrollWheel {
- position: Vector2F,
- delta: Vector2F,
- precise: bool,
- },
- LeftMouseDown {
- position: Vector2F,
- ctrl: bool,
- alt: bool,
- shift: bool,
- cmd: bool,
- click_count: usize,
- },
- LeftMouseUp {
- position: Vector2F,
- click_count: usize,
- },
- LeftMouseDragged {
- position: Vector2F,
- ctrl: bool,
- alt: bool,
- shift: bool,
- cmd: bool,
- },
- RightMouseDown {
- position: Vector2F,
- ctrl: bool,
- alt: bool,
- shift: bool,
- cmd: bool,
- click_count: usize,
- },
- RightMouseUp {
- position: Vector2F,
- click_count: usize,
- },
- NavigateMouseDown {
- position: Vector2F,
- direction: NavigationDirection,
- ctrl: bool,
- alt: bool,
- shift: bool,
- cmd: bool,
- click_count: usize,
- },
- NavigateMouseUp {
- position: Vector2F,
- direction: NavigationDirection,
- },
- MouseMoved {
- position: Vector2F,
- left_mouse_down: bool,
- ctrl: bool,
- cmd: bool,
- alt: bool,
- shift: bool,
- },
+ KeyDown(KeyDownEvent),
+ KeyUp(KeyUpEvent),
+ ModifiersChanged(ModifiersChangedEvent),
+ MouseDown(MouseEvent),
+ MouseUp(MouseEvent),
+ MouseMoved(MouseMovedEvent),
+ ScrollWheel(ScrollWheelEvent),
}
impl Event {
@@ -88,15 +80,9 @@ impl Event {
Event::KeyDown { .. } => None,
Event::KeyUp { .. } => None,
Event::ModifiersChanged { .. } => None,
- Event::ScrollWheel { position, .. }
- | Event::LeftMouseDown { position, .. }
- | Event::LeftMouseUp { position, .. }
- | Event::LeftMouseDragged { position, .. }
- | Event::RightMouseDown { position, .. }
- | Event::RightMouseUp { position, .. }
- | Event::NavigateMouseDown { position, .. }
- | Event::NavigateMouseUp { position, .. }
- | Event::MouseMoved { position, .. } => Some(*position),
+ Event::MouseDown(event) | Event::MouseUp(event) => Some(event.position),
+ Event::MouseMoved(event) => Some(event.position),
+ Event::ScrollWheel(event) => Some(event.position),
}
}
}
diff --git a/crates/gpui/src/platform/mac/event.rs b/crates/gpui/src/platform/mac/event.rs
index ed880513e823a291e81b7d5cbf8a33418ed99c89..5e23859675cbc173254428f376f832207a2b3c00 100644
--- a/crates/gpui/src/platform/mac/event.rs
+++ b/crates/gpui/src/platform/mac/event.rs
@@ -2,10 +2,12 @@ use crate::{
geometry::vector::vec2f,
keymap::Keystroke,
platform::{Event, NavigationDirection},
+ KeyDownEvent, KeyUpEvent, ModifiersChangedEvent, MouseButton, MouseEvent, MouseMovedEvent,
+ ScrollWheelEvent,
};
use cocoa::{
appkit::{NSEvent, NSEventModifierFlags, NSEventType},
- base::{id, nil, YES},
+ base::{id, YES},
foundation::NSString as _,
};
use std::{borrow::Cow, ffi::CStr, os::raw::c_char};
@@ -59,12 +61,12 @@ impl Event {
let shift = modifiers.contains(NSEventModifierFlags::NSShiftKeyMask);
let cmd = modifiers.contains(NSEventModifierFlags::NSCommandKeyMask);
- Some(Self::ModifiersChanged {
+ Some(Self::ModifiersChanged(ModifiersChangedEvent {
ctrl,
alt,
shift,
cmd,
- })
+ }))
}
NSEventType::NSKeyDown => {
let modifiers = native_event.modifierFlags();
@@ -76,7 +78,7 @@ impl Event {
let (unmodified_chars, input) = get_key_text(native_event, cmd, ctrl, function)?;
- Some(Self::KeyDown {
+ Some(Self::KeyDown(KeyDownEvent {
keystroke: Keystroke {
ctrl,
alt,
@@ -86,7 +88,7 @@ impl Event {
},
input,
is_held: native_event.isARepeat() == YES,
- })
+ }))
}
NSEventType::NSKeyUp => {
let modifiers = native_event.modifierFlags();
@@ -98,7 +100,7 @@ impl Event {
let (unmodified_chars, input) = get_key_text(native_event, cmd, ctrl, function)?;
- Some(Self::KeyUp {
+ Some(Self::KeyUp(KeyUpEvent {
keystroke: Keystroke {
ctrl,
alt,
@@ -107,125 +109,120 @@ impl Event {
key: unmodified_chars.into(),
},
input,
- })
- }
- NSEventType::NSLeftMouseDown => {
- let modifiers = native_event.modifierFlags();
- window_height.map(|window_height| Self::LeftMouseDown {
- position: vec2f(
- native_event.locationInWindow().x as f32,
- window_height - native_event.locationInWindow().y as f32,
- ),
- ctrl: modifiers.contains(NSEventModifierFlags::NSControlKeyMask),
- alt: modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask),
- shift: modifiers.contains(NSEventModifierFlags::NSShiftKeyMask),
- cmd: modifiers.contains(NSEventModifierFlags::NSCommandKeyMask),
- click_count: native_event.clickCount() as usize,
- })
+ }))
}
- NSEventType::NSLeftMouseUp => window_height.map(|window_height| Self::LeftMouseUp {
- position: vec2f(
- native_event.locationInWindow().x as f32,
- window_height - native_event.locationInWindow().y as f32,
- ),
- click_count: native_event.clickCount() as usize,
- }),
- NSEventType::NSRightMouseDown => {
+ NSEventType::NSLeftMouseDown
+ | NSEventType::NSRightMouseDown
+ | NSEventType::NSOtherMouseDown => {
+ let button = match native_event.buttonNumber() {
+ 0 => MouseButton::Left,
+ 1 => MouseButton::Right,
+ 2 => MouseButton::Middle,
+ 3 => MouseButton::Navigate(NavigationDirection::Back),
+ 4 => MouseButton::Navigate(NavigationDirection::Forward),
+ // Other mouse buttons aren't tracked currently
+ _ => return None,
+ };
let modifiers = native_event.modifierFlags();
- window_height.map(|window_height| Self::RightMouseDown {
- position: vec2f(
- native_event.locationInWindow().x as f32,
- window_height - native_event.locationInWindow().y as f32,
- ),
- ctrl: modifiers.contains(NSEventModifierFlags::NSControlKeyMask),
- alt: modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask),
- shift: modifiers.contains(NSEventModifierFlags::NSShiftKeyMask),
- cmd: modifiers.contains(NSEventModifierFlags::NSCommandKeyMask),
- click_count: native_event.clickCount() as usize,
+
+ window_height.map(|window_height| {
+ Self::MouseDown(MouseEvent {
+ button,
+ position: vec2f(
+ native_event.locationInWindow().x as f32,
+ window_height - native_event.locationInWindow().y as f32,
+ ),
+ ctrl: modifiers.contains(NSEventModifierFlags::NSControlKeyMask),
+ alt: modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask),
+ shift: modifiers.contains(NSEventModifierFlags::NSShiftKeyMask),
+ cmd: modifiers.contains(NSEventModifierFlags::NSCommandKeyMask),
+ click_count: native_event.clickCount() as usize,
+ })
})
}
- NSEventType::NSRightMouseUp => window_height.map(|window_height| Self::RightMouseUp {
- position: vec2f(
- native_event.locationInWindow().x as f32,
- window_height - native_event.locationInWindow().y as f32,
- ),
- click_count: native_event.clickCount() as usize,
- }),
- NSEventType::NSOtherMouseDown => {
- let direction = match native_event.buttonNumber() {
- 3 => NavigationDirection::Back,
- 4 => NavigationDirection::Forward,
+ NSEventType::NSLeftMouseUp
+ | NSEventType::NSRightMouseUp
+ | NSEventType::NSOtherMouseUp => {
+ let button = match native_event.buttonNumber() {
+ 0 => MouseButton::Left,
+ 1 => MouseButton::Right,
+ 2 => MouseButton::Middle,
+ 3 => MouseButton::Navigate(NavigationDirection::Back),
+ 4 => MouseButton::Navigate(NavigationDirection::Forward),
// Other mouse buttons aren't tracked currently
_ => return None,
};
- let modifiers = native_event.modifierFlags();
- window_height.map(|window_height| Self::NavigateMouseDown {
+ window_height.map(|window_height| {
+ let modifiers = native_event.modifierFlags();
+ Self::MouseUp(MouseEvent {
+ button,
+ position: vec2f(
+ native_event.locationInWindow().x as f32,
+ window_height - native_event.locationInWindow().y as f32,
+ ),
+ ctrl: modifiers.contains(NSEventModifierFlags::NSControlKeyMask),
+ alt: modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask),
+ shift: modifiers.contains(NSEventModifierFlags::NSShiftKeyMask),
+ cmd: modifiers.contains(NSEventModifierFlags::NSCommandKeyMask),
+ click_count: native_event.clickCount() as usize,
+ })
+ })
+ }
+ NSEventType::NSScrollWheel => window_height.map(|window_height| {
+ Self::ScrollWheel(ScrollWheelEvent {
position: vec2f(
native_event.locationInWindow().x as f32,
window_height - native_event.locationInWindow().y as f32,
),
- direction,
- ctrl: modifiers.contains(NSEventModifierFlags::NSControlKeyMask),
- alt: modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask),
- shift: modifiers.contains(NSEventModifierFlags::NSShiftKeyMask),
- cmd: modifiers.contains(NSEventModifierFlags::NSCommandKeyMask),
- click_count: native_event.clickCount() as usize,
+ delta: vec2f(
+ native_event.scrollingDeltaX() as f32,
+ native_event.scrollingDeltaY() as f32,
+ ),
+ precise: native_event.hasPreciseScrollingDeltas() == YES,
})
- }
- NSEventType::NSOtherMouseUp => {
- let direction = match native_event.buttonNumber() {
- 3 => NavigationDirection::Back,
- 4 => NavigationDirection::Forward,
+ }),
+ NSEventType::NSLeftMouseDragged
+ | NSEventType::NSRightMouseDragged
+ | NSEventType::NSOtherMouseDragged => {
+ let pressed_button = match native_event.buttonNumber() {
+ 0 => MouseButton::Left,
+ 1 => MouseButton::Right,
+ 2 => MouseButton::Middle,
+ 3 => MouseButton::Navigate(NavigationDirection::Back),
+ 4 => MouseButton::Navigate(NavigationDirection::Forward),
// Other mouse buttons aren't tracked currently
_ => return None,
};
- window_height.map(|window_height| Self::NavigateMouseUp {
- position: vec2f(
- native_event.locationInWindow().x as f32,
- window_height - native_event.locationInWindow().y as f32,
- ),
- direction,
+ window_height.map(|window_height| {
+ let modifiers = native_event.modifierFlags();
+ Self::MouseMoved(MouseMovedEvent {
+ pressed_button: Some(pressed_button),
+ position: vec2f(
+ native_event.locationInWindow().x as f32,
+ window_height - native_event.locationInWindow().y as f32,
+ ),
+ ctrl: modifiers.contains(NSEventModifierFlags::NSControlKeyMask),
+ alt: modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask),
+ shift: modifiers.contains(NSEventModifierFlags::NSShiftKeyMask),
+ cmd: modifiers.contains(NSEventModifierFlags::NSCommandKeyMask),
+ })
})
}
- NSEventType::NSLeftMouseDragged => window_height.map(|window_height| {
- let modifiers = native_event.modifierFlags();
- Self::LeftMouseDragged {
- position: vec2f(
- native_event.locationInWindow().x as f32,
- window_height - native_event.locationInWindow().y as f32,
- ),
- ctrl: modifiers.contains(NSEventModifierFlags::NSControlKeyMask),
- alt: modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask),
- shift: modifiers.contains(NSEventModifierFlags::NSShiftKeyMask),
- cmd: modifiers.contains(NSEventModifierFlags::NSCommandKeyMask),
- }
- }),
- NSEventType::NSScrollWheel => window_height.map(|window_height| Self::ScrollWheel {
- position: vec2f(
- native_event.locationInWindow().x as f32,
- window_height - native_event.locationInWindow().y as f32,
- ),
- delta: vec2f(
- native_event.scrollingDeltaX() as f32,
- native_event.scrollingDeltaY() as f32,
- ),
- precise: native_event.hasPreciseScrollingDeltas() == YES,
- }),
NSEventType::NSMouseMoved => window_height.map(|window_height| {
let modifiers = native_event.modifierFlags();
- Self::MouseMoved {
+ Self::MouseMoved(MouseMovedEvent {
position: vec2f(
native_event.locationInWindow().x as f32,
window_height - native_event.locationInWindow().y as f32,
),
- left_mouse_down: NSEvent::pressedMouseButtons(nil) & 1 != 0,
+ pressed_button: None,
ctrl: modifiers.contains(NSEventModifierFlags::NSControlKeyMask),
alt: modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask),
shift: modifiers.contains(NSEventModifierFlags::NSShiftKeyMask),
cmd: modifiers.contains(NSEventModifierFlags::NSCommandKeyMask),
- }
+ })
}),
_ => None,
}
diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs
index c845693ba996af356a04e5de77c93cbe4639944d..5e6b3b9c190fa6ff16a2cfee0c245a35fea0b444 100644
--- a/crates/gpui/src/platform/mac/window.rs
+++ b/crates/gpui/src/platform/mac/window.rs
@@ -6,7 +6,7 @@ use crate::{
},
keymap::Keystroke,
platform::{self, Event, WindowBounds, WindowContext},
- Scene,
+ KeyDownEvent, ModifiersChangedEvent, MouseButton, MouseEvent, MouseMovedEvent, Scene,
};
use block::ConcreteBlock;
use cocoa::{
@@ -562,11 +562,11 @@ extern "C" fn handle_key_equivalent(this: &Object, _: Sel, native_event: id) ->
let event = unsafe { Event::from_native(native_event, Some(window_state_borrow.size().y())) };
if let Some(event) = event {
match &event {
- Event::KeyDown {
+ Event::KeyDown(KeyDownEvent {
keystroke,
input,
is_held,
- } => {
+ }) => {
let keydown = (keystroke.clone(), input.clone());
// Ignore events from held-down keys after some of the initially-pressed keys
// were released.
@@ -603,33 +603,41 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
if let Some(event) = event {
match &event {
- Event::LeftMouseDragged { position, .. } => {
+ Event::MouseMoved(
+ event @ MouseMovedEvent {
+ pressed_button: Some(_),
+ ..
+ },
+ ) => {
window_state_borrow.synthetic_drag_counter += 1;
window_state_borrow
.executor
.spawn(synthetic_drag(
weak_window_state,
window_state_borrow.synthetic_drag_counter,
- *position,
+ *event,
))
.detach();
}
- Event::LeftMouseUp { .. } => {
+ Event::MouseUp(MouseEvent {
+ button: MouseButton::Left,
+ ..
+ }) => {
window_state_borrow.synthetic_drag_counter += 1;
}
- Event::ModifiersChanged {
+ Event::ModifiersChanged(ModifiersChangedEvent {
ctrl,
alt,
shift,
cmd,
- } => {
+ }) => {
// Only raise modifiers changed event when they have actually changed
- if let Some(Event::ModifiersChanged {
+ if let Some(Event::ModifiersChanged(ModifiersChangedEvent {
ctrl: prev_ctrl,
alt: prev_alt,
shift: prev_shift,
cmd: prev_cmd,
- }) = &window_state_borrow.previous_modifiers_changed_event
+ })) = &window_state_borrow.previous_modifiers_changed_event
{
if prev_ctrl == ctrl
&& prev_alt == alt
@@ -667,11 +675,11 @@ extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
shift: false,
key: chars.clone(),
};
- let event = Event::KeyDown {
+ let event = Event::KeyDown(KeyDownEvent {
keystroke: keystroke.clone(),
input: Some(chars.clone()),
is_held: false,
- };
+ });
window_state_borrow.last_fresh_keydown = Some((keystroke, Some(chars)));
if let Some(mut callback) = window_state_borrow.event_callback.take() {
@@ -835,7 +843,7 @@ extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
async fn synthetic_drag(
window_state: Weak>,
drag_id: usize,
- position: Vector2F,
+ event: MouseMovedEvent,
) {
loop {
Timer::after(Duration::from_millis(16)).await;
@@ -844,14 +852,7 @@ async fn synthetic_drag(
if window_state_borrow.synthetic_drag_counter == drag_id {
if let Some(mut callback) = window_state_borrow.event_callback.take() {
drop(window_state_borrow);
- callback(Event::LeftMouseDragged {
- // TODO: Make sure empty modifiers is correct for this
- position,
- shift: false,
- ctrl: false,
- alt: false,
- cmd: false,
- });
+ callback(Event::MouseMoved(event));
window_state.borrow_mut().event_callback = Some(callback);
}
} else {
diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs
index 6285b1be9931b30ada4d4673681ce335398bf252..86a8c4cf3049e924cfcea798ae2ff703ea6132d8 100644
--- a/crates/gpui/src/presenter.rs
+++ b/crates/gpui/src/presenter.rs
@@ -9,9 +9,9 @@ use crate::{
scene::CursorRegion,
text_layout::TextLayoutCache,
Action, AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, AssetCache, ElementBox, Entity,
- FontSystem, ModelHandle, MouseRegion, MouseRegionId, ReadModel, ReadView, RenderContext,
- RenderParams, Scene, UpgradeModelHandle, UpgradeViewHandle, View, ViewHandle, WeakModelHandle,
- WeakViewHandle,
+ FontSystem, ModelHandle, MouseButton, MouseEvent, MouseMovedEvent, MouseRegion, MouseRegionId,
+ ReadModel, ReadView, RenderContext, RenderParams, Scene, UpgradeModelHandle, UpgradeViewHandle,
+ View, ViewHandle, WeakModelHandle, WeakViewHandle,
};
use pathfinder_geometry::vector::{vec2f, Vector2F};
use serde_json::json;
@@ -235,7 +235,11 @@ impl Presenter {
let mut dragged_region = None;
match event {
- Event::LeftMouseDown { position, .. } => {
+ Event::MouseDown(MouseEvent {
+ position,
+ button: MouseButton::Left,
+ ..
+ }) => {
let mut hit = false;
for (region, _) in self.mouse_regions.iter().rev() {
if region.bounds.contains_point(position) {
@@ -251,11 +255,12 @@ impl Presenter {
}
}
}
- Event::LeftMouseUp {
+ Event::MouseUp(MouseEvent {
position,
click_count,
+ button: MouseButton::Left,
..
- } => {
+ }) => {
self.prev_drag_position.take();
if let Some(region) = self.clicked_region.take() {
invalidated_views.push(region.view_id);
@@ -264,7 +269,11 @@ impl Presenter {
}
}
}
- Event::RightMouseDown { position, .. } => {
+ Event::MouseDown(MouseEvent {
+ position,
+ button: MouseButton::Right,
+ ..
+ }) => {
let mut hit = false;
for (region, _) in self.mouse_regions.iter().rev() {
if region.bounds.contains_point(position) {
@@ -279,11 +288,12 @@ impl Presenter {
}
}
}
- Event::RightMouseUp {
+ Event::MouseUp(MouseEvent {
position,
click_count,
+ button: MouseButton::Right,
..
- } => {
+ }) => {
if let Some(region) = self.right_clicked_region.take() {
invalidated_views.push(region.view_id);
if region.bounds.contains_point(position) {
@@ -291,34 +301,37 @@ impl Presenter {
}
}
}
- Event::MouseMoved { .. } => {
- self.last_mouse_moved_event = Some(event.clone());
- }
- Event::LeftMouseDragged {
+ Event::MouseMoved(MouseMovedEvent {
+ pressed_button,
position,
shift,
ctrl,
alt,
cmd,
- } => {
- if let Some((clicked_region, prev_drag_position)) = self
- .clicked_region
- .as_ref()
- .zip(self.prev_drag_position.as_mut())
- {
- dragged_region =
- Some((clicked_region.clone(), position - *prev_drag_position));
- *prev_drag_position = position;
+ ..
+ }) => {
+ if let Some(MouseButton::Left) = pressed_button {
+ if let Some((clicked_region, prev_drag_position)) = self
+ .clicked_region
+ .as_ref()
+ .zip(self.prev_drag_position.as_mut())
+ {
+ dragged_region =
+ Some((clicked_region.clone(), *prev_drag_position, position));
+ *prev_drag_position = position;
+ }
+
+ self.last_mouse_moved_event = Some(Event::MouseMoved(MouseMovedEvent {
+ position,
+ pressed_button: Some(MouseButton::Left),
+ shift,
+ ctrl,
+ alt,
+ cmd,
+ }));
}
- self.last_mouse_moved_event = Some(Event::MouseMoved {
- position,
- left_mouse_down: true,
- shift,
- ctrl,
- alt,
- cmd,
- });
+ self.last_mouse_moved_event = Some(event.clone());
}
_ => {}
}
@@ -366,11 +379,11 @@ impl Presenter {
}
}
- if let Some((dragged_region, delta)) = dragged_region {
+ if let Some((dragged_region, prev_position, position)) = dragged_region {
handled = true;
if let Some(drag_callback) = dragged_region.drag {
event_cx.with_current_view(dragged_region.view_id, |event_cx| {
- drag_callback(delta, event_cx);
+ drag_callback(prev_position, position, event_cx);
})
}
}
@@ -410,13 +423,13 @@ impl Presenter {
let mut unhovered_regions = Vec::new();
let mut hovered_regions = Vec::new();
- if let Event::MouseMoved {
+ if let Event::MouseMoved(MouseMovedEvent {
position,
- left_mouse_down,
+ pressed_button,
..
- } = event
+ }) = event
{
- if !left_mouse_down {
+ if let None = pressed_button {
let mut style_to_assign = CursorStyle::Arrow;
for region in self.cursor_regions.iter().rev() {
if region.bounds.contains_point(*position) {
@@ -648,6 +661,16 @@ impl<'a> PaintContext<'a> {
}
}
+ #[inline]
+ pub fn paint_layer(&mut self, clip_bounds: Option, f: F)
+ where
+ F: FnOnce(&mut Self) -> (),
+ {
+ self.scene.push_layer(clip_bounds);
+ f(self);
+ self.scene.pop_layer();
+ }
+
pub fn current_view_id(&self) -> usize {
*self.view_stack.last().unwrap()
}
diff --git a/crates/gpui/src/scene.rs b/crates/gpui/src/scene.rs
index 1f0e2c0ecc0e3921f7a37625a5b32457e923fef9..769eabe7e58372e13628d316381df24e0b0a2044 100644
--- a/crates/gpui/src/scene.rs
+++ b/crates/gpui/src/scene.rs
@@ -8,7 +8,7 @@ use crate::{
geometry::{rect::RectF, vector::Vector2F},
json::ToJson,
platform::CursorStyle,
- EventContext, ImageData,
+ EventContext, ImageData, MouseEvent, MouseMovedEvent, ScrollWheelEvent,
};
pub struct Scene {
@@ -44,17 +44,28 @@ pub struct CursorRegion {
pub style: CursorStyle,
}
+pub enum MouseRegionEvent {
+ Moved(MouseMovedEvent),
+ Hover(MouseEvent),
+ Down(MouseEvent),
+ Up(MouseEvent),
+ Click(MouseEvent),
+ DownOut(MouseEvent),
+ ScrollWheel(ScrollWheelEvent),
+}
+
#[derive(Clone, Default)]
pub struct MouseRegion {
pub view_id: usize,
pub discriminant: Option<(TypeId, usize)>,
pub bounds: RectF,
+
pub hover: Option>,
pub mouse_down: Option>,
pub click: Option>,
pub right_mouse_down: Option>,
pub right_click: Option>,
- pub drag: Option>,
+ pub drag: Option>,
pub mouse_down_out: Option>,
pub right_mouse_down_out: Option>,
}
diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs
index cebf5f504ea8c634a38155e5c5654362fb345b12..d5ed1c1620da0b514ba22f4c04e050f5043c67fb 100644
--- a/crates/language/src/buffer.rs
+++ b/crates/language/src/buffer.rs
@@ -273,7 +273,7 @@ pub struct Chunk<'a> {
pub is_unnecessary: bool,
}
-pub(crate) struct Diff {
+pub struct Diff {
base_version: clock::Global,
new_text: Arc,
changes: Vec<(ChangeTag, usize)>,
@@ -958,7 +958,7 @@ impl Buffer {
}
}
- pub(crate) fn diff(&self, mut new_text: String, cx: &AppContext) -> Task {
+ pub fn diff(&self, mut new_text: String, cx: &AppContext) -> Task {
let old_text = self.as_rope().clone();
let base_version = self.version();
cx.background().spawn(async move {
@@ -979,11 +979,7 @@ impl Buffer {
})
}
- pub(crate) fn apply_diff(
- &mut self,
- diff: Diff,
- cx: &mut ModelContext,
- ) -> Option<&Transaction> {
+ pub fn apply_diff(&mut self, diff: Diff, cx: &mut ModelContext) -> Option<&Transaction> {
if self.version == diff.base_version {
self.finalize_last_transaction();
self.start_transaction();
diff --git a/crates/project/src/fs.rs b/crates/project/src/fs.rs
index 17d7264f1d80853a02707ccdff6308feeff178e9..5c528016118fdde2126f512170e5c46e6be9e171 100644
--- a/crates/project/src/fs.rs
+++ b/crates/project/src/fs.rs
@@ -334,28 +334,6 @@ impl FakeFs {
})
}
- pub async fn insert_dir(&self, path: impl AsRef) {
- let mut state = self.state.lock().await;
- let path = path.as_ref();
- state.validate_path(path).unwrap();
-
- let inode = state.next_inode;
- state.next_inode += 1;
- state.entries.insert(
- path.to_path_buf(),
- FakeFsEntry {
- metadata: Metadata {
- inode,
- mtime: SystemTime::now(),
- is_dir: true,
- is_symlink: false,
- },
- content: None,
- },
- );
- state.emit_event(&[path]).await;
- }
-
pub async fn insert_file(&self, path: impl AsRef, content: String) {
let mut state = self.state.lock().await;
let path = path.as_ref();
@@ -392,7 +370,7 @@ impl FakeFs {
match tree {
Object(map) => {
- self.insert_dir(path).await;
+ self.create_dir(path).await.unwrap();
for (name, contents) in map {
let mut path = PathBuf::from(path);
path.push(name);
@@ -400,7 +378,7 @@ impl FakeFs {
}
}
Null => {
- self.insert_dir(&path).await;
+ self.create_dir(&path).await.unwrap();
}
String(contents) => {
self.insert_file(&path, contents).await;
diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs
index e4425e341476dfb4a316bfdc9df5b010e97e0964..0ac3064e56b33840130d0e43a98d12d8908f4476 100644
--- a/crates/project/src/project.rs
+++ b/crates/project/src/project.rs
@@ -12,7 +12,7 @@ use anyhow::{anyhow, Context, Result};
use client::{proto, Client, PeerId, TypedEnvelope, User, UserStore};
use clock::ReplicaId;
use collections::{hash_map, BTreeMap, HashMap, HashSet};
-use futures::{future::Shared, Future, FutureExt, StreamExt, TryFutureExt};
+use futures::{future::Shared, AsyncWriteExt, Future, FutureExt, StreamExt, TryFutureExt};
use fuzzy::{PathMatch, PathMatchCandidate, PathMatchCandidateSet};
use gpui::{
AnyModelHandle, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle,
@@ -51,10 +51,12 @@ use std::{
ffi::OsString,
hash::Hash,
mem,
+ num::NonZeroU32,
ops::Range,
os::unix::{ffi::OsStrExt, prelude::OsStringExt},
path::{Component, Path, PathBuf},
rc::Rc,
+ str,
sync::{
atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst},
Arc,
@@ -3025,78 +3027,50 @@ impl Project {
}
for (buffer, buffer_abs_path, language_server) in local_buffers {
- let text_document = lsp::TextDocumentIdentifier::new(
- lsp::Url::from_file_path(&buffer_abs_path).unwrap(),
- );
- let capabilities = &language_server.capabilities();
- let tab_size = cx.update(|cx| {
- let language_name = buffer.read(cx).language().map(|language| language.name());
- cx.global::().tab_size(language_name.as_deref())
+ let (format_on_save, tab_size) = buffer.read_with(&cx, |buffer, cx| {
+ let settings = cx.global::();
+ let language_name = buffer.language().map(|language| language.name());
+ (
+ settings.format_on_save(language_name.as_deref()),
+ settings.tab_size(language_name.as_deref()),
+ )
});
- let lsp_edits = if capabilities
- .document_formatting_provider
- .as_ref()
- .map_or(false, |provider| *provider != lsp::OneOf::Left(false))
- {
- language_server
- .request::(lsp::DocumentFormattingParams {
- text_document,
- options: lsp::FormattingOptions {
- tab_size: tab_size.into(),
- insert_spaces: true,
- insert_final_newline: Some(true),
- ..Default::default()
- },
- work_done_progress_params: Default::default(),
- })
- .await?
- } else if capabilities
- .document_range_formatting_provider
- .as_ref()
- .map_or(false, |provider| *provider != lsp::OneOf::Left(false))
- {
- let buffer_start = lsp::Position::new(0, 0);
- let buffer_end =
- buffer.read_with(&cx, |buffer, _| point_to_lsp(buffer.max_point_utf16()));
- language_server
- .request::(
- lsp::DocumentRangeFormattingParams {
- text_document,
- range: lsp::Range::new(buffer_start, buffer_end),
- options: lsp::FormattingOptions {
- tab_size: tab_size.into(),
- insert_spaces: true,
- insert_final_newline: Some(true),
- ..Default::default()
- },
- work_done_progress_params: Default::default(),
- },
+
+ let transaction = match format_on_save {
+ settings::FormatOnSave::Off => continue,
+ settings::FormatOnSave::LanguageServer => Self::format_via_lsp(
+ &this,
+ &buffer,
+ &buffer_abs_path,
+ &language_server,
+ tab_size,
+ &mut cx,
+ )
+ .await
+ .context("failed to format via language server")?,
+ settings::FormatOnSave::External { command, arguments } => {
+ Self::format_via_external_command(
+ &buffer,
+ &buffer_abs_path,
+ &command,
+ &arguments,
+ &mut cx,
)
- .await?
- } else {
- continue;
+ .await
+ .context(format!(
+ "failed to format via external command {:?}",
+ command
+ ))?
+ }
};
- if let Some(lsp_edits) = lsp_edits {
- let edits = this
- .update(&mut cx, |this, cx| {
- this.edits_from_lsp(&buffer, lsp_edits, None, cx)
- })
- .await?;
- buffer.update(&mut cx, |buffer, cx| {
- buffer.finalize_last_transaction();
- buffer.start_transaction();
- for (range, text) in edits {
- buffer.edit([(range, text)], cx);
- }
- if buffer.end_transaction(cx).is_some() {
- let transaction = buffer.finalize_last_transaction().unwrap().clone();
- if !push_to_history {
- buffer.forget_transaction(transaction.id);
- }
- project_transaction.0.insert(cx.handle(), transaction);
- }
- });
+ if let Some(transaction) = transaction {
+ if !push_to_history {
+ buffer.update(&mut cx, |buffer, _| {
+ buffer.forget_transaction(transaction.id)
+ });
+ }
+ project_transaction.0.insert(buffer, transaction);
}
}
@@ -3104,6 +3078,141 @@ impl Project {
})
}
+ async fn format_via_lsp(
+ this: &ModelHandle,
+ buffer: &ModelHandle,
+ abs_path: &Path,
+ language_server: &Arc,
+ tab_size: NonZeroU32,
+ cx: &mut AsyncAppContext,
+ ) -> Result