Merge branch 'main' into collab-panel
This commit is contained in:
commit
99daa73325
93 changed files with 1908 additions and 1442 deletions
|
@ -165,13 +165,12 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::AppContext) {
|
|||
move |workspace: &mut Workspace, _: &DebugElements, cx: &mut ViewContext<Workspace>| {
|
||||
let app_state = workspace.app_state().clone();
|
||||
let markdown = app_state.languages.language_for_name("JSON");
|
||||
let window_id = cx.window_id();
|
||||
let window = cx.window();
|
||||
cx.spawn(|workspace, mut cx| async move {
|
||||
let markdown = markdown.await.log_err();
|
||||
let content = to_string_pretty(
|
||||
&cx.debug_elements(window_id)
|
||||
.ok_or_else(|| anyhow!("could not debug elements for {window_id}"))?,
|
||||
)
|
||||
let content = to_string_pretty(&window.debug_elements(&cx).ok_or_else(|| {
|
||||
anyhow!("could not debug elements for window {}", window.id())
|
||||
})?)
|
||||
.unwrap();
|
||||
workspace
|
||||
.update(&mut cx, |workspace, cx| {
|
||||
|
@ -405,29 +404,22 @@ pub fn build_window_options(
|
|||
fn quit(_: &Quit, cx: &mut gpui::AppContext) {
|
||||
let should_confirm = settings::get::<WorkspaceSettings>(cx).confirm_quit;
|
||||
cx.spawn(|mut cx| async move {
|
||||
let mut workspaces = cx
|
||||
.window_ids()
|
||||
let mut workspace_windows = cx
|
||||
.windows()
|
||||
.into_iter()
|
||||
.filter_map(|window_id| {
|
||||
Some(
|
||||
cx.root_view(window_id)?
|
||||
.clone()
|
||||
.downcast::<Workspace>()?
|
||||
.downgrade(),
|
||||
)
|
||||
})
|
||||
.filter_map(|window| window.downcast::<Workspace>())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// If multiple windows have unsaved changes, and need a save prompt,
|
||||
// prompt in the active window before switching to a different window.
|
||||
workspaces.sort_by_key(|workspace| !cx.window_is_active(workspace.window_id()));
|
||||
workspace_windows.sort_by_key(|window| window.is_active(&cx) == Some(false));
|
||||
|
||||
if let (true, Some(workspace)) = (should_confirm, workspaces.first()) {
|
||||
let answer = cx.prompt(
|
||||
workspace.window_id(),
|
||||
if let (true, Some(window)) = (should_confirm, workspace_windows.first().copied()) {
|
||||
let answer = window.prompt(
|
||||
PromptLevel::Info,
|
||||
"Are you sure you want to quit?",
|
||||
&["Quit", "Cancel"],
|
||||
&mut cx,
|
||||
);
|
||||
|
||||
if let Some(mut answer) = answer {
|
||||
|
@ -439,14 +431,13 @@ fn quit(_: &Quit, cx: &mut gpui::AppContext) {
|
|||
}
|
||||
|
||||
// If the user cancels any save prompt, then keep the app open.
|
||||
for workspace in workspaces {
|
||||
if !workspace
|
||||
.update(&mut cx, |workspace, cx| {
|
||||
workspace.prepare_to_close(true, cx)
|
||||
})?
|
||||
.await?
|
||||
{
|
||||
return Ok(());
|
||||
for window in workspace_windows {
|
||||
if let Some(close) = window.update_root(&mut cx, |workspace, cx| {
|
||||
workspace.prepare_to_close(false, cx)
|
||||
}) {
|
||||
if close.await? {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
cx.platform().quit();
|
||||
|
@ -723,8 +714,8 @@ mod tests {
|
|||
use editor::{scroll::autoscroll::Autoscroll, DisplayPoint, Editor};
|
||||
use fs::{FakeFs, Fs};
|
||||
use gpui::{
|
||||
actions, elements::Empty, executor::Deterministic, Action, AnyElement, AppContext,
|
||||
AssetSource, Element, Entity, TestAppContext, View, ViewHandle,
|
||||
actions, elements::Empty, executor::Deterministic, Action, AnyElement, AnyWindowHandle,
|
||||
AppContext, AssetSource, Element, Entity, TestAppContext, View, ViewHandle,
|
||||
};
|
||||
use language::LanguageRegistry;
|
||||
use node_runtime::NodeRuntime;
|
||||
|
@ -781,17 +772,13 @@ mod tests {
|
|||
})
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(cx.window_ids().len(), 1);
|
||||
assert_eq!(cx.windows().len(), 1);
|
||||
|
||||
cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, None, cx))
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(cx.window_ids().len(), 1);
|
||||
let workspace_1 = cx
|
||||
.read_window(cx.window_ids()[0], |cx| cx.root_view().clone())
|
||||
.unwrap()
|
||||
.downcast::<Workspace>()
|
||||
.unwrap();
|
||||
assert_eq!(cx.windows().len(), 1);
|
||||
let workspace_1 = cx.windows()[0].downcast::<Workspace>().unwrap().root(cx);
|
||||
workspace_1.update(cx, |workspace, cx| {
|
||||
assert_eq!(workspace.worktrees(cx).count(), 2);
|
||||
assert!(workspace.left_dock().read(cx).is_open());
|
||||
|
@ -808,27 +795,22 @@ mod tests {
|
|||
})
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(cx.window_ids().len(), 2);
|
||||
assert_eq!(cx.windows().len(), 2);
|
||||
|
||||
// Replace existing windows
|
||||
let window_id = cx.window_ids()[0];
|
||||
let window = cx.windows()[0].downcast::<Workspace>().unwrap();
|
||||
cx.update(|cx| {
|
||||
open_paths(
|
||||
&[PathBuf::from("/root/c"), PathBuf::from("/root/d")],
|
||||
&app_state,
|
||||
Some(window_id),
|
||||
Some(window),
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(cx.window_ids().len(), 2);
|
||||
let workspace_1 = cx
|
||||
.read_window(cx.window_ids()[0], |cx| cx.root_view().clone())
|
||||
.unwrap()
|
||||
.clone()
|
||||
.downcast::<Workspace>()
|
||||
.unwrap();
|
||||
assert_eq!(cx.windows().len(), 2);
|
||||
let workspace_1 = cx.windows()[0].downcast::<Workspace>().unwrap().root(cx);
|
||||
workspace_1.update(cx, |workspace, cx| {
|
||||
assert_eq!(
|
||||
workspace
|
||||
|
@ -854,14 +836,11 @@ mod tests {
|
|||
cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, None, cx))
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(cx.window_ids().len(), 1);
|
||||
assert_eq!(cx.windows().len(), 1);
|
||||
|
||||
// When opening the workspace, the window is not in a edited state.
|
||||
let workspace = cx
|
||||
.read_window(cx.window_ids()[0], |cx| cx.root_view().clone())
|
||||
.unwrap()
|
||||
.downcast::<Workspace>()
|
||||
.unwrap();
|
||||
let window = cx.windows()[0].downcast::<Workspace>().unwrap();
|
||||
let workspace = window.root(cx);
|
||||
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
let editor = workspace.read_with(cx, |workspace, cx| {
|
||||
workspace
|
||||
|
@ -870,19 +849,19 @@ mod tests {
|
|||
.downcast::<Editor>()
|
||||
.unwrap()
|
||||
});
|
||||
assert!(!cx.is_window_edited(workspace.window_id()));
|
||||
assert!(!window.is_edited(cx));
|
||||
|
||||
// Editing a buffer marks the window as edited.
|
||||
editor.update(cx, |editor, cx| editor.insert("EDIT", cx));
|
||||
assert!(cx.is_window_edited(workspace.window_id()));
|
||||
assert!(window.is_edited(cx));
|
||||
|
||||
// Undoing the edit restores the window's edited state.
|
||||
editor.update(cx, |editor, cx| editor.undo(&Default::default(), cx));
|
||||
assert!(!cx.is_window_edited(workspace.window_id()));
|
||||
assert!(!window.is_edited(cx));
|
||||
|
||||
// Redoing the edit marks the window as edited again.
|
||||
editor.update(cx, |editor, cx| editor.redo(&Default::default(), cx));
|
||||
assert!(cx.is_window_edited(workspace.window_id()));
|
||||
assert!(window.is_edited(cx));
|
||||
|
||||
// Closing the item restores the window's edited state.
|
||||
let close = pane.update(cx, |pane, cx| {
|
||||
|
@ -890,9 +869,10 @@ mod tests {
|
|||
pane.close_active_item(&Default::default(), cx).unwrap()
|
||||
});
|
||||
executor.run_until_parked();
|
||||
cx.simulate_prompt_answer(workspace.window_id(), 1);
|
||||
|
||||
window.simulate_prompt_answer(1, cx);
|
||||
close.await.unwrap();
|
||||
assert!(!cx.is_window_edited(workspace.window_id()));
|
||||
assert!(!window.is_edited(cx));
|
||||
|
||||
// Opening the buffer again doesn't impact the window's edited state.
|
||||
cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, None, cx))
|
||||
|
@ -905,22 +885,22 @@ mod tests {
|
|||
.downcast::<Editor>()
|
||||
.unwrap()
|
||||
});
|
||||
assert!(!cx.is_window_edited(workspace.window_id()));
|
||||
assert!(!window.is_edited(cx));
|
||||
|
||||
// Editing the buffer marks the window as edited.
|
||||
editor.update(cx, |editor, cx| editor.insert("EDIT", cx));
|
||||
assert!(cx.is_window_edited(workspace.window_id()));
|
||||
assert!(window.is_edited(cx));
|
||||
|
||||
// Ensure closing the window via the mouse gets preempted due to the
|
||||
// buffer having unsaved changes.
|
||||
assert!(!cx.simulate_window_close(workspace.window_id()));
|
||||
assert!(!window.simulate_close(cx));
|
||||
executor.run_until_parked();
|
||||
assert_eq!(cx.window_ids().len(), 1);
|
||||
assert_eq!(cx.windows().len(), 1);
|
||||
|
||||
// The window is successfully closed after the user dismisses the prompt.
|
||||
cx.simulate_prompt_answer(workspace.window_id(), 1);
|
||||
window.simulate_prompt_answer(1, cx);
|
||||
executor.run_until_parked();
|
||||
assert_eq!(cx.window_ids().len(), 0);
|
||||
assert_eq!(cx.windows().len(), 0);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
|
@ -933,12 +913,13 @@ mod tests {
|
|||
})
|
||||
.await;
|
||||
|
||||
let window_id = *cx.window_ids().first().unwrap();
|
||||
let workspace = cx
|
||||
.read_window(window_id, |cx| cx.root_view().clone())
|
||||
let window = cx
|
||||
.windows()
|
||||
.first()
|
||||
.unwrap()
|
||||
.downcast::<Workspace>()
|
||||
.unwrap();
|
||||
let workspace = window.root(cx);
|
||||
|
||||
let editor = workspace.update(cx, |workspace, cx| {
|
||||
workspace
|
||||
|
@ -981,9 +962,8 @@ mod tests {
|
|||
.await;
|
||||
|
||||
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
|
||||
let workspace = cx
|
||||
.add_window(|cx| Workspace::test_new(project, cx))
|
||||
.root(cx);
|
||||
let window = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||
let workspace = window.root(cx);
|
||||
|
||||
let entries = cx.read(|cx| workspace.file_project_paths(cx));
|
||||
let file1 = entries[0].clone();
|
||||
|
@ -1104,12 +1084,8 @@ mod tests {
|
|||
cx.update(|cx| open_paths(&[PathBuf::from("/dir1/")], &app_state, None, cx))
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(cx.window_ids().len(), 1);
|
||||
let workspace = cx
|
||||
.read_window(cx.window_ids()[0], |cx| cx.root_view().clone())
|
||||
.unwrap()
|
||||
.downcast::<Workspace>()
|
||||
.unwrap();
|
||||
assert_eq!(cx.windows().len(), 1);
|
||||
let workspace = cx.windows()[0].downcast::<Workspace>().unwrap().root(cx);
|
||||
|
||||
#[track_caller]
|
||||
fn assert_project_panel_selection(
|
||||
|
@ -1297,7 +1273,6 @@ mod tests {
|
|||
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
|
||||
let window = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||
let workspace = window.root(cx);
|
||||
let window_id = window.window_id();
|
||||
|
||||
// Open a file within an existing worktree.
|
||||
workspace
|
||||
|
@ -1323,7 +1298,7 @@ mod tests {
|
|||
cx.read(|cx| assert!(editor.is_dirty(cx)));
|
||||
|
||||
let save_task = workspace.update(cx, |workspace, cx| workspace.save_active_item(false, cx));
|
||||
cx.simulate_prompt_answer(window_id, 0);
|
||||
window.simulate_prompt_answer(0, cx);
|
||||
save_task.await.unwrap();
|
||||
editor.read_with(cx, |editor, cx| {
|
||||
assert!(!editor.is_dirty(cx));
|
||||
|
@ -1340,11 +1315,10 @@ mod tests {
|
|||
project.update(cx, |project, _| project.languages().add(rust_lang()));
|
||||
let window = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||
let workspace = window.root(cx);
|
||||
let window_id = window.window_id();
|
||||
let worktree = cx.read(|cx| workspace.read(cx).worktrees(cx).next().unwrap());
|
||||
|
||||
// Create a new untitled buffer
|
||||
cx.dispatch_action(window_id, NewFile);
|
||||
cx.dispatch_action(window.into(), NewFile);
|
||||
let editor = workspace.read_with(cx, |workspace, cx| {
|
||||
workspace
|
||||
.active_item(cx)
|
||||
|
@ -1399,7 +1373,7 @@ mod tests {
|
|||
|
||||
// Open the same newly-created file in another pane item. The new editor should reuse
|
||||
// the same buffer.
|
||||
cx.dispatch_action(window_id, NewFile);
|
||||
cx.dispatch_action(window.into(), NewFile);
|
||||
workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
workspace.split_and_clone(
|
||||
|
@ -1435,10 +1409,9 @@ mod tests {
|
|||
project.update(cx, |project, _| project.languages().add(rust_lang()));
|
||||
let window = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||
let workspace = window.root(cx);
|
||||
let window_id = window.window_id();
|
||||
|
||||
// Create a new untitled buffer
|
||||
cx.dispatch_action(window_id, NewFile);
|
||||
cx.dispatch_action(window.into(), NewFile);
|
||||
let editor = workspace.read_with(cx, |workspace, cx| {
|
||||
workspace
|
||||
.active_item(cx)
|
||||
|
@ -1488,7 +1461,6 @@ mod tests {
|
|||
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
|
||||
let window = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||
let workspace = window.root(cx);
|
||||
let window_id = window.window_id();
|
||||
|
||||
let entries = cx.read(|cx| workspace.file_project_paths(cx));
|
||||
let file1 = entries[0].clone();
|
||||
|
@ -1510,7 +1482,7 @@ mod tests {
|
|||
(editor.downgrade(), buffer)
|
||||
});
|
||||
|
||||
cx.dispatch_action(window_id, pane::SplitRight);
|
||||
cx.dispatch_action(window.into(), pane::SplitRight);
|
||||
let editor_2 = cx.update(|cx| {
|
||||
let pane_2 = workspace.read(cx).active_pane().clone();
|
||||
assert_ne!(pane_1, pane_2);
|
||||
|
@ -1520,7 +1492,7 @@ mod tests {
|
|||
|
||||
pane2_item.downcast::<Editor>().unwrap().downgrade()
|
||||
});
|
||||
cx.dispatch_action(window_id, workspace::CloseActiveItem);
|
||||
cx.dispatch_action(window.into(), workspace::CloseActiveItem);
|
||||
|
||||
cx.foreground().run_until_parked();
|
||||
workspace.read_with(cx, |workspace, _| {
|
||||
|
@ -1528,9 +1500,9 @@ mod tests {
|
|||
assert_eq!(workspace.active_pane(), &pane_1);
|
||||
});
|
||||
|
||||
cx.dispatch_action(window_id, workspace::CloseActiveItem);
|
||||
cx.dispatch_action(window.into(), workspace::CloseActiveItem);
|
||||
cx.foreground().run_until_parked();
|
||||
cx.simulate_prompt_answer(window_id, 1);
|
||||
window.simulate_prompt_answer(1, cx);
|
||||
cx.foreground().run_until_parked();
|
||||
|
||||
workspace.read_with(cx, |workspace, cx| {
|
||||
|
@ -2086,11 +2058,10 @@ mod tests {
|
|||
cx.foreground().run_until_parked();
|
||||
|
||||
let window = cx.add_window(|_| TestView);
|
||||
let window_id = window.window_id();
|
||||
|
||||
// Test loading the keymap base at all
|
||||
assert_key_bindings_for(
|
||||
window_id,
|
||||
window.into(),
|
||||
cx,
|
||||
vec![("backspace", &A), ("k", &ActivatePreviousPane)],
|
||||
line!(),
|
||||
|
@ -2117,7 +2088,7 @@ mod tests {
|
|||
cx.foreground().run_until_parked();
|
||||
|
||||
assert_key_bindings_for(
|
||||
window_id,
|
||||
window.into(),
|
||||
cx,
|
||||
vec![("backspace", &B), ("k", &ActivatePreviousPane)],
|
||||
line!(),
|
||||
|
@ -2140,7 +2111,7 @@ mod tests {
|
|||
cx.foreground().run_until_parked();
|
||||
|
||||
assert_key_bindings_for(
|
||||
window_id,
|
||||
window.into(),
|
||||
cx,
|
||||
vec![("backspace", &B), ("[", &ActivatePrevItem)],
|
||||
line!(),
|
||||
|
@ -2148,7 +2119,7 @@ mod tests {
|
|||
|
||||
#[track_caller]
|
||||
fn assert_key_bindings_for<'a>(
|
||||
window_id: usize,
|
||||
window: AnyWindowHandle,
|
||||
cx: &TestAppContext,
|
||||
actions: Vec<(&'static str, &'a dyn Action)>,
|
||||
line: u32,
|
||||
|
@ -2156,7 +2127,7 @@ mod tests {
|
|||
for (key, action) in actions {
|
||||
// assert that...
|
||||
assert!(
|
||||
cx.available_actions(window_id, 0)
|
||||
cx.available_actions(window, 0)
|
||||
.into_iter()
|
||||
.any(|(_, bound_action, b)| {
|
||||
// action names match...
|
||||
|
@ -2257,11 +2228,10 @@ mod tests {
|
|||
cx.foreground().run_until_parked();
|
||||
|
||||
let window = cx.add_window(|_| TestView);
|
||||
let window_id = window.window_id();
|
||||
|
||||
// Test loading the keymap base at all
|
||||
assert_key_bindings_for(
|
||||
window_id,
|
||||
window.into(),
|
||||
cx,
|
||||
vec![("backspace", &A), ("k", &ActivatePreviousPane)],
|
||||
line!(),
|
||||
|
@ -2287,7 +2257,12 @@ mod tests {
|
|||
|
||||
cx.foreground().run_until_parked();
|
||||
|
||||
assert_key_bindings_for(window_id, cx, vec![("k", &ActivatePreviousPane)], line!());
|
||||
assert_key_bindings_for(
|
||||
window.into(),
|
||||
cx,
|
||||
vec![("k", &ActivatePreviousPane)],
|
||||
line!(),
|
||||
);
|
||||
|
||||
// Test modifying the base, while retaining the users keymap
|
||||
fs.save(
|
||||
|
@ -2305,11 +2280,11 @@ mod tests {
|
|||
|
||||
cx.foreground().run_until_parked();
|
||||
|
||||
assert_key_bindings_for(window_id, cx, vec![("[", &ActivatePrevItem)], line!());
|
||||
assert_key_bindings_for(window.into(), cx, vec![("[", &ActivatePrevItem)], line!());
|
||||
|
||||
#[track_caller]
|
||||
fn assert_key_bindings_for<'a>(
|
||||
window_id: usize,
|
||||
window: AnyWindowHandle,
|
||||
cx: &TestAppContext,
|
||||
actions: Vec<(&'static str, &'a dyn Action)>,
|
||||
line: u32,
|
||||
|
@ -2317,7 +2292,7 @@ mod tests {
|
|||
for (key, action) in actions {
|
||||
// assert that...
|
||||
assert!(
|
||||
cx.available_actions(window_id, 0)
|
||||
cx.available_actions(window, 0)
|
||||
.into_iter()
|
||||
.any(|(_, bound_action, b)| {
|
||||
// action names match...
|
||||
|
@ -2351,6 +2326,11 @@ mod tests {
|
|||
.unwrap()
|
||||
.to_vec()
|
||||
.into(),
|
||||
Assets
|
||||
.load("fonts/plex/IBMPlexSans-Regular.ttf")
|
||||
.unwrap()
|
||||
.to_vec()
|
||||
.into(),
|
||||
])
|
||||
.unwrap();
|
||||
let themes = ThemeRegistry::new(Assets, cx.font_cache().clone());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue