History

gpui(windows): Move jumplist creation to background thread (#47956)

This can block the main thread too much otherwise Release Notes: - N/A *or* Added/Fixed/Improved ...

Lukas Wirth committed 9 months ago

Commit 19765cd1 parent 28f04615 files · +77 −51Browse files

Unified · Split

5 files
  1. crates/gpui/src/app.rs +1 −1
  2. crates/gpui/src/platform.rs +2 −2
  3. crates/gpui/src/platform/windows/destination_list.rs +21 −17
  4. crates/gpui/src/platform/windows/platform.rs +26 −5
  5. crates/workspace/src/history_manager.rs +27 −26
crates/gpui/src/app.rs+1−1
@@ -2010,7 +2010,7 @@ impl App { · 2009 unmodified lines
20102010 &self,
20112011 menus: Vec<MenuItem>,
20122012 entries: Vec<SmallVec<[PathBuf; 2]>>,
2013− ) -> Vec<SmallVec<[PathBuf; 2]>> {
2013+ ) -> Task<Vec<SmallVec<[PathBuf; 2]>>> {
20142014 self.platform.update_jump_list(menus, entries)
20152015 }
20162016
crates/gpui/src/platform.rs+2−2
@@ -254,8 +254,8 @@ pub(crate) trait Platform: 'static { · 253 unmodified lines
254254 &self,
255255 _menus: Vec<MenuItem>,
256256 _entries: Vec<SmallVec<[PathBuf; 2]>>,
257− ) -> Vec<SmallVec<[PathBuf; 2]>> {
258− Vec::new()
257+ ) -> Task<Vec<SmallVec<[PathBuf; 2]>>> {
258+ Task::ready(Vec::new())
259259 }
260260 fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>);
261261 fn on_will_open_app_menu(&self, callback: Box<dyn FnMut()>);
crates/gpui/src/platform/windows/destination_list.rs+21−17
@@ -1,4 +1,4 @@
1−use std::path::PathBuf;
1+use std::{path::PathBuf, sync::Arc};
22
33 use itertools::Itertools;
44 use smallvec::SmallVec;
@@ -20,25 +20,25 @@ use windows::{ · 15 unmodified lines
2020 core::{GUID, HSTRING, Interface},
2121 };
2222
23−use crate::{Action, MenuItem};
23+use crate::{Action, MenuItem, SharedString};
2424
2525 pub(crate) struct JumpList {
2626 pub(crate) dock_menus: Vec<DockMenuItem>,
27− pub(crate) recent_workspaces: Vec<SmallVec<[PathBuf; 2]>>,
27+ pub(crate) recent_workspaces: Arc<[SmallVec<[PathBuf; 2]>]>,
2828 }
2929
3030 impl JumpList {
3131 pub(crate) fn new() -> Self {
3232 Self {
33− dock_menus: Vec::new(),
34− recent_workspaces: Vec::new(),
33+ dock_menus: Vec::default(),
34+ recent_workspaces: Arc::default(),
3535 }
3636 }
3737 }
3838
3939 pub(crate) struct DockMenuItem {
40− pub(crate) name: String,
41− pub(crate) description: String,
40+ pub(crate) name: SharedString,
41+ pub(crate) description: SharedString,
4242 pub(crate) action: Box<dyn Action>,
4343 }
4444
@@ -46,11 +46,11 @@ impl DockMenuItem { · 1 unmodified line
4646 pub(crate) fn new(item: MenuItem) -> anyhow::Result<Self> {
4747 match item {
4848 MenuItem::Action { name, action, .. } => Ok(Self {
49− name: name.clone().into(),
49+ name: name.clone(),
5050 description: if name == "New Window" {
51− "Opens a new window".to_string()
51+ "Opens a new window".into()
5252 } else {
53− name.into()
53+ name
5454 },
5555 action,
5656 }),
@@ -62,11 +62,12 @@ impl DockMenuItem { · 5 unmodified lines
6262 // This code is based on the example from Microsoft:
6363 // https://github.com/microsoft/Windows-classic-samples/blob/main/Samples/Win7Samples/winui/shell/appshellintegration/RecipePropertyHandler/RecipePropertyHandler.cpp
6464 pub(crate) fn update_jump_list(
65− jump_list: &JumpList,
65+ recent_workspaces: &[SmallVec<[PathBuf; 2]>],
66+ dock_menus: &[(SharedString, SharedString)],
6667 ) -> anyhow::Result<Vec<SmallVec<[PathBuf; 2]>>> {
6768 let (list, removed) = create_destination_list()?;
68− add_recent_folders(&list, &jump_list.recent_workspaces, removed.as_ref())?;
69− add_dock_menu(&list, &jump_list.dock_menus)?;
69+ add_recent_folders(&list, recent_workspaces, removed.as_ref())?;
70+ add_dock_menu(&list, dock_menus)?;
7071 unsafe { list.CommitList() }?;
7172 Ok(removed)
7273 }
@@ -110,14 +111,17 @@ fn create_destination_list() -> anyhow::Result<(ICustomDestinationList, Vec<Smal · 37 unmodified lines
110111 Ok((list, removed))
111112 }
112113
113−fn add_dock_menu(list: &ICustomDestinationList, dock_menus: &[DockMenuItem]) -> anyhow::Result<()> {
114+fn add_dock_menu(
115+ list: &ICustomDestinationList,
116+ dock_menus: &[(SharedString, SharedString)],
117+) -> anyhow::Result<()> {
114118 unsafe {
115119 let tasks: IObjectCollection =
116120 CoCreateInstance(&EnumerableObjectCollection, None, CLSCTX_INPROC_SERVER)?;
117− for (idx, dock_menu) in dock_menus.iter().enumerate() {
121+ for (idx, (name, description)) in dock_menus.iter().enumerate() {
118122 let argument = HSTRING::from(format!("--dock-action {}", idx));
119− let description = HSTRING::from(dock_menu.description.as_str());
120− let display = dock_menu.name.as_str();
123+ let description = HSTRING::from(description.as_str());
124+ let display = name.as_str();
121125 let task = create_shell_link(argument, description, None, display)?;
122126 tasks.AddObject(&task)?;
123127 }
crates/gpui/src/platform/windows/platform.rs+26−5
@@ -240,14 +240,25 @@ impl WindowsPlatform { · 239 unmodified lines
240240 }
241241 });
242242 self.inner.state.jump_list.borrow_mut().dock_menus = actions;
243− update_jump_list(&self.inner.state.jump_list.borrow()).log_err();
243+ let borrow = self.inner.state.jump_list.borrow();
244+ let dock_menus = borrow
245+ .dock_menus
246+ .iter()
247+ .map(|menu| (menu.name.clone(), menu.description.clone()))
248+ .collect::<Vec<_>>();
249+ let recent_workspaces = borrow.recent_workspaces.clone();
250+ self.background_executor
251+ .spawn(async move {
252+ update_jump_list(&recent_workspaces, &dock_menus).log_err();
253+ })
254+ .detach();
244255 }
245256
246257 fn update_jump_list(
247258 &self,
248259 menus: Vec<MenuItem>,
249260 entries: Vec<SmallVec<[PathBuf; 2]>>,
250− ) -> Vec<SmallVec<[PathBuf; 2]>> {
261+ ) -> Task<Vec<SmallVec<[PathBuf; 2]>>> {
251262 let mut actions = Vec::new();
252263 menus.into_iter().for_each(|menu| {
253264 if let Some(dock_menu) = DockMenuItem::new(menu).log_err() {
@@ -256,8 +267,18 @@ impl WindowsPlatform { · 2 unmodified lines
256267 });
257268 let mut jump_list = self.inner.state.jump_list.borrow_mut();
258269 jump_list.dock_menus = actions;
259− jump_list.recent_workspaces = entries;
260− update_jump_list(&jump_list).log_err().unwrap_or_default()
270+ jump_list.recent_workspaces = entries.into();
271+ let dock_menus = jump_list
272+ .dock_menus
273+ .iter()
274+ .map(|menu| (menu.name.clone(), menu.description.clone()))
275+ .collect::<Vec<_>>();
276+ let recent_workspaces = jump_list.recent_workspaces.clone();
277+ self.background_executor.spawn(async move {
278+ update_jump_list(&recent_workspaces, &dock_menus)
279+ .log_err()
280+ .unwrap_or_default()
281+ })
261282 }
262283
263284 fn find_current_active_window(&self) -> Option<HWND> {
@@ -760,7 +781,7 @@ impl Platform for WindowsPlatform { · 496 unmodified lines
760781 &self,
761782 menus: Vec<MenuItem>,
762783 entries: Vec<SmallVec<[PathBuf; 2]>>,
763− ) -> Vec<SmallVec<[PathBuf; 2]>> {
784+ ) -> Task<Vec<SmallVec<[PathBuf; 2]>>> {
764785 self.update_jump_list(menus, entries)
765786 }
766787 }
crates/workspace/src/history_manager.rs+27−26
@@ -2,7 +2,7 @@ use std::path::PathBuf; · 1 unmodified line
22
33 use gpui::{AppContext, Entity, Global, MenuItem};
44 use smallvec::SmallVec;
5−use ui::App;
5+use ui::{App, Context};
66 use util::{ResultExt, paths::PathExt};
77
88 use crate::{
@@ -71,7 +71,12 @@ impl HistoryManager { · 62 unmodified lines
7171 cx.set_global(GlobalHistoryManager(history_manager));
7272 }
7373
74− pub fn update_history(&mut self, id: WorkspaceId, entry: HistoryManagerEntry, cx: &App) {
74+ pub fn update_history(
75+ &mut self,
76+ id: WorkspaceId,
77+ entry: HistoryManagerEntry,
78+ cx: &mut Context<'_, HistoryManager>,
79+ ) {
7580 if let Some(pos) = self.history.iter().position(|e| e.id == id) {
7681 self.history.remove(pos);
7782 }
@@ -79,7 +84,7 @@ impl HistoryManager { · 1 unmodified line
7984 self.update_jump_list(cx);
8085 }
8186
82− pub fn delete_history(&mut self, id: WorkspaceId, cx: &App) {
87+ pub fn delete_history(&mut self, id: WorkspaceId, cx: &mut Context<'_, HistoryManager>) {
8388 let Some(pos) = self.history.iter().position(|e| e.id == id) else {
8489 return;
8590 };
@@ -87,7 +92,7 @@ impl HistoryManager { · 1 unmodified line
8792 self.update_jump_list(cx);
8893 }
8994
90− fn update_jump_list(&mut self, cx: &App) {
95+ fn update_jump_list(&mut self, cx: &mut Context<'_, HistoryManager>) {
9196 let menus = vec![MenuItem::action("New Window", NewWindow)];
9297 let entries = self
9398 .history
@@ -96,29 +101,25 @@ impl HistoryManager { · 2 unmodified lines
96101 .map(|entry| entry.path.clone())
97102 .collect::<Vec<_>>();
98103 let user_removed = cx.update_jump_list(menus, entries);
99− self.remove_user_removed_workspaces(user_removed, cx);
100− }
101−
102− pub fn remove_user_removed_workspaces(
103− &mut self,
104− user_removed: Vec<SmallVec<[PathBuf; 2]>>,
105− cx: &App,
106− ) {
107− if user_removed.is_empty() {
108− return;
109− }
110− let mut deleted_ids = Vec::new();
111− for idx in (0..self.history.len()).rev() {
112− if let Some(entry) = self.history.get(idx)
113− && user_removed.contains(&entry.path)
114− {
115− deleted_ids.push(entry.id);
116− self.history.remove(idx);
104+ cx.spawn(async move |this, cx| {
105+ let user_removed = user_removed.await;
106+ if user_removed.is_empty() {
107+ return;
117108 }
118− }
119− cx.spawn(async move |_| {
120− for id in deleted_ids.iter() {
121− WORKSPACE_DB.delete_workspace_by_id(*id).await.log_err();
109+ let mut deleted_ids = Vec::new();
110+ if let Ok(()) = this.update(cx, |this, _| {
111+ for idx in (0..this.history.len()).rev() {
112+ if let Some(entry) = this.history.get(idx)
113+ && user_removed.contains(&entry.path)
114+ {
115+ deleted_ids.push(entry.id);
116+ this.history.remove(idx);
117+ }
118+ }
119+ }) {
120+ for id in deleted_ids.iter() {
121+ WORKSPACE_DB.delete_workspace_by_id(*id).await.log_err();
122+ }
122123 }
123124 })
124125 .detach();