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