1use crate::Editor;
2
3use std::{path::Path, sync::Arc};
4
5use anyhow::Context;
6use gpui::WindowContext;
7use language::{BasicContextProvider, ContextProvider};
8use project::{Location, WorktreeId};
9use task::{TaskContext, TaskVariables, VariableName};
10use util::ResultExt;
11use workspace::Workspace;
12
13pub(crate) fn task_context_for_location(
14 workspace: &Workspace,
15 location: Location,
16 cx: &mut WindowContext<'_>,
17) -> Option<TaskContext> {
18 let cwd = workspace::tasks::task_cwd(workspace, cx)
19 .log_err()
20 .flatten();
21
22 let buffer = location.buffer.clone();
23 let language_context_provider = buffer
24 .read(cx)
25 .language()
26 .and_then(|language| language.context_provider())
27 .unwrap_or_else(|| Arc::new(BasicContextProvider));
28
29 let worktree_abs_path = buffer
30 .read(cx)
31 .file()
32 .map(|file| WorktreeId::from_usize(file.worktree_id()))
33 .and_then(|worktree_id| {
34 workspace
35 .project()
36 .read(cx)
37 .worktree_for_id(worktree_id, cx)
38 .map(|worktree| worktree.read(cx).abs_path())
39 });
40 let task_variables = combine_task_variables(
41 worktree_abs_path.as_deref(),
42 location,
43 language_context_provider.as_ref(),
44 cx,
45 )
46 .log_err()?;
47 Some(TaskContext {
48 cwd,
49 task_variables,
50 })
51}
52
53pub(crate) fn task_context_with_editor(
54 workspace: &Workspace,
55 editor: &mut Editor,
56 cx: &mut WindowContext<'_>,
57) -> Option<TaskContext> {
58 let (selection, buffer, editor_snapshot) = {
59 let selection = editor.selections.newest::<usize>(cx);
60 let (buffer, _, _) = editor
61 .buffer()
62 .read(cx)
63 .point_to_buffer_offset(selection.start, cx)?;
64 let snapshot = editor.snapshot(cx);
65 Some((selection, buffer, snapshot))
66 }?;
67 let selection_range = selection.range();
68 let start = editor_snapshot
69 .display_snapshot
70 .buffer_snapshot
71 .anchor_after(selection_range.start)
72 .text_anchor;
73 let end = editor_snapshot
74 .display_snapshot
75 .buffer_snapshot
76 .anchor_after(selection_range.end)
77 .text_anchor;
78 let location = Location {
79 buffer,
80 range: start..end,
81 };
82 task_context_for_location(workspace, location.clone(), cx).map(|mut task_context| {
83 for range in location
84 .buffer
85 .read(cx)
86 .snapshot()
87 .runnable_ranges(location.range)
88 {
89 for (capture_name, value) in range.extra_captures {
90 task_context
91 .task_variables
92 .insert(VariableName::Custom(capture_name.into()), value);
93 }
94 }
95 task_context
96 })
97}
98
99pub fn task_context(workspace: &Workspace, cx: &mut WindowContext<'_>) -> TaskContext {
100 let Some(editor) = workspace
101 .active_item(cx)
102 .and_then(|item| item.act_as::<Editor>(cx))
103 else {
104 return Default::default();
105 };
106 editor.update(cx, |editor, cx| {
107 task_context_with_editor(workspace, editor, cx).unwrap_or_default()
108 })
109}
110
111fn combine_task_variables(
112 worktree_abs_path: Option<&Path>,
113 location: Location,
114 context_provider: &dyn ContextProvider,
115 cx: &mut WindowContext<'_>,
116) -> anyhow::Result<TaskVariables> {
117 if context_provider.is_basic() {
118 context_provider
119 .build_context(worktree_abs_path, &location, cx)
120 .context("building basic provider context")
121 } else {
122 let mut basic_context = BasicContextProvider
123 .build_context(worktree_abs_path, &location, cx)
124 .context("building basic default context")?;
125 basic_context.extend(
126 context_provider
127 .build_context(worktree_abs_path, &location, cx)
128 .context("building provider context ")?,
129 );
130 Ok(basic_context)
131 }
132}