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 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}