tasks.rs

  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}