1use crate::Editor;
2
3use anyhow::Context;
4use gpui::{Model, WindowContext};
5use language::ContextProvider;
6use project::{BasicContextProvider, Location, Project};
7use task::{TaskContext, TaskVariables, VariableName};
8use text::{Point, ToOffset, ToPoint};
9use util::ResultExt;
10use workspace::Workspace;
11
12pub(crate) fn task_context_for_location(
13 captured_variables: TaskVariables,
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 mut task_variables = combine_task_variables(
23 captured_variables,
24 location,
25 workspace.project().clone(),
26 cx,
27 )
28 .log_err()?;
29 // Remove all custom entries starting with _, as they're not intended for use by the end user.
30 task_variables.sweep();
31
32 Some(TaskContext {
33 cwd,
34 task_variables,
35 })
36}
37
38fn task_context_with_editor(
39 workspace: &Workspace,
40 editor: &mut Editor,
41 cx: &mut WindowContext<'_>,
42) -> Option<TaskContext> {
43 let (selection, buffer, editor_snapshot) = {
44 let mut selection = editor.selections.newest::<Point>(cx);
45 if editor.selections.line_mode {
46 selection.start = Point::new(selection.start.row, 0);
47 selection.end = Point::new(selection.end.row + 1, 0);
48 }
49 let (buffer, _, _) = editor
50 .buffer()
51 .read(cx)
52 .point_to_buffer_offset(selection.start, cx)?;
53 let snapshot = editor.snapshot(cx);
54 Some((selection, buffer, snapshot))
55 }?;
56 let selection_range = selection.range();
57 let start = editor_snapshot
58 .display_snapshot
59 .buffer_snapshot
60 .anchor_after(selection_range.start)
61 .text_anchor;
62 let end = editor_snapshot
63 .display_snapshot
64 .buffer_snapshot
65 .anchor_after(selection_range.end)
66 .text_anchor;
67 let location = Location {
68 buffer,
69 range: start..end,
70 };
71 let captured_variables = {
72 let mut variables = TaskVariables::default();
73 let buffer = location.buffer.read(cx);
74 let buffer_id = buffer.remote_id();
75 let snapshot = buffer.snapshot();
76 let starting_point = location.range.start.to_point(&snapshot);
77 let starting_offset = starting_point.to_offset(&snapshot);
78 for (_, tasks) in editor
79 .tasks
80 .range((buffer_id, 0)..(buffer_id, starting_point.row + 1))
81 {
82 if !tasks
83 .context_range
84 .contains(&crate::BufferOffset(starting_offset))
85 {
86 continue;
87 }
88 for (capture_name, value) in tasks.extra_variables.iter() {
89 variables.insert(
90 VariableName::Custom(capture_name.to_owned().into()),
91 value.clone(),
92 );
93 }
94 }
95 variables
96 };
97 task_context_for_location(captured_variables, workspace, location.clone(), cx)
98}
99
100pub fn task_context(workspace: &Workspace, cx: &mut WindowContext<'_>) -> TaskContext {
101 let Some(editor) = workspace
102 .active_item(cx)
103 .and_then(|item| item.act_as::<Editor>(cx))
104 else {
105 return Default::default();
106 };
107 editor.update(cx, |editor, cx| {
108 task_context_with_editor(workspace, editor, cx).unwrap_or_default()
109 })
110}
111
112fn combine_task_variables(
113 mut captured_variables: TaskVariables,
114 location: Location,
115 project: Model<Project>,
116 cx: &mut WindowContext<'_>,
117) -> anyhow::Result<TaskVariables> {
118 let language_context_provider = location
119 .buffer
120 .read(cx)
121 .language()
122 .and_then(|language| language.context_provider());
123 let baseline = BasicContextProvider::new(project)
124 .build_context(&captured_variables, &location, cx)
125 .context("building basic default context")?;
126 captured_variables.extend(baseline);
127 if let Some(provider) = language_context_provider {
128 captured_variables.extend(
129 provider
130 .build_context(&captured_variables, &location, cx)
131 .context("building provider context ")?,
132 );
133 }
134 Ok(captured_variables)
135}