mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-07 20:39:04 +03:00
Alternate files with ctrl-6 (#11367)
This is my stab at #7709 I realize the code is flawed. There's no test coverage, I'm using `clone()` and there are probably better ways to hook into the events. Also, I didn't know what context to use for the keybinding. But maybe with some pointers from someone who actually know what they're doing, I can get this shippable. Release Notes: - vim: Added ctrl-6 for [alternate-file](https://vimhelp.org/editing.txt.html#CTRL-%5E) to navigate back and forth between two buffers. https://github.com/zed-industries/zed/assets/261929/2d10494e-5668-4988-b7b4-417c922d6c61 --------- Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
This commit is contained in:
parent
0d26beb91b
commit
80d3eafa30
@ -1,4 +1,10 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"context": "ProjectPanel || Editor",
|
||||||
|
"bindings": {
|
||||||
|
"ctrl-6": "pane::AlternateFile"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"context": "Editor && VimControl && !VimWaiting && !menu",
|
"context": "Editor && VimControl && !VimWaiting && !menu",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
|
@ -330,6 +330,7 @@ pub trait ItemHandle: 'static + Send {
|
|||||||
fn serialized_item_kind(&self) -> Option<&'static str>;
|
fn serialized_item_kind(&self) -> Option<&'static str>;
|
||||||
fn show_toolbar(&self, cx: &AppContext) -> bool;
|
fn show_toolbar(&self, cx: &AppContext) -> bool;
|
||||||
fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Point<Pixels>>;
|
fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Point<Pixels>>;
|
||||||
|
fn downgrade_item(&self) -> Box<dyn WeakItemHandle>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait WeakItemHandle: Send + Sync {
|
pub trait WeakItemHandle: Send + Sync {
|
||||||
@ -702,6 +703,10 @@ impl<T: Item> ItemHandle for View<T> {
|
|||||||
fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Point<Pixels>> {
|
fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Point<Pixels>> {
|
||||||
self.read(cx).pixel_position_of_cursor(cx)
|
self.read(cx).pixel_position_of_cursor(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn downgrade_item(&self) -> Box<dyn WeakItemHandle> {
|
||||||
|
Box::new(self.downgrade())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Box<dyn ItemHandle>> for AnyView {
|
impl From<Box<dyn ItemHandle>> for AnyView {
|
||||||
|
@ -18,6 +18,7 @@ use gpui::{
|
|||||||
MouseDownEvent, NavigationDirection, Pixels, Point, PromptLevel, Render, ScrollHandle,
|
MouseDownEvent, NavigationDirection, Pixels, Point, PromptLevel, Render, ScrollHandle,
|
||||||
Subscription, Task, View, ViewContext, VisualContext, WeakFocusHandle, WeakView, WindowContext,
|
Subscription, Task, View, ViewContext, VisualContext, WeakFocusHandle, WeakView, WindowContext,
|
||||||
};
|
};
|
||||||
|
use itertools::Itertools;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use project::{Project, ProjectEntryId, ProjectPath};
|
use project::{Project, ProjectEntryId, ProjectPath};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
@ -114,6 +115,7 @@ actions!(
|
|||||||
ActivatePrevItem,
|
ActivatePrevItem,
|
||||||
ActivateNextItem,
|
ActivateNextItem,
|
||||||
ActivateLastItem,
|
ActivateLastItem,
|
||||||
|
AlternateFile,
|
||||||
CloseCleanItems,
|
CloseCleanItems,
|
||||||
CloseItemsToTheLeft,
|
CloseItemsToTheLeft,
|
||||||
CloseItemsToTheRight,
|
CloseItemsToTheRight,
|
||||||
@ -183,6 +185,10 @@ impl fmt::Debug for Event {
|
|||||||
/// responsible for managing item tabs, focus and zoom states and drag and drop features.
|
/// responsible for managing item tabs, focus and zoom states and drag and drop features.
|
||||||
/// Can be split, see `PaneGroup` for more details.
|
/// Can be split, see `PaneGroup` for more details.
|
||||||
pub struct Pane {
|
pub struct Pane {
|
||||||
|
alternate_file_items: (
|
||||||
|
Option<Box<dyn WeakItemHandle>>,
|
||||||
|
Option<Box<dyn WeakItemHandle>>,
|
||||||
|
),
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
items: Vec<Box<dyn ItemHandle>>,
|
items: Vec<Box<dyn ItemHandle>>,
|
||||||
activation_history: Vec<EntityId>,
|
activation_history: Vec<EntityId>,
|
||||||
@ -286,6 +292,7 @@ impl Pane {
|
|||||||
|
|
||||||
let handle = cx.view().downgrade();
|
let handle = cx.view().downgrade();
|
||||||
Self {
|
Self {
|
||||||
|
alternate_file_items: (None, None),
|
||||||
focus_handle,
|
focus_handle,
|
||||||
items: Vec::new(),
|
items: Vec::new(),
|
||||||
activation_history: Vec::new(),
|
activation_history: Vec::new(),
|
||||||
@ -390,6 +397,39 @@ impl Pane {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn alternate_file(&mut self, cx: &mut ViewContext<Pane>) {
|
||||||
|
let (_, alternative) = &self.alternate_file_items;
|
||||||
|
if let Some(alternative) = alternative {
|
||||||
|
let existing = self
|
||||||
|
.items()
|
||||||
|
.find_position(|item| item.item_id() == alternative.id());
|
||||||
|
if let Some((ix, _)) = existing {
|
||||||
|
self.activate_item(ix, true, true, cx);
|
||||||
|
} else {
|
||||||
|
if let Some(upgraded) = alternative.upgrade() {
|
||||||
|
self.add_item(upgraded, true, true, None, cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn track_alternate_file_items(&mut self) {
|
||||||
|
if let Some(item) = self.active_item().map(|item| item.downgrade_item()) {
|
||||||
|
let (current, _) = &self.alternate_file_items;
|
||||||
|
match current {
|
||||||
|
Some(current) => {
|
||||||
|
if current.id() != item.id() {
|
||||||
|
self.alternate_file_items =
|
||||||
|
(Some(item), self.alternate_file_items.0.take());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
self.alternate_file_items = (Some(item), None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn has_focus(&self, cx: &WindowContext) -> bool {
|
pub fn has_focus(&self, cx: &WindowContext) -> bool {
|
||||||
// We not only check whether our focus handle contains focus, but also
|
// We not only check whether our focus handle contains focus, but also
|
||||||
// whether the active_item might have focus, because we might have just activated an item
|
// whether the active_item might have focus, because we might have just activated an item
|
||||||
@ -1981,6 +2021,9 @@ impl Render for Pane {
|
|||||||
.size_full()
|
.size_full()
|
||||||
.flex_none()
|
.flex_none()
|
||||||
.overflow_hidden()
|
.overflow_hidden()
|
||||||
|
.on_action(cx.listener(|pane, _: &AlternateFile, cx| {
|
||||||
|
pane.alternate_file(cx);
|
||||||
|
}))
|
||||||
.on_action(cx.listener(|pane, _: &SplitLeft, cx| pane.split(SplitDirection::Left, cx)))
|
.on_action(cx.listener(|pane, _: &SplitLeft, cx| pane.split(SplitDirection::Left, cx)))
|
||||||
.on_action(cx.listener(|pane, _: &SplitUp, cx| pane.split(SplitDirection::Up, cx)))
|
.on_action(cx.listener(|pane, _: &SplitUp, cx| pane.split(SplitDirection::Up, cx)))
|
||||||
.on_action(
|
.on_action(
|
||||||
|
@ -2499,6 +2499,9 @@ impl Workspace {
|
|||||||
self.zoomed_position = None;
|
self.zoomed_position = None;
|
||||||
cx.emit(Event::ZoomChanged);
|
cx.emit(Event::ZoomChanged);
|
||||||
self.update_active_view_for_followers(cx);
|
self.update_active_view_for_followers(cx);
|
||||||
|
pane.model.update(cx, |pane, _| {
|
||||||
|
pane.track_alternate_file_items();
|
||||||
|
});
|
||||||
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
@ -2516,6 +2519,9 @@ impl Workspace {
|
|||||||
}
|
}
|
||||||
pane::Event::Remove => self.remove_pane(pane, cx),
|
pane::Event::Remove => self.remove_pane(pane, cx),
|
||||||
pane::Event::ActivateItem { local } => {
|
pane::Event::ActivateItem { local } => {
|
||||||
|
pane.model.update(cx, |pane, _| {
|
||||||
|
pane.track_alternate_file_items();
|
||||||
|
});
|
||||||
if *local {
|
if *local {
|
||||||
self.unfollow(&pane, cx);
|
self.unfollow(&pane, cx);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user