mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-08 07:35:01 +03:00
Merge branch 'main' into multibuffer-following
This commit is contained in:
commit
11800a8a78
@ -139,9 +139,7 @@ impl<V: View> DragAndDrop<V> {
|
||||
region_offset,
|
||||
region,
|
||||
}) => {
|
||||
if (dbg!(event.position) - (dbg!(region.origin() + region_offset))).length()
|
||||
> DEAD_ZONE
|
||||
{
|
||||
if (event.position - (region.origin() + region_offset)).length() > DEAD_ZONE {
|
||||
this.currently_dragged = Some(State::Dragging {
|
||||
window_id,
|
||||
region_offset,
|
||||
|
@ -184,7 +184,6 @@ actions!(
|
||||
Paste,
|
||||
Undo,
|
||||
Redo,
|
||||
NextScreen,
|
||||
MoveUp,
|
||||
PageUp,
|
||||
MoveDown,
|
||||
|
@ -4991,9 +4991,11 @@ async fn test_following(cx: &mut gpui::TestAppContext) {
|
||||
)
|
||||
});
|
||||
|
||||
let is_still_following = Rc::new(RefCell::new(true));
|
||||
let pending_update = Rc::new(RefCell::new(None));
|
||||
follower.update(cx, {
|
||||
let update = pending_update.clone();
|
||||
let is_still_following = is_still_following.clone();
|
||||
|_, cx| {
|
||||
cx.subscribe(&leader, move |_, leader, event, cx| {
|
||||
leader
|
||||
@ -5001,6 +5003,13 @@ async fn test_following(cx: &mut gpui::TestAppContext) {
|
||||
.add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
|
||||
})
|
||||
.detach();
|
||||
|
||||
cx.subscribe(&follower, move |_, _, event, cx| {
|
||||
if Editor::should_unfollow_on_event(event, cx) {
|
||||
*is_still_following.borrow_mut() = false;
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
});
|
||||
|
||||
@ -5017,6 +5026,7 @@ async fn test_following(cx: &mut gpui::TestAppContext) {
|
||||
follower.read_with(cx, |follower, cx| {
|
||||
assert_eq!(follower.selections.ranges(cx), vec![1..1]);
|
||||
});
|
||||
assert_eq!(*is_still_following.borrow(), true);
|
||||
|
||||
// Update the scroll position only
|
||||
leader.update(cx, |leader, cx| {
|
||||
@ -5032,6 +5042,7 @@ async fn test_following(cx: &mut gpui::TestAppContext) {
|
||||
follower.update(cx, |follower, cx| follower.scroll_position(cx)),
|
||||
vec2f(1.5, 3.5)
|
||||
);
|
||||
assert_eq!(*is_still_following.borrow(), true);
|
||||
|
||||
// Update the selections and scroll position. The follower's scroll position is updated
|
||||
// via autoscroll, not via the leader's exact scroll position.
|
||||
@ -5050,6 +5061,7 @@ async fn test_following(cx: &mut gpui::TestAppContext) {
|
||||
assert_eq!(follower.scroll_position(cx), vec2f(1.5, 0.0));
|
||||
assert_eq!(follower.selections.ranges(cx), vec![0..0]);
|
||||
});
|
||||
assert_eq!(*is_still_following.borrow(), true);
|
||||
|
||||
// Creating a pending selection that precedes another selection
|
||||
leader.update(cx, |leader, cx| {
|
||||
@ -5065,6 +5077,7 @@ async fn test_following(cx: &mut gpui::TestAppContext) {
|
||||
follower.read_with(cx, |follower, cx| {
|
||||
assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
|
||||
});
|
||||
assert_eq!(*is_still_following.borrow(), true);
|
||||
|
||||
// Extend the pending selection so that it surrounds another selection
|
||||
leader.update(cx, |leader, cx| {
|
||||
@ -5079,6 +5092,19 @@ async fn test_following(cx: &mut gpui::TestAppContext) {
|
||||
follower.read_with(cx, |follower, cx| {
|
||||
assert_eq!(follower.selections.ranges(cx), vec![0..2]);
|
||||
});
|
||||
|
||||
// Scrolling locally breaks the follow
|
||||
follower.update(cx, |follower, cx| {
|
||||
let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
|
||||
follower.set_scroll_anchor(
|
||||
ScrollAnchor {
|
||||
top_anchor,
|
||||
offset: vec2f(0.0, 0.5),
|
||||
},
|
||||
cx,
|
||||
);
|
||||
});
|
||||
assert_eq!(*is_still_following.borrow(), false);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
|
@ -138,7 +138,7 @@ impl FollowableItem for Editor {
|
||||
}
|
||||
|
||||
if let Some(scroll_top_anchor) = scroll_top_anchor {
|
||||
editor.set_scroll_anchor(
|
||||
editor.set_scroll_anchor_remote(
|
||||
ScrollAnchor {
|
||||
top_anchor: scroll_top_anchor,
|
||||
offset: vec2f(state.scroll_x, state.scroll_y),
|
||||
@ -363,7 +363,10 @@ impl FollowableItem for Editor {
|
||||
this.set_selections_from_remote(selections, cx);
|
||||
this.request_autoscroll_remotely(Autoscroll::newest(), cx);
|
||||
} else if let Some(anchor) = scroll_top_anchor {
|
||||
this.set_scroll_anchor(ScrollAnchor {top_anchor: anchor, offset: vec2f(message.scroll_x, message.scroll_y) }, cx);
|
||||
this.set_scroll_anchor_remote(ScrollAnchor {
|
||||
top_anchor: anchor,
|
||||
offset: vec2f(message.scroll_x, message.scroll_y)
|
||||
}, cx);
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
|
@ -284,17 +284,17 @@ impl Editor {
|
||||
}
|
||||
|
||||
pub fn set_scroll_anchor(&mut self, scroll_anchor: ScrollAnchor, cx: &mut ViewContext<Self>) {
|
||||
self.set_scroll_anchor_internal(scroll_anchor, true, cx);
|
||||
hide_hover(self, cx);
|
||||
self.scroll_manager.set_anchor(scroll_anchor, true, cx);
|
||||
}
|
||||
|
||||
pub(crate) fn set_scroll_anchor_internal(
|
||||
pub(crate) fn set_scroll_anchor_remote(
|
||||
&mut self,
|
||||
scroll_anchor: ScrollAnchor,
|
||||
local: bool,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
hide_hover(self, cx);
|
||||
self.scroll_manager.set_anchor(scroll_anchor, local, cx);
|
||||
self.scroll_manager.set_anchor(scroll_anchor, false, cx);
|
||||
}
|
||||
|
||||
pub fn scroll_screen(&mut self, amount: &ScrollAmount, cx: &mut ViewContext<Self>) {
|
||||
|
@ -64,15 +64,15 @@ impl Editor {
|
||||
return None;
|
||||
}
|
||||
|
||||
self.context_menu.as_mut()?;
|
||||
if self.mouse_context_menu.read(cx).visible() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if matches!(self.mode, EditorMode::SingleLine) {
|
||||
cx.propagate_action();
|
||||
return None;
|
||||
}
|
||||
|
||||
self.request_autoscroll(Autoscroll::Next, cx);
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ use anyhow::{anyhow, bail, Context, Result};
|
||||
use db::{define_connection, query, sqlez::connection::Connection, sqlez_macros::sql};
|
||||
use gpui::Axis;
|
||||
|
||||
use util::{iife, unzip_option, ResultExt};
|
||||
use util::{ unzip_option, ResultExt};
|
||||
|
||||
use crate::dock::DockPosition;
|
||||
use crate::WorkspaceId;
|
||||
@ -96,22 +96,16 @@ impl WorkspaceDb {
|
||||
WorkspaceLocation,
|
||||
bool,
|
||||
DockPosition,
|
||||
) = iife!({
|
||||
if worktree_roots.len() == 0 {
|
||||
self.select_row(sql!(
|
||||
SELECT workspace_id, workspace_location, left_sidebar_open, dock_visible, dock_anchor
|
||||
FROM workspaces
|
||||
ORDER BY timestamp DESC LIMIT 1))?()?
|
||||
} else {
|
||||
self.select_row_bound(sql!(
|
||||
SELECT workspace_id, workspace_location, left_sidebar_open, dock_visible, dock_anchor
|
||||
FROM workspaces
|
||||
WHERE workspace_location = ?))?(&workspace_location)?
|
||||
}
|
||||
) =
|
||||
self.select_row_bound(sql!{
|
||||
SELECT workspace_id, workspace_location, left_sidebar_open, dock_visible, dock_anchor
|
||||
FROM workspaces
|
||||
WHERE workspace_location = ?
|
||||
})
|
||||
.and_then(|mut prepared_statement| (prepared_statement)(&workspace_location))
|
||||
.context("No workspaces found")
|
||||
})
|
||||
.warn_on_err()
|
||||
.flatten()?;
|
||||
.warn_on_err()
|
||||
.flatten()?;
|
||||
|
||||
Some(SerializedWorkspace {
|
||||
id: workspace_id,
|
||||
@ -205,11 +199,21 @@ impl WorkspaceDb {
|
||||
}
|
||||
}
|
||||
|
||||
query! {
|
||||
pub fn last_workspace() -> Result<Option<WorkspaceLocation>> {
|
||||
SELECT workspace_location
|
||||
FROM workspaces
|
||||
WHERE workspace_location IS NOT NULL
|
||||
ORDER BY timestamp DESC
|
||||
LIMIT 1
|
||||
}
|
||||
}
|
||||
|
||||
fn get_center_pane_group(&self, workspace_id: WorkspaceId) -> Result<SerializedPaneGroup> {
|
||||
self.get_pane_group(workspace_id, None)?
|
||||
Ok(self.get_pane_group(workspace_id, None)?
|
||||
.into_iter()
|
||||
.next()
|
||||
.context("No center pane group")
|
||||
.unwrap_or_else(|| SerializedPaneGroup::Pane(SerializedPane { active: true, children: vec![] })))
|
||||
}
|
||||
|
||||
fn get_pane_group(
|
||||
@ -263,7 +267,7 @@ impl WorkspaceDb {
|
||||
// Filter out panes and pane groups which don't have any children or items
|
||||
.filter(|pane_group| match pane_group {
|
||||
Ok(SerializedPaneGroup::Group { children, .. }) => !children.is_empty(),
|
||||
Ok(SerializedPaneGroup::Pane(pane)) => !pane.children.is_empty(),
|
||||
Ok(SerializedPaneGroup::Pane(pane)) => !pane.children.is_empty(),
|
||||
_ => true,
|
||||
})
|
||||
.collect::<Result<_>>()
|
||||
@ -371,6 +375,15 @@ impl WorkspaceDb {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
query!{
|
||||
pub async fn update_timestamp(workspace_id: WorkspaceId) -> Result<()> {
|
||||
UPDATE workspaces
|
||||
SET timestamp = CURRENT_TIMESTAMP
|
||||
WHERE workspace_id = ?
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -106,7 +106,6 @@ impl SerializedPaneGroup {
|
||||
.await
|
||||
{
|
||||
members.push(new_member);
|
||||
|
||||
current_active_pane = current_active_pane.or(active_pane);
|
||||
}
|
||||
}
|
||||
@ -115,6 +114,10 @@ impl SerializedPaneGroup {
|
||||
return None;
|
||||
}
|
||||
|
||||
if members.len() == 1 {
|
||||
return Some((members.remove(0), current_active_pane));
|
||||
}
|
||||
|
||||
Some((
|
||||
Member::Axis(PaneAxis {
|
||||
axis: *axis,
|
||||
@ -130,9 +133,10 @@ impl SerializedPaneGroup {
|
||||
.deserialize_to(project, &pane, workspace_id, workspace, cx)
|
||||
.await;
|
||||
|
||||
if pane.read_with(cx, |pane, _| pane.items().next().is_some()) {
|
||||
if pane.read_with(cx, |pane, _| pane.items_len() != 0) {
|
||||
Some((Member::Pane(pane.clone()), active.then(|| pane)))
|
||||
} else {
|
||||
workspace.update(cx, |workspace, cx| workspace.remove_pane(pane, cx));
|
||||
None
|
||||
}
|
||||
}
|
||||
|
@ -178,6 +178,7 @@ pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
cx.add_global_action({
|
||||
let app_state = Arc::downgrade(&app_state);
|
||||
move |_: &NewWindow, cx: &mut MutableAppContext| {
|
||||
@ -2167,7 +2168,11 @@ impl Workspace {
|
||||
}
|
||||
|
||||
pub fn on_window_activation_changed(&mut self, active: bool, cx: &mut ViewContext<Self>) {
|
||||
if !active {
|
||||
if active {
|
||||
cx.background()
|
||||
.spawn(persistence::DB.update_timestamp(self.database_id()))
|
||||
.detach();
|
||||
} else {
|
||||
for pane in &self.panes {
|
||||
pane.update(cx, |pane, cx| {
|
||||
if let Some(item) = pane.active_item() {
|
||||
@ -2281,6 +2286,9 @@ impl Workspace {
|
||||
}
|
||||
|
||||
if let Some(location) = self.location(cx) {
|
||||
// Load bearing special case:
|
||||
// - with_local_workspace() relies on this to not have other stuff open
|
||||
// when you open your log
|
||||
if !location.paths().is_empty() {
|
||||
let dock_pane = serialize_pane_handle(self.dock.pane(), cx);
|
||||
let center_group = build_serialized_pane_group(&self.center.root, cx);
|
||||
@ -2308,9 +2316,14 @@ impl Workspace {
|
||||
) {
|
||||
cx.spawn(|mut cx| async move {
|
||||
if let Some(workspace) = workspace.upgrade(&cx) {
|
||||
let (project, dock_pane_handle) = workspace.read_with(&cx, |workspace, _| {
|
||||
(workspace.project().clone(), workspace.dock_pane().clone())
|
||||
});
|
||||
let (project, dock_pane_handle, old_center_pane) =
|
||||
workspace.read_with(&cx, |workspace, _| {
|
||||
(
|
||||
workspace.project().clone(),
|
||||
workspace.dock_pane().clone(),
|
||||
workspace.last_active_center_pane.clone(),
|
||||
)
|
||||
});
|
||||
|
||||
serialized_workspace
|
||||
.dock_pane
|
||||
@ -2346,11 +2359,14 @@ impl Workspace {
|
||||
cx.focus(workspace.panes.last().unwrap().clone());
|
||||
}
|
||||
} else {
|
||||
cx.focus_self();
|
||||
let old_center_handle = old_center_pane.and_then(|weak| weak.upgrade(cx));
|
||||
if let Some(old_center_handle) = old_center_handle {
|
||||
cx.focus(old_center_handle)
|
||||
} else {
|
||||
cx.focus_self()
|
||||
}
|
||||
}
|
||||
|
||||
// Note, if this is moved after 'set_dock_position'
|
||||
// it causes an infinite loop.
|
||||
if workspace.left_sidebar().read(cx).is_open()
|
||||
!= serialized_workspace.left_sidebar_open
|
||||
{
|
||||
@ -2604,6 +2620,10 @@ pub fn activate_workspace_for_project(
|
||||
None
|
||||
}
|
||||
|
||||
pub fn last_opened_workspace_paths() -> Option<WorkspaceLocation> {
|
||||
DB.last_workspace().log_err().flatten()
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn open_paths(
|
||||
abs_paths: &[PathBuf],
|
||||
|
@ -169,7 +169,7 @@ fn main() {
|
||||
cx.platform().activate(true);
|
||||
let paths = collect_path_args();
|
||||
if paths.is_empty() {
|
||||
cx.dispatch_global_action(NewFile);
|
||||
restore_or_create_workspace(cx);
|
||||
} else {
|
||||
cx.dispatch_global_action(OpenPaths { paths });
|
||||
}
|
||||
@ -178,7 +178,7 @@ fn main() {
|
||||
cx.spawn(|cx| handle_cli_connection(connection, app_state.clone(), cx))
|
||||
.detach();
|
||||
} else {
|
||||
cx.dispatch_global_action(NewFile);
|
||||
restore_or_create_workspace(cx);
|
||||
}
|
||||
cx.spawn(|cx| async move {
|
||||
while let Some(connection) = cli_connections_rx.next().await {
|
||||
@ -202,6 +202,16 @@ fn main() {
|
||||
});
|
||||
}
|
||||
|
||||
fn restore_or_create_workspace(cx: &mut gpui::MutableAppContext) {
|
||||
if let Some(location) = workspace::last_opened_workspace_paths() {
|
||||
cx.dispatch_global_action(OpenPaths {
|
||||
paths: location.paths().as_ref().clone(),
|
||||
})
|
||||
} else {
|
||||
cx.dispatch_global_action(NewFile);
|
||||
}
|
||||
}
|
||||
|
||||
fn init_paths() {
|
||||
std::fs::create_dir_all(&*util::paths::CONFIG_DIR).expect("could not create config path");
|
||||
std::fs::create_dir_all(&*util::paths::LANGUAGES_DIR).expect("could not create languages path");
|
||||
|
Loading…
Reference in New Issue
Block a user