tasks.rs

  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}