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;
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 for range in location
74 .buffer
75 .read(cx)
76 .snapshot()
77 .runnable_ranges(location.range.clone())
78 {
79 for (capture_name, value) in range.extra_captures {
80 variables.insert(VariableName::Custom(capture_name.into()), value);
81 }
82 }
83 variables
84 };
85 task_context_for_location(captured_variables, workspace, location.clone(), cx)
86}
87
88pub fn task_context(workspace: &Workspace, cx: &mut WindowContext<'_>) -> TaskContext {
89 let Some(editor) = workspace
90 .active_item(cx)
91 .and_then(|item| item.act_as::<Editor>(cx))
92 else {
93 return Default::default();
94 };
95 editor.update(cx, |editor, cx| {
96 task_context_with_editor(workspace, editor, cx).unwrap_or_default()
97 })
98}
99
100fn combine_task_variables(
101 mut captured_variables: TaskVariables,
102 location: Location,
103 project: Model<Project>,
104 cx: &mut WindowContext<'_>,
105) -> anyhow::Result<TaskVariables> {
106 let language_context_provider = location
107 .buffer
108 .read(cx)
109 .language()
110 .and_then(|language| language.context_provider());
111 let baseline = BasicContextProvider::new(project)
112 .build_context(&captured_variables, &location, cx)
113 .context("building basic default context")?;
114 captured_variables.extend(baseline);
115 if let Some(provider) = language_context_provider {
116 captured_variables.extend(
117 provider
118 .build_context(&captured_variables, &location, cx)
119 .context("building provider context ")?,
120 );
121 }
122 Ok(captured_variables)
123}