1use std::path::PathBuf;
2
3use gpui::{AppContext, ViewContext, WindowContext};
4use modal::RunnablesModal;
5pub use oneshot_source::OneshotSource;
6use runnable::Runnable;
7use util::ResultExt;
8use workspace::Workspace;
9
10mod modal;
11mod oneshot_source;
12
13pub fn init(cx: &mut AppContext) {
14 cx.observe_new_views(
15 |workspace: &mut Workspace, _: &mut ViewContext<Workspace>| {
16 workspace
17 .register_action(|workspace, _: &modal::Spawn, cx| {
18 let inventory = workspace.project().read(cx).runnable_inventory().clone();
19 let workspace_handle = workspace.weak_handle();
20 workspace.toggle_modal(cx, |cx| {
21 RunnablesModal::new(inventory, workspace_handle, cx)
22 })
23 })
24 .register_action(move |workspace, _: &modal::Rerun, cx| {
25 if let Some(runnable) = workspace.project().update(cx, |project, cx| {
26 project
27 .runnable_inventory()
28 .update(cx, |inventory, cx| inventory.last_scheduled_runnable(cx))
29 }) {
30 schedule_runnable(workspace, runnable.as_ref(), cx)
31 };
32 });
33 },
34 )
35 .detach();
36}
37
38fn schedule_runnable(
39 workspace: &Workspace,
40 runnable: &dyn Runnable,
41 cx: &mut ViewContext<'_, Workspace>,
42) {
43 let cwd = match runnable.cwd() {
44 Some(cwd) => Some(cwd.to_path_buf()),
45 None => runnable_cwd(workspace, cx).log_err().flatten(),
46 };
47 let spawn_in_terminal = runnable.exec(cwd);
48 if let Some(spawn_in_terminal) = spawn_in_terminal {
49 workspace.project().update(cx, |project, cx| {
50 project.runnable_inventory().update(cx, |inventory, _| {
51 inventory.last_scheduled_runnable = Some(runnable.id().clone());
52 })
53 });
54 cx.emit(workspace::Event::SpawnRunnable(spawn_in_terminal));
55 }
56}
57
58fn runnable_cwd(workspace: &Workspace, cx: &mut WindowContext) -> anyhow::Result<Option<PathBuf>> {
59 let project = workspace.project().read(cx);
60 let available_worktrees = project
61 .worktrees()
62 .filter(|worktree| {
63 let worktree = worktree.read(cx);
64 worktree.is_visible()
65 && worktree.is_local()
66 && worktree.root_entry().map_or(false, |e| e.is_dir())
67 })
68 .collect::<Vec<_>>();
69 let cwd = match available_worktrees.len() {
70 0 => None,
71 1 => Some(available_worktrees[0].read(cx).abs_path()),
72 _ => {
73 let cwd_for_active_entry = project.active_entry().and_then(|entry_id| {
74 available_worktrees.into_iter().find_map(|worktree| {
75 let worktree = worktree.read(cx);
76 if worktree.contains_entry(entry_id) {
77 Some(worktree.abs_path())
78 } else {
79 None
80 }
81 })
82 });
83 anyhow::ensure!(
84 cwd_for_active_entry.is_some(),
85 "Cannot determine runnable cwd for multiple worktrees"
86 );
87 cwd_for_active_entry
88 }
89 };
90 Ok(cwd.map(|path| path.to_path_buf()))
91}